Merge pull request #9497 from Pokechu22/better-fifo-analyzer
Graphics refactoring + add names and descriptions in FIFO analyzer
This commit is contained in:
commit
089250fde6
|
@ -32,6 +32,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstddef>
|
#include <cstddef>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <iterator>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
|
@ -99,6 +101,8 @@
|
||||||
* explicit cast must be performed on the BitField object to make sure it gets
|
* explicit cast must be performed on the BitField object to make sure it gets
|
||||||
* passed correctly, e.g.:
|
* passed correctly, e.g.:
|
||||||
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
* printf("Value: %d", (s32)some_register.some_signed_fields);
|
||||||
|
* Note that this does not apply when using fmt, as a formatter is provided that
|
||||||
|
* handles this conversion automatically.
|
||||||
*
|
*
|
||||||
* 2)
|
* 2)
|
||||||
* Not really a caveat, but potentially irritating: This class is used in some
|
* Not really a caveat, but potentially irritating: This class is used in some
|
||||||
|
@ -110,7 +114,13 @@
|
||||||
* symptoms.
|
* symptoms.
|
||||||
*/
|
*/
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
template <std::size_t position, std::size_t bits, typename T>
|
template <std::size_t position, std::size_t bits, typename T,
|
||||||
|
// 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.
|
||||||
|
typename StorageType = typename std::conditional_t<
|
||||||
|
std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
|
||||||
struct BitField
|
struct BitField
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -149,20 +159,13 @@ public:
|
||||||
constexpr std::size_t NumBits() const { return bits; }
|
constexpr std::size_t NumBits() const { return bits; }
|
||||||
|
|
||||||
private:
|
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.
|
|
||||||
using StorageType = typename std::conditional_t<std::is_enum<T>::value, std::underlying_type<T>,
|
|
||||||
std::enable_if<true, T>>::type;
|
|
||||||
|
|
||||||
// Unsigned version of StorageType
|
// Unsigned version of StorageType
|
||||||
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||||
|
|
||||||
constexpr T Value(std::true_type) const
|
constexpr T Value(std::true_type) const
|
||||||
{
|
{
|
||||||
using shift_amount = std::integral_constant<size_t, 8 * sizeof(T) - bits>;
|
const size_t shift_amount = 8 * sizeof(StorageType) - bits;
|
||||||
return static_cast<T>((storage << (shift_amount() - position)) >> shift_amount());
|
return static_cast<T>((storage << (shift_amount - position)) >> shift_amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr T Value(std::false_type) const
|
constexpr T Value(std::false_type) const
|
||||||
|
@ -172,16 +175,333 @@ private:
|
||||||
|
|
||||||
static constexpr StorageType GetMask()
|
static constexpr StorageType GetMask()
|
||||||
{
|
{
|
||||||
return (std::numeric_limits<StorageTypeU>::max() >> (8 * sizeof(T) - bits)) << position;
|
return (std::numeric_limits<StorageTypeU>::max() >> (8 * sizeof(StorageType) - bits))
|
||||||
|
<< position;
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageType storage;
|
StorageType storage;
|
||||||
|
|
||||||
static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range");
|
static_assert(bits + position <= 8 * sizeof(StorageType), "Bitfield out of range");
|
||||||
|
static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType");
|
||||||
|
|
||||||
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||||
static_assert(position < 8 * sizeof(T), "Invalid position");
|
static_assert(position < 8 * sizeof(StorageType), "Invalid position");
|
||||||
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||||
static_assert(bits > 0, "Invalid number of bits");
|
static_assert(bits > 0, "Invalid number of bits");
|
||||||
};
|
};
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
|
// Use the underlying type's formatter for BitFields, if one exists
|
||||||
|
template <std::size_t position, std::size_t bits, typename T, typename S>
|
||||||
|
struct fmt::formatter<BitField<position, bits, T, S>>
|
||||||
|
{
|
||||||
|
fmt::formatter<T> m_formatter;
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const BitField<position, bits, T, S>& bitfield, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return m_formatter.format(bitfield.Value(), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Language limitations require the following to make these formattable
|
||||||
|
// (formatter<BitFieldArray<position, bits, size, T>::Ref> is not legal)
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayConstRef;
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayRef;
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayConstIterator;
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayIterator;
|
||||||
|
|
||||||
|
#pragma pack(1)
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T,
|
||||||
|
// 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.
|
||||||
|
typename StorageType = typename std::conditional_t<
|
||||||
|
std::is_enum<T>::value, std::underlying_type<T>, std::enable_if<true, T>>::type>
|
||||||
|
struct BitFieldArray
|
||||||
|
{
|
||||||
|
using Ref = BitFieldArrayRef<position, bits, size, T, StorageType>;
|
||||||
|
using ConstRef = BitFieldArrayConstRef<position, bits, size, T, StorageType>;
|
||||||
|
using Iterator = BitFieldArrayIterator<position, bits, size, T, StorageType>;
|
||||||
|
using ConstIterator = BitFieldArrayConstIterator<position, bits, size, T, StorageType>;
|
||||||
|
|
||||||
|
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!
|
||||||
|
BitFieldArray(T val) = delete;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Force default constructor to be created
|
||||||
|
// so that we can use this within unions
|
||||||
|
constexpr BitFieldArray() = default;
|
||||||
|
|
||||||
|
// Visual Studio (as of VS2017) considers BitField to not be trivially
|
||||||
|
// copyable if we delete this copy assignment operator.
|
||||||
|
// https://developercommunity.visualstudio.com/content/problem/101208/c-compiler-is-overly-strict-regarding-whether-a-cl.html
|
||||||
|
#ifndef _MSC_VER
|
||||||
|
// We explicitly delete the copy assignment operator here, because the
|
||||||
|
// default copy assignment would copy the full storage value, rather than
|
||||||
|
// just the bits relevant to this particular bit field.
|
||||||
|
// Ideally, we would just implement the copy assignment to copy only the
|
||||||
|
// relevant bits, but we're prevented from doing that because the savestate
|
||||||
|
// code expects that this class is trivially copyable.
|
||||||
|
BitFieldArray& operator=(const BitFieldArray&) = delete;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr std::size_t StartBit() const { return position; }
|
||||||
|
constexpr std::size_t NumBits() const { return bits; }
|
||||||
|
constexpr std::size_t Size() const { return size; }
|
||||||
|
constexpr std::size_t TotalNumBits() const { return bits * size; }
|
||||||
|
|
||||||
|
constexpr T Value(size_t index) const { return Value(std::is_signed<T>(), index); }
|
||||||
|
void SetValue(size_t index, T value)
|
||||||
|
{
|
||||||
|
const size_t pos = position + bits * index;
|
||||||
|
storage = (storage & ~GetElementMask(index)) |
|
||||||
|
((static_cast<StorageType>(value) << pos) & GetElementMask(index));
|
||||||
|
}
|
||||||
|
Ref operator[](size_t index) { return Ref(this, index); }
|
||||||
|
constexpr const ConstRef operator[](size_t index) const { return ConstRef(this, index); }
|
||||||
|
|
||||||
|
constexpr Iterator begin() { return Iterator(this, 0); }
|
||||||
|
constexpr Iterator end() { return Iterator(this, size); }
|
||||||
|
constexpr ConstIterator begin() const { return ConstIterator(this, 0); }
|
||||||
|
constexpr ConstIterator end() const { return ConstIterator(this, size); }
|
||||||
|
constexpr ConstIterator cbegin() const { return begin(); }
|
||||||
|
constexpr ConstIterator cend() const { return end(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Unsigned version of StorageType
|
||||||
|
using StorageTypeU = std::make_unsigned_t<StorageType>;
|
||||||
|
|
||||||
|
constexpr T Value(std::true_type, size_t index) const
|
||||||
|
{
|
||||||
|
const size_t pos = position + bits * index;
|
||||||
|
const size_t shift_amount = 8 * sizeof(StorageType) - bits;
|
||||||
|
return static_cast<T>((storage << (shift_amount - pos)) >> shift_amount);
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr T Value(std::false_type, size_t index) const
|
||||||
|
{
|
||||||
|
const size_t pos = position + bits * index;
|
||||||
|
return static_cast<T>((storage & GetElementMask(index)) >> pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr StorageType GetElementMask(size_t index)
|
||||||
|
{
|
||||||
|
const size_t pos = position + bits * index;
|
||||||
|
return (std::numeric_limits<StorageTypeU>::max() >> (8 * sizeof(StorageType) - bits)) << pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
StorageType storage;
|
||||||
|
|
||||||
|
static_assert(bits * size + position <= 8 * sizeof(StorageType), "Bitfield array out of range");
|
||||||
|
static_assert(sizeof(T) <= sizeof(StorageType), "T must fit in StorageType");
|
||||||
|
|
||||||
|
// And, you know, just in case people specify something stupid like bits=position=0x80000000
|
||||||
|
static_assert(position < 8 * sizeof(StorageType), "Invalid position");
|
||||||
|
static_assert(bits <= 8 * sizeof(T), "Invalid number of bits");
|
||||||
|
static_assert(bits > 0, "Invalid number of bits");
|
||||||
|
static_assert(size <= 8 * sizeof(StorageType), "Invalid size");
|
||||||
|
static_assert(size > 0, "Invalid size");
|
||||||
|
};
|
||||||
|
#pragma pack()
|
||||||
|
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayConstRef
|
||||||
|
{
|
||||||
|
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||||
|
friend class BitFieldArrayConstIterator<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr T Value() const { return m_array->Value(m_index); };
|
||||||
|
constexpr operator T() const { return Value(); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr BitFieldArrayConstRef(const BitFieldArray<position, bits, size, T, S>* array,
|
||||||
|
size_t index)
|
||||||
|
: m_array(array), m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const BitFieldArray<position, bits, size, T, S>* const m_array;
|
||||||
|
const size_t m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayRef
|
||||||
|
{
|
||||||
|
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||||
|
friend class BitFieldArrayIterator<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
constexpr T Value() const { return m_array->Value(m_index); };
|
||||||
|
constexpr operator T() const { return Value(); }
|
||||||
|
T operator=(const BitFieldArrayRef<position, bits, size, T, S>& value) const
|
||||||
|
{
|
||||||
|
m_array->SetValue(m_index, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
T operator=(T value) const
|
||||||
|
{
|
||||||
|
m_array->SetValue(m_index, value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr BitFieldArrayRef(BitFieldArray<position, bits, size, T, S>* array, size_t index)
|
||||||
|
: m_array(array), m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
BitFieldArray<position, bits, size, T, S>* const m_array;
|
||||||
|
const size_t m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Satisfies LegacyOutputIterator / std::output_iterator.
|
||||||
|
// Does not satisfy LegacyInputIterator / std::input_iterator as std::output_iterator_tag does not
|
||||||
|
// extend std::input_iterator_tag.
|
||||||
|
// Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real
|
||||||
|
// references instead of proxy objects.
|
||||||
|
// This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join.
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayIterator
|
||||||
|
{
|
||||||
|
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::output_iterator_tag;
|
||||||
|
using value_type = T;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = BitFieldArrayRef<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr BitFieldArrayIterator(BitFieldArray<position, bits, size, T, S>* array, size_t index)
|
||||||
|
: m_array(array), m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Required by std::input_or_output_iterator
|
||||||
|
constexpr BitFieldArrayIterator() = default;
|
||||||
|
// Required by LegacyIterator
|
||||||
|
constexpr BitFieldArrayIterator(const BitFieldArrayIterator& other) = default;
|
||||||
|
// Required by LegacyIterator
|
||||||
|
BitFieldArrayIterator& operator=(const BitFieldArrayIterator& other) = default;
|
||||||
|
// Move constructor and assignment operators, explicitly defined for completeness
|
||||||
|
constexpr BitFieldArrayIterator(BitFieldArrayIterator&& other) = default;
|
||||||
|
BitFieldArrayIterator& operator=(BitFieldArrayIterator&& other) = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BitFieldArrayIterator& operator++()
|
||||||
|
{
|
||||||
|
m_index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
BitFieldArrayIterator operator++(int)
|
||||||
|
{
|
||||||
|
BitFieldArrayIterator other(*this);
|
||||||
|
++*this;
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
constexpr reference operator*() const { return reference(m_array, m_index); }
|
||||||
|
constexpr bool operator==(BitFieldArrayIterator other) const { return m_index == other.m_index; }
|
||||||
|
constexpr bool operator!=(BitFieldArrayIterator other) const { return m_index != other.m_index; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
BitFieldArray<position, bits, size, T, S>* m_array;
|
||||||
|
size_t m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Satisfies LegacyInputIterator / std::input_iterator.
|
||||||
|
// Does not satisfy LegacyForwardIterator / std::forward_iterator, as that requires use of real
|
||||||
|
// references instead of proxy objects.
|
||||||
|
// This iterator allows use of BitFieldArray in range-based for loops, and with fmt::join.
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
class BitFieldArrayConstIterator
|
||||||
|
{
|
||||||
|
friend struct BitFieldArray<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
using iterator_category = std::input_iterator_tag;
|
||||||
|
using value_type = T;
|
||||||
|
using difference_type = ptrdiff_t;
|
||||||
|
using pointer = void;
|
||||||
|
using reference = BitFieldArrayConstRef<position, bits, size, T, S>;
|
||||||
|
|
||||||
|
private:
|
||||||
|
constexpr BitFieldArrayConstIterator(const BitFieldArray<position, bits, size, T, S>* array,
|
||||||
|
size_t index)
|
||||||
|
: m_array(array), m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
// Required by std::input_or_output_iterator
|
||||||
|
constexpr BitFieldArrayConstIterator() = default;
|
||||||
|
// Required by LegacyIterator
|
||||||
|
constexpr BitFieldArrayConstIterator(const BitFieldArrayConstIterator& other) = default;
|
||||||
|
// Required by LegacyIterator
|
||||||
|
BitFieldArrayConstIterator& operator=(const BitFieldArrayConstIterator& other) = default;
|
||||||
|
// Move constructor and assignment operators, explicitly defined for completeness
|
||||||
|
constexpr BitFieldArrayConstIterator(BitFieldArrayConstIterator&& other) = default;
|
||||||
|
BitFieldArrayConstIterator& operator=(BitFieldArrayConstIterator&& other) = default;
|
||||||
|
|
||||||
|
public:
|
||||||
|
BitFieldArrayConstIterator& operator++()
|
||||||
|
{
|
||||||
|
m_index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
BitFieldArrayConstIterator operator++(int)
|
||||||
|
{
|
||||||
|
BitFieldArrayConstIterator other(*this);
|
||||||
|
++*this;
|
||||||
|
return other;
|
||||||
|
}
|
||||||
|
constexpr reference operator*() const { return reference(m_array, m_index); }
|
||||||
|
constexpr bool operator==(BitFieldArrayConstIterator other) const
|
||||||
|
{
|
||||||
|
return m_index == other.m_index;
|
||||||
|
}
|
||||||
|
constexpr bool operator!=(BitFieldArrayConstIterator other) const
|
||||||
|
{
|
||||||
|
return m_index != other.m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const BitFieldArray<position, bits, size, T, S>* m_array;
|
||||||
|
size_t m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
struct fmt::formatter<BitFieldArrayRef<position, bits, size, T, S>>
|
||||||
|
{
|
||||||
|
fmt::formatter<T> m_formatter;
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const BitFieldArrayRef<position, bits, size, T, S>& ref, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return m_formatter.format(ref.Value(), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <std::size_t position, std::size_t bits, std::size_t size, typename T, typename S>
|
||||||
|
struct fmt::formatter<BitFieldArrayConstRef<position, bits, size, T, S>>
|
||||||
|
{
|
||||||
|
fmt::formatter<T> m_formatter;
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return m_formatter.parse(ctx); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const BitFieldArrayConstRef<position, bits, size, T, S>& ref, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return m_formatter.format(ref.Value(), ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
|
@ -42,6 +42,7 @@ add_library(common
|
||||||
DynamicLibrary.h
|
DynamicLibrary.h
|
||||||
ENetUtil.cpp
|
ENetUtil.cpp
|
||||||
ENetUtil.h
|
ENetUtil.h
|
||||||
|
EnumFormatter.h
|
||||||
Event.h
|
Event.h
|
||||||
FileSearch.cpp
|
FileSearch.cpp
|
||||||
FileSearch.h
|
FileSearch.h
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <fmt/format.h>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Helper for using enums with fmt.
|
||||||
|
*
|
||||||
|
* Usage example:
|
||||||
|
*
|
||||||
|
* enum class Foo
|
||||||
|
* {
|
||||||
|
* A = 0,
|
||||||
|
* B = 1,
|
||||||
|
* C = 2,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* template <>
|
||||||
|
* struct fmt::formatter<Foo> : EnumFormatter<Foo::C>
|
||||||
|
* {
|
||||||
|
* formatter() : EnumFormatter({"A", "B", "C"}) {}
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* enum class Bar
|
||||||
|
* {
|
||||||
|
* D = 0,
|
||||||
|
* E = 1,
|
||||||
|
* F = 3,
|
||||||
|
* };
|
||||||
|
*
|
||||||
|
* template <>
|
||||||
|
* struct fmt::formatter<Bar> : EnumFormatter<Bar::F>
|
||||||
|
* {
|
||||||
|
* // using std::array here fails due to nullptr not being const char*, at least in MSVC
|
||||||
|
* // (but only when a field is used; directly in the constructor is OK)
|
||||||
|
* static constexpr array_type names = {"D", "E", nullptr, "F"};
|
||||||
|
* formatter() : EnumFormatter(names) {}
|
||||||
|
* };
|
||||||
|
*/
|
||||||
|
template <auto last_member, typename T = decltype(last_member),
|
||||||
|
size_t size = static_cast<size_t>(last_member) + 1,
|
||||||
|
std::enable_if_t<std::is_enum_v<T>, bool> = true>
|
||||||
|
class EnumFormatter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr auto parse(fmt::format_parse_context& ctx)
|
||||||
|
{
|
||||||
|
auto it = ctx.begin(), end = ctx.end();
|
||||||
|
// 'u' for user display, 's' for shader generation
|
||||||
|
if (it != end && (*it == 'u' || *it == 's'))
|
||||||
|
formatting_for_shader = (*it++ == 's');
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const T& e, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
const auto value = static_cast<std::underlying_type_t<T>>(e);
|
||||||
|
|
||||||
|
if (!formatting_for_shader)
|
||||||
|
{
|
||||||
|
if (value >= 0 && value < size && m_names[value] != nullptr)
|
||||||
|
return fmt::format_to(ctx.out(), "{} ({})", m_names[value], value);
|
||||||
|
else
|
||||||
|
return fmt::format_to(ctx.out(), "Invalid ({})", value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value >= 0 && value < size && m_names[value] != nullptr)
|
||||||
|
return fmt::format_to(ctx.out(), "{:#x}u /* {} */", value, m_names[value]);
|
||||||
|
else
|
||||||
|
return fmt::format_to(ctx.out(), "{:#x}u /* Invalid */",
|
||||||
|
static_cast<std::make_unsigned_t<T>>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// This is needed because std::array deduces incorrectly if nullptr is included in the list
|
||||||
|
using array_type = std::array<const char*, size>;
|
||||||
|
|
||||||
|
constexpr explicit EnumFormatter(const array_type names) : m_names(std::move(names)) {}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const array_type m_names;
|
||||||
|
bool formatting_for_shader = false;
|
||||||
|
};
|
|
@ -49,21 +49,17 @@ std::array<int, 21> CalculateVertexElementSizes(int vatIndex, const CPMemory& cp
|
||||||
const VAT& vtxAttr = cpMem.vtxAttr[vatIndex];
|
const VAT& vtxAttr = cpMem.vtxAttr[vatIndex];
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
const std::array<u64, 2> colDesc{
|
const std::array<ColorFormat, 2> colComp{
|
||||||
vtxDesc.Color0,
|
|
||||||
vtxDesc.Color1,
|
|
||||||
};
|
|
||||||
const std::array<u32, 2> colComp{
|
|
||||||
vtxAttr.g0.Color0Comp,
|
vtxAttr.g0.Color0Comp,
|
||||||
vtxAttr.g0.Color1Comp,
|
vtxAttr.g0.Color1Comp,
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array<u32, 8> tcElements{
|
const std::array<TexComponentCount, 8> tcElements{
|
||||||
vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements, vtxAttr.g1.Tex2CoordElements,
|
vtxAttr.g0.Tex0CoordElements, vtxAttr.g1.Tex1CoordElements, vtxAttr.g1.Tex2CoordElements,
|
||||||
vtxAttr.g1.Tex3CoordElements, vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
|
vtxAttr.g1.Tex3CoordElements, vtxAttr.g1.Tex4CoordElements, vtxAttr.g2.Tex5CoordElements,
|
||||||
vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements,
|
vtxAttr.g2.Tex6CoordElements, vtxAttr.g2.Tex7CoordElements,
|
||||||
};
|
};
|
||||||
const std::array<u32, 8> tcFormat{
|
const std::array<ComponentFormat, 8> tcFormat{
|
||||||
vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat, vtxAttr.g1.Tex2CoordFormat,
|
vtxAttr.g0.Tex0CoordFormat, vtxAttr.g1.Tex1CoordFormat, vtxAttr.g1.Tex2CoordFormat,
|
||||||
vtxAttr.g1.Tex3CoordFormat, vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
|
vtxAttr.g1.Tex3CoordFormat, vtxAttr.g1.Tex4CoordFormat, vtxAttr.g2.Tex5CoordFormat,
|
||||||
vtxAttr.g2.Tex6CoordFormat, vtxAttr.g2.Tex7CoordFormat,
|
vtxAttr.g2.Tex6CoordFormat, vtxAttr.g2.Tex7CoordFormat,
|
||||||
|
@ -72,21 +68,20 @@ std::array<int, 21> CalculateVertexElementSizes(int vatIndex, const CPMemory& cp
|
||||||
std::array<int, 21> sizes{};
|
std::array<int, 21> sizes{};
|
||||||
|
|
||||||
// Add position and texture matrix indices
|
// Add position and texture matrix indices
|
||||||
u64 vtxDescHex = cpMem.vtxDesc.Hex;
|
sizes[0] = vtxDesc.low.PosMatIdx;
|
||||||
for (int i = 0; i < 9; ++i)
|
for (size_t i = 0; i < vtxDesc.low.TexMatIdx.Size(); ++i)
|
||||||
{
|
{
|
||||||
sizes[i] = vtxDescHex & 1;
|
sizes[i + 1] = vtxDesc.low.TexMatIdx[i];
|
||||||
vtxDescHex >>= 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
sizes[9] = VertexLoader_Position::GetSize(vtxDesc.Position, vtxAttr.g0.PosFormat,
|
sizes[9] = VertexLoader_Position::GetSize(vtxDesc.low.Position, vtxAttr.g0.PosFormat,
|
||||||
vtxAttr.g0.PosElements);
|
vtxAttr.g0.PosElements);
|
||||||
|
|
||||||
// Normals
|
// Normals
|
||||||
if (vtxDesc.Normal != NOT_PRESENT)
|
if (vtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
sizes[10] = VertexLoader_Normal::GetSize(vtxDesc.Normal, vtxAttr.g0.NormalFormat,
|
sizes[10] = VertexLoader_Normal::GetSize(vtxDesc.low.Normal, vtxAttr.g0.NormalFormat,
|
||||||
vtxAttr.g0.NormalElements, vtxAttr.g0.NormalIndex3);
|
vtxAttr.g0.NormalElements, vtxAttr.g0.NormalIndex3);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -95,33 +90,33 @@ std::array<int, 21> CalculateVertexElementSizes(int vatIndex, const CPMemory& cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
for (size_t i = 0; i < colDesc.size(); i++)
|
for (size_t i = 0; i < vtxDesc.low.Color.Size(); i++)
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
|
||||||
switch (colDesc[i])
|
switch (vtxDesc.low.Color[i])
|
||||||
{
|
{
|
||||||
case NOT_PRESENT:
|
case VertexComponentFormat::NotPresent:
|
||||||
break;
|
break;
|
||||||
case DIRECT:
|
case VertexComponentFormat::Direct:
|
||||||
switch (colComp[i])
|
switch (colComp[i])
|
||||||
{
|
{
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
size = 2;
|
size = 2;
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
size = 3;
|
size = 3;
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
size = 4;
|
size = 4;
|
||||||
break;
|
break;
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
size = 2;
|
size = 2;
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
size = 3;
|
size = 3;
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
size = 4;
|
size = 4;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -129,10 +124,10 @@ std::array<int, 21> CalculateVertexElementSizes(int vatIndex, const CPMemory& cp
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INDEX8:
|
case VertexComponentFormat::Index8:
|
||||||
size = 1;
|
size = 1;
|
||||||
break;
|
break;
|
||||||
case INDEX16:
|
case VertexComponentFormat::Index16:
|
||||||
size = 2;
|
size = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -141,11 +136,10 @@ std::array<int, 21> CalculateVertexElementSizes(int vatIndex, const CPMemory& cp
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texture coordinates
|
// Texture coordinates
|
||||||
vtxDescHex = vtxDesc.Hex >> 17;
|
|
||||||
for (size_t i = 0; i < tcFormat.size(); i++)
|
for (size_t i = 0; i < tcFormat.size(); i++)
|
||||||
{
|
{
|
||||||
sizes[13 + i] = VertexLoader_TextCoord::GetSize(vtxDescHex & 3, tcFormat[i], tcElements[i]);
|
sizes[13 + i] =
|
||||||
vtxDescHex >>= 2;
|
VertexLoader_TextCoord::GetSize(vtxDesc.high.TexCoord[i], tcFormat[i], tcElements[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return sizes;
|
return sizes;
|
||||||
|
@ -267,13 +261,11 @@ void LoadCPReg(u32 subCmd, u32 value, CPMemory& cpMem)
|
||||||
switch (subCmd & 0xF0)
|
switch (subCmd & 0xF0)
|
||||||
{
|
{
|
||||||
case 0x50:
|
case 0x50:
|
||||||
cpMem.vtxDesc.Hex &= ~0x1FFFF; // keep the Upper bits
|
cpMem.vtxDesc.low.Hex = value;
|
||||||
cpMem.vtxDesc.Hex |= value;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x60:
|
case 0x60:
|
||||||
cpMem.vtxDesc.Hex &= 0x1FFFF; // keep the lower 17Bits
|
cpMem.vtxDesc.high.Hex = value;
|
||||||
cpMem.vtxDesc.Hex |= (u64)value << 17;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x70:
|
case 0x70:
|
||||||
|
|
|
@ -467,22 +467,22 @@ void FifoPlayer::LoadRegisters()
|
||||||
}
|
}
|
||||||
|
|
||||||
regs = m_File->GetCPMem();
|
regs = m_File->GetCPMem();
|
||||||
LoadCPReg(0x30, regs[0x30]);
|
LoadCPReg(MATINDEX_A, regs[MATINDEX_A]);
|
||||||
LoadCPReg(0x40, regs[0x40]);
|
LoadCPReg(MATINDEX_B, regs[MATINDEX_B]);
|
||||||
LoadCPReg(0x50, regs[0x50]);
|
LoadCPReg(VCD_LO, regs[VCD_LO]);
|
||||||
LoadCPReg(0x60, regs[0x60]);
|
LoadCPReg(VCD_HI, regs[VCD_HI]);
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < CP_NUM_VAT_REG; ++i)
|
||||||
{
|
{
|
||||||
LoadCPReg(0x70 + i, regs[0x70 + i]);
|
LoadCPReg(CP_VAT_REG_A + i, regs[CP_VAT_REG_A + i]);
|
||||||
LoadCPReg(0x80 + i, regs[0x80 + i]);
|
LoadCPReg(CP_VAT_REG_B + i, regs[CP_VAT_REG_B + i]);
|
||||||
LoadCPReg(0x90 + i, regs[0x90 + i]);
|
LoadCPReg(CP_VAT_REG_C + i, regs[CP_VAT_REG_C + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < CP_NUM_ARRAYS; ++i)
|
||||||
{
|
{
|
||||||
LoadCPReg(0xa0 + i, regs[0xa0 + i]);
|
LoadCPReg(ARRAY_BASE + i, regs[ARRAY_BASE + i]);
|
||||||
LoadCPReg(0xb0 + i, regs[0xb0 + i]);
|
LoadCPReg(ARRAY_STRIDE + i, regs[ARRAY_STRIDE + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
regs = m_File->GetXFMem();
|
regs = m_File->GetXFMem();
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
#include "Core/FifoPlayer/FifoAnalyzer.h"
|
#include "Core/FifoPlayer/FifoAnalyzer.h"
|
||||||
#include "Core/FifoPlayer/FifoRecorder.h"
|
#include "Core/FifoPlayer/FifoRecorder.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
@ -44,14 +45,28 @@ void FifoRecordAnalyzer::WriteVertexArray(int arrayIndex, const u8* vertexData,
|
||||||
int numVertices)
|
int numVertices)
|
||||||
{
|
{
|
||||||
// Skip if not indexed array
|
// Skip if not indexed array
|
||||||
int arrayType = (s_CpMem.vtxDesc.Hex >> (9 + (arrayIndex * 2))) & 3;
|
VertexComponentFormat arrayType;
|
||||||
if (arrayType < 2)
|
if (arrayIndex == ARRAY_POSITION)
|
||||||
|
arrayType = s_CpMem.vtxDesc.low.Position;
|
||||||
|
else if (arrayIndex == ARRAY_NORMAL)
|
||||||
|
arrayType = s_CpMem.vtxDesc.low.Normal;
|
||||||
|
else if (arrayIndex == ARRAY_COLOR || arrayIndex == ARRAY_COLOR2)
|
||||||
|
arrayType = s_CpMem.vtxDesc.low.Color[arrayIndex - ARRAY_COLOR];
|
||||||
|
else if (arrayIndex >= ARRAY_POSITION && arrayIndex < ARRAY_POSITION + 8)
|
||||||
|
arrayType = s_CpMem.vtxDesc.high.TexCoord[arrayIndex - ARRAY_POSITION];
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PanicAlertFmt("Invalid arrayIndex {}", arrayIndex);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsIndexed(arrayType))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int maxIndex = 0;
|
int maxIndex = 0;
|
||||||
|
|
||||||
// Determine min and max indices
|
// Determine min and max indices
|
||||||
if (arrayType == INDEX8)
|
if (arrayType == VertexComponentFormat::Index8)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < numVertices; ++i)
|
for (int i = 0; i < numVertices; ++i)
|
||||||
{
|
{
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
<ClInclude Include="Common\DebugInterface.h" />
|
<ClInclude Include="Common\DebugInterface.h" />
|
||||||
<ClInclude Include="Common\DynamicLibrary.h" />
|
<ClInclude Include="Common\DynamicLibrary.h" />
|
||||||
<ClInclude Include="Common\ENetUtil.h" />
|
<ClInclude Include="Common\ENetUtil.h" />
|
||||||
|
<ClInclude Include="Common\EnumFormatter.h" />
|
||||||
<ClInclude Include="Common\Event.h" />
|
<ClInclude Include="Common\Event.h" />
|
||||||
<ClInclude Include="Common\FileSearch.h" />
|
<ClInclude Include="Common\FileSearch.h" />
|
||||||
<ClInclude Include="Common\FileUtil.h" />
|
<ClInclude Include="Common\FileUtil.h" />
|
||||||
|
|
|
@ -4,6 +4,12 @@
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
// X.h defines None to be 0L, but other parts of Dolphin undef that so that
|
||||||
|
// None can be used in enums. Work around that here by copying the definition
|
||||||
|
// before it is undefined.
|
||||||
|
#include <X11/X.h>
|
||||||
|
static constexpr auto X_None = None;
|
||||||
|
|
||||||
#include "DolphinNoGUI/Platform.h"
|
#include "DolphinNoGUI/Platform.h"
|
||||||
|
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
@ -47,7 +53,7 @@ private:
|
||||||
|
|
||||||
Display* m_display = nullptr;
|
Display* m_display = nullptr;
|
||||||
Window m_window = {};
|
Window m_window = {};
|
||||||
Cursor m_blank_cursor = None;
|
Cursor m_blank_cursor = X_None;
|
||||||
#ifdef HAVE_XRANDR
|
#ifdef HAVE_XRANDR
|
||||||
X11Utils::XRRConfiguration* m_xrr_config = nullptr;
|
X11Utils::XRRConfiguration* m_xrr_config = nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -23,7 +23,9 @@
|
||||||
#include "DolphinQt/Settings.h"
|
#include "DolphinQt/Settings.h"
|
||||||
|
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
|
#include "VideoCommon/CPMemory.h"
|
||||||
#include "VideoCommon/OpcodeDecoding.h"
|
#include "VideoCommon/OpcodeDecoding.h"
|
||||||
|
#include "VideoCommon/XFStructs.h"
|
||||||
|
|
||||||
constexpr int FRAME_ROLE = Qt::UserRole;
|
constexpr int FRAME_ROLE = Qt::UserRole;
|
||||||
constexpr int OBJECT_ROLE = Qt::UserRole + 1;
|
constexpr int OBJECT_ROLE = Qt::UserRole + 1;
|
||||||
|
@ -224,17 +226,22 @@ void FIFOAnalyzer::UpdateDetails()
|
||||||
u32 cmd2 = *objectdata++;
|
u32 cmd2 = *objectdata++;
|
||||||
u32 value = Common::swap32(objectdata);
|
u32 value = Common::swap32(objectdata);
|
||||||
objectdata += 4;
|
objectdata += 4;
|
||||||
|
const auto [name, desc] = GetCPRegInfo(cmd2, value);
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
new_label = QStringLiteral("CP %1 %2")
|
new_label = QStringLiteral("CP %1 %2 %3")
|
||||||
.arg(cmd2, 2, 16, QLatin1Char('0'))
|
.arg(cmd2, 2, 16, QLatin1Char('0'))
|
||||||
.arg(value, 8, 16, QLatin1Char('0'));
|
.arg(value, 8, 16, QLatin1Char('0'))
|
||||||
|
.arg(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OpcodeDecoder::GX_LOAD_XF_REG:
|
case OpcodeDecoder::GX_LOAD_XF_REG:
|
||||||
{
|
{
|
||||||
|
const auto [name, desc] = GetXFTransferInfo(objectdata);
|
||||||
u32 cmd2 = Common::swap32(objectdata);
|
u32 cmd2 = Common::swap32(objectdata);
|
||||||
objectdata += 4;
|
objectdata += 4;
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
u8 streamSize = ((cmd2 >> 16) & 15) + 1;
|
u8 streamSize = ((cmd2 >> 16) & 15) + 1;
|
||||||
|
|
||||||
|
@ -249,6 +256,8 @@ void FIFOAnalyzer::UpdateDetails()
|
||||||
if (((objectdata - stream_start) % 4) == 0)
|
if (((objectdata - stream_start) % 4) == 0)
|
||||||
new_label += QLatin1Char(' ');
|
new_label += QLatin1Char(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_label += QStringLiteral(" ") + QString::fromStdString(name);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -278,11 +287,17 @@ void FIFOAnalyzer::UpdateDetails()
|
||||||
|
|
||||||
case OpcodeDecoder::GX_LOAD_BP_REG:
|
case OpcodeDecoder::GX_LOAD_BP_REG:
|
||||||
{
|
{
|
||||||
u32 cmd2 = Common::swap32(objectdata);
|
const u8 cmd2 = *objectdata++;
|
||||||
objectdata += 4;
|
const u32 cmddata = Common::swap24(objectdata);
|
||||||
new_label = QStringLiteral("BP %1 %2")
|
objectdata += 3;
|
||||||
.arg(cmd2 >> 24, 2, 16, QLatin1Char('0'))
|
|
||||||
.arg(cmd2 & 0xFFFFFF, 6, 16, QLatin1Char('0'));
|
const auto [name, desc] = GetBPRegInfo(cmd2, cmddata);
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
|
new_label = QStringLiteral("BP %1 %2 %3")
|
||||||
|
.arg(cmd2, 2, 16, QLatin1Char('0'))
|
||||||
|
.arg(cmddata, 6, 16, QLatin1Char('0'))
|
||||||
|
.arg(QString::fromStdString(name));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -467,14 +482,14 @@ void FIFOAnalyzer::UpdateDescription()
|
||||||
QString text;
|
QString text;
|
||||||
if (*cmddata == OpcodeDecoder::GX_LOAD_BP_REG)
|
if (*cmddata == OpcodeDecoder::GX_LOAD_BP_REG)
|
||||||
{
|
{
|
||||||
std::string name;
|
const u8 cmd = *(cmddata + 1);
|
||||||
std::string desc;
|
const u32 value = Common::swap24(cmddata + 2);
|
||||||
GetBPRegInfo(cmddata + 1, &name, &desc);
|
|
||||||
|
const auto [name, desc] = GetBPRegInfo(cmd, value);
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
text = tr("BP register ");
|
text = tr("BP register ");
|
||||||
text += name.empty() ?
|
text += QString::fromStdString(name);
|
||||||
QStringLiteral("UNKNOWN_%1").arg(*(cmddata + 1), 2, 16, QLatin1Char('0')) :
|
|
||||||
QString::fromStdString(name);
|
|
||||||
text += QLatin1Char{'\n'};
|
text += QLatin1Char{'\n'};
|
||||||
|
|
||||||
if (desc.empty())
|
if (desc.empty())
|
||||||
|
@ -484,11 +499,34 @@ void FIFOAnalyzer::UpdateDescription()
|
||||||
}
|
}
|
||||||
else if (*cmddata == OpcodeDecoder::GX_LOAD_CP_REG)
|
else if (*cmddata == OpcodeDecoder::GX_LOAD_CP_REG)
|
||||||
{
|
{
|
||||||
|
const u8 cmd = *(cmddata + 1);
|
||||||
|
const u32 value = Common::swap32(cmddata + 2);
|
||||||
|
|
||||||
|
const auto [name, desc] = GetCPRegInfo(cmd, value);
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
text = tr("CP register ");
|
text = tr("CP register ");
|
||||||
|
text += QString::fromStdString(name);
|
||||||
|
text += QLatin1Char{'\n'};
|
||||||
|
|
||||||
|
if (desc.empty())
|
||||||
|
text += tr("No description available");
|
||||||
|
else
|
||||||
|
text += QString::fromStdString(desc);
|
||||||
}
|
}
|
||||||
else if (*cmddata == OpcodeDecoder::GX_LOAD_XF_REG)
|
else if (*cmddata == OpcodeDecoder::GX_LOAD_XF_REG)
|
||||||
{
|
{
|
||||||
|
const auto [name, desc] = GetXFTransferInfo(cmddata + 1);
|
||||||
|
ASSERT(!name.empty());
|
||||||
|
|
||||||
text = tr("XF register ");
|
text = tr("XF register ");
|
||||||
|
text += QString::fromStdString(name);
|
||||||
|
text += QLatin1Char{'\n'};
|
||||||
|
|
||||||
|
if (desc.empty())
|
||||||
|
text += tr("No description available");
|
||||||
|
else
|
||||||
|
text += QString::fromStdString(desc);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -378,7 +378,7 @@ ID3D11BlendState* StateCache::Get(BlendingState state)
|
||||||
D3D11_LOGIC_OP_COPY_INVERTED, D3D11_LOGIC_OP_OR_INVERTED, D3D11_LOGIC_OP_NAND,
|
D3D11_LOGIC_OP_COPY_INVERTED, D3D11_LOGIC_OP_OR_INVERTED, D3D11_LOGIC_OP_NAND,
|
||||||
D3D11_LOGIC_OP_SET}};
|
D3D11_LOGIC_OP_SET}};
|
||||||
tdesc.LogicOpEnable = TRUE;
|
tdesc.LogicOpEnable = TRUE;
|
||||||
tdesc.LogicOp = logic_ops[state.logicmode];
|
tdesc.LogicOp = logic_ops[u32(state.logicmode.Value())];
|
||||||
|
|
||||||
ComPtr<ID3D11BlendState1> res;
|
ComPtr<ID3D11BlendState1> res;
|
||||||
HRESULT hr = D3D::device1->CreateBlendState1(&desc, res.GetAddressOf());
|
HRESULT hr = D3D::device1->CreateBlendState1(&desc, res.GetAddressOf());
|
||||||
|
@ -416,10 +416,10 @@ ID3D11BlendState* StateCache::Get(BlendingState state)
|
||||||
use_dual_source ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC_ALPHA,
|
use_dual_source ? D3D11_BLEND_INV_SRC1_ALPHA : D3D11_BLEND_INV_SRC_ALPHA,
|
||||||
D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA}};
|
D3D11_BLEND_DEST_ALPHA, D3D11_BLEND_INV_DEST_ALPHA}};
|
||||||
|
|
||||||
tdesc.SrcBlend = src_factors[state.srcfactor];
|
tdesc.SrcBlend = src_factors[u32(state.srcfactor.Value())];
|
||||||
tdesc.SrcBlendAlpha = src_factors[state.srcfactoralpha];
|
tdesc.SrcBlendAlpha = src_factors[u32(state.srcfactoralpha.Value())];
|
||||||
tdesc.DestBlend = dst_factors[state.dstfactor];
|
tdesc.DestBlend = dst_factors[u32(state.dstfactor.Value())];
|
||||||
tdesc.DestBlendAlpha = dst_factors[state.dstfactoralpha];
|
tdesc.DestBlendAlpha = dst_factors[u32(state.dstfactoralpha.Value())];
|
||||||
tdesc.BlendOp = state.subtract ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
|
tdesc.BlendOp = state.subtract ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
|
||||||
tdesc.BlendOpAlpha = state.subtractAlpha ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
|
tdesc.BlendOpAlpha = state.subtractAlpha ? D3D11_BLEND_OP_REV_SUBTRACT : D3D11_BLEND_OP_ADD;
|
||||||
|
|
||||||
|
@ -441,7 +441,7 @@ ID3D11RasterizerState* StateCache::Get(RasterizationState state)
|
||||||
|
|
||||||
D3D11_RASTERIZER_DESC desc = {};
|
D3D11_RASTERIZER_DESC desc = {};
|
||||||
desc.FillMode = D3D11_FILL_SOLID;
|
desc.FillMode = D3D11_FILL_SOLID;
|
||||||
desc.CullMode = cull_modes[state.cullmode];
|
desc.CullMode = cull_modes[u32(state.cullmode.Value())];
|
||||||
desc.ScissorEnable = TRUE;
|
desc.ScissorEnable = TRUE;
|
||||||
|
|
||||||
ComPtr<ID3D11RasterizerState> res;
|
ComPtr<ID3D11RasterizerState> res;
|
||||||
|
@ -477,7 +477,7 @@ ID3D11DepthStencilState* StateCache::Get(DepthState state)
|
||||||
depthdc.DepthEnable = TRUE;
|
depthdc.DepthEnable = TRUE;
|
||||||
depthdc.DepthWriteMask =
|
depthdc.DepthWriteMask =
|
||||||
state.updateenable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
|
state.updateenable ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO;
|
||||||
depthdc.DepthFunc = d3dCmpFuncs[state.func];
|
depthdc.DepthFunc = d3dCmpFuncs[u32(state.func.Value())];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -66,7 +66,7 @@ static void GetD3DRasterizerDesc(D3D12_RASTERIZER_DESC* desc, const Rasterizatio
|
||||||
{D3D12_CULL_MODE_NONE, D3D12_CULL_MODE_BACK, D3D12_CULL_MODE_FRONT, D3D12_CULL_MODE_FRONT}};
|
{D3D12_CULL_MODE_NONE, D3D12_CULL_MODE_BACK, D3D12_CULL_MODE_FRONT, D3D12_CULL_MODE_FRONT}};
|
||||||
|
|
||||||
desc->FillMode = D3D12_FILL_MODE_SOLID;
|
desc->FillMode = D3D12_FILL_MODE_SOLID;
|
||||||
desc->CullMode = cull_modes[rs_state.cullmode];
|
desc->CullMode = cull_modes[u32(rs_state.cullmode.Value())];
|
||||||
desc->MultisampleEnable = fb_state.samples > 1;
|
desc->MultisampleEnable = fb_state.samples > 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ static void GetD3DDepthDesc(D3D12_DEPTH_STENCIL_DESC* desc, const DepthState& st
|
||||||
D3D12_COMPARISON_FUNC_ALWAYS}};
|
D3D12_COMPARISON_FUNC_ALWAYS}};
|
||||||
|
|
||||||
desc->DepthEnable = state.testenable;
|
desc->DepthEnable = state.testenable;
|
||||||
desc->DepthFunc = compare_funcs[state.func];
|
desc->DepthFunc = compare_funcs[u32(state.func.Value())];
|
||||||
desc->DepthWriteMask =
|
desc->DepthWriteMask =
|
||||||
state.updateenable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
state.updateenable ? D3D12_DEPTH_WRITE_MASK_ALL : D3D12_DEPTH_WRITE_MASK_ZERO;
|
||||||
}
|
}
|
||||||
|
@ -135,24 +135,24 @@ static void GetD3DBlendDesc(D3D12_BLEND_DESC* desc, const BlendingState& state)
|
||||||
rtblend->BlendOpAlpha = state.subtractAlpha ? D3D12_BLEND_OP_REV_SUBTRACT : D3D12_BLEND_OP_ADD;
|
rtblend->BlendOpAlpha = state.subtractAlpha ? D3D12_BLEND_OP_REV_SUBTRACT : D3D12_BLEND_OP_ADD;
|
||||||
if (state.usedualsrc)
|
if (state.usedualsrc)
|
||||||
{
|
{
|
||||||
rtblend->SrcBlend = src_dual_src_factors[state.srcfactor];
|
rtblend->SrcBlend = src_dual_src_factors[u32(state.srcfactor.Value())];
|
||||||
rtblend->SrcBlendAlpha = src_dual_src_factors[state.srcfactoralpha];
|
rtblend->SrcBlendAlpha = src_dual_src_factors[u32(state.srcfactoralpha.Value())];
|
||||||
rtblend->DestBlend = dst_dual_src_factors[state.dstfactor];
|
rtblend->DestBlend = dst_dual_src_factors[u32(state.dstfactor.Value())];
|
||||||
rtblend->DestBlendAlpha = dst_dual_src_factors[state.dstfactoralpha];
|
rtblend->DestBlendAlpha = dst_dual_src_factors[u32(state.dstfactoralpha.Value())];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rtblend->SrcBlend = src_factors[state.srcfactor];
|
rtblend->SrcBlend = src_factors[u32(state.srcfactor.Value())];
|
||||||
rtblend->SrcBlendAlpha = src_factors[state.srcfactoralpha];
|
rtblend->SrcBlendAlpha = src_factors[u32(state.srcfactoralpha.Value())];
|
||||||
rtblend->DestBlend = dst_factors[state.dstfactor];
|
rtblend->DestBlend = dst_factors[u32(state.dstfactor.Value())];
|
||||||
rtblend->DestBlendAlpha = dst_factors[state.dstfactoralpha];
|
rtblend->DestBlendAlpha = dst_factors[u32(state.dstfactoralpha.Value())];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
rtblend->LogicOpEnable = state.logicopenable;
|
rtblend->LogicOpEnable = state.logicopenable;
|
||||||
if (state.logicopenable)
|
if (state.logicopenable)
|
||||||
rtblend->LogicOp = logic_ops[state.logicmode];
|
rtblend->LogicOp = logic_ops[u32(state.logicmode.Value())];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1140,11 +1140,11 @@ void Renderer::ApplyRasterizationState(const RasterizationState state)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// none, ccw, cw, ccw
|
// none, ccw, cw, ccw
|
||||||
if (state.cullmode != GenMode::CULL_NONE)
|
if (state.cullmode != CullMode::None)
|
||||||
{
|
{
|
||||||
// TODO: GX_CULL_ALL not supported, yet!
|
// TODO: GX_CULL_ALL not supported, yet!
|
||||||
glEnable(GL_CULL_FACE);
|
glEnable(GL_CULL_FACE);
|
||||||
glFrontFace(state.cullmode == GenMode::CULL_FRONT ? GL_CCW : GL_CW);
|
glFrontFace(state.cullmode == CullMode::Front ? GL_CCW : GL_CW);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1166,7 +1166,7 @@ void Renderer::ApplyDepthState(const DepthState state)
|
||||||
{
|
{
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE);
|
glDepthMask(state.updateenable ? GL_TRUE : GL_FALSE);
|
||||||
glDepthFunc(glCmpFuncs[state.func]);
|
glDepthFunc(glCmpFuncs[u32(state.func.Value())]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1229,8 +1229,10 @@ void Renderer::ApplyBlendingState(const BlendingState state)
|
||||||
GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
GLenum equation = state.subtract ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
||||||
GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
GLenum equationAlpha = state.subtractAlpha ? GL_FUNC_REVERSE_SUBTRACT : GL_FUNC_ADD;
|
||||||
glBlendEquationSeparate(equation, equationAlpha);
|
glBlendEquationSeparate(equation, equationAlpha);
|
||||||
glBlendFuncSeparate(src_factors[state.srcfactor], dst_factors[state.dstfactor],
|
glBlendFuncSeparate(src_factors[u32(state.srcfactor.Value())],
|
||||||
src_factors[state.srcfactoralpha], dst_factors[state.dstfactoralpha]);
|
dst_factors[u32(state.dstfactor.Value())],
|
||||||
|
src_factors[u32(state.srcfactoralpha.Value())],
|
||||||
|
dst_factors[u32(state.dstfactoralpha.Value())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const GLenum logic_op_codes[16] = {
|
const GLenum logic_op_codes[16] = {
|
||||||
|
@ -1244,7 +1246,7 @@ void Renderer::ApplyBlendingState(const BlendingState state)
|
||||||
if (state.logicopenable)
|
if (state.logicopenable)
|
||||||
{
|
{
|
||||||
glEnable(GL_COLOR_LOGIC_OP);
|
glEnable(GL_COLOR_LOGIC_OP);
|
||||||
glLogicOp(logic_op_codes[state.logicmode]);
|
glLogicOp(logic_op_codes[u32(state.logicmode.Value())]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -489,13 +489,16 @@ bool CullTest(const OutputVertexData* v0, const OutputVertexData* v1, const Outp
|
||||||
|
|
||||||
backface = normalZDir <= 0.0f;
|
backface = normalZDir <= 0.0f;
|
||||||
|
|
||||||
if ((bpmem.genMode.cullmode & 1) && !backface) // cull frontfacing
|
// TODO: Are these tests / the definition of backface above backwards?
|
||||||
|
if ((bpmem.genMode.cullmode == CullMode::Back || bpmem.genMode.cullmode == CullMode::All) &&
|
||||||
|
!backface) // cull frontfacing
|
||||||
{
|
{
|
||||||
INCSTAT(g_stats.this_frame.num_triangles_culled)
|
INCSTAT(g_stats.this_frame.num_triangles_culled)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((bpmem.genMode.cullmode & 2) && backface) // cull backfacing
|
if ((bpmem.genMode.cullmode == CullMode::Front || bpmem.genMode.cullmode == CullMode::All) &&
|
||||||
|
backface) // cull backfacing
|
||||||
{
|
{
|
||||||
INCSTAT(g_stats.this_frame.num_triangles_culled)
|
INCSTAT(g_stats.this_frame.num_triangles_culled)
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -41,12 +41,12 @@ static void SetPixelAlphaOnly(u32 offset, u8 a)
|
||||||
{
|
{
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
// do nothing
|
// do nothing
|
||||||
break;
|
break;
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
{
|
{
|
||||||
u32 a32 = a;
|
u32 a32 = a;
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -56,8 +56,7 @@ static void SetPixelAlphaOnly(u32 offset, u8 a)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,8 +65,8 @@ static void SetPixelColorOnly(u32 offset, u8* rgb)
|
||||||
{
|
{
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
{
|
{
|
||||||
u32 src = *(u32*)rgb;
|
u32 src = *(u32*)rgb;
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -76,7 +75,7 @@ static void SetPixelColorOnly(u32 offset, u8* rgb)
|
||||||
*dst = val;
|
*dst = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
{
|
{
|
||||||
u32 src = *(u32*)rgb;
|
u32 src = *(u32*)rgb;
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -87,7 +86,7 @@ static void SetPixelColorOnly(u32 offset, u8* rgb)
|
||||||
*dst = val;
|
*dst = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
||||||
u32 src = *(u32*)rgb;
|
u32 src = *(u32*)rgb;
|
||||||
|
@ -98,8 +97,7 @@ static void SetPixelColorOnly(u32 offset, u8* rgb)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -108,8 +106,8 @@ static void SetPixelAlphaColor(u32 offset, u8* color)
|
||||||
{
|
{
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
{
|
{
|
||||||
u32 src = *(u32*)color;
|
u32 src = *(u32*)color;
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -118,7 +116,7 @@ static void SetPixelAlphaColor(u32 offset, u8* color)
|
||||||
*dst = val;
|
*dst = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
{
|
{
|
||||||
u32 src = *(u32*)color;
|
u32 src = *(u32*)color;
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -130,7 +128,7 @@ static void SetPixelAlphaColor(u32 offset, u8* color)
|
||||||
*dst = val;
|
*dst = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
||||||
u32 src = *(u32*)color;
|
u32 src = *(u32*)color;
|
||||||
|
@ -141,8 +139,7 @@ static void SetPixelAlphaColor(u32 offset, u8* color)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,23 +151,22 @@ static u32 GetPixelColor(u32 offset)
|
||||||
|
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
return 0xff | ((src & 0x00ffffff) << 8);
|
return 0xff | ((src & 0x00ffffff) << 8);
|
||||||
|
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
return Convert6To8(src & 0x3f) | // Alpha
|
return Convert6To8(src & 0x3f) | // Alpha
|
||||||
Convert6To8((src >> 6) & 0x3f) << 8 | // Blue
|
Convert6To8((src >> 6) & 0x3f) << 8 | // Blue
|
||||||
Convert6To8((src >> 12) & 0x3f) << 16 | // Green
|
Convert6To8((src >> 12) & 0x3f) << 16 | // Green
|
||||||
Convert6To8((src >> 18) & 0x3f) << 24; // Red
|
Convert6To8((src >> 18) & 0x3f) << 24; // Red
|
||||||
|
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
||||||
return 0xff | ((src & 0x00ffffff) << 8);
|
return 0xff | ((src & 0x00ffffff) << 8);
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,9 +175,9 @@ static void SetPixelDepth(u32 offset, u32 depth)
|
||||||
{
|
{
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
{
|
{
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
u32 val = *dst & 0xff000000;
|
u32 val = *dst & 0xff000000;
|
||||||
|
@ -189,7 +185,7 @@ static void SetPixelDepth(u32 offset, u32 depth)
|
||||||
*dst = val;
|
*dst = val;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
||||||
u32* dst = (u32*)&efb[offset];
|
u32* dst = (u32*)&efb[offset];
|
||||||
|
@ -199,8 +195,7 @@ static void SetPixelDepth(u32 offset, u32 depth)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -211,59 +206,58 @@ static u32 GetPixelDepth(u32 offset)
|
||||||
|
|
||||||
switch (bpmem.zcontrol.pixel_format)
|
switch (bpmem.zcontrol.pixel_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
{
|
{
|
||||||
depth = (*(u32*)&efb[offset]) & 0x00ffffff;
|
depth = (*(u32*)&efb[offset]) & 0x00ffffff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
{
|
{
|
||||||
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
INFO_LOG_FMT(VIDEO, "RGB565_Z16 is not supported correctly yet");
|
||||||
depth = (*(u32*)&efb[offset]) & 0x00ffffff;
|
depth = (*(u32*)&efb[offset]) & 0x00ffffff;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}",
|
ERROR_LOG_FMT(VIDEO, "Unsupported pixel format: {}", bpmem.zcontrol.pixel_format);
|
||||||
static_cast<int>(bpmem.zcontrol.pixel_format));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return depth;
|
return depth;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 GetSourceFactor(u8* srcClr, u8* dstClr, BlendMode::BlendFactor mode)
|
static u32 GetSourceFactor(u8* srcClr, u8* dstClr, SrcBlendFactor mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case BlendMode::ZERO:
|
case SrcBlendFactor::Zero:
|
||||||
return 0;
|
return 0;
|
||||||
case BlendMode::ONE:
|
case SrcBlendFactor::One:
|
||||||
return 0xffffffff;
|
return 0xffffffff;
|
||||||
case BlendMode::DSTCLR:
|
case SrcBlendFactor::DstClr:
|
||||||
return *(u32*)dstClr;
|
return *(u32*)dstClr;
|
||||||
case BlendMode::INVDSTCLR:
|
case SrcBlendFactor::InvDstClr:
|
||||||
return 0xffffffff - *(u32*)dstClr;
|
return 0xffffffff - *(u32*)dstClr;
|
||||||
case BlendMode::SRCALPHA:
|
case SrcBlendFactor::SrcAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = srcClr[ALP_C];
|
u8 alpha = srcClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::INVSRCALPHA:
|
case SrcBlendFactor::InvSrcAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = 0xff - srcClr[ALP_C];
|
u8 alpha = 0xff - srcClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::DSTALPHA:
|
case SrcBlendFactor::DstAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = dstClr[ALP_C];
|
u8 alpha = dstClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::INVDSTALPHA:
|
case SrcBlendFactor::InvDstAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = 0xff - dstClr[ALP_C];
|
u8 alpha = 0xff - dstClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
|
@ -274,37 +268,37 @@ static u32 GetSourceFactor(u8* srcClr, u8* dstClr, BlendMode::BlendFactor mode)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 GetDestinationFactor(u8* srcClr, u8* dstClr, BlendMode::BlendFactor mode)
|
static u32 GetDestinationFactor(u8* srcClr, u8* dstClr, DstBlendFactor mode)
|
||||||
{
|
{
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case BlendMode::ZERO:
|
case DstBlendFactor::Zero:
|
||||||
return 0;
|
return 0;
|
||||||
case BlendMode::ONE:
|
case DstBlendFactor::One:
|
||||||
return 0xffffffff;
|
return 0xffffffff;
|
||||||
case BlendMode::SRCCLR:
|
case DstBlendFactor::SrcClr:
|
||||||
return *(u32*)srcClr;
|
return *(u32*)srcClr;
|
||||||
case BlendMode::INVSRCCLR:
|
case DstBlendFactor::InvSrcClr:
|
||||||
return 0xffffffff - *(u32*)srcClr;
|
return 0xffffffff - *(u32*)srcClr;
|
||||||
case BlendMode::SRCALPHA:
|
case DstBlendFactor::SrcAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = srcClr[ALP_C];
|
u8 alpha = srcClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::INVSRCALPHA:
|
case DstBlendFactor::InvSrcAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = 0xff - srcClr[ALP_C];
|
u8 alpha = 0xff - srcClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::DSTALPHA:
|
case DstBlendFactor::DstAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = dstClr[ALP_C] & 0xff;
|
u8 alpha = dstClr[ALP_C] & 0xff;
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
case BlendMode::INVDSTALPHA:
|
case DstBlendFactor::InvDstAlpha:
|
||||||
{
|
{
|
||||||
u8 alpha = 0xff - dstClr[ALP_C];
|
u8 alpha = 0xff - dstClr[ALP_C];
|
||||||
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
u32 factor = alpha << 24 | alpha << 16 | alpha << 8 | alpha;
|
||||||
|
@ -337,56 +331,56 @@ static void BlendColor(u8* srcClr, u8* dstClr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void LogicBlend(u32 srcClr, u32* dstClr, BlendMode::LogicOp op)
|
static void LogicBlend(u32 srcClr, u32* dstClr, LogicOp op)
|
||||||
{
|
{
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
case BlendMode::CLEAR:
|
case LogicOp::Clear:
|
||||||
*dstClr = 0;
|
*dstClr = 0;
|
||||||
break;
|
break;
|
||||||
case BlendMode::AND:
|
case LogicOp::And:
|
||||||
*dstClr = srcClr & *dstClr;
|
*dstClr = srcClr & *dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::AND_REVERSE:
|
case LogicOp::AndReverse:
|
||||||
*dstClr = srcClr & (~*dstClr);
|
*dstClr = srcClr & (~*dstClr);
|
||||||
break;
|
break;
|
||||||
case BlendMode::COPY:
|
case LogicOp::Copy:
|
||||||
*dstClr = srcClr;
|
*dstClr = srcClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::AND_INVERTED:
|
case LogicOp::AndInverted:
|
||||||
*dstClr = (~srcClr) & *dstClr;
|
*dstClr = (~srcClr) & *dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::NOOP:
|
case LogicOp::NoOp:
|
||||||
// Do nothing
|
// Do nothing
|
||||||
break;
|
break;
|
||||||
case BlendMode::XOR:
|
case LogicOp::Xor:
|
||||||
*dstClr = srcClr ^ *dstClr;
|
*dstClr = srcClr ^ *dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::OR:
|
case LogicOp::Or:
|
||||||
*dstClr = srcClr | *dstClr;
|
*dstClr = srcClr | *dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::NOR:
|
case LogicOp::Nor:
|
||||||
*dstClr = ~(srcClr | *dstClr);
|
*dstClr = ~(srcClr | *dstClr);
|
||||||
break;
|
break;
|
||||||
case BlendMode::EQUIV:
|
case LogicOp::Equiv:
|
||||||
*dstClr = ~(srcClr ^ *dstClr);
|
*dstClr = ~(srcClr ^ *dstClr);
|
||||||
break;
|
break;
|
||||||
case BlendMode::INVERT:
|
case LogicOp::Invert:
|
||||||
*dstClr = ~*dstClr;
|
*dstClr = ~*dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::OR_REVERSE:
|
case LogicOp::OrReverse:
|
||||||
*dstClr = srcClr | (~*dstClr);
|
*dstClr = srcClr | (~*dstClr);
|
||||||
break;
|
break;
|
||||||
case BlendMode::COPY_INVERTED:
|
case LogicOp::CopyInverted:
|
||||||
*dstClr = ~srcClr;
|
*dstClr = ~srcClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::OR_INVERTED:
|
case LogicOp::OrInverted:
|
||||||
*dstClr = (~srcClr) | *dstClr;
|
*dstClr = (~srcClr) | *dstClr;
|
||||||
break;
|
break;
|
||||||
case BlendMode::NAND:
|
case LogicOp::Nand:
|
||||||
*dstClr = ~(srcClr & *dstClr);
|
*dstClr = ~(srcClr & *dstClr);
|
||||||
break;
|
break;
|
||||||
case BlendMode::SET:
|
case LogicOp::Set:
|
||||||
*dstClr = 0xffffffff;
|
*dstClr = 0xffffffff;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +398,7 @@ static void SubtractBlend(u8* srcClr, u8* dstClr)
|
||||||
static void Dither(u16 x, u16 y, u8* color)
|
static void Dither(u16 x, u16 y, u8* color)
|
||||||
{
|
{
|
||||||
// No blending for RGB8 mode
|
// No blending for RGB8 mode
|
||||||
if (!bpmem.blendmode.dither || bpmem.zcontrol.pixel_format != PEControl::PixelFormat::RGBA6_Z24)
|
if (!bpmem.blendmode.dither || bpmem.zcontrol.pixel_format != PixelFormat::RGBA6_Z24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering
|
// Flipper uses a standard 2x2 Bayer Matrix for 6 bit dithering
|
||||||
|
@ -662,33 +656,33 @@ bool ZCompare(u16 x, u16 y, u32 z)
|
||||||
|
|
||||||
switch (bpmem.zmode.func)
|
switch (bpmem.zmode.func)
|
||||||
{
|
{
|
||||||
case ZMode::NEVER:
|
case CompareMode::Never:
|
||||||
pass = false;
|
pass = false;
|
||||||
break;
|
break;
|
||||||
case ZMode::LESS:
|
case CompareMode::Less:
|
||||||
pass = z < depth;
|
pass = z < depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::EQUAL:
|
case CompareMode::Equal:
|
||||||
pass = z == depth;
|
pass = z == depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::LEQUAL:
|
case CompareMode::LEqual:
|
||||||
pass = z <= depth;
|
pass = z <= depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::GREATER:
|
case CompareMode::Greater:
|
||||||
pass = z > depth;
|
pass = z > depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::NEQUAL:
|
case CompareMode::NEqual:
|
||||||
pass = z != depth;
|
pass = z != depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::GEQUAL:
|
case CompareMode::GEqual:
|
||||||
pass = z >= depth;
|
pass = z >= depth;
|
||||||
break;
|
break;
|
||||||
case ZMode::ALWAYS:
|
case CompareMode::Always:
|
||||||
pass = true;
|
pass = true;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
pass = false;
|
pass = false;
|
||||||
ERROR_LOG_FMT(VIDEO, "Bad Z compare mode {}", static_cast<int>(bpmem.zmode.func));
|
ERROR_LOG_FMT(VIDEO, "Bad Z compare mode {}", bpmem.zmode.func);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -173,7 +173,7 @@ static inline void CalculateLOD(s32* lodp, bool* linear, u32 texmap, u32 texcoor
|
||||||
const TexMode1& tm1 = texUnit.texMode1[subTexmap];
|
const TexMode1& tm1 = texUnit.texMode1[subTexmap];
|
||||||
|
|
||||||
float sDelta, tDelta;
|
float sDelta, tDelta;
|
||||||
if (tm0.diag_lod)
|
if (tm0.diag_lod == LODType::Diagonal)
|
||||||
{
|
{
|
||||||
float* uv0 = rasterBlock.Pixel[0][0].Uv[texcoord];
|
float* uv0 = rasterBlock.Pixel[0][0].Uv[texcoord];
|
||||||
float* uv1 = rasterBlock.Pixel[1][1].Uv[texcoord];
|
float* uv1 = rasterBlock.Pixel[1][1].Uv[texcoord];
|
||||||
|
@ -199,7 +199,8 @@ static inline void CalculateLOD(s32* lodp, bool* linear, u32 texmap, u32 texcoor
|
||||||
bias >>= 1;
|
bias >>= 1;
|
||||||
lod += bias;
|
lod += bias;
|
||||||
|
|
||||||
*linear = ((lod > 0 && (tm0.min_filter & 4)) || (lod <= 0 && tm0.mag_filter));
|
*linear = ((lod > 0 && tm0.min_filter == FilterMode::Linear) ||
|
||||||
|
(lod <= 0 && tm0.mag_filter == FilterMode::Linear));
|
||||||
|
|
||||||
// NOTE: The order of comparisons for this clamp check matters.
|
// NOTE: The order of comparisons for this clamp check matters.
|
||||||
if (lod > static_cast<s32>(tm1.max_lod))
|
if (lod > static_cast<s32>(tm1.max_lod))
|
||||||
|
|
|
@ -183,8 +183,8 @@ static void ParseColorAttributes(InputVertexData* dst, DataReader& src,
|
||||||
const auto set_default_color = [](u8* color, int i) {
|
const auto set_default_color = [](u8* color, int i) {
|
||||||
// The default alpha channel seems to depend on the number of components in the vertex format.
|
// The default alpha channel seems to depend on the number of components in the vertex format.
|
||||||
const auto& g0 = g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0;
|
const auto& g0 = g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0;
|
||||||
const u32 color_elements = i == 0 ? g0.Color0Elements : g0.Color1Elements;
|
const auto color_elements = i == 0 ? g0.Color0Elements.Value() : g0.Color1Elements.Value();
|
||||||
color[0] = color_elements == 0 ? 255 : 0;
|
color[0] = color_elements == ColorComponentCount::RGB ? 255 : 0;
|
||||||
color[1] = 255;
|
color[1] = 255;
|
||||||
color[2] = 255;
|
color[2] = 255;
|
||||||
color[3] = 255;
|
color[3] = 255;
|
||||||
|
|
|
@ -172,11 +172,11 @@ static inline s16 Clamp1024(s16 in)
|
||||||
return in > 1023 ? 1023 : (in < -1024 ? -1024 : in);
|
return in > 1023 ? 1023 : (in < -1024 ? -1024 : in);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tev::SetRasColor(int colorChan, int swaptable)
|
void Tev::SetRasColor(RasColorChan colorChan, int swaptable)
|
||||||
{
|
{
|
||||||
switch (colorChan)
|
switch (colorChan)
|
||||||
{
|
{
|
||||||
case 0: // Color0
|
case RasColorChan::Color0:
|
||||||
{
|
{
|
||||||
const u8* color = Color[0];
|
const u8* color = Color[0];
|
||||||
RasColor[RED_C] = color[bpmem.tevksel[swaptable].swap1];
|
RasColor[RED_C] = color[bpmem.tevksel[swaptable].swap1];
|
||||||
|
@ -186,7 +186,7 @@ void Tev::SetRasColor(int colorChan, int swaptable)
|
||||||
RasColor[ALP_C] = color[bpmem.tevksel[swaptable].swap2];
|
RasColor[ALP_C] = color[bpmem.tevksel[swaptable].swap2];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // Color1
|
case RasColorChan::Color1:
|
||||||
{
|
{
|
||||||
const u8* color = Color[1];
|
const u8* color = Color[1];
|
||||||
RasColor[RED_C] = color[bpmem.tevksel[swaptable].swap1];
|
RasColor[RED_C] = color[bpmem.tevksel[swaptable].swap1];
|
||||||
|
@ -196,7 +196,7 @@ void Tev::SetRasColor(int colorChan, int swaptable)
|
||||||
RasColor[ALP_C] = color[bpmem.tevksel[swaptable].swap2];
|
RasColor[ALP_C] = color[bpmem.tevksel[swaptable].swap2];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 5: // alpha bump
|
case RasColorChan::AlphaBump:
|
||||||
{
|
{
|
||||||
for (s16& comp : RasColor)
|
for (s16& comp : RasColor)
|
||||||
{
|
{
|
||||||
|
@ -204,7 +204,7 @@ void Tev::SetRasColor(int colorChan, int swaptable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 6: // alpha bump normalized
|
case RasColorChan::NormalizedAlphaBump:
|
||||||
{
|
{
|
||||||
const u8 normalized = AlphaBump | AlphaBump >> 5;
|
const u8 normalized = AlphaBump | AlphaBump >> 5;
|
||||||
for (s16& comp : RasColor)
|
for (s16& comp : RasColor)
|
||||||
|
@ -213,8 +213,11 @@ void Tev::SetRasColor(int colorChan, int swaptable)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default: // zero
|
default:
|
||||||
{
|
{
|
||||||
|
if (colorChan != RasColorChan::Zero)
|
||||||
|
PanicAlertFmt("Invalid ras color channel: {}", colorChan);
|
||||||
|
|
||||||
for (s16& comp : RasColor)
|
for (s16& comp : RasColor)
|
||||||
{
|
{
|
||||||
comp = 0;
|
comp = 0;
|
||||||
|
@ -226,22 +229,24 @@ void Tev::SetRasColor(int colorChan, int swaptable)
|
||||||
|
|
||||||
void Tev::DrawColorRegular(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4])
|
void Tev::DrawColorRegular(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4])
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = BLU_C; i <= RED_C; i++)
|
||||||
{
|
{
|
||||||
const InputRegType& InputReg = inputs[BLU_C + i];
|
const InputRegType& InputReg = inputs[i];
|
||||||
|
|
||||||
const u16 c = InputReg.c + (InputReg.c >> 7);
|
const u16 c = InputReg.c + (InputReg.c >> 7);
|
||||||
|
|
||||||
s32 temp = InputReg.a * (256 - c) + (InputReg.b * c);
|
s32 temp = InputReg.a * (256 - c) + (InputReg.b * c);
|
||||||
temp <<= m_ScaleLShiftLUT[cc.shift];
|
temp <<= m_ScaleLShiftLUT[u32(cc.scale.Value())];
|
||||||
temp += (cc.shift == 3) ? 0 : (cc.op == 1) ? 127 : 128;
|
temp += (cc.scale == TevScale::Divide2) ? 0 : (cc.op == TevOp::Sub) ? 127 : 128;
|
||||||
temp >>= 8;
|
temp >>= 8;
|
||||||
temp = cc.op ? -temp : temp;
|
temp = cc.op == TevOp::Sub ? -temp : temp;
|
||||||
|
|
||||||
s32 result = ((InputReg.d + m_BiasLUT[cc.bias]) << m_ScaleLShiftLUT[cc.shift]) + temp;
|
s32 result = ((InputReg.d + m_BiasLUT[u32(cc.bias.Value())])
|
||||||
result = result >> m_ScaleRShiftLUT[cc.shift];
|
<< m_ScaleLShiftLUT[u32(cc.scale.Value())]) +
|
||||||
|
temp;
|
||||||
|
result = result >> m_ScaleRShiftLUT[u32(cc.scale.Value())];
|
||||||
|
|
||||||
Reg[cc.dest][BLU_C + i] = result;
|
Reg[u32(cc.dest.Value())][i] = result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -249,56 +254,38 @@ void Tev::DrawColorCompare(const TevStageCombiner::ColorCombiner& cc, const Inpu
|
||||||
{
|
{
|
||||||
for (int i = BLU_C; i <= RED_C; i++)
|
for (int i = BLU_C; i <= RED_C; i++)
|
||||||
{
|
{
|
||||||
switch ((cc.shift << 1) | cc.op | 8) // encoded compare mode
|
u32 a, b;
|
||||||
|
switch (cc.compare_mode)
|
||||||
{
|
{
|
||||||
case TEVCMP_R8_GT:
|
case TevCompareMode::R8:
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((inputs[RED_C].a > inputs[RED_C].b) ? inputs[i].c : 0);
|
a = inputs[RED_C].a;
|
||||||
|
b = inputs[RED_C].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_R8_EQ:
|
case TevCompareMode::GR16:
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((inputs[RED_C].a == inputs[RED_C].b) ? inputs[i].c : 0);
|
a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
||||||
|
b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_GR16_GT:
|
case TevCompareMode::BGR24:
|
||||||
{
|
a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
||||||
const u32 a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
||||||
const u32 b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((a > b) ? inputs[i].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_GR16_EQ:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((a == b) ? inputs[i].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_BGR24_GT:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((a > b) ? inputs[i].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_BGR24_EQ:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((a == b) ? inputs[i].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_RGB8_GT:
|
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((inputs[i].a > inputs[i].b) ? inputs[i].c : 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_RGB8_EQ:
|
case TevCompareMode::RGB8:
|
||||||
Reg[cc.dest][i] = inputs[i].d + ((inputs[i].a == inputs[i].b) ? inputs[i].c : 0);
|
a = inputs[i].a;
|
||||||
|
b = inputs[i].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PanicAlertFmt("Invalid compare mode {}", cc.compare_mode);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cc.comparison == TevComparison::GT)
|
||||||
|
Reg[u32(cc.dest.Value())][i] = inputs[i].d + ((a > b) ? inputs[i].c : 0);
|
||||||
|
else
|
||||||
|
Reg[u32(cc.dest.Value())][i] = inputs[i].d + ((a == b) ? inputs[i].c : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -309,95 +296,76 @@ void Tev::DrawAlphaRegular(const TevStageCombiner::AlphaCombiner& ac, const Inpu
|
||||||
const u16 c = InputReg.c + (InputReg.c >> 7);
|
const u16 c = InputReg.c + (InputReg.c >> 7);
|
||||||
|
|
||||||
s32 temp = InputReg.a * (256 - c) + (InputReg.b * c);
|
s32 temp = InputReg.a * (256 - c) + (InputReg.b * c);
|
||||||
temp <<= m_ScaleLShiftLUT[ac.shift];
|
temp <<= m_ScaleLShiftLUT[u32(ac.scale.Value())];
|
||||||
temp += (ac.shift != 3) ? 0 : (ac.op == 1) ? 127 : 128;
|
temp += (ac.scale != TevScale::Divide2) ? 0 : (ac.op == TevOp::Sub) ? 127 : 128;
|
||||||
temp = ac.op ? (-temp >> 8) : (temp >> 8);
|
temp = ac.op == TevOp::Sub ? (-temp >> 8) : (temp >> 8);
|
||||||
|
|
||||||
s32 result = ((InputReg.d + m_BiasLUT[ac.bias]) << m_ScaleLShiftLUT[ac.shift]) + temp;
|
s32 result =
|
||||||
result = result >> m_ScaleRShiftLUT[ac.shift];
|
((InputReg.d + m_BiasLUT[u32(ac.bias.Value())]) << m_ScaleLShiftLUT[u32(ac.scale.Value())]) +
|
||||||
|
temp;
|
||||||
|
result = result >> m_ScaleRShiftLUT[u32(ac.scale.Value())];
|
||||||
|
|
||||||
Reg[ac.dest][ALP_C] = result;
|
Reg[u32(ac.dest.Value())][ALP_C] = result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Tev::DrawAlphaCompare(const TevStageCombiner::AlphaCombiner& ac, const InputRegType inputs[4])
|
void Tev::DrawAlphaCompare(const TevStageCombiner::AlphaCombiner& ac, const InputRegType inputs[4])
|
||||||
{
|
{
|
||||||
switch ((ac.shift << 1) | ac.op | 8) // encoded compare mode
|
u32 a, b;
|
||||||
|
switch (ac.compare_mode)
|
||||||
{
|
{
|
||||||
case TEVCMP_R8_GT:
|
case TevCompareMode::R8:
|
||||||
Reg[ac.dest][ALP_C] =
|
a = inputs[RED_C].a;
|
||||||
inputs[ALP_C].d + ((inputs[RED_C].a > inputs[RED_C].b) ? inputs[ALP_C].c : 0);
|
b = inputs[RED_C].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_R8_EQ:
|
case TevCompareMode::GR16:
|
||||||
Reg[ac.dest][ALP_C] =
|
a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
||||||
inputs[ALP_C].d + ((inputs[RED_C].a == inputs[RED_C].b) ? inputs[ALP_C].c : 0);
|
b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_GR16_GT:
|
case TevCompareMode::BGR24:
|
||||||
{
|
a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
||||||
const u32 a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
||||||
const u32 b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[ac.dest][ALP_C] = inputs[ALP_C].d + ((a > b) ? inputs[ALP_C].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_GR16_EQ:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[ac.dest][ALP_C] = inputs[ALP_C].d + ((a == b) ? inputs[ALP_C].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_BGR24_GT:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[ac.dest][ALP_C] = inputs[ALP_C].d + ((a > b) ? inputs[ALP_C].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_BGR24_EQ:
|
|
||||||
{
|
|
||||||
const u32 a = (inputs[BLU_C].a << 16) | (inputs[GRN_C].a << 8) | inputs[RED_C].a;
|
|
||||||
const u32 b = (inputs[BLU_C].b << 16) | (inputs[GRN_C].b << 8) | inputs[RED_C].b;
|
|
||||||
Reg[ac.dest][ALP_C] = inputs[ALP_C].d + ((a == b) ? inputs[ALP_C].c : 0);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TEVCMP_A8_GT:
|
|
||||||
Reg[ac.dest][ALP_C] =
|
|
||||||
inputs[ALP_C].d + ((inputs[ALP_C].a > inputs[ALP_C].b) ? inputs[ALP_C].c : 0);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TEVCMP_A8_EQ:
|
case TevCompareMode::A8:
|
||||||
Reg[ac.dest][ALP_C] =
|
a = inputs[ALP_C].a;
|
||||||
inputs[ALP_C].d + ((inputs[ALP_C].a == inputs[ALP_C].b) ? inputs[ALP_C].c : 0);
|
b = inputs[ALP_C].b;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
PanicAlertFmt("Invalid compare mode {}", ac.compare_mode);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ac.comparison == TevComparison::GT)
|
||||||
|
Reg[u32(ac.dest.Value())][ALP_C] = inputs[ALP_C].d + ((a > b) ? inputs[ALP_C].c : 0);
|
||||||
|
else
|
||||||
|
Reg[u32(ac.dest.Value())][ALP_C] = inputs[ALP_C].d + ((a == b) ? inputs[ALP_C].c : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool AlphaCompare(int alpha, int ref, AlphaTest::CompareMode comp)
|
static bool AlphaCompare(int alpha, int ref, CompareMode comp)
|
||||||
{
|
{
|
||||||
switch (comp)
|
switch (comp)
|
||||||
{
|
{
|
||||||
case AlphaTest::ALWAYS:
|
case CompareMode::Always:
|
||||||
return true;
|
return true;
|
||||||
case AlphaTest::NEVER:
|
case CompareMode::Never:
|
||||||
return false;
|
return false;
|
||||||
case AlphaTest::LEQUAL:
|
case CompareMode::LEqual:
|
||||||
return alpha <= ref;
|
return alpha <= ref;
|
||||||
case AlphaTest::LESS:
|
case CompareMode::Less:
|
||||||
return alpha < ref;
|
return alpha < ref;
|
||||||
case AlphaTest::GEQUAL:
|
case CompareMode::GEqual:
|
||||||
return alpha >= ref;
|
return alpha >= ref;
|
||||||
case AlphaTest::GREATER:
|
case CompareMode::Greater:
|
||||||
return alpha > ref;
|
return alpha > ref;
|
||||||
case AlphaTest::EQUAL:
|
case CompareMode::Equal:
|
||||||
return alpha == ref;
|
return alpha == ref;
|
||||||
case AlphaTest::NEQUAL:
|
case CompareMode::NEqual:
|
||||||
return alpha != ref;
|
return alpha != ref;
|
||||||
default:
|
default:
|
||||||
|
PanicAlertFmt("Invalid compare mode {}", comp);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -409,38 +377,40 @@ static bool TevAlphaTest(int alpha)
|
||||||
|
|
||||||
switch (bpmem.alpha_test.logic)
|
switch (bpmem.alpha_test.logic)
|
||||||
{
|
{
|
||||||
case 0:
|
case AlphaTestOp::And:
|
||||||
return comp0 && comp1; // and
|
return comp0 && comp1;
|
||||||
case 1:
|
case AlphaTestOp::Or:
|
||||||
return comp0 || comp1; // or
|
return comp0 || comp1;
|
||||||
case 2:
|
case AlphaTestOp::Xor:
|
||||||
return comp0 ^ comp1; // xor
|
return comp0 ^ comp1;
|
||||||
case 3:
|
case AlphaTestOp::Xnor:
|
||||||
return !(comp0 ^ comp1); // xnor
|
return !(comp0 ^ comp1);
|
||||||
default:
|
default:
|
||||||
|
PanicAlertFmt("Invalid AlphaTestOp {}", bpmem.alpha_test.logic);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline s32 WrapIndirectCoord(s32 coord, int wrapMode)
|
static inline s32 WrapIndirectCoord(s32 coord, IndTexWrap wrapMode)
|
||||||
{
|
{
|
||||||
switch (wrapMode)
|
switch (wrapMode)
|
||||||
{
|
{
|
||||||
case ITW_OFF:
|
case IndTexWrap::ITW_OFF:
|
||||||
return coord;
|
return coord;
|
||||||
case ITW_256:
|
case IndTexWrap::ITW_256:
|
||||||
return (coord & ((256 << 7) - 1));
|
return (coord & ((256 << 7) - 1));
|
||||||
case ITW_128:
|
case IndTexWrap::ITW_128:
|
||||||
return (coord & ((128 << 7) - 1));
|
return (coord & ((128 << 7) - 1));
|
||||||
case ITW_64:
|
case IndTexWrap::ITW_64:
|
||||||
return (coord & ((64 << 7) - 1));
|
return (coord & ((64 << 7) - 1));
|
||||||
case ITW_32:
|
case IndTexWrap::ITW_32:
|
||||||
return (coord & ((32 << 7) - 1));
|
return (coord & ((32 << 7) - 1));
|
||||||
case ITW_16:
|
case IndTexWrap::ITW_16:
|
||||||
return (coord & ((16 << 7) - 1));
|
return (coord & ((16 << 7) - 1));
|
||||||
case ITW_0:
|
case IndTexWrap::ITW_0:
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
|
PanicAlertFmt("Invalid indirect wrap mode {}", wrapMode);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,56 +425,59 @@ void Tev::Indirect(unsigned int stageNum, s32 s, s32 t)
|
||||||
// alpha bump select
|
// alpha bump select
|
||||||
switch (indirect.bs)
|
switch (indirect.bs)
|
||||||
{
|
{
|
||||||
case ITBA_OFF:
|
case IndTexBumpAlpha::Off:
|
||||||
AlphaBump = 0;
|
AlphaBump = 0;
|
||||||
break;
|
break;
|
||||||
case ITBA_S:
|
case IndTexBumpAlpha::S:
|
||||||
AlphaBump = indmap[TextureSampler::ALP_SMP];
|
AlphaBump = indmap[TextureSampler::ALP_SMP];
|
||||||
break;
|
break;
|
||||||
case ITBA_T:
|
case IndTexBumpAlpha::T:
|
||||||
AlphaBump = indmap[TextureSampler::BLU_SMP];
|
AlphaBump = indmap[TextureSampler::BLU_SMP];
|
||||||
break;
|
break;
|
||||||
case ITBA_U:
|
case IndTexBumpAlpha::U:
|
||||||
AlphaBump = indmap[TextureSampler::GRN_SMP];
|
AlphaBump = indmap[TextureSampler::GRN_SMP];
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
PanicAlertFmt("Invalid alpha bump {}", indirect.bs);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// bias select
|
// bias select
|
||||||
const s16 biasValue = indirect.fmt == ITF_8 ? -128 : 1;
|
const s16 biasValue = indirect.fmt == IndTexFormat::ITF_8 ? -128 : 1;
|
||||||
s16 bias[3];
|
s16 bias[3];
|
||||||
bias[0] = indirect.bias & 1 ? biasValue : 0;
|
bias[0] = indirect.bias_s ? biasValue : 0;
|
||||||
bias[1] = indirect.bias & 2 ? biasValue : 0;
|
bias[1] = indirect.bias_t ? biasValue : 0;
|
||||||
bias[2] = indirect.bias & 4 ? biasValue : 0;
|
bias[2] = indirect.bias_u ? biasValue : 0;
|
||||||
|
|
||||||
// format
|
// format
|
||||||
switch (indirect.fmt)
|
switch (indirect.fmt)
|
||||||
{
|
{
|
||||||
case ITF_8:
|
case IndTexFormat::ITF_8:
|
||||||
indcoord[0] = indmap[TextureSampler::ALP_SMP] + bias[0];
|
indcoord[0] = indmap[TextureSampler::ALP_SMP] + bias[0];
|
||||||
indcoord[1] = indmap[TextureSampler::BLU_SMP] + bias[1];
|
indcoord[1] = indmap[TextureSampler::BLU_SMP] + bias[1];
|
||||||
indcoord[2] = indmap[TextureSampler::GRN_SMP] + bias[2];
|
indcoord[2] = indmap[TextureSampler::GRN_SMP] + bias[2];
|
||||||
AlphaBump = AlphaBump & 0xf8;
|
AlphaBump = AlphaBump & 0xf8;
|
||||||
break;
|
break;
|
||||||
case ITF_5:
|
case IndTexFormat::ITF_5:
|
||||||
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x1f) + bias[0];
|
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x1f) + bias[0];
|
||||||
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x1f) + bias[1];
|
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x1f) + bias[1];
|
||||||
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x1f) + bias[2];
|
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x1f) + bias[2];
|
||||||
AlphaBump = AlphaBump & 0xe0;
|
AlphaBump = AlphaBump & 0xe0;
|
||||||
break;
|
break;
|
||||||
case ITF_4:
|
case IndTexFormat::ITF_4:
|
||||||
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x0f) + bias[0];
|
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x0f) + bias[0];
|
||||||
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x0f) + bias[1];
|
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x0f) + bias[1];
|
||||||
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x0f) + bias[2];
|
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x0f) + bias[2];
|
||||||
AlphaBump = AlphaBump & 0xf0;
|
AlphaBump = AlphaBump & 0xf0;
|
||||||
break;
|
break;
|
||||||
case ITF_3:
|
case IndTexFormat::ITF_3:
|
||||||
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x07) + bias[0];
|
indcoord[0] = (indmap[TextureSampler::ALP_SMP] & 0x07) + bias[0];
|
||||||
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x07) + bias[1];
|
indcoord[1] = (indmap[TextureSampler::BLU_SMP] & 0x07) + bias[1];
|
||||||
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x07) + bias[2];
|
indcoord[2] = (indmap[TextureSampler::GRN_SMP] & 0x07) + bias[2];
|
||||||
AlphaBump = AlphaBump & 0xf8;
|
AlphaBump = AlphaBump & 0xf8;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Tev::Indirect");
|
PanicAlertFmt("Invalid indirect format {}", indirect.fmt);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,8 +621,8 @@ void Tev::Draw()
|
||||||
}
|
}
|
||||||
|
|
||||||
// set konst for this stage
|
// set konst for this stage
|
||||||
const int kc = kSel.getKC(stageOdd);
|
const auto kc = u32(kSel.getKC(stageOdd));
|
||||||
const int ka = kSel.getKA(stageOdd);
|
const auto ka = u32(kSel.getKA(stageOdd));
|
||||||
StageKonst[RED_C] = *(m_KonstLUT[kc][RED_C]);
|
StageKonst[RED_C] = *(m_KonstLUT[kc][RED_C]);
|
||||||
StageKonst[GRN_C] = *(m_KonstLUT[kc][GRN_C]);
|
StageKonst[GRN_C] = *(m_KonstLUT[kc][GRN_C]);
|
||||||
StageKonst[BLU_C] = *(m_KonstLUT[kc][BLU_C]);
|
StageKonst[BLU_C] = *(m_KonstLUT[kc][BLU_C]);
|
||||||
|
@ -662,43 +635,43 @@ void Tev::Draw()
|
||||||
InputRegType inputs[4];
|
InputRegType inputs[4];
|
||||||
for (int i = 0; i < 3; i++)
|
for (int i = 0; i < 3; i++)
|
||||||
{
|
{
|
||||||
inputs[BLU_C + i].a = *m_ColorInputLUT[cc.a][i];
|
inputs[BLU_C + i].a = *m_ColorInputLUT[u32(cc.a.Value())][i];
|
||||||
inputs[BLU_C + i].b = *m_ColorInputLUT[cc.b][i];
|
inputs[BLU_C + i].b = *m_ColorInputLUT[u32(cc.b.Value())][i];
|
||||||
inputs[BLU_C + i].c = *m_ColorInputLUT[cc.c][i];
|
inputs[BLU_C + i].c = *m_ColorInputLUT[u32(cc.c.Value())][i];
|
||||||
inputs[BLU_C + i].d = *m_ColorInputLUT[cc.d][i];
|
inputs[BLU_C + i].d = *m_ColorInputLUT[u32(cc.d.Value())][i];
|
||||||
}
|
}
|
||||||
inputs[ALP_C].a = *m_AlphaInputLUT[ac.a];
|
inputs[ALP_C].a = *m_AlphaInputLUT[u32(ac.a.Value())];
|
||||||
inputs[ALP_C].b = *m_AlphaInputLUT[ac.b];
|
inputs[ALP_C].b = *m_AlphaInputLUT[u32(ac.b.Value())];
|
||||||
inputs[ALP_C].c = *m_AlphaInputLUT[ac.c];
|
inputs[ALP_C].c = *m_AlphaInputLUT[u32(ac.c.Value())];
|
||||||
inputs[ALP_C].d = *m_AlphaInputLUT[ac.d];
|
inputs[ALP_C].d = *m_AlphaInputLUT[u32(ac.d.Value())];
|
||||||
|
|
||||||
if (cc.bias != 3)
|
if (cc.bias != TevBias::Compare)
|
||||||
DrawColorRegular(cc, inputs);
|
DrawColorRegular(cc, inputs);
|
||||||
else
|
else
|
||||||
DrawColorCompare(cc, inputs);
|
DrawColorCompare(cc, inputs);
|
||||||
|
|
||||||
if (cc.clamp)
|
if (cc.clamp)
|
||||||
{
|
{
|
||||||
Reg[cc.dest][RED_C] = Clamp255(Reg[cc.dest][RED_C]);
|
Reg[u32(cc.dest.Value())][RED_C] = Clamp255(Reg[u32(cc.dest.Value())][RED_C]);
|
||||||
Reg[cc.dest][GRN_C] = Clamp255(Reg[cc.dest][GRN_C]);
|
Reg[u32(cc.dest.Value())][GRN_C] = Clamp255(Reg[u32(cc.dest.Value())][GRN_C]);
|
||||||
Reg[cc.dest][BLU_C] = Clamp255(Reg[cc.dest][BLU_C]);
|
Reg[u32(cc.dest.Value())][BLU_C] = Clamp255(Reg[u32(cc.dest.Value())][BLU_C]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Reg[cc.dest][RED_C] = Clamp1024(Reg[cc.dest][RED_C]);
|
Reg[u32(cc.dest.Value())][RED_C] = Clamp1024(Reg[u32(cc.dest.Value())][RED_C]);
|
||||||
Reg[cc.dest][GRN_C] = Clamp1024(Reg[cc.dest][GRN_C]);
|
Reg[u32(cc.dest.Value())][GRN_C] = Clamp1024(Reg[u32(cc.dest.Value())][GRN_C]);
|
||||||
Reg[cc.dest][BLU_C] = Clamp1024(Reg[cc.dest][BLU_C]);
|
Reg[u32(cc.dest.Value())][BLU_C] = Clamp1024(Reg[u32(cc.dest.Value())][BLU_C]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ac.bias != 3)
|
if (ac.bias != TevBias::Compare)
|
||||||
DrawAlphaRegular(ac, inputs);
|
DrawAlphaRegular(ac, inputs);
|
||||||
else
|
else
|
||||||
DrawAlphaCompare(ac, inputs);
|
DrawAlphaCompare(ac, inputs);
|
||||||
|
|
||||||
if (ac.clamp)
|
if (ac.clamp)
|
||||||
Reg[ac.dest][ALP_C] = Clamp255(Reg[ac.dest][ALP_C]);
|
Reg[u32(ac.dest.Value())][ALP_C] = Clamp255(Reg[u32(ac.dest.Value())][ALP_C]);
|
||||||
else
|
else
|
||||||
Reg[ac.dest][ALP_C] = Clamp1024(Reg[ac.dest][ALP_C]);
|
Reg[u32(ac.dest.Value())][ALP_C] = Clamp1024(Reg[u32(ac.dest.Value())][ALP_C]);
|
||||||
|
|
||||||
#if ALLOW_TEV_DUMPS
|
#if ALLOW_TEV_DUMPS
|
||||||
if (g_ActiveConfig.bDumpTevStages)
|
if (g_ActiveConfig.bDumpTevStages)
|
||||||
|
@ -712,8 +685,8 @@ void Tev::Draw()
|
||||||
// convert to 8 bits per component
|
// convert to 8 bits per component
|
||||||
// the results of the last tev stage are put onto the screen,
|
// the results of the last tev stage are put onto the screen,
|
||||||
// regardless of the used destination register - TODO: Verify!
|
// regardless of the used destination register - TODO: Verify!
|
||||||
const u32 color_index = bpmem.combiners[bpmem.genMode.numtevstages].colorC.dest;
|
const u32 color_index = u32(bpmem.combiners[bpmem.genMode.numtevstages].colorC.dest.Value());
|
||||||
const u32 alpha_index = bpmem.combiners[bpmem.genMode.numtevstages].alphaC.dest;
|
const u32 alpha_index = u32(bpmem.combiners[bpmem.genMode.numtevstages].alphaC.dest.Value());
|
||||||
u8 output[4] = {(u8)Reg[alpha_index][ALP_C], (u8)Reg[color_index][BLU_C],
|
u8 output[4] = {(u8)Reg[alpha_index][ALP_C], (u8)Reg[color_index][BLU_C],
|
||||||
(u8)Reg[color_index][GRN_C], (u8)Reg[color_index][RED_C]};
|
(u8)Reg[color_index][GRN_C], (u8)Reg[color_index][RED_C]};
|
||||||
|
|
||||||
|
@ -721,34 +694,36 @@ void Tev::Draw()
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// z texture
|
// z texture
|
||||||
if (bpmem.ztex2.op)
|
if (bpmem.ztex2.op != ZTexOp::Disabled)
|
||||||
{
|
{
|
||||||
u32 ztex = bpmem.ztex1.bias;
|
u32 ztex = bpmem.ztex1.bias;
|
||||||
switch (bpmem.ztex2.type)
|
switch (bpmem.ztex2.type)
|
||||||
{
|
{
|
||||||
case 0: // 8 bit
|
case ZTexFormat::U8:
|
||||||
ztex += TexColor[ALP_C];
|
ztex += TexColor[ALP_C];
|
||||||
break;
|
break;
|
||||||
case 1: // 16 bit
|
case ZTexFormat::U16:
|
||||||
ztex += TexColor[ALP_C] << 8 | TexColor[RED_C];
|
ztex += TexColor[ALP_C] << 8 | TexColor[RED_C];
|
||||||
break;
|
break;
|
||||||
case 2: // 24 bit
|
case ZTexFormat::U24:
|
||||||
ztex += TexColor[RED_C] << 16 | TexColor[GRN_C] << 8 | TexColor[BLU_C];
|
ztex += TexColor[RED_C] << 16 | TexColor[GRN_C] << 8 | TexColor[BLU_C];
|
||||||
break;
|
break;
|
||||||
|
default:
|
||||||
|
PanicAlertFmt("Invalid ztex format {}", bpmem.ztex2.type);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bpmem.ztex2.op == ZTEXTURE_ADD)
|
if (bpmem.ztex2.op == ZTexOp::Add)
|
||||||
ztex += Position[2];
|
ztex += Position[2];
|
||||||
|
|
||||||
Position[2] = ztex & 0x00ffffff;
|
Position[2] = ztex & 0x00ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
// fog
|
// fog
|
||||||
if (bpmem.fog.c_proj_fsel.fsel)
|
if (bpmem.fog.c_proj_fsel.fsel != FogType::Off)
|
||||||
{
|
{
|
||||||
float ze;
|
float ze;
|
||||||
|
|
||||||
if (bpmem.fog.c_proj_fsel.proj == 0)
|
if (bpmem.fog.c_proj_fsel.proj == FogProjection::Perspective)
|
||||||
{
|
{
|
||||||
// perspective
|
// perspective
|
||||||
// ze = A/(B - (Zs >> B_SHF))
|
// ze = A/(B - (Zs >> B_SHF))
|
||||||
|
@ -804,17 +779,17 @@ void Tev::Draw()
|
||||||
|
|
||||||
switch (bpmem.fog.c_proj_fsel.fsel)
|
switch (bpmem.fog.c_proj_fsel.fsel)
|
||||||
{
|
{
|
||||||
case 4: // exp
|
case FogType::Exp:
|
||||||
fog = 1.0f - pow(2.0f, -8.0f * fog);
|
fog = 1.0f - pow(2.0f, -8.0f * fog);
|
||||||
break;
|
break;
|
||||||
case 5: // exp2
|
case FogType::ExpSq:
|
||||||
fog = 1.0f - pow(2.0f, -8.0f * fog * fog);
|
fog = 1.0f - pow(2.0f, -8.0f * fog * fog);
|
||||||
break;
|
break;
|
||||||
case 6: // backward exp
|
case FogType::BackwardsExp:
|
||||||
fog = 1.0f - fog;
|
fog = 1.0f - fog;
|
||||||
fog = pow(2.0f, -8.0f * fog);
|
fog = pow(2.0f, -8.0f * fog);
|
||||||
break;
|
break;
|
||||||
case 7: // backward exp2
|
case FogType::BackwardsExpSq:
|
||||||
fog = 1.0f - fog;
|
fog = 1.0f - fog;
|
||||||
fog = pow(2.0f, -8.0f * fog * fog);
|
fog = pow(2.0f, -8.0f * fog * fog);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -57,7 +57,7 @@ class Tev
|
||||||
INDIRECT = 32
|
INDIRECT = 32
|
||||||
};
|
};
|
||||||
|
|
||||||
void SetRasColor(int colorChan, int swaptable);
|
void SetRasColor(RasColorChan colorChan, int swaptable);
|
||||||
|
|
||||||
void DrawColorRegular(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4]);
|
void DrawColorRegular(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4]);
|
||||||
void DrawColorCompare(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4]);
|
void DrawColorCompare(const TevStageCombiner::ColorCombiner& cc, const InputRegType inputs[4]);
|
||||||
|
|
|
@ -504,7 +504,7 @@ static void EncodeRGBA6(u8* dst, const u8* src, EFBCopyFormat format, bool yuv)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -743,7 +743,7 @@ static void EncodeRGBA6halfscale(u8* dst, const u8* src, EFBCopyFormat format, b
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -960,7 +960,7 @@ static void EncodeRGB8(u8* dst, const u8* src, EFBCopyFormat format, bool yuv)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1192,7 +1192,7 @@ static void EncodeRGB8halfscale(u8* dst, const u8* src, EFBCopyFormat format, bo
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1300,7 +1300,7 @@ static void EncodeZ24(u8* dst, const u8* src, EFBCopyFormat format)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1414,7 +1414,7 @@ static void EncodeZ24halfscale(u8* dst, const u8* src, EFBCopyFormat format)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown texture copy format: {:#x}\n", format);
|
PanicAlertFmt("Unknown texture copy format: {}\n", format);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1431,16 +1431,16 @@ void EncodeEfbCopy(u8* dst, const EFBCopyParams& params, u32 native_width, u32 b
|
||||||
{
|
{
|
||||||
switch (params.efb_format)
|
switch (params.efb_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
EncodeRGBA6halfscale(dst, src, params.copy_format, params.yuv);
|
EncodeRGBA6halfscale(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
EncodeRGB8halfscale(dst, src, params.copy_format, params.yuv);
|
EncodeRGB8halfscale(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
EncodeRGB8halfscale(dst, src, params.copy_format, params.yuv);
|
EncodeRGB8halfscale(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
EncodeZ24halfscale(dst, src, params.copy_format);
|
EncodeZ24halfscale(dst, src, params.copy_format);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1451,16 +1451,16 @@ void EncodeEfbCopy(u8* dst, const EFBCopyParams& params, u32 native_width, u32 b
|
||||||
{
|
{
|
||||||
switch (params.efb_format)
|
switch (params.efb_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
EncodeRGBA6(dst, src, params.copy_format, params.yuv);
|
EncodeRGBA6(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
EncodeRGB8(dst, src, params.copy_format, params.yuv);
|
EncodeRGB8(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
EncodeRGB8(dst, src, params.copy_format, params.yuv);
|
EncodeRGB8(dst, src, params.copy_format, params.yuv);
|
||||||
break;
|
break;
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
EncodeZ24(dst, src, params.copy_format);
|
EncodeZ24(dst, src, params.copy_format);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
|
@ -18,27 +19,29 @@
|
||||||
|
|
||||||
namespace TextureSampler
|
namespace TextureSampler
|
||||||
{
|
{
|
||||||
static inline void WrapCoord(int* coordp, int wrapMode, int imageSize)
|
static inline void WrapCoord(int* coordp, WrapMode wrapMode, int imageSize)
|
||||||
{
|
{
|
||||||
int coord = *coordp;
|
int coord = *coordp;
|
||||||
switch (wrapMode)
|
switch (wrapMode)
|
||||||
{
|
{
|
||||||
case 0: // clamp
|
case WrapMode::Clamp:
|
||||||
coord = (coord > imageSize) ? imageSize : (coord < 0) ? 0 : coord;
|
coord = (coord > imageSize) ? imageSize : (coord < 0) ? 0 : coord;
|
||||||
break;
|
break;
|
||||||
case 1: // wrap
|
case WrapMode::Repeat:
|
||||||
coord = coord % (imageSize + 1);
|
coord = coord % (imageSize + 1);
|
||||||
coord = (coord < 0) ? imageSize + coord : coord;
|
coord = (coord < 0) ? imageSize + coord : coord;
|
||||||
break;
|
break;
|
||||||
case 2: // mirror
|
case WrapMode::Mirror:
|
||||||
{
|
{
|
||||||
const int sizePlus1 = imageSize + 1;
|
const int sizePlus1 = imageSize + 1;
|
||||||
const int div = coord / sizePlus1;
|
const int div = coord / sizePlus1;
|
||||||
coord = coord - (div * sizePlus1);
|
coord = coord - (div * sizePlus1);
|
||||||
coord = (coord < 0) ? -coord : coord;
|
coord = (coord < 0) ? -coord : coord;
|
||||||
coord = (div & 1) ? imageSize - coord : coord;
|
coord = (div & 1) ? imageSize - coord : coord;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
default:
|
||||||
|
PanicAlertFmt("Invalid wrap mode: {}", wrapMode);
|
||||||
}
|
}
|
||||||
*coordp = coord;
|
*coordp = coord;
|
||||||
}
|
}
|
||||||
|
@ -74,10 +77,11 @@ void Sample(s32 s, s32 t, s32 lod, bool linear, u8 texmap, u8* sample)
|
||||||
{
|
{
|
||||||
// use mipmap
|
// use mipmap
|
||||||
baseMip = lod >> 4;
|
baseMip = lod >> 4;
|
||||||
mipLinear = (lodFract && tm0.min_filter & TexMode0::TEXF_LINEAR);
|
mipLinear = (lodFract && tm0.mipmap_filter == MipMode::Linear);
|
||||||
|
|
||||||
// if using nearest mip filter and lodFract >= 0.5 round up to next mip
|
// if using nearest mip filter and lodFract >= 0.5 round up to next mip
|
||||||
baseMip += (lodFract >> 3) & (tm0.min_filter & TexMode0::TEXF_POINT);
|
if (tm0.mipmap_filter == MipMode::Point && lodFract >= 8)
|
||||||
|
baseMip++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mipLinear)
|
if (mipLinear)
|
||||||
|
@ -111,12 +115,12 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
|
||||||
const TexMode0& tm0 = texUnit.texMode0[subTexmap];
|
const TexMode0& tm0 = texUnit.texMode0[subTexmap];
|
||||||
const TexImage0& ti0 = texUnit.texImage0[subTexmap];
|
const TexImage0& ti0 = texUnit.texImage0[subTexmap];
|
||||||
const TexTLUT& texTlut = texUnit.texTlut[subTexmap];
|
const TexTLUT& texTlut = texUnit.texTlut[subTexmap];
|
||||||
const TextureFormat texfmt = static_cast<TextureFormat>(ti0.format);
|
const TextureFormat texfmt = ti0.format;
|
||||||
const TLUTFormat tlutfmt = static_cast<TLUTFormat>(texTlut.tlut_format);
|
const TLUTFormat tlutfmt = texTlut.tlut_format;
|
||||||
|
|
||||||
const u8* imageSrc;
|
const u8* imageSrc;
|
||||||
const u8* imageSrcOdd = nullptr;
|
const u8* imageSrcOdd = nullptr;
|
||||||
if (texUnit.texImage1[subTexmap].image_type)
|
if (texUnit.texImage1[subTexmap].cache_manually_managed)
|
||||||
{
|
{
|
||||||
imageSrc = &texMem[texUnit.texImage1[subTexmap].tmem_even * TMEM_LINE_SIZE];
|
imageSrc = &texMem[texUnit.texImage1[subTexmap].tmem_even * TMEM_LINE_SIZE];
|
||||||
if (texfmt == TextureFormat::RGBA8)
|
if (texfmt == TextureFormat::RGBA8)
|
||||||
|
@ -188,7 +192,7 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
|
||||||
WrapCoord(&imageSPlus1, tm0.wrap_s, imageWidth);
|
WrapCoord(&imageSPlus1, tm0.wrap_s, imageWidth);
|
||||||
WrapCoord(&imageTPlus1, tm0.wrap_t, imageHeight);
|
WrapCoord(&imageTPlus1, tm0.wrap_t, imageHeight);
|
||||||
|
|
||||||
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1[subTexmap].image_type))
|
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1[subTexmap].cache_manually_managed))
|
||||||
{
|
{
|
||||||
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageS, imageT, imageWidth, texfmt, tlut,
|
TexDecoder_DecodeTexel(sampledTex, imageSrc, imageS, imageT, imageWidth, texfmt, tlut,
|
||||||
tlutfmt);
|
tlutfmt);
|
||||||
|
@ -240,7 +244,7 @@ void SampleMip(s32 s, s32 t, s32 mip, bool linear, u8 texmap, u8* sample)
|
||||||
WrapCoord(&imageS, tm0.wrap_s, imageWidth);
|
WrapCoord(&imageS, tm0.wrap_s, imageWidth);
|
||||||
WrapCoord(&imageT, tm0.wrap_t, imageHeight);
|
WrapCoord(&imageT, tm0.wrap_t, imageHeight);
|
||||||
|
|
||||||
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1[subTexmap].image_type))
|
if (!(texfmt == TextureFormat::RGBA8 && texUnit.texImage1[subTexmap].cache_manually_managed))
|
||||||
TexDecoder_DecodeTexel(sample, imageSrc, imageS, imageT, imageWidth, texfmt, tlut, tlutfmt);
|
TexDecoder_DecodeTexel(sample, imageSrc, imageS, imageT, imageWidth, texfmt, tlut, tlutfmt);
|
||||||
else
|
else
|
||||||
TexDecoder_DecodeTexelRGBA8FromTmem(sample, imageSrc, imageSrcOdd, imageS, imageT,
|
TexDecoder_DecodeTexelRGBA8FromTmem(sample, imageSrc, imageSrcOdd, imageS, imageT,
|
||||||
|
|
|
@ -80,7 +80,7 @@ void TransformPosition(const InputVertexData* src, OutputVertexData* dst)
|
||||||
const float* mat = &xfmem.posMatrices[src->posMtx * 4];
|
const float* mat = &xfmem.posMatrices[src->posMtx * 4];
|
||||||
MultiplyVec3Mat34(src->position, mat, dst->mvPosition);
|
MultiplyVec3Mat34(src->position, mat, dst->mvPosition);
|
||||||
|
|
||||||
if (xfmem.projection.type == GX_PERSPECTIVE)
|
if (xfmem.projection.type == ProjectionType::Perspective)
|
||||||
{
|
{
|
||||||
MultipleVec3Perspective(dst->mvPosition, xfmem.projection.rawProjection,
|
MultipleVec3Perspective(dst->mvPosition, xfmem.projection.rawProjection,
|
||||||
dst->projectedPosition);
|
dst->projectedPosition);
|
||||||
|
@ -115,39 +115,42 @@ static void TransformTexCoordRegular(const TexMtxInfo& texinfo, int coordNum,
|
||||||
Vec3 src;
|
Vec3 src;
|
||||||
switch (texinfo.sourcerow)
|
switch (texinfo.sourcerow)
|
||||||
{
|
{
|
||||||
case XF_SRCGEOM_INROW:
|
case SourceRow::Geom:
|
||||||
src = srcVertex->position;
|
src = srcVertex->position;
|
||||||
break;
|
break;
|
||||||
case XF_SRCNORMAL_INROW:
|
case SourceRow::Normal:
|
||||||
src = srcVertex->normal[0];
|
src = srcVertex->normal[0];
|
||||||
break;
|
break;
|
||||||
case XF_SRCBINORMAL_T_INROW:
|
case SourceRow::BinormalT:
|
||||||
src = srcVertex->normal[1];
|
src = srcVertex->normal[1];
|
||||||
break;
|
break;
|
||||||
case XF_SRCBINORMAL_B_INROW:
|
case SourceRow::BinormalB:
|
||||||
src = srcVertex->normal[2];
|
src = srcVertex->normal[2];
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(texinfo.sourcerow >= XF_SRCTEX0_INROW && texinfo.sourcerow <= XF_SRCTEX7_INROW);
|
{
|
||||||
src.x = srcVertex->texCoords[texinfo.sourcerow - XF_SRCTEX0_INROW][0];
|
ASSERT(texinfo.sourcerow >= SourceRow::Tex0 && texinfo.sourcerow <= SourceRow::Tex7);
|
||||||
src.y = srcVertex->texCoords[texinfo.sourcerow - XF_SRCTEX0_INROW][1];
|
u32 texnum = static_cast<u32>(texinfo.sourcerow.Value()) - static_cast<u32>(SourceRow::Tex0);
|
||||||
|
src.x = srcVertex->texCoords[texnum][0];
|
||||||
|
src.y = srcVertex->texCoords[texnum][1];
|
||||||
src.z = 1.0f;
|
src.z = 1.0f;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const float* mat = &xfmem.posMatrices[srcVertex->texMtx[coordNum] * 4];
|
const float* mat = &xfmem.posMatrices[srcVertex->texMtx[coordNum] * 4];
|
||||||
Vec3* dst = &dstVertex->texCoords[coordNum];
|
Vec3* dst = &dstVertex->texCoords[coordNum];
|
||||||
|
|
||||||
if (texinfo.projection == XF_TEXPROJ_ST)
|
if (texinfo.projection == TexSize::ST)
|
||||||
{
|
{
|
||||||
if (texinfo.inputform == XF_TEXINPUT_AB11)
|
if (texinfo.inputform == TexInputForm::AB11)
|
||||||
MultiplyVec2Mat24(src, mat, *dst);
|
MultiplyVec2Mat24(src, mat, *dst);
|
||||||
else
|
else
|
||||||
MultiplyVec3Mat24(src, mat, *dst);
|
MultiplyVec3Mat24(src, mat, *dst);
|
||||||
}
|
}
|
||||||
else // texinfo.projection == XF_TEXPROJ_STQ
|
else // texinfo.projection == TexSize::STQ
|
||||||
{
|
{
|
||||||
if (texinfo.inputform == XF_TEXINPUT_AB11)
|
if (texinfo.inputform == TexInputForm::AB11)
|
||||||
MultiplyVec2Mat34(src, mat, *dst);
|
MultiplyVec2Mat34(src, mat, *dst);
|
||||||
else
|
else
|
||||||
MultiplyVec3Mat34(src, mat, *dst);
|
MultiplyVec3Mat34(src, mat, *dst);
|
||||||
|
@ -209,28 +212,28 @@ static float CalculateLightAttn(const LightPointer* light, Vec3* _ldir, const Ve
|
||||||
|
|
||||||
switch (chan.attnfunc)
|
switch (chan.attnfunc)
|
||||||
{
|
{
|
||||||
case LIGHTATTN_NONE:
|
case AttenuationFunc::None:
|
||||||
case LIGHTATTN_DIR:
|
case AttenuationFunc::Dir:
|
||||||
{
|
{
|
||||||
ldir = ldir.Normalized();
|
ldir = ldir.Normalized();
|
||||||
if (ldir == Vec3(0.0f, 0.0f, 0.0f))
|
if (ldir == Vec3(0.0f, 0.0f, 0.0f))
|
||||||
ldir = normal;
|
ldir = normal;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LIGHTATTN_SPEC:
|
case AttenuationFunc::Spec:
|
||||||
{
|
{
|
||||||
ldir = ldir.Normalized();
|
ldir = ldir.Normalized();
|
||||||
attn = (ldir * normal) >= 0.0 ? std::max(0.0f, light->dir * normal) : 0;
|
attn = (ldir * normal) >= 0.0 ? std::max(0.0f, light->dir * normal) : 0;
|
||||||
Vec3 attLen = Vec3(1.0, attn, attn * attn);
|
Vec3 attLen = Vec3(1.0, attn, attn * attn);
|
||||||
Vec3 cosAttn = light->cosatt;
|
Vec3 cosAttn = light->cosatt;
|
||||||
Vec3 distAttn = light->distatt;
|
Vec3 distAttn = light->distatt;
|
||||||
if (chan.diffusefunc != LIGHTDIF_NONE)
|
if (chan.diffusefunc != DiffuseFunc::None)
|
||||||
distAttn = distAttn.Normalized();
|
distAttn = distAttn.Normalized();
|
||||||
|
|
||||||
attn = SafeDivide(std::max(0.0f, attLen * cosAttn), attLen * distAttn);
|
attn = SafeDivide(std::max(0.0f, attLen * cosAttn), attLen * distAttn);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LIGHTATTN_SPOT:
|
case AttenuationFunc::Spot:
|
||||||
{
|
{
|
||||||
float dist2 = ldir.Length2();
|
float dist2 = ldir.Length2();
|
||||||
float dist = sqrtf(dist2);
|
float dist = sqrtf(dist2);
|
||||||
|
@ -243,7 +246,7 @@ static float CalculateLightAttn(const LightPointer* light, Vec3* _ldir, const Ve
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("LightColor");
|
PanicAlertFmt("Invalid attnfunc: {}", chan.attnfunc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return attn;
|
return attn;
|
||||||
|
@ -260,18 +263,18 @@ static void LightColor(const Vec3& pos, const Vec3& normal, u8 lightNum, const L
|
||||||
float difAttn = ldir * normal;
|
float difAttn = ldir * normal;
|
||||||
switch (chan.diffusefunc)
|
switch (chan.diffusefunc)
|
||||||
{
|
{
|
||||||
case LIGHTDIF_NONE:
|
case DiffuseFunc::None:
|
||||||
AddScaledIntegerColor(light->color, attn, lightCol);
|
AddScaledIntegerColor(light->color, attn, lightCol);
|
||||||
break;
|
break;
|
||||||
case LIGHTDIF_SIGN:
|
case DiffuseFunc::Sign:
|
||||||
AddScaledIntegerColor(light->color, attn * difAttn, lightCol);
|
AddScaledIntegerColor(light->color, attn * difAttn, lightCol);
|
||||||
break;
|
break;
|
||||||
case LIGHTDIF_CLAMP:
|
case DiffuseFunc::Clamp:
|
||||||
difAttn = std::max(0.0f, difAttn);
|
difAttn = std::max(0.0f, difAttn);
|
||||||
AddScaledIntegerColor(light->color, attn * difAttn, lightCol);
|
AddScaledIntegerColor(light->color, attn * difAttn, lightCol);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(0);
|
PanicAlertFmt("Invalid diffusefunc: {}", chan.attnfunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,18 +289,18 @@ static void LightAlpha(const Vec3& pos, const Vec3& normal, u8 lightNum, const L
|
||||||
float difAttn = ldir * normal;
|
float difAttn = ldir * normal;
|
||||||
switch (chan.diffusefunc)
|
switch (chan.diffusefunc)
|
||||||
{
|
{
|
||||||
case LIGHTDIF_NONE:
|
case DiffuseFunc::None:
|
||||||
lightCol += light->color[0] * attn;
|
lightCol += light->color[0] * attn;
|
||||||
break;
|
break;
|
||||||
case LIGHTDIF_SIGN:
|
case DiffuseFunc::Sign:
|
||||||
lightCol += light->color[0] * attn * difAttn;
|
lightCol += light->color[0] * attn * difAttn;
|
||||||
break;
|
break;
|
||||||
case LIGHTDIF_CLAMP:
|
case DiffuseFunc::Clamp:
|
||||||
difAttn = std::max(0.0f, difAttn);
|
difAttn = std::max(0.0f, difAttn);
|
||||||
lightCol += light->color[0] * attn * difAttn;
|
lightCol += light->color[0] * attn * difAttn;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(0);
|
PanicAlertFmt("Invalid diffusefunc: {}", chan.attnfunc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -311,17 +314,16 @@ void TransformColor(const InputVertexData* src, OutputVertexData* dst)
|
||||||
|
|
||||||
// color
|
// color
|
||||||
const LitChannel& colorchan = xfmem.color[chan];
|
const LitChannel& colorchan = xfmem.color[chan];
|
||||||
if (colorchan.matsource)
|
if (colorchan.matsource == MatSource::Vertex)
|
||||||
matcolor = src->color[chan]; // vertex
|
matcolor = src->color[chan];
|
||||||
else
|
else
|
||||||
std::memcpy(matcolor.data(), &xfmem.matColor[chan], sizeof(u32));
|
std::memcpy(matcolor.data(), &xfmem.matColor[chan], sizeof(u32));
|
||||||
|
|
||||||
if (colorchan.enablelighting)
|
if (colorchan.enablelighting)
|
||||||
{
|
{
|
||||||
Vec3 lightCol;
|
Vec3 lightCol;
|
||||||
if (colorchan.ambsource)
|
if (colorchan.ambsource == AmbSource::Vertex)
|
||||||
{
|
{
|
||||||
// vertex
|
|
||||||
lightCol.x = src->color[chan][1];
|
lightCol.x = src->color[chan][1];
|
||||||
lightCol.y = src->color[chan][2];
|
lightCol.y = src->color[chan][2];
|
||||||
lightCol.z = src->color[chan][3];
|
lightCol.z = src->color[chan][3];
|
||||||
|
@ -355,16 +357,16 @@ void TransformColor(const InputVertexData* src, OutputVertexData* dst)
|
||||||
|
|
||||||
// alpha
|
// alpha
|
||||||
const LitChannel& alphachan = xfmem.alpha[chan];
|
const LitChannel& alphachan = xfmem.alpha[chan];
|
||||||
if (alphachan.matsource)
|
if (alphachan.matsource == MatSource::Vertex)
|
||||||
matcolor[0] = src->color[chan][0]; // vertex
|
matcolor[0] = src->color[chan][0];
|
||||||
else
|
else
|
||||||
matcolor[0] = xfmem.matColor[chan] & 0xff;
|
matcolor[0] = xfmem.matColor[chan] & 0xff;
|
||||||
|
|
||||||
if (xfmem.alpha[chan].enablelighting)
|
if (xfmem.alpha[chan].enablelighting)
|
||||||
{
|
{
|
||||||
float lightCol;
|
float lightCol;
|
||||||
if (alphachan.ambsource)
|
if (alphachan.ambsource == AmbSource::Vertex)
|
||||||
lightCol = src->color[chan][0]; // vertex
|
lightCol = src->color[chan][0];
|
||||||
else
|
else
|
||||||
lightCol = static_cast<float>(xfmem.ambColor[chan] & 0xff);
|
lightCol = static_cast<float>(xfmem.ambColor[chan] & 0xff);
|
||||||
|
|
||||||
|
@ -397,10 +399,10 @@ void TransformTexCoord(const InputVertexData* src, OutputVertexData* dst)
|
||||||
|
|
||||||
switch (texinfo.texgentype)
|
switch (texinfo.texgentype)
|
||||||
{
|
{
|
||||||
case XF_TEXGEN_REGULAR:
|
case TexGenType::Regular:
|
||||||
TransformTexCoordRegular(texinfo, coordNum, src, dst);
|
TransformTexCoordRegular(texinfo, coordNum, src, dst);
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_EMBOSS_MAP:
|
case TexGenType::EmbossMap:
|
||||||
{
|
{
|
||||||
const LightPointer* light = (const LightPointer*)&xfmem.lights[texinfo.embosslightshift];
|
const LightPointer* light = (const LightPointer*)&xfmem.lights[texinfo.embosslightshift];
|
||||||
|
|
||||||
|
@ -413,22 +415,22 @@ void TransformTexCoord(const InputVertexData* src, OutputVertexData* dst)
|
||||||
dst->texCoords[coordNum].z = dst->texCoords[texinfo.embosssourceshift].z;
|
dst->texCoords[coordNum].z = dst->texCoords[texinfo.embosssourceshift].z;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_COLOR_STRGBC0:
|
case TexGenType::Color0:
|
||||||
ASSERT(texinfo.sourcerow == XF_SRCCOLORS_INROW);
|
ASSERT(texinfo.sourcerow == SourceRow::Colors);
|
||||||
ASSERT(texinfo.inputform == XF_TEXINPUT_AB11);
|
ASSERT(texinfo.inputform == TexInputForm::AB11);
|
||||||
dst->texCoords[coordNum].x = (float)dst->color[0][0] / 255.0f;
|
dst->texCoords[coordNum].x = (float)dst->color[0][0] / 255.0f;
|
||||||
dst->texCoords[coordNum].y = (float)dst->color[0][1] / 255.0f;
|
dst->texCoords[coordNum].y = (float)dst->color[0][1] / 255.0f;
|
||||||
dst->texCoords[coordNum].z = 1.0f;
|
dst->texCoords[coordNum].z = 1.0f;
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_COLOR_STRGBC1:
|
case TexGenType::Color1:
|
||||||
ASSERT(texinfo.sourcerow == XF_SRCCOLORS_INROW);
|
ASSERT(texinfo.sourcerow == SourceRow::Colors);
|
||||||
ASSERT(texinfo.inputform == XF_TEXINPUT_AB11);
|
ASSERT(texinfo.inputform == TexInputForm::AB11);
|
||||||
dst->texCoords[coordNum].x = (float)dst->color[1][0] / 255.0f;
|
dst->texCoords[coordNum].x = (float)dst->color[1][0] / 255.0f;
|
||||||
dst->texCoords[coordNum].y = (float)dst->color[1][1] / 255.0f;
|
dst->texCoords[coordNum].y = (float)dst->color[1][1] / 255.0f;
|
||||||
dst->texCoords[coordNum].z = 1.0f;
|
dst->texCoords[coordNum].z = 1.0f;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ERROR_LOG_FMT(VIDEO, "Bad tex gen type {}", texinfo.texgentype.Value());
|
ERROR_LOG_FMT(VIDEO, "Bad tex gen type {}", texinfo.texgentype);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,13 +52,13 @@ GetVulkanRasterizationState(const RasterizationState& state)
|
||||||
depth_clamp, // VkBool32 depthClampEnable
|
depth_clamp, // VkBool32 depthClampEnable
|
||||||
VK_FALSE, // VkBool32 rasterizerDiscardEnable
|
VK_FALSE, // VkBool32 rasterizerDiscardEnable
|
||||||
VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode
|
VK_POLYGON_MODE_FILL, // VkPolygonMode polygonMode
|
||||||
cull_modes[state.cullmode], // VkCullModeFlags cullMode
|
cull_modes[u32(state.cullmode.Value())], // VkCullModeFlags cullMode
|
||||||
VK_FRONT_FACE_CLOCKWISE, // VkFrontFace frontFace
|
VK_FRONT_FACE_CLOCKWISE, // VkFrontFace frontFace
|
||||||
VK_FALSE, // VkBool32 depthBiasEnable
|
VK_FALSE, // VkBool32 depthBiasEnable
|
||||||
0.0f, // float depthBiasConstantFactor
|
0.0f, // float depthBiasConstantFactor
|
||||||
0.0f, // float depthBiasClamp
|
0.0f, // float depthBiasClamp
|
||||||
0.0f, // float depthBiasSlopeFactor
|
0.0f, // float depthBiasSlopeFactor
|
||||||
1.0f // float lineWidth
|
1.0f // float lineWidth
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,31 +85,32 @@ static VkPipelineDepthStencilStateCreateInfo GetVulkanDepthStencilState(const De
|
||||||
bool inverted_depth = !g_ActiveConfig.backend_info.bSupportsReversedDepthRange;
|
bool inverted_depth = !g_ActiveConfig.backend_info.bSupportsReversedDepthRange;
|
||||||
switch (state.func)
|
switch (state.func)
|
||||||
{
|
{
|
||||||
case ZMode::NEVER:
|
case CompareMode::Never:
|
||||||
compare_op = VK_COMPARE_OP_NEVER;
|
compare_op = VK_COMPARE_OP_NEVER;
|
||||||
break;
|
break;
|
||||||
case ZMode::LESS:
|
case CompareMode::Less:
|
||||||
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER : VK_COMPARE_OP_LESS;
|
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER : VK_COMPARE_OP_LESS;
|
||||||
break;
|
break;
|
||||||
case ZMode::EQUAL:
|
case CompareMode::Equal:
|
||||||
compare_op = VK_COMPARE_OP_EQUAL;
|
compare_op = VK_COMPARE_OP_EQUAL;
|
||||||
break;
|
break;
|
||||||
case ZMode::LEQUAL:
|
case CompareMode::LEqual:
|
||||||
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER_OR_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL;
|
compare_op = inverted_depth ? VK_COMPARE_OP_GREATER_OR_EQUAL : VK_COMPARE_OP_LESS_OR_EQUAL;
|
||||||
break;
|
break;
|
||||||
case ZMode::GREATER:
|
case CompareMode::Greater:
|
||||||
compare_op = inverted_depth ? VK_COMPARE_OP_LESS : VK_COMPARE_OP_GREATER;
|
compare_op = inverted_depth ? VK_COMPARE_OP_LESS : VK_COMPARE_OP_GREATER;
|
||||||
break;
|
break;
|
||||||
case ZMode::NEQUAL:
|
case CompareMode::NEqual:
|
||||||
compare_op = VK_COMPARE_OP_NOT_EQUAL;
|
compare_op = VK_COMPARE_OP_NOT_EQUAL;
|
||||||
break;
|
break;
|
||||||
case ZMode::GEQUAL:
|
case CompareMode::GEqual:
|
||||||
compare_op = inverted_depth ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_GREATER_OR_EQUAL;
|
compare_op = inverted_depth ? VK_COMPARE_OP_LESS_OR_EQUAL : VK_COMPARE_OP_GREATER_OR_EQUAL;
|
||||||
break;
|
break;
|
||||||
case ZMode::ALWAYS:
|
case CompareMode::Always:
|
||||||
compare_op = VK_COMPARE_OP_ALWAYS;
|
compare_op = VK_COMPARE_OP_ALWAYS;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
PanicAlertFmt("Invalid compare mode {}", state.func);
|
||||||
compare_op = VK_COMPARE_OP_ALWAYS;
|
compare_op = VK_COMPARE_OP_ALWAYS;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -150,10 +151,10 @@ static VkPipelineColorBlendAttachmentState GetVulkanAttachmentBlendState(const B
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, VK_BLEND_FACTOR_DST_ALPHA,
|
VK_BLEND_FACTOR_ONE_MINUS_SRC1_ALPHA, VK_BLEND_FACTOR_DST_ALPHA,
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA}};
|
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA}};
|
||||||
|
|
||||||
vk_state.srcColorBlendFactor = src_factors[state.srcfactor];
|
vk_state.srcColorBlendFactor = src_factors[u32(state.srcfactor.Value())];
|
||||||
vk_state.srcAlphaBlendFactor = src_factors[state.srcfactoralpha];
|
vk_state.srcAlphaBlendFactor = src_factors[u32(state.srcfactoralpha.Value())];
|
||||||
vk_state.dstColorBlendFactor = dst_factors[state.dstfactor];
|
vk_state.dstColorBlendFactor = dst_factors[u32(state.dstfactor.Value())];
|
||||||
vk_state.dstAlphaBlendFactor = dst_factors[state.dstfactoralpha];
|
vk_state.dstAlphaBlendFactor = dst_factors[u32(state.dstfactoralpha.Value())];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -169,10 +170,10 @@ static VkPipelineColorBlendAttachmentState GetVulkanAttachmentBlendState(const B
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_DST_ALPHA,
|
VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, VK_BLEND_FACTOR_DST_ALPHA,
|
||||||
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA}};
|
VK_BLEND_FACTOR_ONE_MINUS_DST_ALPHA}};
|
||||||
|
|
||||||
vk_state.srcColorBlendFactor = src_factors[state.srcfactor];
|
vk_state.srcColorBlendFactor = src_factors[u32(state.srcfactor.Value())];
|
||||||
vk_state.srcAlphaBlendFactor = src_factors[state.srcfactoralpha];
|
vk_state.srcAlphaBlendFactor = src_factors[u32(state.srcfactoralpha.Value())];
|
||||||
vk_state.dstColorBlendFactor = dst_factors[state.dstfactor];
|
vk_state.dstColorBlendFactor = dst_factors[u32(state.dstfactor.Value())];
|
||||||
vk_state.dstAlphaBlendFactor = dst_factors[state.dstfactoralpha];
|
vk_state.dstAlphaBlendFactor = dst_factors[u32(state.dstfactoralpha.Value())];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.colorupdate)
|
if (state.colorupdate)
|
||||||
|
@ -211,7 +212,8 @@ GetVulkanColorBlendState(const BlendingState& state,
|
||||||
vk_logic_op_enable = VK_FALSE;
|
vk_logic_op_enable = VK_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
VkLogicOp vk_logic_op = vk_logic_op_enable ? vk_logic_ops[state.logicmode] : VK_LOGIC_OP_CLEAR;
|
VkLogicOp vk_logic_op =
|
||||||
|
vk_logic_op_enable ? vk_logic_ops[u32(state.logicmode.Value())] : VK_LOGIC_OP_CLEAR;
|
||||||
|
|
||||||
VkPipelineColorBlendStateCreateInfo vk_state = {
|
VkPipelineColorBlendStateCreateInfo vk_state = {
|
||||||
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO, // VkStructureType sType
|
||||||
|
|
|
@ -166,9 +166,9 @@ void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool color_enable
|
||||||
|
|
||||||
// Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
|
// Determine whether the EFB has an alpha channel. If it doesn't, we can clear the alpha
|
||||||
// channel to 0xFF. This hopefully allows us to use the fast path in most cases.
|
// channel to 0xFF. This hopefully allows us to use the fast path in most cases.
|
||||||
if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16 ||
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16 ||
|
||||||
bpmem.zcontrol.pixel_format == PEControl::RGB8_Z24 ||
|
bpmem.zcontrol.pixel_format == PixelFormat::RGB8_Z24 ||
|
||||||
bpmem.zcontrol.pixel_format == PEControl::Z24)
|
bpmem.zcontrol.pixel_format == PixelFormat::Z24)
|
||||||
{
|
{
|
||||||
// Force alpha writes, and clear the alpha channel. This is different to the other backends,
|
// Force alpha writes, and clear the alpha channel. This is different to the other backends,
|
||||||
// where the existing values of the alpha channel are preserved.
|
// where the existing values of the alpha channel are preserved.
|
||||||
|
|
|
@ -184,8 +184,8 @@ void ClearScreen(const MathUtil::Rectangle<int>& rc)
|
||||||
auto pixel_format = bpmem.zcontrol.pixel_format;
|
auto pixel_format = bpmem.zcontrol.pixel_format;
|
||||||
|
|
||||||
// (1): Disable unused color channels
|
// (1): Disable unused color channels
|
||||||
if (pixel_format == PEControl::RGB8_Z24 || pixel_format == PEControl::RGB565_Z16 ||
|
if (pixel_format == PixelFormat::RGB8_Z24 || pixel_format == PixelFormat::RGB565_Z16 ||
|
||||||
pixel_format == PEControl::Z24)
|
pixel_format == PixelFormat::Z24)
|
||||||
{
|
{
|
||||||
alphaEnable = false;
|
alphaEnable = false;
|
||||||
}
|
}
|
||||||
|
@ -196,11 +196,11 @@ void ClearScreen(const MathUtil::Rectangle<int>& rc)
|
||||||
u32 z = bpmem.clearZValue;
|
u32 z = bpmem.clearZValue;
|
||||||
|
|
||||||
// (2) drop additional accuracy
|
// (2) drop additional accuracy
|
||||||
if (pixel_format == PEControl::RGBA6_Z24)
|
if (pixel_format == PixelFormat::RGBA6_Z24)
|
||||||
{
|
{
|
||||||
color = RGBA8ToRGBA6ToRGBA8(color);
|
color = RGBA8ToRGBA6ToRGBA8(color);
|
||||||
}
|
}
|
||||||
else if (pixel_format == PEControl::RGB565_Z16)
|
else if (pixel_format == PixelFormat::RGB565_Z16)
|
||||||
{
|
{
|
||||||
color = RGBA8ToRGB565ToRGBA8(color);
|
color = RGBA8ToRGB565ToRGBA8(color);
|
||||||
z = Z24ToZ16ToZ24(z);
|
z = Z24ToZ16ToZ24(z);
|
||||||
|
@ -228,29 +228,28 @@ void OnPixelFormatChange()
|
||||||
const auto new_format = bpmem.zcontrol.pixel_format;
|
const auto new_format = bpmem.zcontrol.pixel_format;
|
||||||
g_renderer->StorePixelFormat(new_format);
|
g_renderer->StorePixelFormat(new_format);
|
||||||
|
|
||||||
DEBUG_LOG_FMT(VIDEO, "pixelfmt: pixel={}, zc={}", static_cast<int>(new_format),
|
DEBUG_LOG_FMT(VIDEO, "pixelfmt: pixel={}, zc={}", new_format, bpmem.zcontrol.zformat);
|
||||||
static_cast<int>(bpmem.zcontrol.zformat));
|
|
||||||
|
|
||||||
// no need to reinterpret pixel data in these cases
|
// no need to reinterpret pixel data in these cases
|
||||||
if (new_format == old_format || old_format == PEControl::INVALID_FMT)
|
if (new_format == old_format || old_format == PixelFormat::INVALID_FMT)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Check for pixel format changes
|
// Check for pixel format changes
|
||||||
switch (old_format)
|
switch (old_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
case PEControl::Z24:
|
case PixelFormat::Z24:
|
||||||
{
|
{
|
||||||
// Z24 and RGB8_Z24 are treated equal, so just return in this case
|
// Z24 and RGB8_Z24 are treated equal, so just return in this case
|
||||||
if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
|
if (new_format == PixelFormat::RGB8_Z24 || new_format == PixelFormat::Z24)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (new_format == PEControl::RGBA6_Z24)
|
if (new_format == PixelFormat::RGBA6_Z24)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGBA6);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGBA6);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (new_format == PEControl::RGB565_Z16)
|
else if (new_format == PixelFormat::RGB565_Z16)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGB565);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB8ToRGB565);
|
||||||
return;
|
return;
|
||||||
|
@ -258,14 +257,14 @@ void OnPixelFormatChange()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
{
|
{
|
||||||
if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
|
if (new_format == PixelFormat::RGB8_Z24 || new_format == PixelFormat::Z24)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB8);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (new_format == PEControl::RGB565_Z16)
|
else if (new_format == PixelFormat::RGB565_Z16)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB565);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGBA6ToRGB565);
|
||||||
return;
|
return;
|
||||||
|
@ -273,14 +272,14 @@ void OnPixelFormatChange()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
{
|
{
|
||||||
if (new_format == PEControl::RGB8_Z24 || new_format == PEControl::Z24)
|
if (new_format == PixelFormat::RGB8_Z24 || new_format == PixelFormat::Z24)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGB8);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGB8);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (new_format == PEControl::RGBA6_Z24)
|
else if (new_format == PixelFormat::RGBA6_Z24)
|
||||||
{
|
{
|
||||||
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGBA6);
|
g_renderer->ReinterpretPixelData(EFBReinterpretType::RGB565ToRGBA6);
|
||||||
return;
|
return;
|
||||||
|
@ -292,8 +291,7 @@ void OnPixelFormatChange()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ERROR_LOG_FMT(VIDEO, "Unhandled EFB format change: {} to {}", static_cast<int>(old_format),
|
ERROR_LOG_FMT(VIDEO, "Unhandled EFB format change: {} to {}", old_format, new_format);
|
||||||
static_cast<int>(new_format));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetInterlacingMode(const BPCmd& bp)
|
void SetInterlacingMode(const BPCmd& bp)
|
||||||
|
@ -305,17 +303,15 @@ void SetInterlacingMode(const BPCmd& bp)
|
||||||
{
|
{
|
||||||
// SDK always sets bpmem.lineptwidth.lineaspect via BPMEM_LINEPTWIDTH
|
// SDK always sets bpmem.lineptwidth.lineaspect via BPMEM_LINEPTWIDTH
|
||||||
// just before this cmd
|
// just before this cmd
|
||||||
static constexpr std::string_view action[] = {"don't adjust", "adjust"};
|
DEBUG_LOG_FMT(VIDEO, "BPMEM_FIELDMODE texLOD:{} lineaspect:{}", bpmem.fieldmode.texLOD,
|
||||||
DEBUG_LOG_FMT(VIDEO, "BPMEM_FIELDMODE texLOD:{} lineaspect:{}", action[bpmem.fieldmode.texLOD],
|
bpmem.lineptwidth.adjust_for_aspect_ratio);
|
||||||
action[bpmem.lineptwidth.lineaspect]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BPMEM_FIELDMASK:
|
case BPMEM_FIELDMASK:
|
||||||
{
|
{
|
||||||
// Determines if fields will be written to EFB (always computed)
|
// Determines if fields will be written to EFB (always computed)
|
||||||
static constexpr std::string_view action[] = {"skip", "write"};
|
DEBUG_LOG_FMT(VIDEO, "BPMEM_FIELDMASK even:{} odd:{}", bpmem.fieldmask.even,
|
||||||
DEBUG_LOG_FMT(VIDEO, "BPMEM_FIELDMASK even:{} odd:{}", action[bpmem.fieldmask.even],
|
bpmem.fieldmask.odd);
|
||||||
action[bpmem.fieldmask.odd]);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -17,7 +17,7 @@ bool BlendMode::UseLogicOp() const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
|
// Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
|
||||||
if (logicmode == BlendMode::NOOP)
|
if (logicmode == LogicOp::NoOp)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -47,16 +47,26 @@ bool FogParams::IsNaNCase() const
|
||||||
return a.exp == 255 && c_proj_fsel.c_exp == 255;
|
return a.exp == 255 && c_proj_fsel.c_exp == 255;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float FogParam0::FloatValue() const
|
||||||
|
{
|
||||||
|
// scale mantissa from 11 to 23 bits
|
||||||
|
const u32 integral = (sign << 31) | (exp << 23) | (mant << 12);
|
||||||
|
return Common::BitCast<float>(integral);
|
||||||
|
}
|
||||||
|
|
||||||
|
float FogParam3::FloatValue() const
|
||||||
|
{
|
||||||
|
// scale mantissa from 11 to 23 bits
|
||||||
|
const u32 integral = (c_sign << 31) | (c_exp << 23) | (c_mant << 12);
|
||||||
|
return Common::BitCast<float>(integral);
|
||||||
|
}
|
||||||
|
|
||||||
float FogParams::GetA() const
|
float FogParams::GetA() const
|
||||||
{
|
{
|
||||||
if (IsNaNCase())
|
if (IsNaNCase())
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
// scale mantissa from 11 to 23 bits
|
return a.FloatValue();
|
||||||
const u32 integral = (static_cast<u32>(a.sign) << 31) | (static_cast<u32>(a.exp) << 23) |
|
|
||||||
(static_cast<u32>(a.mant) << 12);
|
|
||||||
|
|
||||||
return Common::BitCast<float>(integral);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
float FogParams::GetC() const
|
float FogParams::GetC() const
|
||||||
|
@ -67,9 +77,5 @@ float FogParams::GetC() const
|
||||||
return !a.sign && !c_proj_fsel.c_sign ? -inf : inf;
|
return !a.sign && !c_proj_fsel.c_sign ? -inf : inf;
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale mantissa from 11 to 23 bits
|
return c_proj_fsel.FloatValue();
|
||||||
const u32 integral = (c_proj_fsel.c_sign.Value() << 31) | (c_proj_fsel.c_exp.Value() << 23) |
|
|
||||||
(c_proj_fsel.c_mant.Value() << 12);
|
|
||||||
|
|
||||||
return Common::BitCast<float>(integral);
|
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -17,7 +17,9 @@ void DoCPState(PointerWrap& p)
|
||||||
p.DoArray(g_main_cp_state.array_strides);
|
p.DoArray(g_main_cp_state.array_strides);
|
||||||
p.Do(g_main_cp_state.matrix_index_a);
|
p.Do(g_main_cp_state.matrix_index_a);
|
||||||
p.Do(g_main_cp_state.matrix_index_b);
|
p.Do(g_main_cp_state.matrix_index_b);
|
||||||
p.Do(g_main_cp_state.vtx_desc.Hex);
|
u64 vtx_desc = g_main_cp_state.vtx_desc.GetLegacyHex();
|
||||||
|
p.Do(vtx_desc);
|
||||||
|
g_main_cp_state.vtx_desc.SetLegacyHex(vtx_desc);
|
||||||
p.DoArray(g_main_cp_state.vtx_attr);
|
p.DoArray(g_main_cp_state.vtx_attr);
|
||||||
p.DoMarker("CP Memory");
|
p.DoMarker("CP Memory");
|
||||||
if (p.mode == PointerWrap::MODE_READ)
|
if (p.mode == PointerWrap::MODE_READ)
|
||||||
|
@ -31,3 +33,44 @@ void CopyPreprocessCPStateFromMain()
|
||||||
{
|
{
|
||||||
memcpy(&g_preprocess_cp_state, &g_main_cp_state, sizeof(CPState));
|
memcpy(&g_preprocess_cp_state, &g_main_cp_state, sizeof(CPState));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> GetCPRegInfo(u8 cmd, u32 value)
|
||||||
|
{
|
||||||
|
switch (cmd & CP_COMMAND_MASK)
|
||||||
|
{
|
||||||
|
case MATINDEX_A:
|
||||||
|
return std::make_pair("MATINDEX_A", fmt::to_string(TMatrixIndexA{.Hex = value}));
|
||||||
|
case MATINDEX_B:
|
||||||
|
return std::make_pair("MATINDEX_B", fmt::to_string(TMatrixIndexB{.Hex = value}));
|
||||||
|
case VCD_LO:
|
||||||
|
return std::make_pair("VCD_LO", fmt::to_string(TVtxDesc::Low{.Hex = value}));
|
||||||
|
case VCD_HI:
|
||||||
|
return std::make_pair("VCD_HI", fmt::to_string(TVtxDesc::High{.Hex = value}));
|
||||||
|
case CP_VAT_REG_A:
|
||||||
|
if (cmd - CP_VAT_REG_A >= CP_NUM_VAT_REG)
|
||||||
|
return std::make_pair("CP_VAT_REG_A invalid", "");
|
||||||
|
|
||||||
|
return std::make_pair(fmt::format("CP_VAT_REG_A - Format {}", cmd & CP_VAT_MASK),
|
||||||
|
fmt::to_string(UVAT_group0{.Hex = value}));
|
||||||
|
case CP_VAT_REG_B:
|
||||||
|
if (cmd - CP_VAT_REG_B >= CP_NUM_VAT_REG)
|
||||||
|
return std::make_pair("CP_VAT_REG_B invalid", "");
|
||||||
|
|
||||||
|
return std::make_pair(fmt::format("CP_VAT_REG_B - Format {}", cmd & CP_VAT_MASK),
|
||||||
|
fmt::to_string(UVAT_group1{.Hex = value}));
|
||||||
|
case CP_VAT_REG_C:
|
||||||
|
if (cmd - CP_VAT_REG_C >= CP_NUM_VAT_REG)
|
||||||
|
return std::make_pair("CP_VAT_REG_C invalid", "");
|
||||||
|
|
||||||
|
return std::make_pair(fmt::format("CP_VAT_REG_C - Format {}", cmd & CP_VAT_MASK),
|
||||||
|
fmt::to_string(UVAT_group2{.Hex = value}));
|
||||||
|
case ARRAY_BASE:
|
||||||
|
return std::make_pair(fmt::format("ARRAY_BASE Array {}", cmd & CP_ARRAY_MASK),
|
||||||
|
fmt::format("Base address {:08x}", value));
|
||||||
|
case ARRAY_STRIDE:
|
||||||
|
return std::make_pair(fmt::format("ARRAY_STRIDE Array {}", cmd - ARRAY_STRIDE),
|
||||||
|
fmt::format("Stride {:02x}", value & 0xff));
|
||||||
|
default:
|
||||||
|
return std::make_pair(fmt::format("Invalid CP register {:02x} = {:08x}", cmd, value), "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -4,8 +4,47 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "Common/BitField.h"
|
||||||
#include "Common/BitSet.h"
|
#include "Common/BitSet.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
|
#include "Common/MsgHandler.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
// These commands use the high nybble for the command itself, and the lower nybble is an argument.
|
||||||
|
// TODO: However, Dolphin's implementation (in LoadCPReg) and YAGCD disagree about what values are
|
||||||
|
// valid for the lower nybble.
|
||||||
|
|
||||||
|
// YAGCD says 0x30 only; LoadCPReg allows any
|
||||||
|
MATINDEX_A = 0x30,
|
||||||
|
// YAGCD says 0x40 only; LoadCPReg allows any
|
||||||
|
MATINDEX_B = 0x40,
|
||||||
|
// YAGCD says 0x50-0x57 for distinct VCDs; LoadCPReg allows any for a single VCD
|
||||||
|
VCD_LO = 0x50,
|
||||||
|
// YAGCD says 0x60-0x67 for distinct VCDs; LoadCPReg allows any for a single VCD
|
||||||
|
VCD_HI = 0x60,
|
||||||
|
// YAGCD and LoadCPReg both agree that only 0x70-0x77 are valid
|
||||||
|
CP_VAT_REG_A = 0x70,
|
||||||
|
// YAGCD and LoadCPReg both agree that only 0x80-0x87 are valid
|
||||||
|
CP_VAT_REG_B = 0x80,
|
||||||
|
// YAGCD and LoadCPReg both agree that only 0x90-0x97 are valid
|
||||||
|
CP_VAT_REG_C = 0x90,
|
||||||
|
// YAGCD and LoadCPReg agree that 0xa0-0xaf are valid
|
||||||
|
ARRAY_BASE = 0xa0,
|
||||||
|
// YAGCD and LoadCPReg agree that 0xb0-0xbf are valid
|
||||||
|
ARRAY_STRIDE = 0xb0,
|
||||||
|
|
||||||
|
CP_COMMAND_MASK = 0xf0,
|
||||||
|
CP_NUM_VAT_REG = 0x08,
|
||||||
|
CP_VAT_MASK = 0x07,
|
||||||
|
CP_NUM_ARRAYS = 0x10,
|
||||||
|
CP_ARRAY_MASK = 0x0f,
|
||||||
|
};
|
||||||
|
|
||||||
// Vertex array numbers
|
// Vertex array numbers
|
||||||
enum
|
enum
|
||||||
|
@ -18,176 +57,407 @@ enum
|
||||||
};
|
};
|
||||||
|
|
||||||
// Vertex components
|
// Vertex components
|
||||||
enum
|
enum class VertexComponentFormat
|
||||||
{
|
{
|
||||||
NOT_PRESENT = 0,
|
NotPresent = 0,
|
||||||
DIRECT = 1,
|
Direct = 1,
|
||||||
INDEX8 = 2,
|
Index8 = 2,
|
||||||
INDEX16 = 3,
|
Index16 = 3,
|
||||||
|
};
|
||||||
MASK_INDEXED = 2,
|
template <>
|
||||||
|
struct fmt::formatter<VertexComponentFormat> : EnumFormatter<VertexComponentFormat::Index16>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"Not present", "Direct", "8-bit index", "16-bit index"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
constexpr bool IsIndexed(VertexComponentFormat format)
|
||||||
{
|
{
|
||||||
FORMAT_UBYTE = 0, // 2 Cmp
|
return format == VertexComponentFormat::Index8 || format == VertexComponentFormat::Index16;
|
||||||
FORMAT_BYTE = 1, // 3 Cmp
|
}
|
||||||
FORMAT_USHORT = 2,
|
|
||||||
FORMAT_SHORT = 3,
|
enum class ComponentFormat
|
||||||
FORMAT_FLOAT = 4,
|
{
|
||||||
|
UByte = 0, // Invalid for normals
|
||||||
|
Byte = 1,
|
||||||
|
UShort = 2, // Invalid for normals
|
||||||
|
Short = 3,
|
||||||
|
Float = 4,
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<ComponentFormat> : EnumFormatter<ComponentFormat::Float>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"Unsigned Byte", "Byte", "Unsigned Short", "Short", "Float"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
constexpr u32 GetElementSize(ComponentFormat format)
|
||||||
{
|
{
|
||||||
FORMAT_16B_565 = 0, // NA
|
switch (format)
|
||||||
FORMAT_24B_888 = 1,
|
|
||||||
FORMAT_32B_888x = 2,
|
|
||||||
FORMAT_16B_4444 = 3,
|
|
||||||
FORMAT_24B_6666 = 4,
|
|
||||||
FORMAT_32B_8888 = 5,
|
|
||||||
};
|
|
||||||
|
|
||||||
#pragma pack(4)
|
|
||||||
union TVtxDesc
|
|
||||||
{
|
|
||||||
u64 Hex;
|
|
||||||
struct
|
|
||||||
{
|
{
|
||||||
// 0: not present
|
case ComponentFormat::UByte:
|
||||||
// 1: present
|
case ComponentFormat::Byte:
|
||||||
u64 PosMatIdx : 1;
|
return 1;
|
||||||
u64 Tex0MatIdx : 1;
|
case ComponentFormat::UShort:
|
||||||
u64 Tex1MatIdx : 1;
|
case ComponentFormat::Short:
|
||||||
u64 Tex2MatIdx : 1;
|
return 2;
|
||||||
u64 Tex3MatIdx : 1;
|
case ComponentFormat::Float:
|
||||||
u64 Tex4MatIdx : 1;
|
return 4;
|
||||||
u64 Tex5MatIdx : 1;
|
default:
|
||||||
u64 Tex6MatIdx : 1;
|
PanicAlertFmt("Unknown format {}", format);
|
||||||
u64 Tex7MatIdx : 1;
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 00: not present
|
enum class CoordComponentCount
|
||||||
// 01: direct
|
{
|
||||||
// 10: 8 bit index
|
XY = 0,
|
||||||
// 11: 16 bit index
|
XYZ = 1,
|
||||||
u64 Position : 2;
|
};
|
||||||
u64 Normal : 2;
|
template <>
|
||||||
u64 Color0 : 2;
|
struct fmt::formatter<CoordComponentCount> : EnumFormatter<CoordComponentCount::XYZ>
|
||||||
u64 Color1 : 2;
|
{
|
||||||
u64 Tex0Coord : 2;
|
formatter() : EnumFormatter({"2 (x, y)", "3 (x, y, z)"}) {}
|
||||||
u64 Tex1Coord : 2;
|
};
|
||||||
u64 Tex2Coord : 2;
|
|
||||||
u64 Tex3Coord : 2;
|
enum class NormalComponentCount
|
||||||
u64 Tex4Coord : 2;
|
{
|
||||||
u64 Tex5Coord : 2;
|
N = 0,
|
||||||
u64 Tex6Coord : 2;
|
NBT = 1,
|
||||||
u64 Tex7Coord : 2;
|
};
|
||||||
u64 : 31;
|
template <>
|
||||||
|
struct fmt::formatter<NormalComponentCount> : EnumFormatter<NormalComponentCount::NBT>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"1 (n)", "3 (n, b, t)"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ColorComponentCount
|
||||||
|
{
|
||||||
|
RGB = 0,
|
||||||
|
RGBA = 1,
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<ColorComponentCount> : EnumFormatter<ColorComponentCount::RGBA>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"3 (r, g, b)", "4 (r, g, b, a)"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ColorFormat
|
||||||
|
{
|
||||||
|
RGB565 = 0, // 16b
|
||||||
|
RGB888 = 1, // 24b
|
||||||
|
RGB888x = 2, // 32b
|
||||||
|
RGBA4444 = 3, // 16b
|
||||||
|
RGBA6666 = 4, // 24b
|
||||||
|
RGBA8888 = 5, // 32b
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<ColorFormat> : EnumFormatter<ColorFormat::RGBA8888>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {
|
||||||
|
"RGB 16 bits 565", "RGB 24 bits 888", "RGB 32 bits 888x",
|
||||||
|
"RGBA 16 bits 4444", "RGBA 24 bits 6666", "RGBA 32 bits 8888",
|
||||||
|
};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TexComponentCount
|
||||||
|
{
|
||||||
|
S = 0,
|
||||||
|
ST = 1,
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TexComponentCount> : EnumFormatter<TexComponentCount::ST>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"1 (s)", "2 (s, t)"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TVtxDesc
|
||||||
|
{
|
||||||
|
union Low
|
||||||
|
{
|
||||||
|
// false: not present
|
||||||
|
// true: present
|
||||||
|
BitField<0, 1, bool, u32> PosMatIdx;
|
||||||
|
BitField<1, 1, bool, u32> Tex0MatIdx;
|
||||||
|
BitField<2, 1, bool, u32> Tex1MatIdx;
|
||||||
|
BitField<3, 1, bool, u32> Tex2MatIdx;
|
||||||
|
BitField<4, 1, bool, u32> Tex3MatIdx;
|
||||||
|
BitField<5, 1, bool, u32> Tex4MatIdx;
|
||||||
|
BitField<6, 1, bool, u32> Tex5MatIdx;
|
||||||
|
BitField<7, 1, bool, u32> Tex6MatIdx;
|
||||||
|
BitField<8, 1, bool, u32> Tex7MatIdx;
|
||||||
|
BitFieldArray<1, 1, 8, bool, u32> TexMatIdx;
|
||||||
|
|
||||||
|
BitField<9, 2, VertexComponentFormat> Position;
|
||||||
|
BitField<11, 2, VertexComponentFormat> Normal;
|
||||||
|
BitField<13, 2, VertexComponentFormat> Color0;
|
||||||
|
BitField<15, 2, VertexComponentFormat> Color1;
|
||||||
|
BitFieldArray<13, 2, 2, VertexComponentFormat> Color;
|
||||||
|
|
||||||
|
u32 Hex;
|
||||||
|
};
|
||||||
|
union High
|
||||||
|
{
|
||||||
|
BitField<0, 2, VertexComponentFormat> Tex0Coord;
|
||||||
|
BitField<2, 2, VertexComponentFormat> Tex1Coord;
|
||||||
|
BitField<4, 2, VertexComponentFormat> Tex2Coord;
|
||||||
|
BitField<6, 2, VertexComponentFormat> Tex3Coord;
|
||||||
|
BitField<8, 2, VertexComponentFormat> Tex4Coord;
|
||||||
|
BitField<10, 2, VertexComponentFormat> Tex5Coord;
|
||||||
|
BitField<12, 2, VertexComponentFormat> Tex6Coord;
|
||||||
|
BitField<14, 2, VertexComponentFormat> Tex7Coord;
|
||||||
|
BitFieldArray<0, 2, 8, VertexComponentFormat> TexCoord;
|
||||||
|
|
||||||
|
u32 Hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
Low low;
|
||||||
{
|
High high;
|
||||||
u32 Hex0, Hex1;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Easily index into the Position..Tex7Coord fields.
|
// This structure was originally packed into bits 0..32, using 33 total bits.
|
||||||
u32 GetVertexArrayStatus(int idx) { return (Hex >> (9 + idx * 2)) & 0x3; }
|
// The actual format has 17 bits in the low one and 16 bits in the high one,
|
||||||
|
// but the old format is still supported for compatibility.
|
||||||
|
u64 GetLegacyHex() const { return (low.Hex & 0x1FFFF) | (u64(high.Hex) << 17); }
|
||||||
|
u32 GetLegacyHex0() const { return static_cast<u32>(GetLegacyHex()); }
|
||||||
|
// Only *1* bit is used in this
|
||||||
|
u32 GetLegacyHex1() const { return static_cast<u32>(GetLegacyHex() >> 32); }
|
||||||
|
void SetLegacyHex(u64 value)
|
||||||
|
{
|
||||||
|
low.Hex = value & 0x1FFFF;
|
||||||
|
high.Hex = value >> 17;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TVtxDesc::Low>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const TVtxDesc::Low& desc, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
static constexpr std::array<const char*, 2> present = {"Not present", "Present"};
|
||||||
|
|
||||||
|
return format_to(ctx.out(),
|
||||||
|
"Position and normal matrix index: {}\n"
|
||||||
|
"Texture Coord 0 matrix index: {}\n"
|
||||||
|
"Texture Coord 1 matrix index: {}\n"
|
||||||
|
"Texture Coord 2 matrix index: {}\n"
|
||||||
|
"Texture Coord 3 matrix index: {}\n"
|
||||||
|
"Texture Coord 4 matrix index: {}\n"
|
||||||
|
"Texture Coord 5 matrix index: {}\n"
|
||||||
|
"Texture Coord 6 matrix index: {}\n"
|
||||||
|
"Texture Coord 7 matrix index: {}\n"
|
||||||
|
"Position: {}\n"
|
||||||
|
"Normal: {}\n"
|
||||||
|
"Color 0: {}\n"
|
||||||
|
"Color 1: {}",
|
||||||
|
present[desc.PosMatIdx], present[desc.Tex0MatIdx], present[desc.Tex1MatIdx],
|
||||||
|
present[desc.Tex2MatIdx], present[desc.Tex3MatIdx], present[desc.Tex4MatIdx],
|
||||||
|
present[desc.Tex5MatIdx], present[desc.Tex6MatIdx], present[desc.Tex7MatIdx],
|
||||||
|
desc.Position, desc.Normal, desc.Color0, desc.Color1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TVtxDesc::High>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const TVtxDesc::High& desc, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(),
|
||||||
|
"Texture Coord 0: {}\n"
|
||||||
|
"Texture Coord 1: {}\n"
|
||||||
|
"Texture Coord 2: {}\n"
|
||||||
|
"Texture Coord 3: {}\n"
|
||||||
|
"Texture Coord 4: {}\n"
|
||||||
|
"Texture Coord 5: {}\n"
|
||||||
|
"Texture Coord 6: {}\n"
|
||||||
|
"Texture Coord 7: {}",
|
||||||
|
desc.Tex0Coord, desc.Tex1Coord, desc.Tex2Coord, desc.Tex3Coord, desc.Tex4Coord,
|
||||||
|
desc.Tex5Coord, desc.Tex6Coord, desc.Tex7Coord);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TVtxDesc>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const TVtxDesc& desc, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(), "{}\n{}", desc.low, desc.high);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
union UVAT_group0
|
union UVAT_group0
|
||||||
{
|
{
|
||||||
u32 Hex;
|
u32 Hex;
|
||||||
struct
|
// 0:8
|
||||||
|
BitField<0, 1, CoordComponentCount> PosElements;
|
||||||
|
BitField<1, 3, ComponentFormat> PosFormat;
|
||||||
|
BitField<4, 5, u32> PosFrac;
|
||||||
|
// 9:12
|
||||||
|
BitField<9, 1, NormalComponentCount> NormalElements;
|
||||||
|
BitField<10, 3, ComponentFormat> NormalFormat;
|
||||||
|
// 13:16
|
||||||
|
BitField<13, 1, ColorComponentCount> Color0Elements;
|
||||||
|
BitField<14, 3, ColorFormat> Color0Comp;
|
||||||
|
// 17:20
|
||||||
|
BitField<17, 1, ColorComponentCount> Color1Elements;
|
||||||
|
BitField<18, 3, ColorFormat> Color1Comp;
|
||||||
|
// 21:29
|
||||||
|
BitField<21, 1, TexComponentCount> Tex0CoordElements;
|
||||||
|
BitField<22, 3, ComponentFormat> Tex0CoordFormat;
|
||||||
|
BitField<25, 5, u32> Tex0Frac;
|
||||||
|
// 30:31
|
||||||
|
BitField<30, 1, u32> ByteDequant;
|
||||||
|
BitField<31, 1, u32> NormalIndex3;
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<UVAT_group0>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const UVAT_group0& g0, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
// 0:8
|
static constexpr std::array<const char*, 2> byte_dequant = {
|
||||||
u32 PosElements : 1;
|
"shift does not apply to u8/s8 components", "shift applies to u8/s8 components"};
|
||||||
u32 PosFormat : 3;
|
static constexpr std::array<const char*, 2> normalindex3 = {"single index per normal",
|
||||||
u32 PosFrac : 5;
|
"triple-index per nine-normal"};
|
||||||
// 9:12
|
|
||||||
u32 NormalElements : 1;
|
return format_to(ctx.out(),
|
||||||
u32 NormalFormat : 3;
|
"Position elements: {}\n"
|
||||||
// 13:16
|
"Position format: {}\n"
|
||||||
u32 Color0Elements : 1;
|
"Position shift: {} ({})\n"
|
||||||
u32 Color0Comp : 3;
|
"Normal elements: {}\n"
|
||||||
// 17:20
|
"Normal format: {}\n"
|
||||||
u32 Color1Elements : 1;
|
"Color 0 elements: {}\n"
|
||||||
u32 Color1Comp : 3;
|
"Color 0 format: {}\n"
|
||||||
// 21:29
|
"Color 1 elements: {}\n"
|
||||||
u32 Tex0CoordElements : 1;
|
"Color 1 format: {}\n"
|
||||||
u32 Tex0CoordFormat : 3;
|
"Texture coord 0 elements: {}\n"
|
||||||
u32 Tex0Frac : 5;
|
"Texture coord 0 format: {}\n"
|
||||||
// 30:31
|
"Texture coord 0 shift: {} ({})\n"
|
||||||
u32 ByteDequant : 1;
|
"Byte dequant: {}\n"
|
||||||
u32 NormalIndex3 : 1;
|
"Normal index 3: {}",
|
||||||
};
|
g0.PosElements, g0.PosFormat, g0.PosFrac, 1.f / (1 << g0.PosFrac),
|
||||||
|
g0.NormalElements, g0.NormalFormat, g0.Color0Elements, g0.Color0Comp,
|
||||||
|
g0.Color1Elements, g0.Color1Comp, g0.Tex0CoordElements, g0.Tex0CoordFormat,
|
||||||
|
g0.Tex0Frac, 1.f / (1 << g0.Tex0Frac), byte_dequant[g0.ByteDequant],
|
||||||
|
normalindex3[g0.NormalIndex3]);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
union UVAT_group1
|
union UVAT_group1
|
||||||
{
|
{
|
||||||
u32 Hex;
|
u32 Hex;
|
||||||
struct
|
// 0:8
|
||||||
|
BitField<0, 1, TexComponentCount> Tex1CoordElements;
|
||||||
|
BitField<1, 3, ComponentFormat> Tex1CoordFormat;
|
||||||
|
BitField<4, 5, u32> Tex1Frac;
|
||||||
|
// 9:17
|
||||||
|
BitField<9, 1, TexComponentCount> Tex2CoordElements;
|
||||||
|
BitField<10, 3, ComponentFormat> Tex2CoordFormat;
|
||||||
|
BitField<13, 5, u32> Tex2Frac;
|
||||||
|
// 18:26
|
||||||
|
BitField<18, 1, TexComponentCount> Tex3CoordElements;
|
||||||
|
BitField<19, 3, ComponentFormat> Tex3CoordFormat;
|
||||||
|
BitField<22, 5, u32> Tex3Frac;
|
||||||
|
// 27:30
|
||||||
|
BitField<27, 1, TexComponentCount> Tex4CoordElements;
|
||||||
|
BitField<28, 3, ComponentFormat> Tex4CoordFormat;
|
||||||
|
// 31
|
||||||
|
BitField<31, 1, u32> VCacheEnhance;
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<UVAT_group1>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const UVAT_group1& g1, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
// 0:8
|
return format_to(ctx.out(),
|
||||||
u32 Tex1CoordElements : 1;
|
"Texture coord 1 elements: {}\n"
|
||||||
u32 Tex1CoordFormat : 3;
|
"Texture coord 1 format: {}\n"
|
||||||
u32 Tex1Frac : 5;
|
"Texture coord 1 shift: {} ({})\n"
|
||||||
// 9:17
|
"Texture coord 2 elements: {}\n"
|
||||||
u32 Tex2CoordElements : 1;
|
"Texture coord 2 format: {}\n"
|
||||||
u32 Tex2CoordFormat : 3;
|
"Texture coord 2 shift: {} ({})\n"
|
||||||
u32 Tex2Frac : 5;
|
"Texture coord 3 elements: {}\n"
|
||||||
// 18:26
|
"Texture coord 3 format: {}\n"
|
||||||
u32 Tex3CoordElements : 1;
|
"Texture coord 3 shift: {} ({})\n"
|
||||||
u32 Tex3CoordFormat : 3;
|
"Texture coord 4 elements: {}\n"
|
||||||
u32 Tex3Frac : 5;
|
"Texture coord 4 format: {}\n"
|
||||||
// 27:30
|
"Enhance VCache (must always be on): {}",
|
||||||
u32 Tex4CoordElements : 1;
|
g1.Tex1CoordElements, g1.Tex1CoordFormat, g1.Tex1Frac,
|
||||||
u32 Tex4CoordFormat : 3;
|
1.f / (1 << g1.Tex1Frac), g1.Tex2CoordElements, g1.Tex2CoordFormat,
|
||||||
//
|
g1.Tex2Frac, 1.f / (1 << g1.Tex2Frac), g1.Tex3CoordElements,
|
||||||
u32 : 1;
|
g1.Tex3CoordFormat, g1.Tex3Frac, 1.f / (1 << g1.Tex3Frac),
|
||||||
};
|
g1.Tex4CoordElements, g1.Tex4CoordFormat, g1.VCacheEnhance ? "Yes" : "No");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
union UVAT_group2
|
union UVAT_group2
|
||||||
{
|
{
|
||||||
u32 Hex;
|
u32 Hex;
|
||||||
struct
|
// 0:4
|
||||||
|
BitField<0, 5, u32> Tex4Frac;
|
||||||
|
// 5:13
|
||||||
|
BitField<5, 1, TexComponentCount> Tex5CoordElements;
|
||||||
|
BitField<6, 3, ComponentFormat> Tex5CoordFormat;
|
||||||
|
BitField<9, 5, u32> Tex5Frac;
|
||||||
|
// 14:22
|
||||||
|
BitField<14, 1, TexComponentCount> Tex6CoordElements;
|
||||||
|
BitField<15, 3, ComponentFormat> Tex6CoordFormat;
|
||||||
|
BitField<18, 5, u32> Tex6Frac;
|
||||||
|
// 23:31
|
||||||
|
BitField<23, 1, TexComponentCount> Tex7CoordElements;
|
||||||
|
BitField<24, 3, ComponentFormat> Tex7CoordFormat;
|
||||||
|
BitField<27, 5, u32> Tex7Frac;
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<UVAT_group2>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const UVAT_group2& g2, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
// 0:4
|
return format_to(ctx.out(),
|
||||||
u32 Tex4Frac : 5;
|
"Texture coord 4 shift: {} ({})\n"
|
||||||
// 5:13
|
"Texture coord 5 elements: {}\n"
|
||||||
u32 Tex5CoordElements : 1;
|
"Texture coord 5 format: {}\n"
|
||||||
u32 Tex5CoordFormat : 3;
|
"Texture coord 5 shift: {} ({})\n"
|
||||||
u32 Tex5Frac : 5;
|
"Texture coord 6 elements: {}\n"
|
||||||
// 14:22
|
"Texture coord 6 format: {}\n"
|
||||||
u32 Tex6CoordElements : 1;
|
"Texture coord 6 shift: {} ({})\n"
|
||||||
u32 Tex6CoordFormat : 3;
|
"Texture coord 7 elements: {}\n"
|
||||||
u32 Tex6Frac : 5;
|
"Texture coord 7 format: {}\n"
|
||||||
// 23:31
|
"Texture coord 7 shift: {} ({})",
|
||||||
u32 Tex7CoordElements : 1;
|
g2.Tex4Frac, 1.f / (1 << g2.Tex4Frac), g2.Tex5CoordElements,
|
||||||
u32 Tex7CoordFormat : 3;
|
g2.Tex5CoordFormat, g2.Tex5Frac, 1.f / (1 << g2.Tex5Frac),
|
||||||
u32 Tex7Frac : 5;
|
g2.Tex6CoordElements, g2.Tex6CoordFormat, g2.Tex6Frac,
|
||||||
};
|
1.f / (1 << g2.Tex6Frac), g2.Tex7CoordElements, g2.Tex7CoordFormat,
|
||||||
|
g2.Tex7Frac, 1.f / (1 << g2.Tex7Frac));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ColorAttr
|
struct ColorAttr
|
||||||
{
|
{
|
||||||
u8 Elements;
|
ColorComponentCount Elements;
|
||||||
u8 Comp;
|
ColorFormat Comp;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TexAttr
|
struct TexAttr
|
||||||
{
|
{
|
||||||
u8 Elements;
|
TexComponentCount Elements;
|
||||||
u8 Format;
|
ComponentFormat Format;
|
||||||
u8 Frac;
|
u8 Frac;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct TVtxAttr
|
struct TVtxAttr
|
||||||
{
|
{
|
||||||
u8 PosElements;
|
CoordComponentCount PosElements;
|
||||||
u8 PosFormat;
|
ComponentFormat PosFormat;
|
||||||
u8 PosFrac;
|
u8 PosFrac;
|
||||||
u8 NormalElements;
|
NormalComponentCount NormalElements;
|
||||||
u8 NormalFormat;
|
ComponentFormat NormalFormat;
|
||||||
ColorAttr color[2];
|
ColorAttr color[2];
|
||||||
TexAttr texCoord[8];
|
TexAttr texCoord[8];
|
||||||
bool ByteDequant;
|
bool ByteDequant;
|
||||||
|
@ -197,38 +467,44 @@ struct TVtxAttr
|
||||||
// Matrix indices
|
// Matrix indices
|
||||||
union TMatrixIndexA
|
union TMatrixIndexA
|
||||||
{
|
{
|
||||||
struct
|
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;
|
||||||
|
u32 Hex;
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TMatrixIndexA>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const TMatrixIndexA& m, FormatContext& ctx)
|
||||||
{
|
{
|
||||||
u32 PosNormalMtxIdx : 6;
|
return format_to(ctx.out(), "PosNormal: {}\nTex0: {}\nTex1: {}\nTex2: {}\nTex3: {}",
|
||||||
u32 Tex0MtxIdx : 6;
|
m.PosNormalMtxIdx, m.Tex0MtxIdx, m.Tex1MtxIdx, m.Tex2MtxIdx, m.Tex3MtxIdx);
|
||||||
u32 Tex1MtxIdx : 6;
|
}
|
||||||
u32 Tex2MtxIdx : 6;
|
|
||||||
u32 Tex3MtxIdx : 6;
|
|
||||||
};
|
|
||||||
struct
|
|
||||||
{
|
|
||||||
u32 Hex : 30;
|
|
||||||
u32 unused : 2;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
union TMatrixIndexB
|
union TMatrixIndexB
|
||||||
{
|
{
|
||||||
struct
|
BitField<0, 6, u32> Tex4MtxIdx;
|
||||||
{
|
BitField<6, 6, u32> Tex5MtxIdx;
|
||||||
u32 Tex4MtxIdx : 6;
|
BitField<12, 6, u32> Tex6MtxIdx;
|
||||||
u32 Tex5MtxIdx : 6;
|
BitField<18, 6, u32> Tex7MtxIdx;
|
||||||
u32 Tex6MtxIdx : 6;
|
u32 Hex;
|
||||||
u32 Tex7MtxIdx : 6;
|
};
|
||||||
};
|
template <>
|
||||||
struct
|
struct fmt::formatter<TMatrixIndexB>
|
||||||
{
|
{
|
||||||
u32 Hex : 24;
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
u32 unused : 8;
|
template <typename FormatContext>
|
||||||
};
|
auto format(const TMatrixIndexB& m, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(), "Tex4: {}\nTex5: {}\nTex6: {}\nTex7: {}", m.Tex4MtxIdx,
|
||||||
|
m.Tex5MtxIdx, m.Tex6MtxIdx, m.Tex7MtxIdx);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#pragma pack()
|
|
||||||
|
|
||||||
struct VAT
|
struct VAT
|
||||||
{
|
{
|
||||||
|
@ -242,18 +518,18 @@ class VertexLoaderBase;
|
||||||
// STATE_TO_SAVE
|
// STATE_TO_SAVE
|
||||||
struct CPState final
|
struct CPState final
|
||||||
{
|
{
|
||||||
u32 array_bases[16];
|
u32 array_bases[CP_NUM_ARRAYS];
|
||||||
u32 array_strides[16];
|
u32 array_strides[CP_NUM_ARRAYS];
|
||||||
TMatrixIndexA matrix_index_a;
|
TMatrixIndexA matrix_index_a;
|
||||||
TMatrixIndexB matrix_index_b;
|
TMatrixIndexB matrix_index_b;
|
||||||
TVtxDesc vtx_desc;
|
TVtxDesc vtx_desc;
|
||||||
// Most games only use the first VtxAttr and simply reconfigure it all the time as needed.
|
// Most games only use the first VtxAttr and simply reconfigure it all the time as needed.
|
||||||
VAT vtx_attr[8];
|
VAT vtx_attr[CP_NUM_VAT_REG];
|
||||||
|
|
||||||
// Attributes that actually belong to VertexLoaderManager:
|
// Attributes that actually belong to VertexLoaderManager:
|
||||||
BitSet32 attr_dirty;
|
BitSet32 attr_dirty;
|
||||||
bool bases_dirty;
|
bool bases_dirty;
|
||||||
VertexLoaderBase* vertex_loaders[8];
|
VertexLoaderBase* vertex_loaders[CP_NUM_VAT_REG];
|
||||||
int last_id;
|
int last_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -271,3 +547,5 @@ void FillCPMemoryArray(u32* memory);
|
||||||
void DoCPState(PointerWrap& p);
|
void DoCPState(PointerWrap& p);
|
||||||
|
|
||||||
void CopyPreprocessCPStateFromMain();
|
void CopyPreprocessCPStateFromMain();
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> GetCPRegInfo(u8 cmd, u32 value);
|
||||||
|
|
|
@ -13,6 +13,9 @@ using float4 = std::array<float, 4>;
|
||||||
using uint4 = std::array<u32, 4>;
|
using uint4 = std::array<u32, 4>;
|
||||||
using int4 = std::array<s32, 4>;
|
using int4 = std::array<s32, 4>;
|
||||||
|
|
||||||
|
enum class SrcBlendFactor : u32;
|
||||||
|
enum class DstBlendFactor : u32;
|
||||||
|
|
||||||
struct PixelShaderConstants
|
struct PixelShaderConstants
|
||||||
{
|
{
|
||||||
std::array<int4, 4> colors;
|
std::array<int4, 4> colors;
|
||||||
|
@ -45,10 +48,10 @@ struct PixelShaderConstants
|
||||||
std::array<int4, 32> konst; // .rgba
|
std::array<int4, 32> konst; // .rgba
|
||||||
// The following are used in ubershaders when using shader_framebuffer_fetch blending
|
// The following are used in ubershaders when using shader_framebuffer_fetch blending
|
||||||
u32 blend_enable;
|
u32 blend_enable;
|
||||||
u32 blend_src_factor;
|
SrcBlendFactor blend_src_factor;
|
||||||
u32 blend_src_factor_alpha;
|
SrcBlendFactor blend_src_factor_alpha;
|
||||||
u32 blend_dst_factor;
|
DstBlendFactor blend_dst_factor;
|
||||||
u32 blend_dst_factor_alpha;
|
DstBlendFactor blend_dst_factor_alpha;
|
||||||
u32 blend_subtract;
|
u32 blend_subtract;
|
||||||
u32 blend_subtract_alpha;
|
u32 blend_subtract_alpha;
|
||||||
};
|
};
|
||||||
|
|
|
@ -45,7 +45,7 @@ void GeometryShaderManager::SetConstants()
|
||||||
{
|
{
|
||||||
s_projection_changed = false;
|
s_projection_changed = false;
|
||||||
|
|
||||||
if (xfmem.projection.type == GX_PERSPECTIVE)
|
if (xfmem.projection.type == ProjectionType::Perspective)
|
||||||
{
|
{
|
||||||
float offset = (g_ActiveConfig.iStereoDepth / 1000.0f) *
|
float offset = (g_ActiveConfig.iStereoDepth / 1000.0f) *
|
||||||
(g_ActiveConfig.iStereoDepthPercentage / 100.0f);
|
(g_ActiveConfig.iStereoDepthPercentage / 100.0f);
|
||||||
|
|
|
@ -17,29 +17,32 @@ static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_d
|
||||||
const char* swizzle = alpha ? "a" : "rgb";
|
const char* swizzle = alpha ? "a" : "rgb";
|
||||||
const char* swizzle_components = (alpha) ? "" : "3";
|
const char* swizzle_components = (alpha) ? "" : "3";
|
||||||
|
|
||||||
const u32 attnfunc = (uid_data.attnfunc >> (2 * litchan_index)) & 0x3;
|
const auto attnfunc =
|
||||||
const u32 diffusefunc = (uid_data.diffusefunc >> (2 * litchan_index)) & 0x3;
|
static_cast<AttenuationFunc>((uid_data.attnfunc >> (2 * litchan_index)) & 0x3);
|
||||||
|
const auto diffusefunc =
|
||||||
|
static_cast<DiffuseFunc>((uid_data.diffusefunc >> (2 * litchan_index)) & 0x3);
|
||||||
|
|
||||||
switch (attnfunc)
|
switch (attnfunc)
|
||||||
{
|
{
|
||||||
case LIGHTATTN_NONE:
|
case AttenuationFunc::None:
|
||||||
case LIGHTATTN_DIR:
|
case AttenuationFunc::Dir:
|
||||||
object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
|
object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
|
||||||
object.Write("attn = 1.0;\n");
|
object.Write("attn = 1.0;\n");
|
||||||
object.Write("if (length(ldir) == 0.0)\n\t ldir = _norm0;\n");
|
object.Write("if (length(ldir) == 0.0)\n\t ldir = _norm0;\n");
|
||||||
break;
|
break;
|
||||||
case LIGHTATTN_SPEC:
|
case AttenuationFunc::Spec:
|
||||||
object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
|
object.Write("ldir = normalize(" LIGHT_POS ".xyz - pos.xyz);\n", LIGHT_POS_PARAMS(index));
|
||||||
object.Write("attn = (dot(_norm0, ldir) >= 0.0) ? max(0.0, dot(_norm0, " LIGHT_DIR
|
object.Write("attn = (dot(_norm0, ldir) >= 0.0) ? max(0.0, dot(_norm0, " LIGHT_DIR
|
||||||
".xyz)) : 0.0;\n",
|
".xyz)) : 0.0;\n",
|
||||||
LIGHT_DIR_PARAMS(index));
|
LIGHT_DIR_PARAMS(index));
|
||||||
object.Write("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index));
|
object.Write("cosAttn = " LIGHT_COSATT ".xyz;\n", LIGHT_COSATT_PARAMS(index));
|
||||||
object.Write("distAttn = {}(" LIGHT_DISTATT ".xyz);\n",
|
object.Write("distAttn = {}(" LIGHT_DISTATT ".xyz);\n",
|
||||||
(diffusefunc == LIGHTDIF_NONE) ? "" : "normalize", LIGHT_DISTATT_PARAMS(index));
|
(diffusefunc == DiffuseFunc::None) ? "" : "normalize",
|
||||||
|
LIGHT_DISTATT_PARAMS(index));
|
||||||
object.Write("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
|
object.Write("attn = max(0.0f, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
|
||||||
"float3(1.0, attn, attn*attn));\n");
|
"float3(1.0, attn, attn*attn));\n");
|
||||||
break;
|
break;
|
||||||
case LIGHTATTN_SPOT:
|
case AttenuationFunc::Spot:
|
||||||
object.Write("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index));
|
object.Write("ldir = " LIGHT_POS ".xyz - pos.xyz;\n", LIGHT_POS_PARAMS(index));
|
||||||
object.Write("dist2 = dot(ldir, ldir);\n"
|
object.Write("dist2 = dot(ldir, ldir);\n"
|
||||||
"dist = sqrt(dist2);\n"
|
"dist = sqrt(dist2);\n"
|
||||||
|
@ -56,14 +59,14 @@ static void GenerateLightShader(ShaderCode& object, const LightingUidData& uid_d
|
||||||
|
|
||||||
switch (diffusefunc)
|
switch (diffusefunc)
|
||||||
{
|
{
|
||||||
case LIGHTDIF_NONE:
|
case DiffuseFunc::None:
|
||||||
object.Write("lacc.{} += int{}(round(attn * float{}(" LIGHT_COL ")));\n", swizzle,
|
object.Write("lacc.{} += int{}(round(attn * float{}(" LIGHT_COL ")));\n", swizzle,
|
||||||
swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
|
swizzle_components, swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
|
||||||
break;
|
break;
|
||||||
case LIGHTDIF_SIGN:
|
case DiffuseFunc::Sign:
|
||||||
case LIGHTDIF_CLAMP:
|
case DiffuseFunc::Clamp:
|
||||||
object.Write("lacc.{} += int{}(round(attn * {}dot(ldir, _norm0)) * float{}(" LIGHT_COL ")));\n",
|
object.Write("lacc.{} += int{}(round(attn * {}dot(ldir, _norm0)) * float{}(" LIGHT_COL ")));\n",
|
||||||
swizzle, swizzle_components, diffusefunc != LIGHTDIF_SIGN ? "max(0.0," : "(",
|
swizzle, swizzle_components, diffusefunc != DiffuseFunc::Sign ? "max(0.0," : "(",
|
||||||
swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
|
swizzle_components, LIGHT_COL_PARAMS(index, swizzle));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -151,23 +154,23 @@ void GetLightingShaderUid(LightingUidData& uid_data)
|
||||||
{
|
{
|
||||||
for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
|
for (u32 j = 0; j < NUM_XF_COLOR_CHANNELS; j++)
|
||||||
{
|
{
|
||||||
uid_data.matsource |= xfmem.color[j].matsource << j;
|
uid_data.matsource |= static_cast<u32>(xfmem.color[j].matsource.Value()) << j;
|
||||||
uid_data.matsource |= xfmem.alpha[j].matsource << (j + 2);
|
uid_data.matsource |= static_cast<u32>(xfmem.alpha[j].matsource.Value()) << (j + 2);
|
||||||
uid_data.enablelighting |= xfmem.color[j].enablelighting << j;
|
uid_data.enablelighting |= xfmem.color[j].enablelighting << j;
|
||||||
uid_data.enablelighting |= xfmem.alpha[j].enablelighting << (j + 2);
|
uid_data.enablelighting |= xfmem.alpha[j].enablelighting << (j + 2);
|
||||||
|
|
||||||
if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights
|
if ((uid_data.enablelighting & (1 << j)) != 0) // Color lights
|
||||||
{
|
{
|
||||||
uid_data.ambsource |= xfmem.color[j].ambsource << j;
|
uid_data.ambsource |= static_cast<u32>(xfmem.color[j].ambsource.Value()) << j;
|
||||||
uid_data.attnfunc |= xfmem.color[j].attnfunc << (2 * j);
|
uid_data.attnfunc |= static_cast<u32>(xfmem.color[j].attnfunc.Value()) << (2 * j);
|
||||||
uid_data.diffusefunc |= xfmem.color[j].diffusefunc << (2 * j);
|
uid_data.diffusefunc |= static_cast<u32>(xfmem.color[j].diffusefunc.Value()) << (2 * j);
|
||||||
uid_data.light_mask |= xfmem.color[j].GetFullLightMask() << (8 * j);
|
uid_data.light_mask |= xfmem.color[j].GetFullLightMask() << (8 * j);
|
||||||
}
|
}
|
||||||
if ((uid_data.enablelighting & (1 << (j + 2))) != 0) // Alpha lights
|
if ((uid_data.enablelighting & (1 << (j + 2))) != 0) // Alpha lights
|
||||||
{
|
{
|
||||||
uid_data.ambsource |= xfmem.alpha[j].ambsource << (j + 2);
|
uid_data.ambsource |= static_cast<u32>(xfmem.alpha[j].ambsource.Value()) << (j + 2);
|
||||||
uid_data.attnfunc |= xfmem.alpha[j].attnfunc << (2 * (j + 2));
|
uid_data.attnfunc |= static_cast<u32>(xfmem.alpha[j].attnfunc.Value()) << (2 * (j + 2));
|
||||||
uid_data.diffusefunc |= xfmem.alpha[j].diffusefunc << (2 * (j + 2));
|
uid_data.diffusefunc |= static_cast<u32>(xfmem.alpha[j].diffusefunc.Value()) << (2 * (j + 2));
|
||||||
uid_data.light_mask |= xfmem.alpha[j].GetFullLightMask() << (8 * (j + 2));
|
uid_data.light_mask |= xfmem.alpha[j].GetFullLightMask() << (8 * (j + 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,14 +174,14 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
|
|
||||||
pixel_shader_uid_data* const uid_data = out.GetUidData();
|
pixel_shader_uid_data* const uid_data = out.GetUidData();
|
||||||
uid_data->useDstAlpha = bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate &&
|
uid_data->useDstAlpha = bpmem.dstalpha.enable && bpmem.blendmode.alphaupdate &&
|
||||||
bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24;
|
bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24;
|
||||||
|
|
||||||
uid_data->genMode_numindstages = bpmem.genMode.numindstages;
|
uid_data->genMode_numindstages = bpmem.genMode.numindstages;
|
||||||
uid_data->genMode_numtevstages = bpmem.genMode.numtevstages;
|
uid_data->genMode_numtevstages = bpmem.genMode.numtevstages;
|
||||||
uid_data->genMode_numtexgens = bpmem.genMode.numtexgens;
|
uid_data->genMode_numtexgens = bpmem.genMode.numtexgens;
|
||||||
uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && BoundingBox::IsEnabled();
|
uid_data->bounding_box = g_ActiveConfig.bBBoxEnable && BoundingBox::IsEnabled();
|
||||||
uid_data->rgba6_format =
|
uid_data->rgba6_format =
|
||||||
bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor;
|
bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor;
|
||||||
uid_data->dither = bpmem.blendmode.dither && uid_data->rgba6_format;
|
uid_data->dither = bpmem.blendmode.dither && uid_data->rgba6_format;
|
||||||
uid_data->uint_output = bpmem.blendmode.UseLogicOp();
|
uid_data->uint_output = bpmem.blendmode.UseLogicOp();
|
||||||
|
|
||||||
|
@ -189,12 +189,13 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
|
|
||||||
const bool forced_early_z =
|
const bool forced_early_z =
|
||||||
bpmem.UseEarlyDepthTest() &&
|
bpmem.UseEarlyDepthTest() &&
|
||||||
(g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED)
|
(g_ActiveConfig.bFastDepthCalc ||
|
||||||
|
bpmem.alpha_test.TestResult() == AlphaTestResult::Undetermined)
|
||||||
// We can't allow early_ztest for zfreeze because depth is overridden per-pixel.
|
// We can't allow early_ztest for zfreeze because depth is overridden per-pixel.
|
||||||
// This means it's impossible for zcomploc to be emulated on a zfrozen polygon.
|
// This means it's impossible for zcomploc to be emulated on a zfrozen polygon.
|
||||||
&& !(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
&& !(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
||||||
const bool per_pixel_depth =
|
const bool per_pixel_depth =
|
||||||
(bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) ||
|
(bpmem.ztex2.op != ZTexOp::Disabled && bpmem.UseLateDepthTest()) ||
|
||||||
(!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z) ||
|
(!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !forced_early_z) ||
|
||||||
(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
||||||
|
|
||||||
|
@ -212,7 +213,8 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
for (unsigned int i = 0; i < uid_data->genMode_numtexgens; ++i)
|
for (unsigned int i = 0; i < uid_data->genMode_numtexgens; ++i)
|
||||||
{
|
{
|
||||||
// optional perspective divides
|
// optional perspective divides
|
||||||
uid_data->texMtxInfo_n_projection |= xfmem.texMtxInfo[i].projection << i;
|
uid_data->texMtxInfo_n_projection |= static_cast<u32>(xfmem.texMtxInfo[i].projection.Value())
|
||||||
|
<< i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,10 +254,12 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
uid_data->stagehash[n].cc = cc.hex & 0xFFFFFF;
|
uid_data->stagehash[n].cc = cc.hex & 0xFFFFFF;
|
||||||
uid_data->stagehash[n].ac = ac.hex & 0xFFFFF0; // Storing rswap and tswap later
|
uid_data->stagehash[n].ac = ac.hex & 0xFFFFF0; // Storing rswap and tswap later
|
||||||
|
|
||||||
if (cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC || cc.b == TEVCOLORARG_RASA ||
|
if (cc.a == TevColorArg::RasAlpha || cc.a == TevColorArg::RasColor ||
|
||||||
cc.b == TEVCOLORARG_RASC || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC ||
|
cc.b == TevColorArg::RasAlpha || cc.b == TevColorArg::RasColor ||
|
||||||
cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC || ac.a == TEVALPHAARG_RASA ||
|
cc.c == TevColorArg::RasAlpha || cc.c == TevColorArg::RasColor ||
|
||||||
ac.b == TEVALPHAARG_RASA || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA)
|
cc.d == TevColorArg::RasAlpha || cc.d == TevColorArg::RasColor ||
|
||||||
|
ac.a == TevAlphaArg::RasAlpha || ac.b == TevAlphaArg::RasAlpha ||
|
||||||
|
ac.c == TevAlphaArg::RasAlpha || ac.d == TevAlphaArg::RasAlpha)
|
||||||
{
|
{
|
||||||
const int i = bpmem.combiners[n].alphaC.rswap;
|
const int i = bpmem.combiners[n].alphaC.rswap;
|
||||||
uid_data->stagehash[n].tevksel_swap1a = bpmem.tevksel[i * 2].swap1;
|
uid_data->stagehash[n].tevksel_swap1a = bpmem.tevksel[i * 2].swap1;
|
||||||
|
@ -276,9 +280,9 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
uid_data->stagehash[n].tevorders_texmap = bpmem.tevorders[n / 2].getTexMap(n & 1);
|
uid_data->stagehash[n].tevorders_texmap = bpmem.tevorders[n / 2].getTexMap(n & 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST ||
|
if (cc.a == TevColorArg::Konst || cc.b == TevColorArg::Konst || cc.c == TevColorArg::Konst ||
|
||||||
cc.d == TEVCOLORARG_KONST || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST ||
|
cc.d == TevColorArg::Konst || ac.a == TevAlphaArg::Konst || ac.b == TevAlphaArg::Konst ||
|
||||||
ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST)
|
ac.c == TevAlphaArg::Konst || ac.d == TevAlphaArg::Konst)
|
||||||
{
|
{
|
||||||
uid_data->stagehash[n].tevksel_kc = bpmem.tevksel[n / 2].getKC(n & 1);
|
uid_data->stagehash[n].tevksel_kc = bpmem.tevksel[n / 2].getKC(n & 1);
|
||||||
uid_data->stagehash[n].tevksel_ka = bpmem.tevksel[n / 2].getKA(n & 1);
|
uid_data->stagehash[n].tevksel_ka = bpmem.tevksel[n / 2].getKA(n & 1);
|
||||||
|
@ -290,15 +294,14 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
sizeof(*uid_data) :
|
sizeof(*uid_data) :
|
||||||
MY_STRUCT_OFFSET(*uid_data, stagehash[numStages]);
|
MY_STRUCT_OFFSET(*uid_data, stagehash[numStages]);
|
||||||
|
|
||||||
AlphaTest::TEST_RESULT Pretest = bpmem.alpha_test.TestResult();
|
uid_data->Pretest = bpmem.alpha_test.TestResult();
|
||||||
uid_data->Pretest = Pretest;
|
|
||||||
uid_data->late_ztest = bpmem.UseLateDepthTest();
|
uid_data->late_ztest = bpmem.UseLateDepthTest();
|
||||||
|
|
||||||
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
|
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
|
||||||
// (in this case we need to write a depth value if depth test passes regardless of the alpha
|
// (in this case we need to write a depth value if depth test passes regardless of the alpha
|
||||||
// testing result)
|
// testing result)
|
||||||
if (uid_data->Pretest == AlphaTest::UNDETERMINED ||
|
if (uid_data->Pretest == AlphaTestResult::Undetermined ||
|
||||||
(uid_data->Pretest == AlphaTest::FAIL && uid_data->late_ztest))
|
(uid_data->Pretest == AlphaTestResult::Fail && uid_data->late_ztest))
|
||||||
{
|
{
|
||||||
uid_data->alpha_test_comp0 = bpmem.alpha_test.comp0;
|
uid_data->alpha_test_comp0 = bpmem.alpha_test.comp0;
|
||||||
uid_data->alpha_test_comp1 = bpmem.alpha_test.comp1;
|
uid_data->alpha_test_comp1 = bpmem.alpha_test.comp1;
|
||||||
|
@ -319,7 +322,7 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
uid_data->zfreeze = bpmem.genMode.zfreeze;
|
uid_data->zfreeze = bpmem.genMode.zfreeze;
|
||||||
uid_data->ztex_op = bpmem.ztex2.op;
|
uid_data->ztex_op = bpmem.ztex2.op;
|
||||||
uid_data->early_ztest = bpmem.UseEarlyDepthTest();
|
uid_data->early_ztest = bpmem.UseEarlyDepthTest();
|
||||||
uid_data->fog_fsel = bpmem.fog.c_proj_fsel.fsel;
|
|
||||||
uid_data->fog_fsel = bpmem.fog.c_proj_fsel.fsel;
|
uid_data->fog_fsel = bpmem.fog.c_proj_fsel.fsel;
|
||||||
uid_data->fog_proj = bpmem.fog.c_proj_fsel.proj;
|
uid_data->fog_proj = bpmem.fog.c_proj_fsel.proj;
|
||||||
uid_data->fog_RangeBaseEnabled = bpmem.fogRange.Base.Enabled;
|
uid_data->fog_RangeBaseEnabled = bpmem.fogRange.Base.Enabled;
|
||||||
|
@ -516,8 +519,8 @@ void UpdateBoundingBox(float2 rawpos) {{
|
||||||
|
|
||||||
static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, int n,
|
static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, int n,
|
||||||
APIType api_type, bool stereo);
|
APIType api_type, bool stereo);
|
||||||
static void WriteTevRegular(ShaderCode& out, std::string_view components, int bias, int op,
|
static void WriteTevRegular(ShaderCode& out, std::string_view components, TevBias bias, TevOp op,
|
||||||
int clamp, int shift, bool alpha);
|
bool clamp, TevScale scale, bool alpha);
|
||||||
static void SampleTexture(ShaderCode& out, std::string_view texcoords, std::string_view texswap,
|
static void SampleTexture(ShaderCode& out, std::string_view texcoords, std::string_view texswap,
|
||||||
int texmap, bool stereo, APIType api_type);
|
int texmap, bool stereo, APIType api_type);
|
||||||
static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_data, APIType api_type,
|
static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_data, APIType api_type,
|
||||||
|
@ -825,13 +828,13 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
||||||
TevStageCombiner::AlphaCombiner last_ac;
|
TevStageCombiner::AlphaCombiner last_ac;
|
||||||
last_cc.hex = uid_data->stagehash[uid_data->genMode_numtevstages].cc;
|
last_cc.hex = uid_data->stagehash[uid_data->genMode_numtevstages].cc;
|
||||||
last_ac.hex = uid_data->stagehash[uid_data->genMode_numtevstages].ac;
|
last_ac.hex = uid_data->stagehash[uid_data->genMode_numtevstages].ac;
|
||||||
if (last_cc.dest != 0)
|
if (last_cc.dest != TevOutput::Prev)
|
||||||
{
|
{
|
||||||
out.Write("\tprev.rgb = {};\n", tev_c_output_table[last_cc.dest]);
|
out.Write("\tprev.rgb = {};\n", tev_c_output_table[u32(last_cc.dest.Value())]);
|
||||||
}
|
}
|
||||||
if (last_ac.dest != 0)
|
if (last_ac.dest != TevOutput::Prev)
|
||||||
{
|
{
|
||||||
out.Write("\tprev.a = {};\n", tev_a_output_table[last_ac.dest]);
|
out.Write("\tprev.a = {};\n", tev_a_output_table[u32(last_ac.dest.Value())]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out.Write("\tprev = prev & 255;\n");
|
out.Write("\tprev = prev & 255;\n");
|
||||||
|
@ -839,8 +842,8 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
||||||
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
|
// NOTE: Fragment may not be discarded if alpha test always fails and early depth test is enabled
|
||||||
// (in this case we need to write a depth value if depth test passes regardless of the alpha
|
// (in this case we need to write a depth value if depth test passes regardless of the alpha
|
||||||
// testing result)
|
// testing result)
|
||||||
if (uid_data->Pretest == AlphaTest::UNDETERMINED ||
|
if (uid_data->Pretest == AlphaTestResult::Undetermined ||
|
||||||
(uid_data->Pretest == AlphaTest::FAIL && uid_data->late_ztest))
|
(uid_data->Pretest == AlphaTestResult::Fail && uid_data->late_ztest))
|
||||||
{
|
{
|
||||||
WriteAlphaTest(out, uid_data, api_type, uid_data->per_pixel_depth,
|
WriteAlphaTest(out, uid_data, api_type, uid_data->per_pixel_depth,
|
||||||
use_dual_source || use_shader_blend);
|
use_dual_source || use_shader_blend);
|
||||||
|
@ -883,7 +886,7 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
||||||
|
|
||||||
// depth texture can safely be ignored if the result won't be written to the depth buffer
|
// depth texture can safely be ignored if the result won't be written to the depth buffer
|
||||||
// (early_ztest) and isn't used for fog either
|
// (early_ztest) and isn't used for fog either
|
||||||
const bool skip_ztexture = !uid_data->per_pixel_depth && !uid_data->fog_fsel;
|
const bool skip_ztexture = !uid_data->per_pixel_depth && uid_data->fog_fsel == FogType::Off;
|
||||||
|
|
||||||
// Note: z-textures are not written to depth buffer if early depth test is used
|
// Note: z-textures are not written to depth buffer if early depth test is used
|
||||||
if (uid_data->per_pixel_depth && uid_data->early_ztest)
|
if (uid_data->per_pixel_depth && uid_data->early_ztest)
|
||||||
|
@ -897,13 +900,13 @@ ShaderCode GeneratePixelShaderCode(APIType api_type, const ShaderHostConfig& hos
|
||||||
// Note: depth texture output is only written to depth buffer if late depth test is used
|
// Note: depth texture output is only written to depth buffer if late depth test is used
|
||||||
// theoretical final depth value is used for fog calculation, though, so we have to emulate
|
// theoretical final depth value is used for fog calculation, though, so we have to emulate
|
||||||
// ztextures anyway
|
// ztextures anyway
|
||||||
if (uid_data->ztex_op != ZTEXTURE_DISABLE && !skip_ztexture)
|
if (uid_data->ztex_op != ZTexOp::Disabled && !skip_ztexture)
|
||||||
{
|
{
|
||||||
// use the texture input of the last texture stage (textemp), hopefully this has been read and
|
// use the texture input of the last texture stage (textemp), hopefully this has been read and
|
||||||
// is in correct format...
|
// is in correct format...
|
||||||
out.SetConstantsUsed(C_ZBIAS, C_ZBIAS + 1);
|
out.SetConstantsUsed(C_ZBIAS, C_ZBIAS + 1);
|
||||||
out.Write("\tzCoord = idot(" I_ZBIAS "[0].xyzw, textemp.xyzw) + " I_ZBIAS "[1].w {};\n",
|
out.Write("\tzCoord = idot(" I_ZBIAS "[0].xyzw, textemp.xyzw) + " I_ZBIAS "[1].w {};\n",
|
||||||
(uid_data->ztex_op == ZTEXTURE_ADD) ? "+ zCoord" : "");
|
(uid_data->ztex_op == ZTexOp::Add) ? "+ zCoord" : "");
|
||||||
out.Write("\tzCoord = zCoord & 0xFFFFFF;\n");
|
out.Write("\tzCoord = zCoord & 0xFFFFFF;\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -962,7 +965,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
|
|
||||||
// Perform the indirect op on the incoming regular coordinates
|
// Perform the indirect op on the incoming regular coordinates
|
||||||
// using iindtex{} as the offset coords
|
// using iindtex{} as the offset coords
|
||||||
if (tevind.bs != ITBA_OFF)
|
if (tevind.bs != IndTexBumpAlpha::Off)
|
||||||
{
|
{
|
||||||
static constexpr std::array<const char*, 4> tev_ind_alpha_sel{
|
static constexpr std::array<const char*, 4> tev_ind_alpha_sel{
|
||||||
"",
|
"",
|
||||||
|
@ -979,8 +982,9 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
"248",
|
"248",
|
||||||
};
|
};
|
||||||
|
|
||||||
out.Write("alphabump = iindtex{}.{} & {};\n", tevind.bt.Value(), tev_ind_alpha_sel[tevind.bs],
|
out.Write("alphabump = iindtex{}.{} & {};\n", tevind.bt.Value(),
|
||||||
tev_ind_alpha_mask[tevind.fmt]);
|
tev_ind_alpha_sel[u32(tevind.bs.Value())],
|
||||||
|
tev_ind_alpha_mask[u32(tevind.fmt.Value())]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -997,7 +1001,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
"7",
|
"7",
|
||||||
};
|
};
|
||||||
out.Write("\tint3 iindtevcrd{} = iindtex{} & {};\n", n, tevind.bt.Value(),
|
out.Write("\tint3 iindtevcrd{} = iindtex{} & {};\n", n, tevind.bt.Value(),
|
||||||
tev_ind_fmt_mask[tevind.fmt]);
|
tev_ind_fmt_mask[u32(tevind.fmt.Value())]);
|
||||||
|
|
||||||
// bias - TODO: Check if this needs to be this complicated...
|
// bias - TODO: Check if this needs to be this complicated...
|
||||||
// indexed by bias
|
// indexed by bias
|
||||||
|
@ -1013,21 +1017,25 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
"1",
|
"1",
|
||||||
};
|
};
|
||||||
|
|
||||||
if (tevind.bias == ITB_S || tevind.bias == ITB_T || tevind.bias == ITB_U)
|
if (tevind.bias == IndTexBias::S || tevind.bias == IndTexBias::T ||
|
||||||
|
tevind.bias == IndTexBias::U)
|
||||||
{
|
{
|
||||||
out.Write("\tiindtevcrd{}.{} += int({});\n", n, tev_ind_bias_field[tevind.bias],
|
out.Write("\tiindtevcrd{}.{} += int({});\n", n,
|
||||||
tev_ind_bias_add[tevind.fmt]);
|
tev_ind_bias_field[u32(tevind.bias.Value())],
|
||||||
|
tev_ind_bias_add[u32(tevind.fmt.Value())]);
|
||||||
}
|
}
|
||||||
else if (tevind.bias == ITB_ST || tevind.bias == ITB_SU || tevind.bias == ITB_TU)
|
else if (tevind.bias == IndTexBias::ST || tevind.bias == IndTexBias::SU ||
|
||||||
|
tevind.bias == IndTexBias::TU_)
|
||||||
{
|
{
|
||||||
out.Write("\tiindtevcrd{}.{} += int2({}, {});\n", n, tev_ind_bias_field[tevind.bias],
|
out.Write("\tiindtevcrd{0}.{1} += int2({2}, {2});\n", n,
|
||||||
tev_ind_bias_add[tevind.fmt], tev_ind_bias_add[tevind.fmt]);
|
tev_ind_bias_field[u32(tevind.bias.Value())],
|
||||||
|
tev_ind_bias_add[u32(tevind.fmt.Value())]);
|
||||||
}
|
}
|
||||||
else if (tevind.bias == ITB_STU)
|
else if (tevind.bias == IndTexBias::STU)
|
||||||
{
|
{
|
||||||
out.Write("\tiindtevcrd{}.{} += int3({}, {}, {});\n", n, tev_ind_bias_field[tevind.bias],
|
out.Write("\tiindtevcrd{0}.{1} += int3({2}, {2}, {2});\n", n,
|
||||||
tev_ind_bias_add[tevind.fmt], tev_ind_bias_add[tevind.fmt],
|
tev_ind_bias_field[u32(tevind.bias.Value())],
|
||||||
tev_ind_bias_add[tevind.fmt]);
|
tev_ind_bias_add[u32(tevind.fmt.Value())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// multiply by offset matrix and scale - calculations are likely to overflow badly,
|
// multiply by offset matrix and scale - calculations are likely to overflow badly,
|
||||||
|
@ -1121,33 +1129,33 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
};
|
};
|
||||||
|
|
||||||
// wrap S
|
// wrap S
|
||||||
if (tevind.sw == ITW_OFF)
|
if (tevind.sw == IndTexWrap::ITW_OFF)
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.x = fixpoint_uv{}.x;\n", texcoord);
|
out.Write("\twrappedcoord.x = fixpoint_uv{}.x;\n", texcoord);
|
||||||
}
|
}
|
||||||
else if (tevind.sw == ITW_0)
|
else if (tevind.sw == IndTexWrap::ITW_0)
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.x = 0;\n");
|
out.Write("\twrappedcoord.x = 0;\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.x = fixpoint_uv{}.x & ({} - 1);\n", texcoord,
|
out.Write("\twrappedcoord.x = fixpoint_uv{}.x & ({} - 1);\n", texcoord,
|
||||||
tev_ind_wrap_start[tevind.sw]);
|
tev_ind_wrap_start[u32(tevind.sw.Value())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// wrap T
|
// wrap T
|
||||||
if (tevind.tw == ITW_OFF)
|
if (tevind.tw == IndTexWrap::ITW_OFF)
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.y = fixpoint_uv{}.y;\n", texcoord);
|
out.Write("\twrappedcoord.y = fixpoint_uv{}.y;\n", texcoord);
|
||||||
}
|
}
|
||||||
else if (tevind.tw == ITW_0)
|
else if (tevind.tw == IndTexWrap::ITW_0)
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.y = 0;\n");
|
out.Write("\twrappedcoord.y = 0;\n");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
out.Write("\twrappedcoord.y = fixpoint_uv{}.y & ({} - 1);\n", texcoord,
|
out.Write("\twrappedcoord.y = fixpoint_uv{}.y & ({} - 1);\n", texcoord,
|
||||||
tev_ind_wrap_start[tevind.tw]);
|
tev_ind_wrap_start[u32(tevind.tw.Value())]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tevind.fb_addprev) // add previous tevcoord
|
if (tevind.fb_addprev) // add previous tevcoord
|
||||||
|
@ -1164,10 +1172,12 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
cc.hex = stage.cc;
|
cc.hex = stage.cc;
|
||||||
ac.hex = stage.ac;
|
ac.hex = stage.ac;
|
||||||
|
|
||||||
if (cc.a == TEVCOLORARG_RASA || cc.a == TEVCOLORARG_RASC || cc.b == TEVCOLORARG_RASA ||
|
if (cc.a == TevColorArg::RasAlpha || cc.a == TevColorArg::RasColor ||
|
||||||
cc.b == TEVCOLORARG_RASC || cc.c == TEVCOLORARG_RASA || cc.c == TEVCOLORARG_RASC ||
|
cc.b == TevColorArg::RasAlpha || cc.b == TevColorArg::RasColor ||
|
||||||
cc.d == TEVCOLORARG_RASA || cc.d == TEVCOLORARG_RASC || ac.a == TEVALPHAARG_RASA ||
|
cc.c == TevColorArg::RasAlpha || cc.c == TevColorArg::RasColor ||
|
||||||
ac.b == TEVALPHAARG_RASA || ac.c == TEVALPHAARG_RASA || ac.d == TEVALPHAARG_RASA)
|
cc.d == TevColorArg::RasAlpha || cc.d == TevColorArg::RasColor ||
|
||||||
|
ac.a == TevAlphaArg::RasAlpha || ac.b == TevAlphaArg::RasAlpha ||
|
||||||
|
ac.c == TevAlphaArg::RasAlpha || ac.d == TevAlphaArg::RasAlpha)
|
||||||
{
|
{
|
||||||
// Generate swizzle string to represent the Ras color channel swapping
|
// Generate swizzle string to represent the Ras color channel swapping
|
||||||
const char rasswap[5] = {
|
const char rasswap[5] = {
|
||||||
|
@ -1178,7 +1188,7 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
'\0',
|
'\0',
|
||||||
};
|
};
|
||||||
|
|
||||||
out.Write("\trastemp = {}.{};\n", tev_ras_table[stage.tevorders_colorchan], rasswap);
|
out.Write("\trastemp = {}.{};\n", tev_ras_table[u32(stage.tevorders_colorchan)], rasswap);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stage.tevorders_enable)
|
if (stage.tevorders_enable)
|
||||||
|
@ -1208,72 +1218,73 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
out.Write("\ttextemp = int4(255, 255, 255, 255);\n");
|
out.Write("\ttextemp = int4(255, 255, 255, 255);\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc.a == TEVCOLORARG_KONST || cc.b == TEVCOLORARG_KONST || cc.c == TEVCOLORARG_KONST ||
|
if (cc.a == TevColorArg::Konst || cc.b == TevColorArg::Konst || cc.c == TevColorArg::Konst ||
|
||||||
cc.d == TEVCOLORARG_KONST || ac.a == TEVALPHAARG_KONST || ac.b == TEVALPHAARG_KONST ||
|
cc.d == TevColorArg::Konst || ac.a == TevAlphaArg::Konst || ac.b == TevAlphaArg::Konst ||
|
||||||
ac.c == TEVALPHAARG_KONST || ac.d == TEVALPHAARG_KONST)
|
ac.c == TevAlphaArg::Konst || ac.d == TevAlphaArg::Konst)
|
||||||
{
|
{
|
||||||
out.Write("\tkonsttemp = int4({}, {});\n", tev_ksel_table_c[stage.tevksel_kc],
|
out.Write("\tkonsttemp = int4({}, {});\n", tev_ksel_table_c[u32(stage.tevksel_kc)],
|
||||||
tev_ksel_table_a[stage.tevksel_ka]);
|
tev_ksel_table_a[u32(stage.tevksel_ka)]);
|
||||||
|
|
||||||
if (stage.tevksel_kc > 7)
|
if (u32(stage.tevksel_kc) > 7)
|
||||||
{
|
{
|
||||||
out.SetConstantsUsed(C_KCOLORS + ((stage.tevksel_kc - 0xc) % 4),
|
out.SetConstantsUsed(C_KCOLORS + ((u32(stage.tevksel_kc) - 0xc) % 4),
|
||||||
C_KCOLORS + ((stage.tevksel_kc - 0xc) % 4));
|
C_KCOLORS + ((u32(stage.tevksel_kc) - 0xc) % 4));
|
||||||
}
|
}
|
||||||
if (stage.tevksel_ka > 7)
|
if (u32(stage.tevksel_ka) > 7)
|
||||||
{
|
{
|
||||||
out.SetConstantsUsed(C_KCOLORS + ((stage.tevksel_ka - 0xc) % 4),
|
out.SetConstantsUsed(C_KCOLORS + ((u32(stage.tevksel_ka) - 0xc) % 4),
|
||||||
C_KCOLORS + ((stage.tevksel_ka - 0xc) % 4));
|
C_KCOLORS + ((u32(stage.tevksel_ka) - 0xc) % 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cc.d == TEVCOLORARG_C0 || cc.d == TEVCOLORARG_A0 || ac.d == TEVALPHAARG_A0)
|
if (cc.d == TevColorArg::Color0 || cc.d == TevColorArg::Alpha0 || ac.d == TevAlphaArg::Alpha0)
|
||||||
out.SetConstantsUsed(C_COLORS + 1, C_COLORS + 1);
|
out.SetConstantsUsed(C_COLORS + 1, C_COLORS + 1);
|
||||||
|
|
||||||
if (cc.d == TEVCOLORARG_C1 || cc.d == TEVCOLORARG_A1 || ac.d == TEVALPHAARG_A1)
|
if (cc.d == TevColorArg::Color1 || cc.d == TevColorArg::Alpha1 || ac.d == TevAlphaArg::Alpha1)
|
||||||
out.SetConstantsUsed(C_COLORS + 2, C_COLORS + 2);
|
out.SetConstantsUsed(C_COLORS + 2, C_COLORS + 2);
|
||||||
|
|
||||||
if (cc.d == TEVCOLORARG_C2 || cc.d == TEVCOLORARG_A2 || ac.d == TEVALPHAARG_A2)
|
if (cc.d == TevColorArg::Color2 || cc.d == TevColorArg::Alpha2 || ac.d == TevAlphaArg::Alpha2)
|
||||||
out.SetConstantsUsed(C_COLORS + 3, C_COLORS + 3);
|
out.SetConstantsUsed(C_COLORS + 3, C_COLORS + 3);
|
||||||
|
|
||||||
if (cc.dest >= GX_TEVREG0)
|
if (cc.dest >= TevOutput::Color0)
|
||||||
out.SetConstantsUsed(C_COLORS + cc.dest, C_COLORS + cc.dest);
|
out.SetConstantsUsed(C_COLORS + u32(cc.dest.Value()), C_COLORS + u32(cc.dest.Value()));
|
||||||
|
|
||||||
if (ac.dest >= GX_TEVREG0)
|
if (ac.dest >= TevOutput::Color0)
|
||||||
out.SetConstantsUsed(C_COLORS + ac.dest, C_COLORS + ac.dest);
|
out.SetConstantsUsed(C_COLORS + u32(ac.dest.Value()), C_COLORS + u32(ac.dest.Value()));
|
||||||
|
|
||||||
out.Write("\ttevin_a = int4({}, {})&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.a],
|
out.Write("\ttevin_a = int4({}, {})&int4(255, 255, 255, 255);\n",
|
||||||
tev_a_input_table[ac.a]);
|
tev_c_input_table[u32(cc.a.Value())], tev_a_input_table[u32(ac.a.Value())]);
|
||||||
out.Write("\ttevin_b = int4({}, {})&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.b],
|
out.Write("\ttevin_b = int4({}, {})&int4(255, 255, 255, 255);\n",
|
||||||
tev_a_input_table[ac.b]);
|
tev_c_input_table[u32(cc.b.Value())], tev_a_input_table[u32(ac.b.Value())]);
|
||||||
out.Write("\ttevin_c = int4({}, {})&int4(255, 255, 255, 255);\n", tev_c_input_table[cc.c],
|
out.Write("\ttevin_c = int4({}, {})&int4(255, 255, 255, 255);\n",
|
||||||
tev_a_input_table[ac.c]);
|
tev_c_input_table[u32(cc.c.Value())], tev_a_input_table[u32(ac.c.Value())]);
|
||||||
out.Write("\ttevin_d = int4({}, {});\n", tev_c_input_table[cc.d], tev_a_input_table[ac.d]);
|
out.Write("\ttevin_d = int4({}, {});\n", tev_c_input_table[u32(cc.d.Value())],
|
||||||
|
tev_a_input_table[u32(ac.d.Value())]);
|
||||||
|
|
||||||
out.Write("\t// color combine\n");
|
out.Write("\t// color combine\n");
|
||||||
out.Write("\t{} = clamp(", tev_c_output_table[cc.dest]);
|
out.Write("\t{} = clamp(", tev_c_output_table[u32(cc.dest.Value())]);
|
||||||
if (cc.bias != TEVBIAS_COMPARE)
|
if (cc.bias != TevBias::Compare)
|
||||||
{
|
{
|
||||||
WriteTevRegular(out, "rgb", cc.bias, cc.op, cc.clamp, cc.shift, false);
|
WriteTevRegular(out, "rgb", cc.bias, cc.op, cc.clamp, cc.scale, false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static constexpr std::array<const char*, 8> function_table{
|
static constexpr std::array<const char*, 8> function_table{
|
||||||
"((tevin_a.r > tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // TEVCMP_R8_GT
|
"((tevin_a.r > tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // TevCompareMode::R8, GT
|
||||||
"((tevin_a.r == tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // TEVCMP_R8_EQ
|
"((tevin_a.r == tevin_b.r) ? tevin_c.rgb : int3(0,0,0))", // R8, TevComparison::EQ
|
||||||
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : "
|
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : "
|
||||||
"int3(0,0,0))", // TEVCMP_GR16_GT
|
"int3(0,0,0))", // GR16, GT
|
||||||
"((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : "
|
"((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.rgb : "
|
||||||
"int3(0,0,0))", // TEVCMP_GR16_EQ
|
"int3(0,0,0))", // GR16, EQ
|
||||||
"((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : "
|
"((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : "
|
||||||
"int3(0,0,0))", // TEVCMP_BGR24_GT
|
"int3(0,0,0))", // BGR24, GT
|
||||||
"((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : "
|
"((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.rgb : "
|
||||||
"int3(0,0,0))", // TEVCMP_BGR24_EQ
|
"int3(0,0,0))", // BGR24, EQ
|
||||||
"(max(sign(tevin_a.rgb - tevin_b.rgb), int3(0,0,0)) * tevin_c.rgb)", // TEVCMP_RGB8_GT
|
"(max(sign(tevin_a.rgb - tevin_b.rgb), int3(0,0,0)) * tevin_c.rgb)", // RGB8, GT
|
||||||
"((int3(1,1,1) - sign(abs(tevin_a.rgb - tevin_b.rgb))) * tevin_c.rgb)" // TEVCMP_RGB8_EQ
|
"((int3(1,1,1) - sign(abs(tevin_a.rgb - tevin_b.rgb))) * tevin_c.rgb)" // RGB8, EQ
|
||||||
};
|
};
|
||||||
|
|
||||||
const u32 mode = (cc.shift << 1) | cc.op;
|
const u32 mode = (u32(cc.compare_mode.Value()) << 1) | u32(cc.comparison.Value());
|
||||||
out.Write(" tevin_d.rgb + ");
|
out.Write(" tevin_d.rgb + ");
|
||||||
out.Write("{}", function_table[mode]);
|
out.Write("{}", function_table[mode]);
|
||||||
}
|
}
|
||||||
|
@ -1284,25 +1295,25 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
out.Write(";\n");
|
out.Write(";\n");
|
||||||
|
|
||||||
out.Write("\t// alpha combine\n");
|
out.Write("\t// alpha combine\n");
|
||||||
out.Write("\t{} = clamp(", tev_a_output_table[ac.dest]);
|
out.Write("\t{} = clamp(", tev_a_output_table[u32(ac.dest.Value())]);
|
||||||
if (ac.bias != TEVBIAS_COMPARE)
|
if (ac.bias != TevBias::Compare)
|
||||||
{
|
{
|
||||||
WriteTevRegular(out, "a", ac.bias, ac.op, ac.clamp, ac.shift, true);
|
WriteTevRegular(out, "a", ac.bias, ac.op, ac.clamp, ac.scale, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
static constexpr std::array<const char*, 8> function_table{
|
static constexpr std::array<const char*, 8> function_table{
|
||||||
"((tevin_a.r > tevin_b.r) ? tevin_c.a : 0)", // TEVCMP_R8_GT
|
"((tevin_a.r > tevin_b.r) ? tevin_c.a : 0)", // TevCompareMode::R8, GT
|
||||||
"((tevin_a.r == tevin_b.r) ? tevin_c.a : 0)", // TEVCMP_R8_EQ
|
"((tevin_a.r == tevin_b.r) ? tevin_c.a : 0)", // R8, TevComparison::EQ
|
||||||
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // TEVCMP_GR16_GT
|
"((idot(tevin_a.rgb, comp16) > idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // GR16, GT
|
||||||
"((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // TEVCMP_GR16_EQ
|
"((idot(tevin_a.rgb, comp16) == idot(tevin_b.rgb, comp16)) ? tevin_c.a : 0)", // GR16, EQ
|
||||||
"((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // TEVCMP_BGR24_GT
|
"((idot(tevin_a.rgb, comp24) > idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // BGR24, GT
|
||||||
"((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // TEVCMP_BGR24_EQ
|
"((idot(tevin_a.rgb, comp24) == idot(tevin_b.rgb, comp24)) ? tevin_c.a : 0)", // BGR24, EQ
|
||||||
"((tevin_a.a > tevin_b.a) ? tevin_c.a : 0)", // TEVCMP_A8_GT
|
"((tevin_a.a > tevin_b.a) ? tevin_c.a : 0)", // A8, GT
|
||||||
"((tevin_a.a == tevin_b.a) ? tevin_c.a : 0)" // TEVCMP_A8_EQ
|
"((tevin_a.a == tevin_b.a) ? tevin_c.a : 0)" // A8, EQ
|
||||||
};
|
};
|
||||||
|
|
||||||
const u32 mode = (ac.shift << 1) | ac.op;
|
const u32 mode = (u32(ac.compare_mode.Value()) << 1) | u32(ac.comparison.Value());
|
||||||
out.Write(" tevin_d.a + ");
|
out.Write(" tevin_d.a + ");
|
||||||
out.Write("{}", function_table[mode]);
|
out.Write("{}", function_table[mode]);
|
||||||
}
|
}
|
||||||
|
@ -1314,24 +1325,24 @@ static void WriteStage(ShaderCode& out, const pixel_shader_uid_data* uid_data, i
|
||||||
out.Write(";\n");
|
out.Write(";\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void WriteTevRegular(ShaderCode& out, std::string_view components, int bias, int op,
|
static void WriteTevRegular(ShaderCode& out, std::string_view components, TevBias bias, TevOp op,
|
||||||
int clamp, int shift, bool alpha)
|
bool clamp, TevScale scale, bool alpha)
|
||||||
{
|
{
|
||||||
static constexpr std::array<const char*, 4> tev_scale_table_left{
|
static constexpr std::array<const char*, 4> tev_scale_table_left{
|
||||||
"", // SCALE_1
|
"", // Scale1
|
||||||
" << 1", // SCALE_2
|
" << 1", // Scale2
|
||||||
" << 2", // SCALE_4
|
" << 2", // Scale4
|
||||||
"", // DIVIDE_2
|
"", // Divide2
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<const char*, 4> tev_scale_table_right{
|
static constexpr std::array<const char*, 4> tev_scale_table_right{
|
||||||
"", // SCALE_1
|
"", // Scale1
|
||||||
"", // SCALE_2
|
"", // Scale2
|
||||||
"", // SCALE_4
|
"", // Scale4
|
||||||
" >> 1", // DIVIDE_2
|
" >> 1", // Divide2
|
||||||
};
|
};
|
||||||
|
|
||||||
// indexed by 2*op+(shift==3)
|
// indexed by 2*op+(scale==Divide2)
|
||||||
static constexpr std::array<const char*, 4> tev_lerp_bias{
|
static constexpr std::array<const char*, 4> tev_lerp_bias{
|
||||||
"",
|
"",
|
||||||
" + 128",
|
" + 128",
|
||||||
|
@ -1340,15 +1351,15 @@ static void WriteTevRegular(ShaderCode& out, std::string_view components, int bi
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<const char*, 4> tev_bias_table{
|
static constexpr std::array<const char*, 4> tev_bias_table{
|
||||||
"", // ZERO,
|
"", // Zero,
|
||||||
" + 128", // ADDHALF,
|
" + 128", // AddHalf,
|
||||||
" - 128", // SUBHALF,
|
" - 128", // SubHalf,
|
||||||
"",
|
"",
|
||||||
};
|
};
|
||||||
|
|
||||||
static constexpr std::array<char, 2> tev_op_table{
|
static constexpr std::array<char, 2> tev_op_table{
|
||||||
'+', // TEVOP_ADD = 0,
|
'+', // TevOp::Add = 0,
|
||||||
'-', // TEVOP_SUB = 1,
|
'-', // TevOp::Sub = 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Regular TEV stage: (d + bias + lerp(a,b,c)) * scale
|
// Regular TEV stage: (d + bias + lerp(a,b,c)) * scale
|
||||||
|
@ -1356,12 +1367,14 @@ static void WriteTevRegular(ShaderCode& out, std::string_view components, int bi
|
||||||
// - c is scaled from 0..255 to 0..256, which allows dividing the result by 256 instead of 255
|
// - c is scaled from 0..255 to 0..256, which allows dividing the result by 256 instead of 255
|
||||||
// - if scale is bigger than one, it is moved inside the lerp calculation for increased accuracy
|
// - if scale is bigger than one, it is moved inside the lerp calculation for increased accuracy
|
||||||
// - a rounding bias is added before dividing by 256
|
// - a rounding bias is added before dividing by 256
|
||||||
out.Write("(((tevin_d.{}{}){})", components, tev_bias_table[bias], tev_scale_table_left[shift]);
|
out.Write("(((tevin_d.{}{}){})", components, tev_bias_table[u32(bias)],
|
||||||
out.Write(" {} ", tev_op_table[op]);
|
tev_scale_table_left[u32(scale)]);
|
||||||
|
out.Write(" {} ", tev_op_table[u32(op)]);
|
||||||
out.Write("(((((tevin_a.{}<<8) + (tevin_b.{}-tevin_a.{})*(tevin_c.{}+(tevin_c.{}>>7))){}){})>>8)",
|
out.Write("(((((tevin_a.{}<<8) + (tevin_b.{}-tevin_a.{})*(tevin_c.{}+(tevin_c.{}>>7))){}){})>>8)",
|
||||||
components, components, components, components, components, tev_scale_table_left[shift],
|
components, components, components, components, components,
|
||||||
tev_lerp_bias[2 * op + ((shift == 3) == alpha)]);
|
tev_scale_table_left[u32(scale)],
|
||||||
out.Write("){}", tev_scale_table_right[shift]);
|
tev_lerp_bias[2 * u32(op) + ((scale == TevScale::Divide2) == alpha)]);
|
||||||
|
out.Write("){}", tev_scale_table_right[u32(scale)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void SampleTexture(ShaderCode& out, std::string_view texcoords, std::string_view texswap,
|
static void SampleTexture(ShaderCode& out, std::string_view texcoords, std::string_view texswap,
|
||||||
|
@ -1383,14 +1396,14 @@ static void SampleTexture(ShaderCode& out, std::string_view texcoords, std::stri
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr std::array<const char*, 8> tev_alpha_funcs_table{
|
constexpr std::array<const char*, 8> tev_alpha_funcs_table{
|
||||||
"(false)", // NEVER
|
"(false)", // CompareMode::Never
|
||||||
"(prev.a < {})", // LESS
|
"(prev.a < {})", // CompareMode::Less
|
||||||
"(prev.a == {})", // EQUAL
|
"(prev.a == {})", // CompareMode::Equal
|
||||||
"(prev.a <= {})", // LEQUAL
|
"(prev.a <= {})", // CompareMode::LEqual
|
||||||
"(prev.a > {})", // GREATER
|
"(prev.a > {})", // CompareMode::Greater
|
||||||
"(prev.a != {})", // NEQUAL
|
"(prev.a != {})", // CompareMode::NEqual
|
||||||
"(prev.a >= {})", // GEQUAL
|
"(prev.a >= {})", // CompareMode::GEqual
|
||||||
"(true)" // ALWAYS
|
"(true)" // CompareMode::Always
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr std::array<const char*, 4> tev_alpha_funclogic_table{
|
constexpr std::array<const char*, 4> tev_alpha_funclogic_table{
|
||||||
|
@ -1408,12 +1421,12 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
|
||||||
I_ALPHA ".g",
|
I_ALPHA ".g",
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto write_alpha_func = [&out](int index, std::string_view ref) {
|
const auto write_alpha_func = [&out](CompareMode mode, std::string_view ref) {
|
||||||
const bool has_no_arguments = index == 0 || index == tev_alpha_funcs_table.size() - 1;
|
const bool has_no_arguments = mode == CompareMode::Never || mode == CompareMode::Always;
|
||||||
if (has_no_arguments)
|
if (has_no_arguments)
|
||||||
out.Write("{}", tev_alpha_funcs_table[index]);
|
out.Write("{}", tev_alpha_funcs_table[u32(mode)]);
|
||||||
else
|
else
|
||||||
out.Write(tev_alpha_funcs_table[index], ref);
|
out.Write(tev_alpha_funcs_table[u32(mode)], ref);
|
||||||
};
|
};
|
||||||
|
|
||||||
out.SetConstantsUsed(C_ALPHA, C_ALPHA);
|
out.SetConstantsUsed(C_ALPHA, C_ALPHA);
|
||||||
|
@ -1424,15 +1437,13 @@ static void WriteAlphaTest(ShaderCode& out, const pixel_shader_uid_data* uid_dat
|
||||||
out.Write("\tif(!( ");
|
out.Write("\tif(!( ");
|
||||||
|
|
||||||
// Lookup the first component from the alpha function table
|
// Lookup the first component from the alpha function table
|
||||||
const int comp0_index = uid_data->alpha_test_comp0;
|
write_alpha_func(uid_data->alpha_test_comp0, alpha_ref[0]);
|
||||||
write_alpha_func(comp0_index, alpha_ref[0]);
|
|
||||||
|
|
||||||
// Lookup the logic op
|
// Lookup the logic op
|
||||||
out.Write("{}", tev_alpha_funclogic_table[uid_data->alpha_test_logic]);
|
out.Write("{}", tev_alpha_funclogic_table[u32(uid_data->alpha_test_logic)]);
|
||||||
|
|
||||||
// Lookup the second component from the alpha function table
|
// Lookup the second component from the alpha function table
|
||||||
const int comp1_index = uid_data->alpha_test_comp1;
|
write_alpha_func(uid_data->alpha_test_comp1, alpha_ref[1]);
|
||||||
write_alpha_func(comp1_index, alpha_ref[1]);
|
|
||||||
|
|
||||||
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_NEGATED_BOOLEAN))
|
if (DriverDetails::HasBug(DriverDetails::BUG_BROKEN_NEGATED_BOOLEAN))
|
||||||
out.Write(") == false) {{\n");
|
out.Write(") == false) {{\n");
|
||||||
|
@ -1472,13 +1483,13 @@ constexpr std::array<const char*, 8> tev_fog_funcs_table{
|
||||||
|
|
||||||
static void WriteFog(ShaderCode& out, const pixel_shader_uid_data* uid_data)
|
static void WriteFog(ShaderCode& out, const pixel_shader_uid_data* uid_data)
|
||||||
{
|
{
|
||||||
if (uid_data->fog_fsel == 0)
|
if (uid_data->fog_fsel == FogType::Off)
|
||||||
return; // no Fog
|
return; // no Fog
|
||||||
|
|
||||||
out.SetConstantsUsed(C_FOGCOLOR, C_FOGCOLOR);
|
out.SetConstantsUsed(C_FOGCOLOR, C_FOGCOLOR);
|
||||||
out.SetConstantsUsed(C_FOGI, C_FOGI);
|
out.SetConstantsUsed(C_FOGI, C_FOGI);
|
||||||
out.SetConstantsUsed(C_FOGF, C_FOGF + 1);
|
out.SetConstantsUsed(C_FOGF, C_FOGF + 1);
|
||||||
if (uid_data->fog_proj == 0)
|
if (uid_data->fog_proj == FogProjection::Perspective)
|
||||||
{
|
{
|
||||||
// perspective
|
// perspective
|
||||||
// ze = A/(B - (Zs >> B_SHF)
|
// ze = A/(B - (Zs >> B_SHF)
|
||||||
|
@ -1514,14 +1525,14 @@ static void WriteFog(ShaderCode& out, const pixel_shader_uid_data* uid_data)
|
||||||
|
|
||||||
out.Write("\tfloat fog = clamp(ze - " I_FOGF ".y, 0.0, 1.0);\n");
|
out.Write("\tfloat fog = clamp(ze - " I_FOGF ".y, 0.0, 1.0);\n");
|
||||||
|
|
||||||
if (uid_data->fog_fsel > 3)
|
if (uid_data->fog_fsel >= FogType::Exp)
|
||||||
{
|
{
|
||||||
out.Write("{}", tev_fog_funcs_table[uid_data->fog_fsel]);
|
out.Write("{}", tev_fog_funcs_table[u32(uid_data->fog_fsel)]);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (uid_data->fog_fsel != 2)
|
if (uid_data->fog_fsel != FogType::Linear)
|
||||||
WARN_LOG_FMT(VIDEO, "Unknown Fog Type! {:08x}", uid_data->fog_fsel);
|
WARN_LOG_FMT(VIDEO, "Unknown Fog Type! {}", uid_data->fog_fsel);
|
||||||
}
|
}
|
||||||
|
|
||||||
out.Write("\tint ifog = iround(fog * 256.0);\n");
|
out.Write("\tint ifog = iround(fog * 256.0);\n");
|
||||||
|
@ -1610,11 +1621,13 @@ static void WriteBlend(ShaderCode& out, const pixel_shader_uid_data* uid_data)
|
||||||
"1.0 - initial_ocol0.a;", // INVDSTALPHA
|
"1.0 - initial_ocol0.a;", // INVDSTALPHA
|
||||||
};
|
};
|
||||||
out.Write("\tfloat4 blend_src;\n");
|
out.Write("\tfloat4 blend_src;\n");
|
||||||
out.Write("\tblend_src.rgb = {}\n", blend_src_factor[uid_data->blend_src_factor]);
|
out.Write("\tblend_src.rgb = {}\n", blend_src_factor[u32(uid_data->blend_src_factor)]);
|
||||||
out.Write("\tblend_src.a = {}\n", blend_src_factor_alpha[uid_data->blend_src_factor_alpha]);
|
out.Write("\tblend_src.a = {}\n",
|
||||||
|
blend_src_factor_alpha[u32(uid_data->blend_src_factor_alpha)]);
|
||||||
out.Write("\tfloat4 blend_dst;\n");
|
out.Write("\tfloat4 blend_dst;\n");
|
||||||
out.Write("\tblend_dst.rgb = {}\n", blend_dst_factor[uid_data->blend_dst_factor]);
|
out.Write("\tblend_dst.rgb = {}\n", blend_dst_factor[u32(uid_data->blend_dst_factor)]);
|
||||||
out.Write("\tblend_dst.a = {}\n", blend_dst_factor_alpha[uid_data->blend_dst_factor_alpha]);
|
out.Write("\tblend_dst.a = {}\n",
|
||||||
|
blend_dst_factor_alpha[u32(uid_data->blend_dst_factor_alpha)]);
|
||||||
|
|
||||||
out.Write("\tfloat4 blend_result;\n");
|
out.Write("\tfloat4 blend_result;\n");
|
||||||
if (uid_data->blend_subtract)
|
if (uid_data->blend_subtract)
|
||||||
|
|
|
@ -9,6 +9,15 @@
|
||||||
#include "VideoCommon/ShaderGenCommon.h"
|
#include "VideoCommon/ShaderGenCommon.h"
|
||||||
|
|
||||||
enum class APIType;
|
enum class APIType;
|
||||||
|
enum class AlphaTestResult;
|
||||||
|
enum class SrcBlendFactor : u32;
|
||||||
|
enum class DstBlendFactor : u32;
|
||||||
|
enum class CompareMode : u32;
|
||||||
|
enum class AlphaTestOp : u32;
|
||||||
|
enum class RasColorChan : u32;
|
||||||
|
enum class KonstSel : u32;
|
||||||
|
enum class FogProjection : u32;
|
||||||
|
enum class FogType : u32;
|
||||||
|
|
||||||
#pragma pack(1)
|
#pragma pack(1)
|
||||||
struct pixel_shader_uid_data
|
struct pixel_shader_uid_data
|
||||||
|
@ -19,18 +28,18 @@ struct pixel_shader_uid_data
|
||||||
u32 NumValues() const { return num_values; }
|
u32 NumValues() const { return num_values; }
|
||||||
u32 pad0 : 4;
|
u32 pad0 : 4;
|
||||||
u32 useDstAlpha : 1;
|
u32 useDstAlpha : 1;
|
||||||
u32 Pretest : 2;
|
AlphaTestResult Pretest : 2;
|
||||||
u32 nIndirectStagesUsed : 4;
|
u32 nIndirectStagesUsed : 4;
|
||||||
u32 genMode_numtexgens : 4;
|
u32 genMode_numtexgens : 4;
|
||||||
u32 genMode_numtevstages : 4;
|
u32 genMode_numtevstages : 4;
|
||||||
u32 genMode_numindstages : 3;
|
u32 genMode_numindstages : 3;
|
||||||
u32 alpha_test_comp0 : 3;
|
CompareMode alpha_test_comp0 : 3;
|
||||||
u32 alpha_test_comp1 : 3;
|
CompareMode alpha_test_comp1 : 3;
|
||||||
u32 alpha_test_logic : 2;
|
AlphaTestOp alpha_test_logic : 2;
|
||||||
u32 alpha_test_use_zcomploc_hack : 1;
|
u32 alpha_test_use_zcomploc_hack : 1;
|
||||||
u32 fog_proj : 1;
|
FogProjection fog_proj : 1;
|
||||||
|
|
||||||
u32 fog_fsel : 3;
|
FogType fog_fsel : 3;
|
||||||
u32 fog_RangeBaseEnabled : 1;
|
u32 fog_RangeBaseEnabled : 1;
|
||||||
u32 ztex_op : 2;
|
u32 ztex_op : 2;
|
||||||
u32 per_pixel_depth : 1;
|
u32 per_pixel_depth : 1;
|
||||||
|
@ -43,13 +52,13 @@ struct pixel_shader_uid_data
|
||||||
u32 rgba6_format : 1;
|
u32 rgba6_format : 1;
|
||||||
u32 dither : 1;
|
u32 dither : 1;
|
||||||
u32 uint_output : 1;
|
u32 uint_output : 1;
|
||||||
u32 blend_enable : 1; // Only used with shader_framebuffer_fetch blend
|
u32 blend_enable : 1; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_src_factor : 3; // Only used with shader_framebuffer_fetch blend
|
SrcBlendFactor blend_src_factor : 3; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_src_factor_alpha : 3; // Only used with shader_framebuffer_fetch blend
|
SrcBlendFactor blend_src_factor_alpha : 3; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_dst_factor : 3; // Only used with shader_framebuffer_fetch blend
|
DstBlendFactor blend_dst_factor : 3; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_dst_factor_alpha : 3; // Only used with shader_framebuffer_fetch blend
|
DstBlendFactor blend_dst_factor_alpha : 3; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_subtract : 1; // Only used with shader_framebuffer_fetch blend
|
u32 blend_subtract : 1; // Only used with shader_framebuffer_fetch blend
|
||||||
u32 blend_subtract_alpha : 1; // Only used with shader_framebuffer_fetch blend
|
u32 blend_subtract_alpha : 1; // Only used with shader_framebuffer_fetch blend
|
||||||
|
|
||||||
u32 texMtxInfo_n_projection : 8; // 8x1 bit
|
u32 texMtxInfo_n_projection : 8; // 8x1 bit
|
||||||
u32 tevindref_bi0 : 3;
|
u32 tevindref_bi0 : 3;
|
||||||
|
@ -136,7 +145,7 @@ struct pixel_shader_uid_data
|
||||||
u32 tevorders_texmap : 3;
|
u32 tevorders_texmap : 3;
|
||||||
u32 tevorders_texcoord : 3;
|
u32 tevorders_texcoord : 3;
|
||||||
u32 tevorders_enable : 1;
|
u32 tevorders_enable : 1;
|
||||||
u32 tevorders_colorchan : 3;
|
RasColorChan tevorders_colorchan : 3;
|
||||||
u32 pad1 : 6;
|
u32 pad1 : 6;
|
||||||
|
|
||||||
// TODO: Clean up the swapXY mess
|
// TODO: Clean up the swapXY mess
|
||||||
|
@ -152,8 +161,8 @@ struct pixel_shader_uid_data
|
||||||
u32 tevksel_swap2c : 2;
|
u32 tevksel_swap2c : 2;
|
||||||
u32 tevksel_swap1d : 2;
|
u32 tevksel_swap1d : 2;
|
||||||
u32 tevksel_swap2d : 2;
|
u32 tevksel_swap2d : 2;
|
||||||
u32 tevksel_kc : 5;
|
KonstSel tevksel_kc : 5;
|
||||||
u32 tevksel_ka : 5;
|
KonstSel tevksel_ka : 5;
|
||||||
u32 pad3 : 14;
|
u32 pad3 : 14;
|
||||||
} stagehash[16];
|
} stagehash[16];
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,7 @@ void PixelShaderManager::SetConstants()
|
||||||
// Destination alpha is only enabled if alpha writes are enabled. Force entire uniform to zero
|
// Destination alpha is only enabled if alpha writes are enabled. Force entire uniform to zero
|
||||||
// when disabled.
|
// when disabled.
|
||||||
u32 dstalpha = bpmem.blendmode.alphaupdate && bpmem.dstalpha.enable &&
|
u32 dstalpha = bpmem.blendmode.alphaupdate && bpmem.dstalpha.enable &&
|
||||||
bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24 ?
|
bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 ?
|
||||||
bpmem.dstalpha.hex :
|
bpmem.dstalpha.hex :
|
||||||
0;
|
0;
|
||||||
|
|
||||||
|
@ -270,7 +270,7 @@ void PixelShaderManager::SetAlphaTestChanged()
|
||||||
// TODO: we could optimize this further and check the actual constants,
|
// TODO: we could optimize this further and check the actual constants,
|
||||||
// i.e. "a <= 0" and "a >= 255" will always pass.
|
// i.e. "a <= 0" and "a >= 255" will always pass.
|
||||||
u32 alpha_test =
|
u32 alpha_test =
|
||||||
bpmem.alpha_test.TestResult() != AlphaTest::PASS ? bpmem.alpha_test.hex | 1 << 31 : 0;
|
bpmem.alpha_test.TestResult() != AlphaTestResult::Pass ? bpmem.alpha_test.hex | 1 << 31 : 0;
|
||||||
if (constants.alphaTest != alpha_test)
|
if (constants.alphaTest != alpha_test)
|
||||||
{
|
{
|
||||||
constants.alphaTest = alpha_test;
|
constants.alphaTest = alpha_test;
|
||||||
|
@ -362,25 +362,26 @@ void PixelShaderManager::SetZTextureTypeChanged()
|
||||||
{
|
{
|
||||||
switch (bpmem.ztex2.type)
|
switch (bpmem.ztex2.type)
|
||||||
{
|
{
|
||||||
case TEV_ZTEX_TYPE_U8:
|
case ZTexFormat::U8:
|
||||||
constants.zbias[0][0] = 0;
|
constants.zbias[0][0] = 0;
|
||||||
constants.zbias[0][1] = 0;
|
constants.zbias[0][1] = 0;
|
||||||
constants.zbias[0][2] = 0;
|
constants.zbias[0][2] = 0;
|
||||||
constants.zbias[0][3] = 1;
|
constants.zbias[0][3] = 1;
|
||||||
break;
|
break;
|
||||||
case TEV_ZTEX_TYPE_U16:
|
case ZTexFormat::U16:
|
||||||
constants.zbias[0][0] = 1;
|
constants.zbias[0][0] = 1;
|
||||||
constants.zbias[0][1] = 0;
|
constants.zbias[0][1] = 0;
|
||||||
constants.zbias[0][2] = 0;
|
constants.zbias[0][2] = 0;
|
||||||
constants.zbias[0][3] = 256;
|
constants.zbias[0][3] = 256;
|
||||||
break;
|
break;
|
||||||
case TEV_ZTEX_TYPE_U24:
|
case ZTexFormat::U24:
|
||||||
constants.zbias[0][0] = 65536;
|
constants.zbias[0][0] = 65536;
|
||||||
constants.zbias[0][1] = 256;
|
constants.zbias[0][1] = 256;
|
||||||
constants.zbias[0][2] = 1;
|
constants.zbias[0][2] = 1;
|
||||||
constants.zbias[0][3] = 0;
|
constants.zbias[0][3] = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
PanicAlertFmt("Invalid ztex format {}", bpmem.ztex2.type);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
dirty = true;
|
dirty = true;
|
||||||
|
@ -457,8 +458,9 @@ void PixelShaderManager::SetZModeControl()
|
||||||
{
|
{
|
||||||
u32 late_ztest = bpmem.UseLateDepthTest();
|
u32 late_ztest = bpmem.UseLateDepthTest();
|
||||||
u32 rgba6_format =
|
u32 rgba6_format =
|
||||||
(bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor) ? 1 :
|
(bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24 && !g_ActiveConfig.bForceTrueColor) ?
|
||||||
0;
|
1 :
|
||||||
|
0;
|
||||||
u32 dither = rgba6_format && bpmem.blendmode.dither;
|
u32 dither = rgba6_format && bpmem.blendmode.dither;
|
||||||
if (constants.late_ztest != late_ztest || constants.rgba6_format != rgba6_format ||
|
if (constants.late_ztest != late_ztest || constants.rgba6_format != rgba6_format ||
|
||||||
constants.dither != dither)
|
constants.dither != dither)
|
||||||
|
|
|
@ -171,7 +171,7 @@ void Renderer::SetAndClearFramebuffer(AbstractFramebuffer* framebuffer,
|
||||||
|
|
||||||
bool Renderer::EFBHasAlphaChannel() const
|
bool Renderer::EFBHasAlphaChannel() const
|
||||||
{
|
{
|
||||||
return m_prev_efb_format == PEControl::RGBA6_Z24;
|
return m_prev_efb_format == PixelFormat::RGBA6_Z24;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool colorEnable, bool alphaEnable,
|
void Renderer::ClearScreen(const MathUtil::Rectangle<int>& rc, bool colorEnable, bool alphaEnable,
|
||||||
|
@ -197,15 +197,15 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
|
||||||
// check what to do with the alpha channel (GX_PokeAlphaRead)
|
// check what to do with the alpha channel (GX_PokeAlphaRead)
|
||||||
PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
|
PixelEngine::UPEAlphaReadReg alpha_read_mode = PixelEngine::GetAlphaReadMode();
|
||||||
|
|
||||||
if (bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24)
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24)
|
||||||
{
|
{
|
||||||
color = RGBA8ToRGBA6ToRGBA8(color);
|
color = RGBA8ToRGBA6ToRGBA8(color);
|
||||||
}
|
}
|
||||||
else if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
|
else if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16)
|
||||||
{
|
{
|
||||||
color = RGBA8ToRGB565ToRGBA8(color);
|
color = RGBA8ToRGB565ToRGBA8(color);
|
||||||
}
|
}
|
||||||
if (bpmem.zcontrol.pixel_format != PEControl::RGBA6_Z24)
|
if (bpmem.zcontrol.pixel_format != PixelFormat::RGBA6_Z24)
|
||||||
{
|
{
|
||||||
color |= 0xFF000000;
|
color |= 0xFF000000;
|
||||||
}
|
}
|
||||||
|
@ -231,7 +231,7 @@ u32 Renderer::AccessEFB(EFBAccessType type, u32 x, u32 y, u32 poke_data)
|
||||||
depth = 1.0f - depth;
|
depth = 1.0f - depth;
|
||||||
|
|
||||||
u32 ret = 0;
|
u32 ret = 0;
|
||||||
if (bpmem.zcontrol.pixel_format == PEControl::RGB565_Z16)
|
if (bpmem.zcontrol.pixel_format == PixelFormat::RGB565_Z16)
|
||||||
{
|
{
|
||||||
// if Z is in 16 bit format you must return a 16 bit integer
|
// if Z is in 16 bit format you must return a 16 bit integer
|
||||||
ret = std::clamp<u32>(static_cast<u32>(depth * 65536.0f), 0, 0xFFFF);
|
ret = std::clamp<u32>(static_cast<u32>(depth * 65536.0f), 0, 0xFFFF);
|
||||||
|
@ -994,10 +994,10 @@ bool Renderer::RecompileImGuiPipeline()
|
||||||
pconfig.depth_state = RenderState::GetNoDepthTestingDepthState();
|
pconfig.depth_state = RenderState::GetNoDepthTestingDepthState();
|
||||||
pconfig.blending_state = RenderState::GetNoBlendingBlendState();
|
pconfig.blending_state = RenderState::GetNoBlendingBlendState();
|
||||||
pconfig.blending_state.blendenable = true;
|
pconfig.blending_state.blendenable = true;
|
||||||
pconfig.blending_state.srcfactor = BlendMode::SRCALPHA;
|
pconfig.blending_state.srcfactor = SrcBlendFactor::SrcAlpha;
|
||||||
pconfig.blending_state.dstfactor = BlendMode::INVSRCALPHA;
|
pconfig.blending_state.dstfactor = DstBlendFactor::InvSrcAlpha;
|
||||||
pconfig.blending_state.srcfactoralpha = BlendMode::ZERO;
|
pconfig.blending_state.srcfactoralpha = SrcBlendFactor::Zero;
|
||||||
pconfig.blending_state.dstfactoralpha = BlendMode::ONE;
|
pconfig.blending_state.dstfactoralpha = DstBlendFactor::One;
|
||||||
pconfig.framebuffer_state.color_texture_format = m_backbuffer_format;
|
pconfig.framebuffer_state.color_texture_format = m_backbuffer_format;
|
||||||
pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined;
|
pconfig.framebuffer_state.depth_texture_format = AbstractTextureFormat::Undefined;
|
||||||
pconfig.framebuffer_state.samples = 1;
|
pconfig.framebuffer_state.samples = 1;
|
||||||
|
@ -1697,7 +1697,7 @@ bool Renderer::UseVertexDepthRange() const
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// We need a full depth range if a ztexture is used.
|
// We need a full depth range if a ztexture is used.
|
||||||
if (bpmem.ztex2.type != ZTEXTURE_DISABLE && !bpmem.zcontrol.early_ztest)
|
if (bpmem.ztex2.op != ZTexOp::Disabled && !bpmem.zcontrol.early_ztest)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// If an inverted depth range is unsupported, we also need to check if the range is inverted.
|
// If an inverted depth range is unsupported, we also need to check if the range is inverted.
|
||||||
|
|
|
@ -232,8 +232,8 @@ public:
|
||||||
// Called when the configuration changes, and backend structures need to be updated.
|
// Called when the configuration changes, and backend structures need to be updated.
|
||||||
virtual void OnConfigChanged(u32 bits) {}
|
virtual void OnConfigChanged(u32 bits) {}
|
||||||
|
|
||||||
PEControl::PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; }
|
PixelFormat GetPrevPixelFormat() const { return m_prev_efb_format; }
|
||||||
void StorePixelFormat(PEControl::PixelFormat new_format) { m_prev_efb_format = new_format; }
|
void StorePixelFormat(PixelFormat new_format) { m_prev_efb_format = new_format; }
|
||||||
bool EFBHasAlphaChannel() const;
|
bool EFBHasAlphaChannel() const;
|
||||||
VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); }
|
VideoCommon::PostProcessing* GetPostProcessor() const { return m_post_processor.get(); }
|
||||||
// Final surface changing
|
// Final surface changing
|
||||||
|
@ -343,7 +343,7 @@ protected:
|
||||||
private:
|
private:
|
||||||
std::tuple<int, int> CalculateOutputDimensions(int width, int height) const;
|
std::tuple<int, int> CalculateOutputDimensions(int width, int height) const;
|
||||||
|
|
||||||
PEControl::PixelFormat m_prev_efb_format = PEControl::INVALID_FMT;
|
PixelFormat m_prev_efb_format = PixelFormat::INVALID_FMT;
|
||||||
unsigned int m_efb_scale = 1;
|
unsigned int m_efb_scale = 1;
|
||||||
|
|
||||||
// These will be set on the first call to SetWindowSize.
|
// These will be set on the first call to SetWindowSize.
|
||||||
|
|
|
@ -15,7 +15,7 @@ void RasterizationState::Generate(const BPMemory& bp, PrimitiveType primitive_ty
|
||||||
|
|
||||||
// Back-face culling should be disabled for points/lines.
|
// Back-face culling should be disabled for points/lines.
|
||||||
if (primitive_type != PrimitiveType::Triangles && primitive_type != PrimitiveType::TriangleStrip)
|
if (primitive_type != PrimitiveType::Triangles && primitive_type != PrimitiveType::TriangleStrip)
|
||||||
cullmode = GenMode::CULL_NONE;
|
cullmode = CullMode::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
RasterizationState& RasterizationState::operator=(const RasterizationState& rhs)
|
RasterizationState& RasterizationState::operator=(const RasterizationState& rhs)
|
||||||
|
@ -47,14 +47,27 @@ DepthState& DepthState::operator=(const DepthState& rhs)
|
||||||
// ONE on blending. As the backends may emulate this framebuffer
|
// ONE on blending. As the backends may emulate this framebuffer
|
||||||
// configuration with an alpha channel, we just drop all references
|
// configuration with an alpha channel, we just drop all references
|
||||||
// to the destination alpha channel.
|
// to the destination alpha channel.
|
||||||
static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor)
|
static SrcBlendFactor RemoveDstAlphaUsage(SrcBlendFactor factor)
|
||||||
{
|
{
|
||||||
switch (factor)
|
switch (factor)
|
||||||
{
|
{
|
||||||
case BlendMode::DSTALPHA:
|
case SrcBlendFactor::DstAlpha:
|
||||||
return BlendMode::ONE;
|
return SrcBlendFactor::One;
|
||||||
case BlendMode::INVDSTALPHA:
|
case SrcBlendFactor::InvDstAlpha:
|
||||||
return BlendMode::ZERO;
|
return SrcBlendFactor::Zero;
|
||||||
|
default:
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static DstBlendFactor RemoveDstAlphaUsage(DstBlendFactor factor)
|
||||||
|
{
|
||||||
|
switch (factor)
|
||||||
|
{
|
||||||
|
case DstBlendFactor::DstAlpha:
|
||||||
|
return DstBlendFactor::One;
|
||||||
|
case DstBlendFactor::InvDstAlpha:
|
||||||
|
return DstBlendFactor::Zero;
|
||||||
default:
|
default:
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
@ -64,14 +77,14 @@ static BlendMode::BlendFactor RemoveDstAlphaUsage(BlendMode::BlendFactor factor)
|
||||||
// the alpha component, CLR and ALPHA are indentical. So just always
|
// the alpha component, CLR and ALPHA are indentical. So just always
|
||||||
// use ALPHA as this makes it easier for the backends to use the second
|
// use ALPHA as this makes it easier for the backends to use the second
|
||||||
// alpha value of dual source blending.
|
// alpha value of dual source blending.
|
||||||
static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor)
|
static DstBlendFactor RemoveSrcColorUsage(DstBlendFactor factor)
|
||||||
{
|
{
|
||||||
switch (factor)
|
switch (factor)
|
||||||
{
|
{
|
||||||
case BlendMode::SRCCLR:
|
case DstBlendFactor::SrcClr:
|
||||||
return BlendMode::SRCALPHA;
|
return DstBlendFactor::SrcAlpha;
|
||||||
case BlendMode::INVSRCCLR:
|
case DstBlendFactor::InvSrcClr:
|
||||||
return BlendMode::INVSRCALPHA;
|
return DstBlendFactor::InvSrcAlpha;
|
||||||
default:
|
default:
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
@ -79,14 +92,14 @@ static BlendMode::BlendFactor RemoveSrcColorUsage(BlendMode::BlendFactor factor)
|
||||||
|
|
||||||
// Same as RemoveSrcColorUsage, but because of the overlapping enum,
|
// Same as RemoveSrcColorUsage, but because of the overlapping enum,
|
||||||
// this must be written as another function.
|
// this must be written as another function.
|
||||||
static BlendMode::BlendFactor RemoveDstColorUsage(BlendMode::BlendFactor factor)
|
static SrcBlendFactor RemoveDstColorUsage(SrcBlendFactor factor)
|
||||||
{
|
{
|
||||||
switch (factor)
|
switch (factor)
|
||||||
{
|
{
|
||||||
case BlendMode::DSTCLR:
|
case SrcBlendFactor::DstClr:
|
||||||
return BlendMode::DSTALPHA;
|
return SrcBlendFactor::DstAlpha;
|
||||||
case BlendMode::INVDSTCLR:
|
case SrcBlendFactor::InvDstClr:
|
||||||
return BlendMode::INVDSTALPHA;
|
return SrcBlendFactor::InvDstAlpha;
|
||||||
default:
|
default:
|
||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
@ -97,11 +110,11 @@ void BlendingState::Generate(const BPMemory& bp)
|
||||||
// Start with everything disabled.
|
// Start with everything disabled.
|
||||||
hex = 0;
|
hex = 0;
|
||||||
|
|
||||||
bool target_has_alpha = bp.zcontrol.pixel_format == PEControl::RGBA6_Z24;
|
bool target_has_alpha = bp.zcontrol.pixel_format == PixelFormat::RGBA6_Z24;
|
||||||
bool alpha_test_may_success = bp.alpha_test.TestResult() != AlphaTest::FAIL;
|
bool alpha_test_may_succeed = bp.alpha_test.TestResult() != AlphaTestResult::Fail;
|
||||||
|
|
||||||
colorupdate = bp.blendmode.colorupdate && alpha_test_may_success;
|
colorupdate = bp.blendmode.colorupdate && alpha_test_may_succeed;
|
||||||
alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_success;
|
alphaupdate = bp.blendmode.alphaupdate && target_has_alpha && alpha_test_may_succeed;
|
||||||
dstalpha = bp.dstalpha.enable && alphaupdate;
|
dstalpha = bp.dstalpha.enable && alphaupdate;
|
||||||
usedualsrc = true;
|
usedualsrc = true;
|
||||||
|
|
||||||
|
@ -110,14 +123,14 @@ void BlendingState::Generate(const BPMemory& bp)
|
||||||
{
|
{
|
||||||
blendenable = true;
|
blendenable = true;
|
||||||
subtractAlpha = subtract = true;
|
subtractAlpha = subtract = true;
|
||||||
srcfactoralpha = srcfactor = BlendMode::ONE;
|
srcfactoralpha = srcfactor = SrcBlendFactor::One;
|
||||||
dstfactoralpha = dstfactor = BlendMode::ONE;
|
dstfactoralpha = dstfactor = DstBlendFactor::One;
|
||||||
|
|
||||||
if (dstalpha)
|
if (dstalpha)
|
||||||
{
|
{
|
||||||
subtractAlpha = false;
|
subtractAlpha = false;
|
||||||
srcfactoralpha = BlendMode::ONE;
|
srcfactoralpha = SrcBlendFactor::One;
|
||||||
dstfactoralpha = BlendMode::ZERO;
|
dstfactoralpha = DstBlendFactor::Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,22 +146,22 @@ void BlendingState::Generate(const BPMemory& bp)
|
||||||
srcfactor = RemoveDstAlphaUsage(srcfactor);
|
srcfactor = RemoveDstAlphaUsage(srcfactor);
|
||||||
dstfactor = RemoveDstAlphaUsage(dstfactor);
|
dstfactor = RemoveDstAlphaUsage(dstfactor);
|
||||||
}
|
}
|
||||||
// replaces SRCCLR with SRCALPHA and DSTCLR with DSTALPHA, it is important to
|
// replaces SrcClr with SrcAlpha and DstClr with DstAlpha, it is important to
|
||||||
// use the dst function for the src factor and vice versa
|
// use the dst function for the src factor and vice versa
|
||||||
srcfactoralpha = RemoveDstColorUsage(srcfactor);
|
srcfactoralpha = RemoveDstColorUsage(srcfactor);
|
||||||
dstfactoralpha = RemoveSrcColorUsage(dstfactor);
|
dstfactoralpha = RemoveSrcColorUsage(dstfactor);
|
||||||
|
|
||||||
if (dstalpha)
|
if (dstalpha)
|
||||||
{
|
{
|
||||||
srcfactoralpha = BlendMode::ONE;
|
srcfactoralpha = SrcBlendFactor::One;
|
||||||
dstfactoralpha = BlendMode::ZERO;
|
dstfactoralpha = DstBlendFactor::Zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The logicop bit has the lowest priority
|
// The logicop bit has the lowest priority
|
||||||
else if (bp.blendmode.logicopenable)
|
else if (bp.blendmode.logicopenable)
|
||||||
{
|
{
|
||||||
if (bp.blendmode.logicmode == BlendMode::NOOP)
|
if (bp.blendmode.logicmode == LogicOp::NoOp)
|
||||||
{
|
{
|
||||||
// Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
|
// Fast path for Kirby's Return to Dreamland, they use it with dstAlpha.
|
||||||
colorupdate = false;
|
colorupdate = false;
|
||||||
|
@ -169,38 +182,39 @@ void BlendingState::Generate(const BPMemory& bp)
|
||||||
|
|
||||||
void BlendingState::ApproximateLogicOpWithBlending()
|
void BlendingState::ApproximateLogicOpWithBlending()
|
||||||
{
|
{
|
||||||
// Any of these which use SRC as srcFactor or DST as dstFactor won't be correct.
|
|
||||||
// This is because the two are aliased to one another (see the enum).
|
|
||||||
struct LogicOpApproximation
|
struct LogicOpApproximation
|
||||||
{
|
{
|
||||||
bool subtract;
|
bool subtract;
|
||||||
BlendMode::BlendFactor srcfactor;
|
SrcBlendFactor srcfactor;
|
||||||
BlendMode::BlendFactor dstfactor;
|
DstBlendFactor dstfactor;
|
||||||
};
|
};
|
||||||
|
// TODO: This previously had a warning about SRC and DST being aliased and not to mix them,
|
||||||
|
// but INVSRCCLR and INVDSTCLR were also aliased and were mixed.
|
||||||
|
// Thus, NOR, EQUIV, INVERT, COPY_INVERTED, and OR_INVERTED duplicate(d) other values.
|
||||||
static constexpr std::array<LogicOpApproximation, 16> approximations = {{
|
static constexpr std::array<LogicOpApproximation, 16> approximations = {{
|
||||||
{false, BlendMode::ZERO, BlendMode::ZERO}, // CLEAR
|
{false, SrcBlendFactor::Zero, DstBlendFactor::Zero}, // CLEAR
|
||||||
{false, BlendMode::DSTCLR, BlendMode::ZERO}, // AND
|
{false, SrcBlendFactor::DstClr, DstBlendFactor::Zero}, // AND
|
||||||
{true, BlendMode::ONE, BlendMode::INVSRCCLR}, // AND_REVERSE
|
{true, SrcBlendFactor::One, DstBlendFactor::InvSrcClr}, // AND_REVERSE
|
||||||
{false, BlendMode::ONE, BlendMode::ZERO}, // COPY
|
{false, SrcBlendFactor::One, DstBlendFactor::Zero}, // COPY
|
||||||
{true, BlendMode::DSTCLR, BlendMode::ONE}, // AND_INVERTED
|
{true, SrcBlendFactor::DstClr, DstBlendFactor::One}, // AND_INVERTED
|
||||||
{false, BlendMode::ZERO, BlendMode::ONE}, // NOOP
|
{false, SrcBlendFactor::Zero, DstBlendFactor::One}, // NOOP
|
||||||
{false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR}, // XOR
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // XOR
|
||||||
{false, BlendMode::INVDSTCLR, BlendMode::ONE}, // OR
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::One}, // OR
|
||||||
{false, BlendMode::INVSRCCLR, BlendMode::INVDSTCLR}, // NOR
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // NOR
|
||||||
{false, BlendMode::INVSRCCLR, BlendMode::ZERO}, // EQUIV
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::Zero}, // EQUIV
|
||||||
{false, BlendMode::INVDSTCLR, BlendMode::INVDSTCLR}, // INVERT
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // INVERT
|
||||||
{false, BlendMode::ONE, BlendMode::INVDSTALPHA}, // OR_REVERSE
|
{false, SrcBlendFactor::One, DstBlendFactor::InvDstAlpha}, // OR_REVERSE
|
||||||
{false, BlendMode::INVSRCCLR, BlendMode::INVSRCCLR}, // COPY_INVERTED
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // COPY_INVERTED
|
||||||
{false, BlendMode::INVSRCCLR, BlendMode::ONE}, // OR_INVERTED
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::One}, // OR_INVERTED
|
||||||
{false, BlendMode::INVDSTCLR, BlendMode::INVSRCCLR}, // NAND
|
{false, SrcBlendFactor::InvDstClr, DstBlendFactor::InvSrcClr}, // NAND
|
||||||
{false, BlendMode::ONE, BlendMode::ONE}, // SET
|
{false, SrcBlendFactor::One, DstBlendFactor::One}, // SET
|
||||||
}};
|
}};
|
||||||
|
|
||||||
logicopenable = false;
|
logicopenable = false;
|
||||||
blendenable = true;
|
blendenable = true;
|
||||||
subtract = approximations[logicmode].subtract;
|
subtract = approximations[u32(logicmode.Value())].subtract;
|
||||||
srcfactor = approximations[logicmode].srcfactor;
|
srcfactor = approximations[u32(logicmode.Value())].srcfactor;
|
||||||
dstfactor = approximations[logicmode].dstfactor;
|
dstfactor = approximations[u32(logicmode.Value())].dstfactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
BlendingState& BlendingState::operator=(const BlendingState& rhs)
|
BlendingState& BlendingState::operator=(const BlendingState& rhs)
|
||||||
|
@ -217,20 +231,20 @@ void SamplerState::Generate(const BPMemory& bp, u32 index)
|
||||||
|
|
||||||
// GX can configure the mip filter to none. However, D3D and Vulkan can't express this in their
|
// GX can configure the mip filter to none. However, D3D and Vulkan can't express this in their
|
||||||
// sampler states. Therefore, we set the min/max LOD to zero if this option is used.
|
// sampler states. Therefore, we set the min/max LOD to zero if this option is used.
|
||||||
min_filter = (tm0.min_filter & 4) != 0 ? Filter::Linear : Filter::Point;
|
min_filter = tm0.min_filter == FilterMode::Linear ? Filter::Linear : Filter::Point;
|
||||||
mipmap_filter = (tm0.min_filter & 3) == TexMode0::TEXF_LINEAR ? Filter::Linear : Filter::Point;
|
mipmap_filter = tm0.mipmap_filter == MipMode::Linear ? Filter::Linear : Filter::Point;
|
||||||
mag_filter = tm0.mag_filter != 0 ? Filter::Linear : Filter::Point;
|
mag_filter = tm0.mag_filter == FilterMode::Linear ? Filter::Linear : Filter::Point;
|
||||||
|
|
||||||
// If mipmaps are disabled, clamp min/max lod
|
// If mipmaps are disabled, clamp min/max lod
|
||||||
max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod : 0;
|
max_lod = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm1.max_lod.Value() : 0;
|
||||||
min_lod = std::min(max_lod.Value(), static_cast<u64>(tm1.min_lod));
|
min_lod = std::min(max_lod.Value(), static_cast<u64>(tm1.min_lod));
|
||||||
lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias * (256 / 32) : 0;
|
lod_bias = SamplerCommon::AreBpTexMode0MipmapsEnabled(tm0) ? tm0.lod_bias * (256 / 32) : 0;
|
||||||
|
|
||||||
// Address modes
|
// Address modes
|
||||||
static constexpr std::array<AddressMode, 4> address_modes = {
|
static constexpr std::array<AddressMode, 4> address_modes = {
|
||||||
{AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Repeat}};
|
{AddressMode::Clamp, AddressMode::Repeat, AddressMode::MirroredRepeat, AddressMode::Repeat}};
|
||||||
wrap_u = address_modes[tm0.wrap_s];
|
wrap_u = address_modes[u32(tm0.wrap_s.Value())];
|
||||||
wrap_v = address_modes[tm0.wrap_t];
|
wrap_v = address_modes[u32(tm0.wrap_t.Value())];
|
||||||
anisotropic_filtering = 0;
|
anisotropic_filtering = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -252,7 +266,7 @@ RasterizationState GetInvalidRasterizationState()
|
||||||
RasterizationState GetNoCullRasterizationState(PrimitiveType primitive)
|
RasterizationState GetNoCullRasterizationState(PrimitiveType primitive)
|
||||||
{
|
{
|
||||||
RasterizationState state = {};
|
RasterizationState state = {};
|
||||||
state.cullmode = GenMode::CULL_NONE;
|
state.cullmode = CullMode::None;
|
||||||
state.primitive = primitive;
|
state.primitive = primitive;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -260,7 +274,7 @@ RasterizationState GetNoCullRasterizationState(PrimitiveType primitive)
|
||||||
RasterizationState GetCullBackFaceRasterizationState(PrimitiveType primitive)
|
RasterizationState GetCullBackFaceRasterizationState(PrimitiveType primitive)
|
||||||
{
|
{
|
||||||
RasterizationState state = {};
|
RasterizationState state = {};
|
||||||
state.cullmode = GenMode::CULL_BACK;
|
state.cullmode = CullMode::Back;
|
||||||
state.primitive = primitive;
|
state.primitive = primitive;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -277,7 +291,7 @@ DepthState GetNoDepthTestingDepthState()
|
||||||
DepthState state = {};
|
DepthState state = {};
|
||||||
state.testenable = false;
|
state.testenable = false;
|
||||||
state.updateenable = false;
|
state.updateenable = false;
|
||||||
state.func = ZMode::ALWAYS;
|
state.func = CompareMode::Always;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,7 +300,7 @@ DepthState GetAlwaysWriteDepthState()
|
||||||
DepthState state = {};
|
DepthState state = {};
|
||||||
state.testenable = true;
|
state.testenable = true;
|
||||||
state.updateenable = true;
|
state.updateenable = true;
|
||||||
state.func = ZMode::ALWAYS;
|
state.func = CompareMode::Always;
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,10 +316,10 @@ BlendingState GetNoBlendingBlendState()
|
||||||
BlendingState state = {};
|
BlendingState state = {};
|
||||||
state.usedualsrc = false;
|
state.usedualsrc = false;
|
||||||
state.blendenable = false;
|
state.blendenable = false;
|
||||||
state.srcfactor = BlendMode::ONE;
|
state.srcfactor = SrcBlendFactor::One;
|
||||||
state.srcfactoralpha = BlendMode::ONE;
|
state.srcfactoralpha = SrcBlendFactor::One;
|
||||||
state.dstfactor = BlendMode::ZERO;
|
state.dstfactor = DstBlendFactor::Zero;
|
||||||
state.dstfactoralpha = BlendMode::ZERO;
|
state.dstfactoralpha = DstBlendFactor::Zero;
|
||||||
state.logicopenable = false;
|
state.logicopenable = false;
|
||||||
state.colorupdate = true;
|
state.colorupdate = true;
|
||||||
state.alphaupdate = true;
|
state.alphaupdate = true;
|
||||||
|
@ -317,10 +331,10 @@ BlendingState GetNoColorWriteBlendState()
|
||||||
BlendingState state = {};
|
BlendingState state = {};
|
||||||
state.usedualsrc = false;
|
state.usedualsrc = false;
|
||||||
state.blendenable = false;
|
state.blendenable = false;
|
||||||
state.srcfactor = BlendMode::ONE;
|
state.srcfactor = SrcBlendFactor::One;
|
||||||
state.srcfactoralpha = BlendMode::ONE;
|
state.srcfactoralpha = SrcBlendFactor::One;
|
||||||
state.dstfactor = BlendMode::ZERO;
|
state.dstfactor = DstBlendFactor::Zero;
|
||||||
state.dstfactoralpha = BlendMode::ZERO;
|
state.dstfactoralpha = DstBlendFactor::Zero;
|
||||||
state.logicopenable = false;
|
state.logicopenable = false;
|
||||||
state.colorupdate = false;
|
state.colorupdate = false;
|
||||||
state.alphaupdate = false;
|
state.alphaupdate = false;
|
||||||
|
|
|
@ -28,7 +28,7 @@ union RasterizationState
|
||||||
bool operator==(const RasterizationState& rhs) const { return hex == rhs.hex; }
|
bool operator==(const RasterizationState& rhs) const { return hex == rhs.hex; }
|
||||||
bool operator!=(const RasterizationState& rhs) const { return hex != rhs.hex; }
|
bool operator!=(const RasterizationState& rhs) const { return hex != rhs.hex; }
|
||||||
bool operator<(const RasterizationState& rhs) const { return hex < rhs.hex; }
|
bool operator<(const RasterizationState& rhs) const { return hex < rhs.hex; }
|
||||||
BitField<0, 2, GenMode::CullMode> cullmode;
|
BitField<0, 2, CullMode> cullmode;
|
||||||
BitField<3, 2, PrimitiveType> primitive;
|
BitField<3, 2, PrimitiveType> primitive;
|
||||||
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
|
@ -59,7 +59,7 @@ union DepthState
|
||||||
bool operator<(const DepthState& rhs) const { return hex < rhs.hex; }
|
bool operator<(const DepthState& rhs) const { return hex < rhs.hex; }
|
||||||
BitField<0, 1, u32> testenable;
|
BitField<0, 1, u32> testenable;
|
||||||
BitField<1, 1, u32> updateenable;
|
BitField<1, 1, u32> updateenable;
|
||||||
BitField<2, 3, ZMode::CompareMode> func;
|
BitField<2, 3, CompareMode> func;
|
||||||
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
@ -85,11 +85,11 @@ union BlendingState
|
||||||
BitField<5, 1, u32> subtract;
|
BitField<5, 1, u32> subtract;
|
||||||
BitField<6, 1, u32> subtractAlpha;
|
BitField<6, 1, u32> subtractAlpha;
|
||||||
BitField<7, 1, u32> usedualsrc;
|
BitField<7, 1, u32> usedualsrc;
|
||||||
BitField<8, 3, BlendMode::BlendFactor> dstfactor;
|
BitField<8, 3, DstBlendFactor> dstfactor;
|
||||||
BitField<11, 3, BlendMode::BlendFactor> srcfactor;
|
BitField<11, 3, SrcBlendFactor> srcfactor;
|
||||||
BitField<14, 3, BlendMode::BlendFactor> dstfactoralpha;
|
BitField<14, 3, DstBlendFactor> dstfactoralpha;
|
||||||
BitField<17, 3, BlendMode::BlendFactor> srcfactoralpha;
|
BitField<17, 3, SrcBlendFactor> srcfactoralpha;
|
||||||
BitField<20, 4, BlendMode::LogicOp> logicmode;
|
BitField<20, 4, LogicOp> logicmode;
|
||||||
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
|
@ -16,13 +16,13 @@ namespace SamplerCommon
|
||||||
template <class T>
|
template <class T>
|
||||||
constexpr bool IsBpTexMode0PointFiltering(const T& tm0)
|
constexpr bool IsBpTexMode0PointFiltering(const T& tm0)
|
||||||
{
|
{
|
||||||
return tm0.min_filter < 4 && !tm0.mag_filter;
|
return tm0.min_filter == FilterMode::Near && tm0.mag_filter == FilterMode::Near;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the minification filter has mipmap based filtering modes enabled.
|
// Check if the minification filter has mipmap based filtering modes enabled.
|
||||||
template <class T>
|
template <class T>
|
||||||
constexpr bool AreBpTexMode0MipmapsEnabled(const T& tm0)
|
constexpr bool AreBpTexMode0MipmapsEnabled(const T& tm0)
|
||||||
{
|
{
|
||||||
return (tm0.min_filter & 3) != 0;
|
return tm0.mipmap_filter != MipMode::None;
|
||||||
}
|
}
|
||||||
} // namespace SamplerCommon
|
} // namespace SamplerCommon
|
||||||
|
|
|
@ -1115,7 +1115,7 @@ void ShaderCache::QueueUberShaderPipelines()
|
||||||
{
|
{
|
||||||
// uint_output is only ever enabled when logic ops are enabled.
|
// uint_output is only ever enabled when logic ops are enabled.
|
||||||
config.blending_state.logicopenable = true;
|
config.blending_state.logicopenable = true;
|
||||||
config.blending_state.logicmode = BlendMode::AND;
|
config.blending_state.logicmode = LogicOp::And;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto iter = m_gx_uber_pipeline_cache.find(config);
|
auto iter = m_gx_uber_pipeline_cache.find(config);
|
||||||
|
|
|
@ -1187,12 +1187,12 @@ TextureCacheBase::TCacheEntry* TextureCacheBase::Load(const u32 stage)
|
||||||
const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5;
|
const u32 address = (tex.texImage3[id].image_base /* & 0x1FFFFF*/) << 5;
|
||||||
u32 width = tex.texImage0[id].width + 1;
|
u32 width = tex.texImage0[id].width + 1;
|
||||||
u32 height = tex.texImage0[id].height + 1;
|
u32 height = tex.texImage0[id].height + 1;
|
||||||
const TextureFormat texformat = static_cast<TextureFormat>(tex.texImage0[id].format);
|
const TextureFormat texformat = tex.texImage0[id].format;
|
||||||
const u32 tlutaddr = tex.texTlut[id].tmem_offset << 9;
|
const u32 tlutaddr = tex.texTlut[id].tmem_offset << 9;
|
||||||
const TLUTFormat tlutfmt = static_cast<TLUTFormat>(tex.texTlut[id].tlut_format);
|
const TLUTFormat tlutfmt = tex.texTlut[id].tlut_format;
|
||||||
const bool use_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
|
const bool use_mipmaps = SamplerCommon::AreBpTexMode0MipmapsEnabled(tex.texMode0[id]);
|
||||||
u32 tex_levels = use_mipmaps ? ((tex.texMode1[id].max_lod + 0xf) / 0x10 + 1) : 1;
|
u32 tex_levels = use_mipmaps ? ((tex.texMode1[id].max_lod + 0xf) / 0x10 + 1) : 1;
|
||||||
const bool from_tmem = tex.texImage1[id].image_type != 0;
|
const bool from_tmem = tex.texImage1[id].cache_manually_managed != 0;
|
||||||
const u32 tmem_address_even = from_tmem ? tex.texImage1[id].tmem_even * TMEM_LINE_SIZE : 0;
|
const u32 tmem_address_even = from_tmem ? tex.texImage1[id].tmem_even * TMEM_LINE_SIZE : 0;
|
||||||
const u32 tmem_address_odd = from_tmem ? tex.texImage2[id].tmem_odd * TMEM_LINE_SIZE : 0;
|
const u32 tmem_address_odd = from_tmem ? tex.texImage2[id].tmem_odd * TMEM_LINE_SIZE : 0;
|
||||||
|
|
||||||
|
@ -2204,7 +2204,7 @@ void TextureCacheBase::CopyRenderTargetToTexture(
|
||||||
if (copy_to_ram)
|
if (copy_to_ram)
|
||||||
{
|
{
|
||||||
EFBCopyFilterCoefficients coefficients = GetRAMCopyFilterCoefficients(filter_coefficients);
|
EFBCopyFilterCoefficients coefficients = GetRAMCopyFilterCoefficients(filter_coefficients);
|
||||||
PEControl::PixelFormat srcFormat = bpmem.zcontrol.pixel_format;
|
PixelFormat srcFormat = bpmem.zcontrol.pixel_format;
|
||||||
EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity,
|
EFBCopyParams format(srcFormat, dstFormat, is_depth_copy, isIntensity,
|
||||||
NeedsCopyFilterInShader(coefficients));
|
NeedsCopyFilterInShader(coefficients));
|
||||||
|
|
||||||
|
|
|
@ -50,8 +50,8 @@ struct TextureAndTLUTFormat
|
||||||
|
|
||||||
struct EFBCopyParams
|
struct EFBCopyParams
|
||||||
{
|
{
|
||||||
EFBCopyParams(PEControl::PixelFormat efb_format_, EFBCopyFormat copy_format_, bool depth_,
|
EFBCopyParams(PixelFormat efb_format_, EFBCopyFormat copy_format_, bool depth_, bool yuv_,
|
||||||
bool yuv_, bool copy_filter_)
|
bool copy_filter_)
|
||||||
: efb_format(efb_format_), copy_format(copy_format_), depth(depth_), yuv(yuv_),
|
: efb_format(efb_format_), copy_format(copy_format_), depth(depth_), yuv(yuv_),
|
||||||
copy_filter(copy_filter_)
|
copy_filter(copy_filter_)
|
||||||
{
|
{
|
||||||
|
@ -63,7 +63,7 @@ struct EFBCopyParams
|
||||||
std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv, rhs.copy_filter);
|
std::tie(rhs.efb_format, rhs.copy_format, rhs.depth, rhs.yuv, rhs.copy_filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
PEControl::PixelFormat efb_format;
|
PixelFormat efb_format;
|
||||||
EFBCopyFormat copy_format;
|
EFBCopyFormat copy_format;
|
||||||
bool depth;
|
bool depth;
|
||||||
bool yuv;
|
bool yuv;
|
||||||
|
|
|
@ -127,13 +127,13 @@ static void WriteSampleFunction(ShaderCode& code, const EFBCopyParams& params, A
|
||||||
{
|
{
|
||||||
switch (params.efb_format)
|
switch (params.efb_format)
|
||||||
{
|
{
|
||||||
case PEControl::RGB8_Z24:
|
case PixelFormat::RGB8_Z24:
|
||||||
code.Write("RGBA8ToRGB8(");
|
code.Write("RGBA8ToRGB8(");
|
||||||
break;
|
break;
|
||||||
case PEControl::RGBA6_Z24:
|
case PixelFormat::RGBA6_Z24:
|
||||||
code.Write("RGBA8ToRGBA6(");
|
code.Write("RGBA8ToRGBA6(");
|
||||||
break;
|
break;
|
||||||
case PEControl::RGB565_Z16:
|
case PixelFormat::RGB565_Z16:
|
||||||
code.Write("RGBA8ToRGB565(");
|
code.Write("RGBA8ToRGB565(");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -19,7 +19,7 @@ TCShaderUid GetShaderUid(EFBCopyFormat dst_format, bool is_depth_copy, bool is_i
|
||||||
|
|
||||||
UidData* const uid_data = out.GetUidData();
|
UidData* const uid_data = out.GetUidData();
|
||||||
uid_data->dst_format = dst_format;
|
uid_data->dst_format = dst_format;
|
||||||
uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PEControl::RGBA6_Z24;
|
uid_data->efb_has_alpha = bpmem.zcontrol.pixel_format == PixelFormat::RGBA6_Z24;
|
||||||
uid_data->is_depth_copy = is_depth_copy;
|
uid_data->is_depth_copy = is_depth_copy;
|
||||||
uid_data->is_intensity = is_intensity;
|
uid_data->is_intensity = is_intensity;
|
||||||
uid_data->scale_by_half = scale_by_half;
|
uid_data->scale_by_half = scale_by_half;
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -32,8 +33,17 @@ enum class TextureFormat
|
||||||
// Special texture format used to represent YUVY xfb copies.
|
// Special texture format used to represent YUVY xfb copies.
|
||||||
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
||||||
// to emulate them as part of texture cache.
|
// to emulate them as part of texture cache.
|
||||||
|
// This isn't a real value that can be used on console; it only exists for ease of implementation.
|
||||||
XFB = 0xF,
|
XFB = 0xF,
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TextureFormat> : EnumFormatter<TextureFormat::CMPR>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {"I4", "I8", "IA4", "IA8", "RGB565",
|
||||||
|
"RGB5A3", "RGBA8", nullptr, "C4", "C8",
|
||||||
|
"C14X2", nullptr, nullptr, nullptr, "CMPR"};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
|
};
|
||||||
|
|
||||||
static inline bool IsColorIndexed(TextureFormat format)
|
static inline bool IsColorIndexed(TextureFormat format)
|
||||||
{
|
{
|
||||||
|
@ -82,8 +92,20 @@ enum class EFBCopyFormat
|
||||||
// Special texture format used to represent YUVY xfb copies.
|
// Special texture format used to represent YUVY xfb copies.
|
||||||
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
// They aren't really textures, but they share so much hardware and usecases that it makes sense
|
||||||
// to emulate them as part of texture cache.
|
// to emulate them as part of texture cache.
|
||||||
|
// This isn't a real value that can be used on console; it only exists for ease of implementation.
|
||||||
XFB = 0xF,
|
XFB = 0xF,
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<EFBCopyFormat> : EnumFormatter<EFBCopyFormat::GB8>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {
|
||||||
|
"R4/I4/Z4", "R8/I8/Z8H (?)", "RA4/IA4", "RA8/IA8 (Z16 too?)",
|
||||||
|
"RGB565", "RGB5A3", "RGBA8", "A8",
|
||||||
|
"R8/I8/Z8H", "G8/Z8M", "B8/Z8L", "RG8/Z16R (Note: G and R are reversed)",
|
||||||
|
"GB8/Z16L",
|
||||||
|
};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
|
};
|
||||||
|
|
||||||
enum class TLUTFormat
|
enum class TLUTFormat
|
||||||
{
|
{
|
||||||
|
@ -92,6 +114,11 @@ enum class TLUTFormat
|
||||||
RGB565 = 0x1,
|
RGB565 = 0x1,
|
||||||
RGB5A3 = 0x2,
|
RGB5A3 = 0x2,
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TLUTFormat> : EnumFormatter<TLUTFormat::RGB5A3>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"IA8", "RGB565", "RGB5A3"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
static inline bool IsValidTLUTFormat(TLUTFormat tlutfmt)
|
static inline bool IsValidTLUTFormat(TLUTFormat tlutfmt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -39,26 +39,26 @@ void WriteLightingFunction(ShaderCode& out)
|
||||||
" float dist, dist2, attn;\n"
|
" float dist, dist2, attn;\n"
|
||||||
"\n"
|
"\n"
|
||||||
" switch (attnfunc) {{\n");
|
" switch (attnfunc) {{\n");
|
||||||
out.Write(" case {}u: // LIGNTATTN_NONE\n", LIGHTATTN_NONE);
|
out.Write(" case {:s}:\n", AttenuationFunc::None);
|
||||||
out.Write(" case {}u: // LIGHTATTN_DIR\n", LIGHTATTN_DIR);
|
out.Write(" case {:s}:\n", AttenuationFunc::Dir);
|
||||||
out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
|
out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
|
||||||
" attn = 1.0;\n"
|
" attn = 1.0;\n"
|
||||||
" if (length(ldir) == 0.0)\n"
|
" if (length(ldir) == 0.0)\n"
|
||||||
" ldir = normal;\n"
|
" ldir = normal;\n"
|
||||||
" break;\n\n");
|
" break;\n\n");
|
||||||
out.Write(" case {}u: // LIGHTATTN_SPEC\n", LIGHTATTN_SPEC);
|
out.Write(" case {:s}:\n", AttenuationFunc::Spec);
|
||||||
out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
|
out.Write(" ldir = normalize(" I_LIGHTS "[index].pos.xyz - pos.xyz);\n"
|
||||||
" attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS
|
" attn = (dot(normal, ldir) >= 0.0) ? max(0.0, dot(normal, " I_LIGHTS
|
||||||
"[index].dir.xyz)) : 0.0;\n"
|
"[index].dir.xyz)) : 0.0;\n"
|
||||||
" cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n");
|
" cosAttn = " I_LIGHTS "[index].cosatt.xyz;\n");
|
||||||
out.Write(" if (diffusefunc == {}u) // LIGHTDIF_NONE\n", LIGHTDIF_NONE);
|
out.Write(" if (diffusefunc == {:s})\n", DiffuseFunc::None);
|
||||||
out.Write(" distAttn = " I_LIGHTS "[index].distatt.xyz;\n"
|
out.Write(" distAttn = " I_LIGHTS "[index].distatt.xyz;\n"
|
||||||
" else\n"
|
" else\n"
|
||||||
" distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n"
|
" distAttn = normalize(" I_LIGHTS "[index].distatt.xyz);\n"
|
||||||
" attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
|
" attn = max(0.0, dot(cosAttn, float3(1.0, attn, attn*attn))) / dot(distAttn, "
|
||||||
"float3(1.0, attn, attn*attn));\n"
|
"float3(1.0, attn, attn*attn));\n"
|
||||||
" break;\n\n");
|
" break;\n\n");
|
||||||
out.Write(" case {}u: // LIGHTATTN_SPOT\n", LIGHTATTN_SPOT);
|
out.Write(" case {:s}:\n", AttenuationFunc::Spot);
|
||||||
out.Write(" ldir = " I_LIGHTS "[index].pos.xyz - pos.xyz;\n"
|
out.Write(" ldir = " I_LIGHTS "[index].pos.xyz - pos.xyz;\n"
|
||||||
" dist2 = dot(ldir, ldir);\n"
|
" dist2 = dot(ldir, ldir);\n"
|
||||||
" dist = sqrt(dist2);\n"
|
" dist = sqrt(dist2);\n"
|
||||||
|
@ -75,12 +75,12 @@ void WriteLightingFunction(ShaderCode& out)
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"\n"
|
"\n"
|
||||||
" switch (diffusefunc) {{\n");
|
" switch (diffusefunc) {{\n");
|
||||||
out.Write(" case {}u: // LIGHTDIF_NONE\n", LIGHTDIF_NONE);
|
out.Write(" case {:s}:\n", DiffuseFunc::None);
|
||||||
out.Write(" return int4(round(attn * float4(" I_LIGHTS "[index].color)));\n\n");
|
out.Write(" return int4(round(attn * float4(" I_LIGHTS "[index].color)));\n\n");
|
||||||
out.Write(" case {}u: // LIGHTDIF_SIGN\n", LIGHTDIF_SIGN);
|
out.Write(" case {:s}:\n", DiffuseFunc::Sign);
|
||||||
out.Write(" return int4(round(attn * dot(ldir, normal) * float4(" I_LIGHTS
|
out.Write(" return int4(round(attn * dot(ldir, normal) * float4(" I_LIGHTS
|
||||||
"[index].color)));\n\n");
|
"[index].color)));\n\n");
|
||||||
out.Write(" case {}u: // LIGHTDIF_CLAMP\n", LIGHTDIF_CLAMP);
|
out.Write(" case {:s}:\n", DiffuseFunc::Clamp);
|
||||||
out.Write(" return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(" I_LIGHTS
|
out.Write(" return int4(round(attn * max(0.0, dot(ldir, normal)) * float4(" I_LIGHTS
|
||||||
"[index].color)));\n\n");
|
"[index].color)));\n\n");
|
||||||
out.Write(" default:\n"
|
out.Write(" default:\n"
|
||||||
|
|
|
@ -22,12 +22,12 @@ PixelShaderUid GetPixelShaderUid()
|
||||||
|
|
||||||
pixel_ubershader_uid_data* const uid_data = out.GetUidData();
|
pixel_ubershader_uid_data* const uid_data = out.GetUidData();
|
||||||
uid_data->num_texgens = xfmem.numTexGen.numTexGens;
|
uid_data->num_texgens = xfmem.numTexGen.numTexGens;
|
||||||
uid_data->early_depth =
|
uid_data->early_depth = bpmem.UseEarlyDepthTest() &&
|
||||||
bpmem.UseEarlyDepthTest() &&
|
(g_ActiveConfig.bFastDepthCalc ||
|
||||||
(g_ActiveConfig.bFastDepthCalc || bpmem.alpha_test.TestResult() == AlphaTest::UNDETERMINED) &&
|
bpmem.alpha_test.TestResult() == AlphaTestResult::Undetermined) &&
|
||||||
!(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
!(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
||||||
uid_data->per_pixel_depth =
|
uid_data->per_pixel_depth =
|
||||||
(bpmem.ztex2.op != ZTEXTURE_DISABLE && bpmem.UseLateDepthTest()) ||
|
(bpmem.ztex2.op != ZTexOp::Disabled && bpmem.UseLateDepthTest()) ||
|
||||||
(!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !uid_data->early_depth) ||
|
(!g_ActiveConfig.bFastDepthCalc && bpmem.zmode.testenable && !uid_data->early_depth) ||
|
||||||
(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
(bpmem.zmode.testenable && bpmem.genMode.zfreeze);
|
||||||
uid_data->uint_output = bpmem.blendmode.UseLogicOp();
|
uid_data->uint_output = bpmem.blendmode.UseLogicOp();
|
||||||
|
@ -367,21 +367,21 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
"// which are common to both color and alpha channels\n"
|
"// which are common to both color and alpha channels\n"
|
||||||
"bool tevCompare(uint op, int3 color_A, int3 color_B) {{\n"
|
"bool tevCompare(uint op, int3 color_A, int3 color_B) {{\n"
|
||||||
" switch (op) {{\n"
|
" switch (op) {{\n"
|
||||||
" case 0u: // TEVCMP_R8_GT\n"
|
" case 0u: // TevCompareMode::R8, TevComparison::GT\n"
|
||||||
" return (color_A.r > color_B.r);\n"
|
" return (color_A.r > color_B.r);\n"
|
||||||
" case 1u: // TEVCMP_R8_EQ\n"
|
" case 1u: // TevCompareMode::R8, TevComparison::EQ\n"
|
||||||
" return (color_A.r == color_B.r);\n"
|
" return (color_A.r == color_B.r);\n"
|
||||||
" case 2u: // TEVCMP_GR16_GT\n"
|
" case 2u: // TevCompareMode::GR16, TevComparison::GT\n"
|
||||||
" int A_16 = (color_A.r | (color_A.g << 8));\n"
|
" int A_16 = (color_A.r | (color_A.g << 8));\n"
|
||||||
" int B_16 = (color_B.r | (color_B.g << 8));\n"
|
" int B_16 = (color_B.r | (color_B.g << 8));\n"
|
||||||
" return A_16 > B_16;\n"
|
" return A_16 > B_16;\n"
|
||||||
" case 3u: // TEVCMP_GR16_EQ\n"
|
" case 3u: // TevCompareMode::GR16, TevComparison::EQ\n"
|
||||||
" return (color_A.r == color_B.r && color_A.g == color_B.g);\n"
|
" return (color_A.r == color_B.r && color_A.g == color_B.g);\n"
|
||||||
" case 4u: // TEVCMP_BGR24_GT\n"
|
" case 4u: // TevCompareMode::BGR24, TevComparison::GT\n"
|
||||||
" int A_24 = (color_A.r | (color_A.g << 8) | (color_A.b << 16));\n"
|
" int A_24 = (color_A.r | (color_A.g << 8) | (color_A.b << 16));\n"
|
||||||
" int B_24 = (color_B.r | (color_B.g << 8) | (color_B.b << 16));\n"
|
" int B_24 = (color_B.r | (color_B.g << 8) | (color_B.b << 16));\n"
|
||||||
" return A_24 > B_24;\n"
|
" return A_24 > B_24;\n"
|
||||||
" case 5u: // TEVCMP_BGR24_EQ\n"
|
" case 5u: // TevCompareMode::BGR24, TevComparison::EQ\n"
|
||||||
" return (color_A.r == color_B.r && color_A.g == color_B.g && color_A.b == color_B.b);\n"
|
" return (color_A.r == color_B.r && color_A.g == color_B.g && color_A.b == color_B.b);\n"
|
||||||
" default:\n"
|
" default:\n"
|
||||||
" return false;\n"
|
" return false;\n"
|
||||||
|
@ -814,29 +814,29 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
" s.AlphaBump = indcoord[bs - 1u];\n"
|
" s.AlphaBump = indcoord[bs - 1u];\n"
|
||||||
" switch(fmt)\n"
|
" switch(fmt)\n"
|
||||||
" {{\n"
|
" {{\n"
|
||||||
" case {}u:\n",
|
" case {:s}:\n",
|
||||||
ITF_8);
|
IndTexFormat::ITF_8);
|
||||||
out.Write(" indcoord.x = indcoord.x + ((bias & 1u) != 0u ? -128 : 0);\n"
|
out.Write(" indcoord.x = indcoord.x + ((bias & 1u) != 0u ? -128 : 0);\n"
|
||||||
" indcoord.y = indcoord.y + ((bias & 2u) != 0u ? -128 : 0);\n"
|
" indcoord.y = indcoord.y + ((bias & 2u) != 0u ? -128 : 0);\n"
|
||||||
" indcoord.z = indcoord.z + ((bias & 4u) != 0u ? -128 : 0);\n"
|
" indcoord.z = indcoord.z + ((bias & 4u) != 0u ? -128 : 0);\n"
|
||||||
" s.AlphaBump = s.AlphaBump & 0xf8;\n"
|
" s.AlphaBump = s.AlphaBump & 0xf8;\n"
|
||||||
" break;\n"
|
" break;\n"
|
||||||
" case {}u:\n",
|
" case {:s}:\n",
|
||||||
ITF_5);
|
IndTexFormat::ITF_5);
|
||||||
out.Write(" indcoord.x = (indcoord.x & 0x1f) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
out.Write(" indcoord.x = (indcoord.x & 0x1f) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.y = (indcoord.y & 0x1f) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
" indcoord.y = (indcoord.y & 0x1f) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.z = (indcoord.z & 0x1f) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
" indcoord.z = (indcoord.z & 0x1f) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
||||||
" s.AlphaBump = s.AlphaBump & 0xe0;\n"
|
" s.AlphaBump = s.AlphaBump & 0xe0;\n"
|
||||||
" break;\n"
|
" break;\n"
|
||||||
" case {}u:\n",
|
" case {:s}:\n",
|
||||||
ITF_4);
|
IndTexFormat::ITF_4);
|
||||||
out.Write(" indcoord.x = (indcoord.x & 0x0f) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
out.Write(" indcoord.x = (indcoord.x & 0x0f) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.y = (indcoord.y & 0x0f) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
" indcoord.y = (indcoord.y & 0x0f) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.z = (indcoord.z & 0x0f) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
" indcoord.z = (indcoord.z & 0x0f) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
||||||
" s.AlphaBump = s.AlphaBump & 0xf0;\n"
|
" s.AlphaBump = s.AlphaBump & 0xf0;\n"
|
||||||
" break;\n"
|
" break;\n"
|
||||||
" case {}u:\n",
|
" case {:s}:\n",
|
||||||
ITF_3);
|
IndTexFormat::ITF_3);
|
||||||
out.Write(" indcoord.x = (indcoord.x & 0x07) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
out.Write(" indcoord.x = (indcoord.x & 0x07) + ((bias & 1u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.y = (indcoord.y & 0x07) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
" indcoord.y = (indcoord.y & 0x07) + ((bias & 2u) != 0u ? 1 : 0);\n"
|
||||||
" indcoord.z = (indcoord.z & 0x07) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
" indcoord.z = (indcoord.z & 0x07) + ((bias & 4u) != 0u ? 1 : 0);\n"
|
||||||
|
@ -924,7 +924,7 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
out.Write(" bool color_clamp = bool({});\n",
|
out.Write(" bool color_clamp = bool({});\n",
|
||||||
BitfieldExtract("ss.cc", TevStageCombiner().colorC.clamp));
|
BitfieldExtract("ss.cc", TevStageCombiner().colorC.clamp));
|
||||||
out.Write(" uint color_shift = {};\n",
|
out.Write(" uint color_shift = {};\n",
|
||||||
BitfieldExtract("ss.cc", TevStageCombiner().colorC.shift));
|
BitfieldExtract("ss.cc", TevStageCombiner().colorC.scale));
|
||||||
out.Write(" uint color_dest = {};\n",
|
out.Write(" uint color_dest = {};\n",
|
||||||
BitfieldExtract("ss.cc", TevStageCombiner().colorC.dest));
|
BitfieldExtract("ss.cc", TevStageCombiner().colorC.dest));
|
||||||
|
|
||||||
|
@ -949,12 +949,12 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
" }} else {{ // Compare mode\n"
|
" }} else {{ // Compare mode\n"
|
||||||
" // op 6 and 7 do a select per color channel\n"
|
" // op 6 and 7 do a select per color channel\n"
|
||||||
" if (color_compare_op == 6u) {{\n"
|
" if (color_compare_op == 6u) {{\n"
|
||||||
" // TEVCMP_RGB8_GT\n"
|
" // TevCompareMode::RGB8, TevComparison::GT\n"
|
||||||
" color.r = (color_A.r > color_B.r) ? color_C.r : 0;\n"
|
" color.r = (color_A.r > color_B.r) ? color_C.r : 0;\n"
|
||||||
" color.g = (color_A.g > color_B.g) ? color_C.g : 0;\n"
|
" color.g = (color_A.g > color_B.g) ? color_C.g : 0;\n"
|
||||||
" color.b = (color_A.b > color_B.b) ? color_C.b : 0;\n"
|
" color.b = (color_A.b > color_B.b) ? color_C.b : 0;\n"
|
||||||
" }} else if (color_compare_op == 7u) {{\n"
|
" }} else if (color_compare_op == 7u) {{\n"
|
||||||
" // TEVCMP_RGB8_EQ\n"
|
" // TevCompareMode::RGB8, TevComparison::EQ\n"
|
||||||
" color.r = (color_A.r == color_B.r) ? color_C.r : 0;\n"
|
" color.r = (color_A.r == color_B.r) ? color_C.r : 0;\n"
|
||||||
" color.g = (color_A.g == color_B.g) ? color_C.g : 0;\n"
|
" color.g = (color_A.g == color_B.g) ? color_C.g : 0;\n"
|
||||||
" color.b = (color_A.b == color_B.b) ? color_C.b : 0;\n"
|
" color.b = (color_A.b == color_B.b) ? color_C.b : 0;\n"
|
||||||
|
@ -990,7 +990,7 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
out.Write(" bool alpha_clamp = bool({});\n",
|
out.Write(" bool alpha_clamp = bool({});\n",
|
||||||
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.clamp));
|
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.clamp));
|
||||||
out.Write(" uint alpha_shift = {};\n",
|
out.Write(" uint alpha_shift = {};\n",
|
||||||
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.shift));
|
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.scale));
|
||||||
out.Write(" uint alpha_dest = {};\n",
|
out.Write(" uint alpha_dest = {};\n",
|
||||||
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.dest));
|
BitfieldExtract("ss.ac", TevStageCombiner().alphaC.dest));
|
||||||
|
|
||||||
|
@ -1016,10 +1016,10 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
"true, alpha_shift);\n"
|
"true, alpha_shift);\n"
|
||||||
" }} else {{ // Compare mode\n"
|
" }} else {{ // Compare mode\n"
|
||||||
" if (alpha_compare_op == 6u) {{\n"
|
" if (alpha_compare_op == 6u) {{\n"
|
||||||
" // TEVCMP_A8_GT\n"
|
" // TevCompareMode::A8, TevComparison::GT\n"
|
||||||
" alpha = (alpha_A > alpha_B) ? alpha_C : 0;\n"
|
" alpha = (alpha_A > alpha_B) ? alpha_C : 0;\n"
|
||||||
" }} else if (alpha_compare_op == 7u) {{\n"
|
" }} else if (alpha_compare_op == 7u) {{\n"
|
||||||
" // TEVCMP_A8_EQ\n"
|
" // TevCompareMode::A8, TevComparison::EQ\n"
|
||||||
" alpha = (alpha_A == alpha_B) ? alpha_C : 0;\n"
|
" alpha = (alpha_A == alpha_B) ? alpha_C : 0;\n"
|
||||||
" }} else {{\n"
|
" }} else {{\n"
|
||||||
" // All remaining alpha compare ops actually compare the color channels\n"
|
" // All remaining alpha compare ops actually compare the color channels\n"
|
||||||
|
@ -1157,8 +1157,8 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
out.Write(" // Fog\n"
|
out.Write(" // Fog\n"
|
||||||
" uint fog_function = {};\n",
|
" uint fog_function = {};\n",
|
||||||
BitfieldExtract("bpmem_fogParam3", FogParam3().fsel));
|
BitfieldExtract("bpmem_fogParam3", FogParam3().fsel));
|
||||||
out.Write(" if (fog_function != 0u) {{\n"
|
out.Write(" if (fog_function != {:s}) {{\n", FogType::Off);
|
||||||
" // TODO: This all needs to be converted from float to fixed point\n"
|
out.Write(" // TODO: This all needs to be converted from float to fixed point\n"
|
||||||
" float ze;\n"
|
" float ze;\n"
|
||||||
" if ({} == 0u) {{\n",
|
" if ({} == 0u) {{\n",
|
||||||
BitfieldExtract("bpmem_fogParam3", FogParam3().proj));
|
BitfieldExtract("bpmem_fogParam3", FogParam3().proj));
|
||||||
|
@ -1188,23 +1188,27 @@ ShaderCode GenPixelShader(APIType ApiType, const ShaderHostConfig& host_config,
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"\n"
|
"\n"
|
||||||
" float fog = clamp(ze - " I_FOGF ".y, 0.0, 1.0);\n"
|
" float fog = clamp(ze - " I_FOGF ".y, 0.0, 1.0);\n"
|
||||||
"\n"
|
"\n");
|
||||||
" if (fog_function > 3u) {{\n"
|
out.Write(" if (fog_function >= {:s}) {{\n", FogType::Exp);
|
||||||
" switch (fog_function) {{\n"
|
out.Write(" switch (fog_function) {{\n"
|
||||||
" case 4u:\n"
|
" case {:s}:\n"
|
||||||
" fog = 1.0 - exp2(-8.0 * fog);\n"
|
" fog = 1.0 - exp2(-8.0 * fog);\n"
|
||||||
" break;\n"
|
" break;\n",
|
||||||
" case 5u:\n"
|
FogType::Exp);
|
||||||
|
out.Write(" case {:s}:\n"
|
||||||
" fog = 1.0 - exp2(-8.0 * fog * fog);\n"
|
" fog = 1.0 - exp2(-8.0 * fog * fog);\n"
|
||||||
" break;\n"
|
" break;\n",
|
||||||
" case 6u:\n"
|
FogType::ExpSq);
|
||||||
|
out.Write(" case {:s}:\n"
|
||||||
" fog = exp2(-8.0 * (1.0 - fog));\n"
|
" fog = exp2(-8.0 * (1.0 - fog));\n"
|
||||||
" break;\n"
|
" break;\n",
|
||||||
" case 7u:\n"
|
FogType::BackwardsExp);
|
||||||
|
out.Write(" case {:s}:\n"
|
||||||
" fog = 1.0 - fog;\n"
|
" fog = 1.0 - fog;\n"
|
||||||
" fog = exp2(-8.0 * fog * fog);\n"
|
" fog = exp2(-8.0 * fog * fog);\n"
|
||||||
" break;\n"
|
" break;\n",
|
||||||
" }}\n"
|
FogType::BackwardsExpSq);
|
||||||
|
out.Write(" }}\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
"\n"
|
"\n"
|
||||||
" int ifog = iround(fog * 256.0);\n"
|
" int ifog = iround(fog * 256.0);\n"
|
||||||
|
|
|
@ -403,27 +403,27 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
out.Write(" float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n"
|
out.Write(" float4 coord = float4(0.0, 0.0, 1.0, 1.0);\n"
|
||||||
" uint texMtxInfo = xfmem_texMtxInfo(texgen);\n");
|
" uint texMtxInfo = xfmem_texMtxInfo(texgen);\n");
|
||||||
out.Write(" switch ({}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().sourcerow));
|
out.Write(" switch ({}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().sourcerow));
|
||||||
out.Write(" case {}u: // XF_SRCGEOM_INROW\n", XF_SRCGEOM_INROW);
|
out.Write(" case {:s}:\n", SourceRow::Geom);
|
||||||
out.Write(" coord.xyz = rawpos.xyz;\n");
|
out.Write(" coord.xyz = rawpos.xyz;\n");
|
||||||
out.Write(" break;\n\n");
|
out.Write(" break;\n\n");
|
||||||
out.Write(" case {}u: // XF_SRCNORMAL_INROW\n", XF_SRCNORMAL_INROW);
|
out.Write(" case {:s}:\n", SourceRow::Normal);
|
||||||
out.Write(
|
out.Write(
|
||||||
" coord.xyz = ((components & {}u /* VB_HAS_NRM0 */) != 0u) ? rawnorm0.xyz : coord.xyz;",
|
" coord.xyz = ((components & {}u /* VB_HAS_NRM0 */) != 0u) ? rawnorm0.xyz : coord.xyz;",
|
||||||
VB_HAS_NRM0);
|
VB_HAS_NRM0);
|
||||||
out.Write(" break;\n\n");
|
out.Write(" break;\n\n");
|
||||||
out.Write(" case {}u: // XF_SRCBINORMAL_T_INROW\n", XF_SRCBINORMAL_T_INROW);
|
out.Write(" case {:s}:\n", SourceRow::BinormalT);
|
||||||
out.Write(
|
out.Write(
|
||||||
" coord.xyz = ((components & {}u /* VB_HAS_NRM1 */) != 0u) ? rawnorm1.xyz : coord.xyz;",
|
" coord.xyz = ((components & {}u /* VB_HAS_NRM1 */) != 0u) ? rawnorm1.xyz : coord.xyz;",
|
||||||
VB_HAS_NRM1);
|
VB_HAS_NRM1);
|
||||||
out.Write(" break;\n\n");
|
out.Write(" break;\n\n");
|
||||||
out.Write(" case {}u: // XF_SRCBINORMAL_B_INROW\n", XF_SRCBINORMAL_B_INROW);
|
out.Write(" case {:s}:\n", SourceRow::BinormalB);
|
||||||
out.Write(
|
out.Write(
|
||||||
" coord.xyz = ((components & {}u /* VB_HAS_NRM2 */) != 0u) ? rawnorm2.xyz : coord.xyz;",
|
" coord.xyz = ((components & {}u /* VB_HAS_NRM2 */) != 0u) ? rawnorm2.xyz : coord.xyz;",
|
||||||
VB_HAS_NRM2);
|
VB_HAS_NRM2);
|
||||||
out.Write(" break;\n\n");
|
out.Write(" break;\n\n");
|
||||||
for (u32 i = 0; i < 8; i++)
|
for (u32 i = 0; i < 8; i++)
|
||||||
{
|
{
|
||||||
out.Write(" case {}u: // XF_SRCTEX{}_INROW\n", XF_SRCTEX0_INROW + i, i);
|
out.Write(" case {:s}:\n", static_cast<SourceRow>(static_cast<u32>(SourceRow::Tex0) + i));
|
||||||
out.Write(
|
out.Write(
|
||||||
" coord = ((components & {}u /* VB_HAS_UV{} */) != 0u) ? float4(rawtex{}.x, rawtex{}.y, "
|
" coord = ((components & {}u /* VB_HAS_UV{} */) != 0u) ? float4(rawtex{}.x, rawtex{}.y, "
|
||||||
"1.0, 1.0) : coord;\n",
|
"1.0, 1.0) : coord;\n",
|
||||||
|
@ -434,8 +434,8 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
"\n");
|
"\n");
|
||||||
|
|
||||||
out.Write(" // Input form of AB11 sets z element to 1.0\n");
|
out.Write(" // Input form of AB11 sets z element to 1.0\n");
|
||||||
out.Write(" if ({} == {}u) // inputform == XF_TEXINPUT_AB11\n",
|
out.Write(" if ({} == {:s}) // inputform == AB11\n",
|
||||||
BitfieldExtract("texMtxInfo", TexMtxInfo().inputform), XF_TEXINPUT_AB11);
|
BitfieldExtract("texMtxInfo", TexMtxInfo().inputform), TexInputForm::AB11);
|
||||||
out.Write(" coord.z = 1.0f;\n"
|
out.Write(" coord.z = 1.0f;\n"
|
||||||
"\n");
|
"\n");
|
||||||
|
|
||||||
|
@ -444,7 +444,7 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
out.Write(" float3 output_tex;\n"
|
out.Write(" float3 output_tex;\n"
|
||||||
" switch (texgentype)\n"
|
" switch (texgentype)\n"
|
||||||
" {{\n");
|
" {{\n");
|
||||||
out.Write(" case {}u: // XF_TEXGEN_EMBOSS_MAP\n", XF_TEXGEN_EMBOSS_MAP);
|
out.Write(" case {:s}:\n", TexGenType::EmbossMap);
|
||||||
out.Write(" {{\n");
|
out.Write(" {{\n");
|
||||||
out.Write(" uint light = {};\n",
|
out.Write(" uint light = {};\n",
|
||||||
BitfieldExtract("texMtxInfo", TexMtxInfo().embosslightshift));
|
BitfieldExtract("texMtxInfo", TexMtxInfo().embosslightshift));
|
||||||
|
@ -462,13 +462,14 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
" }}\n"
|
" }}\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
" break;\n\n");
|
" break;\n\n");
|
||||||
out.Write(" case {}u: // XF_TEXGEN_COLOR_STRGBC0\n", XF_TEXGEN_COLOR_STRGBC0);
|
out.Write(" case {:s}:\n", TexGenType::Color0);
|
||||||
out.Write(" output_tex.xyz = float3(o.colors_0.x, o.colors_0.y, 1.0);\n"
|
out.Write(" output_tex.xyz = float3(o.colors_0.x, o.colors_0.y, 1.0);\n"
|
||||||
" break;\n\n");
|
" break;\n\n");
|
||||||
out.Write(" case {}u: // XF_TEXGEN_COLOR_STRGBC1\n", XF_TEXGEN_COLOR_STRGBC1);
|
out.Write(" case {:s}:\n", TexGenType::Color1);
|
||||||
out.Write(" output_tex.xyz = float3(o.colors_1.x, o.colors_1.y, 1.0);\n"
|
out.Write(" output_tex.xyz = float3(o.colors_1.x, o.colors_1.y, 1.0);\n"
|
||||||
" break;\n\n");
|
" break;\n\n");
|
||||||
out.Write(" default: // Also XF_TEXGEN_REGULAR\n"
|
out.Write(" case {:s}:\n", TexGenType::Regular);
|
||||||
|
out.Write(" default:\n"
|
||||||
" {{\n");
|
" {{\n");
|
||||||
out.Write(" if ((components & ({}u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {{\n",
|
out.Write(" if ((components & ({}u /* VB_HAS_TEXMTXIDX0 */ << texgen)) != 0u) {{\n",
|
||||||
VB_HAS_TEXMTXIDX0);
|
VB_HAS_TEXMTXIDX0);
|
||||||
|
@ -480,8 +481,8 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
out.Write(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i);
|
out.Write(" case {}u: tmp = int(rawtex{}.z); break;\n", i, i);
|
||||||
out.Write(" }}\n"
|
out.Write(" }}\n"
|
||||||
"\n");
|
"\n");
|
||||||
out.Write(" if ({} == {}u) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
|
out.Write(" if ({} == {:s}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
|
||||||
XF_TEXPROJ_STQ);
|
TexSize::STQ);
|
||||||
out.Write(" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n"
|
out.Write(" output_tex.xyz = float3(dot(coord, " I_TRANSFORMMATRICES "[tmp]),\n"
|
||||||
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n"
|
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 1]),\n"
|
||||||
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 2]));\n"
|
" dot(coord, " I_TRANSFORMMATRICES "[tmp + 2]));\n"
|
||||||
|
@ -491,8 +492,8 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
" 1.0);\n"
|
" 1.0);\n"
|
||||||
" }}\n"
|
" }}\n"
|
||||||
" }} else {{\n");
|
" }} else {{\n");
|
||||||
out.Write(" if ({} == {}u) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
|
out.Write(" if ({} == {:s}) {{\n", BitfieldExtract("texMtxInfo", TexMtxInfo().projection),
|
||||||
XF_TEXPROJ_STQ);
|
TexSize::STQ);
|
||||||
out.Write(" output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n"
|
out.Write(" output_tex.xyz = float3(dot(coord, " I_TEXMATRICES "[3u * texgen]),\n"
|
||||||
" dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n"
|
" dot(coord, " I_TEXMATRICES "[3u * texgen + 1u]),\n"
|
||||||
" dot(coord, " I_TEXMATRICES "[3u * texgen + 2u]));\n"
|
" dot(coord, " I_TEXMATRICES "[3u * texgen + 2u]));\n"
|
||||||
|
@ -526,8 +527,7 @@ static void GenVertexShaderTexGens(APIType api_type, u32 num_texgen, ShaderCode&
|
||||||
// When q is 0, the GameCube appears to have a special case
|
// When q is 0, the GameCube appears to have a special case
|
||||||
// This can be seen in devkitPro's neheGX Lesson08 example for Wii
|
// This can be seen in devkitPro's neheGX Lesson08 example for Wii
|
||||||
// Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling)
|
// Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling)
|
||||||
out.Write(" if (texgentype == {}u && output_tex.z == 0.0) // XF_TEXGEN_REGULAR\n",
|
out.Write(" if (texgentype == {:s} && output_tex.z == 0.0)\n", TexGenType::Regular);
|
||||||
XF_TEXGEN_REGULAR);
|
|
||||||
out.Write(
|
out.Write(
|
||||||
" output_tex.xy = clamp(output_tex.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n"
|
" output_tex.xy = clamp(output_tex.xy / 2.0f, float2(-1.0f,-1.0f), float2(1.0f,1.0f));\n"
|
||||||
"\n");
|
"\n");
|
||||||
|
|
|
@ -2,11 +2,12 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "VideoCommon/VertexLoader.h"
|
||||||
|
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
#include "VideoCommon/DataReader.h"
|
#include "VideoCommon/DataReader.h"
|
||||||
#include "VideoCommon/VertexLoader.h"
|
|
||||||
#include "VideoCommon/VertexLoaderManager.h"
|
#include "VideoCommon/VertexLoaderManager.h"
|
||||||
#include "VideoCommon/VertexLoaderUtils.h"
|
#include "VideoCommon/VertexLoaderUtils.h"
|
||||||
#include "VideoCommon/VertexLoader_Color.h"
|
#include "VideoCommon/VertexLoader_Color.h"
|
||||||
|
@ -84,20 +85,13 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
// Reset pipeline
|
// Reset pipeline
|
||||||
m_numPipelineStages = 0;
|
m_numPipelineStages = 0;
|
||||||
|
|
||||||
// Colors
|
|
||||||
const u64 col[2] = {m_VtxDesc.Color0, m_VtxDesc.Color1};
|
|
||||||
// TextureCoord
|
|
||||||
const u64 tc[8] = {m_VtxDesc.Tex0Coord, m_VtxDesc.Tex1Coord, m_VtxDesc.Tex2Coord,
|
|
||||||
m_VtxDesc.Tex3Coord, m_VtxDesc.Tex4Coord, m_VtxDesc.Tex5Coord,
|
|
||||||
m_VtxDesc.Tex6Coord, m_VtxDesc.Tex7Coord};
|
|
||||||
|
|
||||||
u32 components = 0;
|
u32 components = 0;
|
||||||
|
|
||||||
// Position in pc vertex format.
|
// Position in pc vertex format.
|
||||||
int nat_offset = 0;
|
int nat_offset = 0;
|
||||||
|
|
||||||
// Position Matrix Index
|
// Position Matrix Index
|
||||||
if (m_VtxDesc.PosMatIdx)
|
if (m_VtxDesc.low.PosMatIdx)
|
||||||
{
|
{
|
||||||
WriteCall(PosMtx_ReadDirect_UByte);
|
WriteCall(PosMtx_ReadDirect_UByte);
|
||||||
components |= VB_HAS_POSMTXIDX;
|
components |= VB_HAS_POSMTXIDX;
|
||||||
|
@ -110,49 +104,49 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_VtxDesc.Tex0MatIdx)
|
if (m_VtxDesc.low.Tex0MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX0;
|
components |= VB_HAS_TEXMTXIDX0;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex1MatIdx)
|
if (m_VtxDesc.low.Tex1MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX1;
|
components |= VB_HAS_TEXMTXIDX1;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex2MatIdx)
|
if (m_VtxDesc.low.Tex2MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX2;
|
components |= VB_HAS_TEXMTXIDX2;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex3MatIdx)
|
if (m_VtxDesc.low.Tex3MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX3;
|
components |= VB_HAS_TEXMTXIDX3;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex4MatIdx)
|
if (m_VtxDesc.low.Tex4MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX4;
|
components |= VB_HAS_TEXMTXIDX4;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex5MatIdx)
|
if (m_VtxDesc.low.Tex5MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX5;
|
components |= VB_HAS_TEXMTXIDX5;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex6MatIdx)
|
if (m_VtxDesc.low.Tex6MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX6;
|
components |= VB_HAS_TEXMTXIDX6;
|
||||||
WriteCall(TexMtx_ReadDirect_UByte);
|
WriteCall(TexMtx_ReadDirect_UByte);
|
||||||
}
|
}
|
||||||
if (m_VtxDesc.Tex7MatIdx)
|
if (m_VtxDesc.low.Tex7MatIdx)
|
||||||
{
|
{
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
components |= VB_HAS_TEXMTXIDX7;
|
components |= VB_HAS_TEXMTXIDX7;
|
||||||
|
@ -160,12 +154,12 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write vertex position loader
|
// Write vertex position loader
|
||||||
WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.Position, m_VtxAttr.PosFormat,
|
WriteCall(VertexLoader_Position::GetFunction(m_VtxDesc.low.Position, m_VtxAttr.PosFormat,
|
||||||
m_VtxAttr.PosElements));
|
m_VtxAttr.PosElements));
|
||||||
|
|
||||||
m_VertexSize += VertexLoader_Position::GetSize(m_VtxDesc.Position, m_VtxAttr.PosFormat,
|
m_VertexSize += VertexLoader_Position::GetSize(m_VtxDesc.low.Position, m_VtxAttr.PosFormat,
|
||||||
m_VtxAttr.PosElements);
|
m_VtxAttr.PosElements);
|
||||||
int pos_elements = m_VtxAttr.PosElements + 2;
|
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
|
||||||
m_native_vtx_decl.position.components = pos_elements;
|
m_native_vtx_decl.position.components = pos_elements;
|
||||||
m_native_vtx_decl.position.enable = true;
|
m_native_vtx_decl.position.enable = true;
|
||||||
m_native_vtx_decl.position.offset = nat_offset;
|
m_native_vtx_decl.position.offset = nat_offset;
|
||||||
|
@ -174,23 +168,24 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
nat_offset += pos_elements * sizeof(float);
|
nat_offset += pos_elements * sizeof(float);
|
||||||
|
|
||||||
// Normals
|
// Normals
|
||||||
if (m_VtxDesc.Normal != NOT_PRESENT)
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
m_VertexSize += VertexLoader_Normal::GetSize(m_VtxDesc.Normal, m_VtxAttr.NormalFormat,
|
m_VertexSize += VertexLoader_Normal::GetSize(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat,
|
||||||
m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
|
m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
|
||||||
|
|
||||||
TPipelineFunction pFunc = VertexLoader_Normal::GetFunction(
|
TPipelineFunction pFunc =
|
||||||
m_VtxDesc.Normal, m_VtxAttr.NormalFormat, m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
|
VertexLoader_Normal::GetFunction(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat,
|
||||||
|
m_VtxAttr.NormalElements, m_VtxAttr.NormalIndex3);
|
||||||
|
|
||||||
if (pFunc == nullptr)
|
if (pFunc == nullptr)
|
||||||
{
|
{
|
||||||
PanicAlertFmt("VertexLoader_Normal::GetFunction({} {} {} {}) returned zero!",
|
PanicAlertFmt("VertexLoader_Normal::GetFunction({} {} {} {}) returned zero!",
|
||||||
m_VtxDesc.Normal, m_VtxAttr.NormalFormat, m_VtxAttr.NormalElements,
|
m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, m_VtxAttr.NormalElements,
|
||||||
m_VtxAttr.NormalIndex3);
|
m_VtxAttr.NormalIndex3);
|
||||||
}
|
}
|
||||||
WriteCall(pFunc);
|
WriteCall(pFunc);
|
||||||
|
|
||||||
for (int i = 0; i < (vtx_attr.NormalElements ? 3 : 1); i++)
|
for (int i = 0; i < (vtx_attr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.normals[i].components = 3;
|
m_native_vtx_decl.normals[i].components = 3;
|
||||||
m_native_vtx_decl.normals[i].enable = true;
|
m_native_vtx_decl.normals[i].enable = true;
|
||||||
|
@ -201,43 +196,43 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
}
|
}
|
||||||
|
|
||||||
components |= VB_HAS_NRM0;
|
components |= VB_HAS_NRM0;
|
||||||
if (m_VtxAttr.NormalElements == 1)
|
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
|
||||||
components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 2; i++)
|
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.colors[i].components = 4;
|
m_native_vtx_decl.colors[i].components = 4;
|
||||||
m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
|
m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
|
||||||
m_native_vtx_decl.colors[i].integer = false;
|
m_native_vtx_decl.colors[i].integer = false;
|
||||||
switch (col[i])
|
switch (m_VtxDesc.low.Color[i])
|
||||||
{
|
{
|
||||||
case NOT_PRESENT:
|
case VertexComponentFormat::NotPresent:
|
||||||
break;
|
break;
|
||||||
case DIRECT:
|
case VertexComponentFormat::Direct:
|
||||||
switch (m_VtxAttr.color[i].Comp)
|
switch (m_VtxAttr.color[i].Comp)
|
||||||
{
|
{
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
m_VertexSize += 2;
|
m_VertexSize += 2;
|
||||||
WriteCall(Color_ReadDirect_16b_565);
|
WriteCall(Color_ReadDirect_16b_565);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
m_VertexSize += 3;
|
m_VertexSize += 3;
|
||||||
WriteCall(Color_ReadDirect_24b_888);
|
WriteCall(Color_ReadDirect_24b_888);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
m_VertexSize += 4;
|
m_VertexSize += 4;
|
||||||
WriteCall(Color_ReadDirect_32b_888x);
|
WriteCall(Color_ReadDirect_32b_888x);
|
||||||
break;
|
break;
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
m_VertexSize += 2;
|
m_VertexSize += 2;
|
||||||
WriteCall(Color_ReadDirect_16b_4444);
|
WriteCall(Color_ReadDirect_16b_4444);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
m_VertexSize += 3;
|
m_VertexSize += 3;
|
||||||
WriteCall(Color_ReadDirect_24b_6666);
|
WriteCall(Color_ReadDirect_24b_6666);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
m_VertexSize += 4;
|
m_VertexSize += 4;
|
||||||
WriteCall(Color_ReadDirect_32b_8888);
|
WriteCall(Color_ReadDirect_32b_8888);
|
||||||
break;
|
break;
|
||||||
|
@ -246,26 +241,26 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INDEX8:
|
case VertexComponentFormat::Index8:
|
||||||
m_VertexSize += 1;
|
m_VertexSize += 1;
|
||||||
switch (m_VtxAttr.color[i].Comp)
|
switch (m_VtxAttr.color[i].Comp)
|
||||||
{
|
{
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
WriteCall(Color_ReadIndex8_16b_565);
|
WriteCall(Color_ReadIndex8_16b_565);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
WriteCall(Color_ReadIndex8_24b_888);
|
WriteCall(Color_ReadIndex8_24b_888);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
WriteCall(Color_ReadIndex8_32b_888x);
|
WriteCall(Color_ReadIndex8_32b_888x);
|
||||||
break;
|
break;
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
WriteCall(Color_ReadIndex8_16b_4444);
|
WriteCall(Color_ReadIndex8_16b_4444);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
WriteCall(Color_ReadIndex8_24b_6666);
|
WriteCall(Color_ReadIndex8_24b_6666);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
WriteCall(Color_ReadIndex8_32b_8888);
|
WriteCall(Color_ReadIndex8_32b_8888);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -273,26 +268,26 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case INDEX16:
|
case VertexComponentFormat::Index16:
|
||||||
m_VertexSize += 2;
|
m_VertexSize += 2;
|
||||||
switch (m_VtxAttr.color[i].Comp)
|
switch (m_VtxAttr.color[i].Comp)
|
||||||
{
|
{
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
WriteCall(Color_ReadIndex16_16b_565);
|
WriteCall(Color_ReadIndex16_16b_565);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
WriteCall(Color_ReadIndex16_24b_888);
|
WriteCall(Color_ReadIndex16_24b_888);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
WriteCall(Color_ReadIndex16_32b_888x);
|
WriteCall(Color_ReadIndex16_32b_888x);
|
||||||
break;
|
break;
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
WriteCall(Color_ReadIndex16_16b_4444);
|
WriteCall(Color_ReadIndex16_16b_4444);
|
||||||
break;
|
break;
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
WriteCall(Color_ReadIndex16_24b_6666);
|
WriteCall(Color_ReadIndex16_24b_6666);
|
||||||
break;
|
break;
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
WriteCall(Color_ReadIndex16_32b_8888);
|
WriteCall(Color_ReadIndex16_32b_8888);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -302,7 +297,7 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Common for the three bottom cases
|
// Common for the three bottom cases
|
||||||
if (col[i] != NOT_PRESENT)
|
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
components |= VB_HAS_COL0 << i;
|
components |= VB_HAS_COL0 << i;
|
||||||
m_native_vtx_decl.colors[i].offset = nat_offset;
|
m_native_vtx_decl.colors[i].offset = nat_offset;
|
||||||
|
@ -312,38 +307,40 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Texture matrix indices (remove if corresponding texture coordinate isn't enabled)
|
// Texture matrix indices (remove if corresponding texture coordinate isn't enabled)
|
||||||
for (int i = 0; i < 8; i++)
|
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.texcoords[i].offset = nat_offset;
|
m_native_vtx_decl.texcoords[i].offset = nat_offset;
|
||||||
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
||||||
m_native_vtx_decl.texcoords[i].integer = false;
|
m_native_vtx_decl.texcoords[i].integer = false;
|
||||||
|
|
||||||
const int format = m_VtxAttr.texCoord[i].Format;
|
const auto tc = m_VtxDesc.high.TexCoord[i].Value();
|
||||||
const int elements = m_VtxAttr.texCoord[i].Elements;
|
const auto format = m_VtxAttr.texCoord[i].Format;
|
||||||
|
const auto elements = m_VtxAttr.texCoord[i].Elements;
|
||||||
|
|
||||||
if (tc[i] != NOT_PRESENT)
|
if (tc != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
ASSERT_MSG(VIDEO, DIRECT <= tc[i] && tc[i] <= INDEX16,
|
ASSERT_MSG(VIDEO, VertexComponentFormat::Direct <= tc && tc <= VertexComponentFormat::Index16,
|
||||||
"Invalid texture coordinates!\n(tc[i] = %d)", (u32)tc[i]);
|
"Invalid texture coordinates!\n(tc = %d)", (u32)tc);
|
||||||
ASSERT_MSG(VIDEO, FORMAT_UBYTE <= format && format <= FORMAT_FLOAT,
|
ASSERT_MSG(VIDEO, ComponentFormat::UByte <= format && format <= ComponentFormat::Float,
|
||||||
"Invalid texture coordinates format!\n(format = %d)", format);
|
"Invalid texture coordinates format!\n(format = %d)", (u32)format);
|
||||||
ASSERT_MSG(VIDEO, 0 <= elements && elements <= 1,
|
ASSERT_MSG(VIDEO, elements == TexComponentCount::S || elements == TexComponentCount::ST,
|
||||||
"Invalid number of texture coordinates elements!\n(elements = %d)", elements);
|
"Invalid number of texture coordinates elements!\n(elements = %d)", (u32)elements);
|
||||||
|
|
||||||
components |= VB_HAS_UV0 << i;
|
components |= VB_HAS_UV0 << i;
|
||||||
WriteCall(VertexLoader_TextCoord::GetFunction(tc[i], format, elements));
|
WriteCall(VertexLoader_TextCoord::GetFunction(tc, format, elements));
|
||||||
m_VertexSize += VertexLoader_TextCoord::GetSize(tc[i], format, elements);
|
m_VertexSize += VertexLoader_TextCoord::GetSize(tc, format, elements);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (components & (VB_HAS_TEXMTXIDX0 << i))
|
if (components & (VB_HAS_TEXMTXIDX0 << i))
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.texcoords[i].enable = true;
|
m_native_vtx_decl.texcoords[i].enable = true;
|
||||||
if (tc[i] != NOT_PRESENT)
|
if (tc != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
// if texmtx is included, texcoord will always be 3 floats, z will be the texmtx index
|
// if texmtx is included, texcoord will always be 3 floats, z will be the texmtx index
|
||||||
m_native_vtx_decl.texcoords[i].components = 3;
|
m_native_vtx_decl.texcoords[i].components = 3;
|
||||||
nat_offset += 12;
|
nat_offset += 12;
|
||||||
WriteCall(m_VtxAttr.texCoord[i].Elements ? TexMtx_Write_Float : TexMtx_Write_Float2);
|
WriteCall(m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? TexMtx_Write_Float :
|
||||||
|
TexMtx_Write_Float2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -354,21 +351,22 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (tc[i] != NOT_PRESENT)
|
if (tc != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.texcoords[i].enable = true;
|
m_native_vtx_decl.texcoords[i].enable = true;
|
||||||
m_native_vtx_decl.texcoords[i].components = vtx_attr.texCoord[i].Elements ? 2 : 1;
|
m_native_vtx_decl.texcoords[i].components =
|
||||||
nat_offset += 4 * (vtx_attr.texCoord[i].Elements ? 2 : 1);
|
vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1;
|
||||||
|
nat_offset += 4 * (vtx_attr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tc[i] == NOT_PRESENT)
|
if (tc == VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
// if there's more tex coords later, have to write a dummy call
|
// if there's more tex coords later, have to write a dummy call
|
||||||
int j = i + 1;
|
size_t j = i + 1;
|
||||||
for (; j < 8; ++j)
|
for (; j < m_VtxDesc.high.TexCoord.Size(); ++j)
|
||||||
{
|
{
|
||||||
if (tc[j] != NOT_PRESENT)
|
if (m_VtxDesc.high.TexCoord[j] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
WriteCall(VertexLoader_TextCoord::GetDummyFunction()); // important to get indices right!
|
WriteCall(VertexLoader_TextCoord::GetDummyFunction()); // important to get indices right!
|
||||||
break;
|
break;
|
||||||
|
@ -383,8 +381,8 @@ void VertexLoader::CompileVertexTranslator()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// indexed position formats may skip a the vertex
|
// indexed position formats may skip the vertex
|
||||||
if (m_VtxDesc.Position & 2)
|
if (IsIndexed(m_VtxDesc.low.Position))
|
||||||
{
|
{
|
||||||
WriteCall(SkipVertex);
|
WriteCall(SkipVertex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "VideoCommon/VertexLoaderARM64.h"
|
#include "VideoCommon/VertexLoaderARM64.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/DataReader.h"
|
#include "VideoCommon/DataReader.h"
|
||||||
#include "VideoCommon/VertexLoaderManager.h"
|
#include "VideoCommon/VertexLoaderManager.h"
|
||||||
|
@ -45,11 +48,11 @@ VertexLoaderARM64::VertexLoaderARM64(const TVtxDesc& vtx_desc, const VAT& vtx_at
|
||||||
WriteProtect();
|
WriteProtect();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderARM64::GetVertexAddr(int array, u64 attribute, ARM64Reg reg)
|
void VertexLoaderARM64::GetVertexAddr(int array, VertexComponentFormat attribute, ARM64Reg reg)
|
||||||
{
|
{
|
||||||
if (attribute & MASK_INDEXED)
|
if (IsIndexed(attribute))
|
||||||
{
|
{
|
||||||
if (attribute == INDEX8)
|
if (attribute == VertexComponentFormat::Index8)
|
||||||
{
|
{
|
||||||
if (m_src_ofs < 4096)
|
if (m_src_ofs < 4096)
|
||||||
{
|
{
|
||||||
|
@ -83,7 +86,8 @@ void VertexLoaderARM64::GetVertexAddr(int array, u64 attribute, ARM64Reg reg)
|
||||||
|
|
||||||
if (array == ARRAY_POSITION)
|
if (array == ARRAY_POSITION)
|
||||||
{
|
{
|
||||||
EOR(scratch2_reg, scratch1_reg, 0, attribute == INDEX8 ? 7 : 15); // 0xFF : 0xFFFF
|
EOR(scratch2_reg, scratch1_reg, 0,
|
||||||
|
attribute == VertexComponentFormat::Index8 ? 7 : 15); // 0xFF : 0xFFFF
|
||||||
m_skip_vertex = CBZ(scratch2_reg);
|
m_skip_vertex = CBZ(scratch2_reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,23 +101,24 @@ void VertexLoaderARM64::GetVertexAddr(int array, u64 attribute, ARM64Reg reg)
|
||||||
ADD(reg, src_reg, m_src_ofs);
|
ADD(reg, src_reg, m_src_ofs);
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 VertexLoaderARM64::GetAddressImm(int array, u64 attribute, Arm64Gen::ARM64Reg reg, u32 align)
|
s32 VertexLoaderARM64::GetAddressImm(int array, VertexComponentFormat attribute,
|
||||||
|
Arm64Gen::ARM64Reg reg, u32 align)
|
||||||
{
|
{
|
||||||
if (attribute & MASK_INDEXED || (m_src_ofs > 255 && (m_src_ofs & (align - 1))))
|
if (IsIndexed(attribute) || (m_src_ofs > 255 && (m_src_ofs & (align - 1))))
|
||||||
GetVertexAddr(array, attribute, reg);
|
GetVertexAddr(array, attribute, reg);
|
||||||
else
|
else
|
||||||
return m_src_ofs;
|
return m_src_ofs;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VertexLoaderARM64::ReadVertex(u64 attribute, int format, int count_in, int count_out,
|
int VertexLoaderARM64::ReadVertex(VertexComponentFormat attribute, ComponentFormat format,
|
||||||
bool dequantize, u8 scaling_exponent,
|
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
AttributeFormat* native_format, s32 offset)
|
AttributeFormat* native_format, s32 offset)
|
||||||
{
|
{
|
||||||
ARM64Reg coords = count_in == 3 ? Q31 : D31;
|
ARM64Reg coords = count_in == 3 ? Q31 : D31;
|
||||||
ARM64Reg scale = count_in == 3 ? Q30 : D30;
|
ARM64Reg scale = count_in == 3 ? Q30 : D30;
|
||||||
|
|
||||||
int elem_size = 1 << (format / 2);
|
int elem_size = GetElementSize(format);
|
||||||
int load_bytes = elem_size * count_in;
|
int load_bytes = elem_size * count_in;
|
||||||
int load_size =
|
int load_size =
|
||||||
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
||||||
|
@ -136,24 +141,24 @@ int VertexLoaderARM64::ReadVertex(u64 attribute, int format, int count_in, int c
|
||||||
m_float_emit.LDR(load_size, IndexType::Unsigned, coords, src_reg, offset);
|
m_float_emit.LDR(load_size, IndexType::Unsigned, coords, src_reg, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format != FORMAT_FLOAT)
|
if (format != ComponentFormat::Float)
|
||||||
{
|
{
|
||||||
// Extend and convert to float
|
// Extend and convert to float
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_UBYTE:
|
case ComponentFormat::UByte:
|
||||||
m_float_emit.UXTL(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.UXTL(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
m_float_emit.UXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.UXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
break;
|
break;
|
||||||
case FORMAT_BYTE:
|
case ComponentFormat::Byte:
|
||||||
m_float_emit.SXTL(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.SXTL(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
m_float_emit.SXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.SXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
break;
|
break;
|
||||||
case FORMAT_USHORT:
|
case ComponentFormat::UShort:
|
||||||
m_float_emit.REV16(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.REV16(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
m_float_emit.UXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.UXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
break;
|
break;
|
||||||
case FORMAT_SHORT:
|
case ComponentFormat::Short:
|
||||||
m_float_emit.REV16(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.REV16(8, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
m_float_emit.SXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
m_float_emit.SXTL(16, EncodeRegToDouble(coords), EncodeRegToDouble(coords));
|
||||||
break;
|
break;
|
||||||
|
@ -207,20 +212,20 @@ int VertexLoaderARM64::ReadVertex(u64 attribute, int format, int count_in, int c
|
||||||
native_format->integer = false;
|
native_format->integer = false;
|
||||||
m_dst_ofs += sizeof(float) * count_out;
|
m_dst_ofs += sizeof(float) * count_out;
|
||||||
|
|
||||||
if (attribute == DIRECT)
|
if (attribute == VertexComponentFormat::Direct)
|
||||||
m_src_ofs += load_bytes;
|
m_src_ofs += load_bytes;
|
||||||
|
|
||||||
return load_bytes;
|
return load_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderARM64::ReadColor(u64 attribute, int format, s32 offset)
|
void VertexLoaderARM64::ReadColor(VertexComponentFormat attribute, ColorFormat format, s32 offset)
|
||||||
{
|
{
|
||||||
int load_bytes = 0;
|
int load_bytes = 0;
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
if (offset == -1)
|
if (offset == -1)
|
||||||
LDR(IndexType::Unsigned, scratch2_reg, EncodeRegTo64(scratch1_reg), 0);
|
LDR(IndexType::Unsigned, scratch2_reg, EncodeRegTo64(scratch1_reg), 0);
|
||||||
else if (offset & 3) // Not aligned - unscaled
|
else if (offset & 3) // Not aligned - unscaled
|
||||||
|
@ -228,13 +233,13 @@ void VertexLoaderARM64::ReadColor(u64 attribute, int format, s32 offset)
|
||||||
else
|
else
|
||||||
LDR(IndexType::Unsigned, scratch2_reg, src_reg, offset);
|
LDR(IndexType::Unsigned, scratch2_reg, src_reg, offset);
|
||||||
|
|
||||||
if (format != FORMAT_32B_8888)
|
if (format != ColorFormat::RGBA8888)
|
||||||
ORRI2R(scratch2_reg, scratch2_reg, 0xFF000000);
|
ORRI2R(scratch2_reg, scratch2_reg, 0xFF000000);
|
||||||
STR(IndexType::Unsigned, scratch2_reg, dst_reg, m_dst_ofs);
|
STR(IndexType::Unsigned, scratch2_reg, dst_reg, m_dst_ofs);
|
||||||
load_bytes = 3 + (format != FORMAT_24B_888);
|
load_bytes = format == ColorFormat::RGB888 ? 3 : 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
// RRRRRGGG GGGBBBBB
|
// RRRRRGGG GGGBBBBB
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
if (offset == -1)
|
if (offset == -1)
|
||||||
|
@ -270,7 +275,7 @@ void VertexLoaderARM64::ReadColor(u64 attribute, int format, s32 offset)
|
||||||
load_bytes = 2;
|
load_bytes = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
// BBBBAAAA RRRRGGGG
|
// BBBBAAAA RRRRGGGG
|
||||||
// REV16 - RRRRGGGG BBBBAAAA
|
// REV16 - RRRRGGGG BBBBAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
|
@ -303,7 +308,7 @@ void VertexLoaderARM64::ReadColor(u64 attribute, int format, s32 offset)
|
||||||
load_bytes = 2;
|
load_bytes = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
// RRRRRRGG GGGGBBBB BBAAAAAA
|
// RRRRRRGG GGGGBBBB BBAAAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
if (offset == -1)
|
if (offset == -1)
|
||||||
|
@ -349,7 +354,7 @@ void VertexLoaderARM64::ReadColor(u64 attribute, int format, s32 offset)
|
||||||
load_bytes = 3;
|
load_bytes = 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (attribute == DIRECT)
|
if (attribute == VertexComponentFormat::Direct)
|
||||||
m_src_ofs += load_bytes;
|
m_src_ofs += load_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -370,24 +375,19 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
// We can touch all except v8-v15
|
// We can touch all except v8-v15
|
||||||
// If we need to use those, we need to retain the lower 64bits(!) of the register
|
// If we need to use those, we need to retain the lower 64bits(!) of the register
|
||||||
|
|
||||||
const u64 tc[8] = {
|
|
||||||
m_VtxDesc.Tex0Coord, m_VtxDesc.Tex1Coord, m_VtxDesc.Tex2Coord, m_VtxDesc.Tex3Coord,
|
|
||||||
m_VtxDesc.Tex4Coord, m_VtxDesc.Tex5Coord, m_VtxDesc.Tex6Coord, m_VtxDesc.Tex7Coord,
|
|
||||||
};
|
|
||||||
|
|
||||||
bool has_tc = false;
|
bool has_tc = false;
|
||||||
bool has_tc_scale = false;
|
bool has_tc_scale = false;
|
||||||
for (int i = 0; i < 8; i++)
|
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
|
||||||
{
|
{
|
||||||
has_tc |= tc[i] != 0;
|
has_tc |= m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent;
|
||||||
has_tc_scale |= !!m_VtxAttr.texCoord[i].Frac;
|
has_tc_scale |= !!m_VtxAttr.texCoord[i].Frac;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool need_scale =
|
bool need_scale = (m_VtxAttr.ByteDequant && m_VtxAttr.PosFrac) || (has_tc && has_tc_scale) ||
|
||||||
(m_VtxAttr.ByteDequant && m_VtxAttr.PosFrac) || (has_tc && has_tc_scale) || m_VtxDesc.Normal;
|
(m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent);
|
||||||
|
|
||||||
AlignCode16();
|
AlignCode16();
|
||||||
if (m_VtxDesc.Position & MASK_INDEXED)
|
if (IsIndexed(m_VtxDesc.low.Position))
|
||||||
MOV(skipped_reg, WZR);
|
MOV(skipped_reg, WZR);
|
||||||
MOV(saved_count, count_reg);
|
MOV(saved_count, count_reg);
|
||||||
|
|
||||||
|
@ -399,7 +399,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
|
|
||||||
const u8* loop_start = GetCodePtr();
|
const u8* loop_start = GetCodePtr();
|
||||||
|
|
||||||
if (m_VtxDesc.PosMatIdx)
|
if (m_VtxDesc.low.PosMatIdx)
|
||||||
{
|
{
|
||||||
LDRB(IndexType::Unsigned, scratch1_reg, src_reg, m_src_ofs);
|
LDRB(IndexType::Unsigned, scratch1_reg, src_reg, m_src_ofs);
|
||||||
AND(scratch1_reg, scratch1_reg, 0, 5);
|
AND(scratch1_reg, scratch1_reg, 0, 5);
|
||||||
|
@ -422,50 +422,47 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
m_dst_ofs += sizeof(u32);
|
m_dst_ofs += sizeof(u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 texmatidx_ofs[8];
|
std::array<u32, 8> texmatidx_ofs;
|
||||||
const u64 tm[8] = {
|
for (size_t i = 0; i < m_VtxDesc.low.TexMatIdx.Size(); i++)
|
||||||
m_VtxDesc.Tex0MatIdx, m_VtxDesc.Tex1MatIdx, m_VtxDesc.Tex2MatIdx, m_VtxDesc.Tex3MatIdx,
|
|
||||||
m_VtxDesc.Tex4MatIdx, m_VtxDesc.Tex5MatIdx, m_VtxDesc.Tex6MatIdx, m_VtxDesc.Tex7MatIdx,
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
{
|
||||||
if (tm[i])
|
if (m_VtxDesc.low.TexMatIdx[i])
|
||||||
texmatidx_ofs[i] = m_src_ofs++;
|
texmatidx_ofs[i] = m_src_ofs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Position
|
// Position
|
||||||
{
|
{
|
||||||
int elem_size = 1 << (m_VtxAttr.PosFormat / 2);
|
int elem_size = GetElementSize(m_VtxAttr.PosFormat);
|
||||||
int load_bytes = elem_size * (m_VtxAttr.PosElements + 2);
|
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
|
||||||
|
int load_bytes = elem_size * pos_elements;
|
||||||
int load_size =
|
int load_size =
|
||||||
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
load_bytes == 1 ? 1 : load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
||||||
load_size <<= 3;
|
load_size <<= 3;
|
||||||
|
|
||||||
s32 offset =
|
s32 offset = GetAddressImm(ARRAY_POSITION, m_VtxDesc.low.Position, EncodeRegTo64(scratch1_reg),
|
||||||
GetAddressImm(ARRAY_POSITION, m_VtxDesc.Position, EncodeRegTo64(scratch1_reg), load_size);
|
load_size);
|
||||||
int pos_elements = m_VtxAttr.PosElements + 2;
|
ReadVertex(m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
|
||||||
ReadVertex(m_VtxDesc.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
|
|
||||||
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position, offset);
|
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_VtxDesc.Normal)
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
static const u8 map[8] = {7, 6, 15, 14};
|
static const u8 map[8] = {7, 6, 15, 14};
|
||||||
u8 scaling_exponent = map[m_VtxAttr.NormalFormat];
|
const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)];
|
||||||
|
const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1;
|
||||||
|
|
||||||
s32 offset = -1;
|
s32 offset = -1;
|
||||||
for (int i = 0; i < (m_VtxAttr.NormalElements ? 3 : 1); i++)
|
for (int i = 0; i < (m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1); i++)
|
||||||
{
|
{
|
||||||
if (!i || m_VtxAttr.NormalIndex3)
|
if (!i || m_VtxAttr.NormalIndex3)
|
||||||
{
|
{
|
||||||
int elem_size = 1 << (m_VtxAttr.NormalFormat / 2);
|
int elem_size = GetElementSize(m_VtxAttr.NormalFormat);
|
||||||
|
|
||||||
int load_bytes = elem_size * 3;
|
int load_bytes = elem_size * 3;
|
||||||
int load_size = load_bytes == 1 ?
|
int load_size = load_bytes == 1 ?
|
||||||
1 :
|
1 :
|
||||||
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
||||||
|
|
||||||
offset = GetAddressImm(ARRAY_NORMAL, m_VtxDesc.Normal, EncodeRegTo64(scratch1_reg),
|
offset = GetAddressImm(ARRAY_NORMAL, m_VtxDesc.low.Normal, EncodeRegTo64(scratch1_reg),
|
||||||
load_size << 3);
|
load_size << 3);
|
||||||
|
|
||||||
if (offset == -1)
|
if (offset == -1)
|
||||||
|
@ -473,7 +470,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
else
|
else
|
||||||
offset += i * elem_size * 3;
|
offset += i * elem_size * 3;
|
||||||
}
|
}
|
||||||
int bytes_read = ReadVertex(m_VtxDesc.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
|
int bytes_read = ReadVertex(m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
|
||||||
scaling_exponent, &m_native_vtx_decl.normals[i], offset);
|
scaling_exponent, &m_native_vtx_decl.normals[i], offset);
|
||||||
|
|
||||||
if (offset == -1)
|
if (offset == -1)
|
||||||
|
@ -483,25 +480,26 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
}
|
}
|
||||||
|
|
||||||
m_native_components |= VB_HAS_NRM0;
|
m_native_components |= VB_HAS_NRM0;
|
||||||
if (m_VtxAttr.NormalElements)
|
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
|
||||||
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 col[2] = {m_VtxDesc.Color0, m_VtxDesc.Color1};
|
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.colors[i].components = 4;
|
m_native_vtx_decl.colors[i].components = 4;
|
||||||
m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
|
m_native_vtx_decl.colors[i].type = VAR_UNSIGNED_BYTE;
|
||||||
m_native_vtx_decl.colors[i].integer = false;
|
m_native_vtx_decl.colors[i].integer = false;
|
||||||
|
|
||||||
if (col[i])
|
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
u32 align = 4;
|
u32 align = 4;
|
||||||
if (m_VtxAttr.color[i].Comp == FORMAT_16B_565 || m_VtxAttr.color[i].Comp == FORMAT_16B_4444)
|
if (m_VtxAttr.color[i].Comp == ColorFormat::RGB565 ||
|
||||||
|
m_VtxAttr.color[i].Comp == ColorFormat::RGBA4444)
|
||||||
align = 2;
|
align = 2;
|
||||||
|
|
||||||
s32 offset = GetAddressImm(ARRAY_COLOR + i, col[i], EncodeRegTo64(scratch1_reg), align);
|
s32 offset = GetAddressImm(ARRAY_COLOR + int(i), m_VtxDesc.low.Color[i],
|
||||||
ReadColor(col[i], m_VtxAttr.color[i].Comp, offset);
|
EncodeRegTo64(scratch1_reg), align);
|
||||||
|
ReadColor(m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp, offset);
|
||||||
m_native_components |= VB_HAS_COL0 << i;
|
m_native_components |= VB_HAS_COL0 << i;
|
||||||
m_native_vtx_decl.colors[i].components = 4;
|
m_native_vtx_decl.colors[i].components = 4;
|
||||||
m_native_vtx_decl.colors[i].enable = true;
|
m_native_vtx_decl.colors[i].enable = true;
|
||||||
|
@ -512,31 +510,32 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++)
|
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
|
||||||
{
|
{
|
||||||
m_native_vtx_decl.texcoords[i].offset = m_dst_ofs;
|
m_native_vtx_decl.texcoords[i].offset = m_dst_ofs;
|
||||||
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
||||||
m_native_vtx_decl.texcoords[i].integer = false;
|
m_native_vtx_decl.texcoords[i].integer = false;
|
||||||
|
|
||||||
int elements = m_VtxAttr.texCoord[i].Elements + 1;
|
int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::S ? 1 : 2;
|
||||||
if (tc[i])
|
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
m_native_components |= VB_HAS_UV0 << i;
|
m_native_components |= VB_HAS_UV0 << i;
|
||||||
|
|
||||||
int elem_size = 1 << (m_VtxAttr.texCoord[i].Format / 2);
|
int elem_size = GetElementSize(m_VtxAttr.texCoord[i].Format);
|
||||||
int load_bytes = elem_size * (elements + 2);
|
int load_bytes = elem_size * (elements + 2);
|
||||||
int load_size = load_bytes == 1 ?
|
int load_size = load_bytes == 1 ?
|
||||||
1 :
|
1 :
|
||||||
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
load_bytes <= 2 ? 2 : load_bytes <= 4 ? 4 : load_bytes <= 8 ? 8 : 16;
|
||||||
load_size <<= 3;
|
load_size <<= 3;
|
||||||
|
|
||||||
s32 offset =
|
s32 offset = GetAddressImm(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i],
|
||||||
GetAddressImm(ARRAY_TEXCOORD0 + i, tc[i], EncodeRegTo64(scratch1_reg), load_size);
|
EncodeRegTo64(scratch1_reg), load_size);
|
||||||
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
|
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
|
||||||
ReadVertex(tc[i], m_VtxAttr.texCoord[i].Format, elements, tm[i] ? 2 : elements,
|
ReadVertex(m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements,
|
||||||
m_VtxAttr.ByteDequant, scaling_exponent, &m_native_vtx_decl.texcoords[i], offset);
|
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent,
|
||||||
|
&m_native_vtx_decl.texcoords[i], offset);
|
||||||
}
|
}
|
||||||
if (tm[i])
|
if (m_VtxDesc.low.TexMatIdx[i])
|
||||||
{
|
{
|
||||||
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
|
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
|
||||||
m_native_vtx_decl.texcoords[i].components = 3;
|
m_native_vtx_decl.texcoords[i].components = 3;
|
||||||
|
@ -547,7 +546,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
LDRB(IndexType::Unsigned, scratch2_reg, src_reg, texmatidx_ofs[i]);
|
LDRB(IndexType::Unsigned, scratch2_reg, src_reg, texmatidx_ofs[i]);
|
||||||
m_float_emit.UCVTF(S31, scratch2_reg);
|
m_float_emit.UCVTF(S31, scratch2_reg);
|
||||||
|
|
||||||
if (tc[i])
|
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
m_float_emit.STR(32, IndexType::Unsigned, D31, dst_reg, m_dst_ofs);
|
m_float_emit.STR(32, IndexType::Unsigned, D31, dst_reg, m_dst_ofs);
|
||||||
m_dst_ofs += sizeof(float);
|
m_dst_ofs += sizeof(float);
|
||||||
|
@ -587,7 +586,7 @@ void VertexLoaderARM64::GenerateVertexLoader()
|
||||||
SUB(count_reg, count_reg, 1);
|
SUB(count_reg, count_reg, 1);
|
||||||
CBNZ(count_reg, loop_start);
|
CBNZ(count_reg, loop_start);
|
||||||
|
|
||||||
if (m_VtxDesc.Position & MASK_INDEXED)
|
if (IsIndexed(m_VtxDesc.low.Position))
|
||||||
{
|
{
|
||||||
SUB(W0, saved_count, skipped_reg);
|
SUB(W0, saved_count, skipped_reg);
|
||||||
RET(X30);
|
RET(X30);
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#include "VideoCommon/VertexLoaderBase.h"
|
#include "VideoCommon/VertexLoaderBase.h"
|
||||||
|
|
||||||
class DataReader;
|
class DataReader;
|
||||||
|
enum class VertexComponentFormat;
|
||||||
|
enum class ComponentFormat;
|
||||||
|
enum class ColorFormat;
|
||||||
|
|
||||||
class VertexLoaderARM64 : public VertexLoaderBase, public Arm64Gen::ARM64CodeBlock
|
class VertexLoaderARM64 : public VertexLoaderBase, public Arm64Gen::ARM64CodeBlock
|
||||||
{
|
{
|
||||||
|
@ -25,10 +28,11 @@ private:
|
||||||
u32 m_dst_ofs = 0;
|
u32 m_dst_ofs = 0;
|
||||||
Arm64Gen::FixupBranch m_skip_vertex;
|
Arm64Gen::FixupBranch m_skip_vertex;
|
||||||
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
Arm64Gen::ARM64FloatEmitter m_float_emit;
|
||||||
void GetVertexAddr(int array, u64 attribute, Arm64Gen::ARM64Reg reg);
|
void GetVertexAddr(int array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg);
|
||||||
s32 GetAddressImm(int array, u64 attribute, Arm64Gen::ARM64Reg reg, u32 align);
|
s32 GetAddressImm(int array, VertexComponentFormat attribute, Arm64Gen::ARM64Reg reg, u32 align);
|
||||||
int ReadVertex(u64 attribute, int format, int count_in, int count_out, bool dequantize,
|
int ReadVertex(VertexComponentFormat attribute, ComponentFormat format, int count_in,
|
||||||
u8 scaling_exponent, AttributeFormat* native_format, s32 offset = -1);
|
int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
void ReadColor(u64 attribute, int format, s32 offset);
|
AttributeFormat* native_format, s32 offset = -1);
|
||||||
|
void ReadColor(VertexComponentFormat attribute, ColorFormat format, s32 offset);
|
||||||
void GenerateVertexLoader();
|
void GenerateVertexLoader();
|
||||||
};
|
};
|
||||||
|
|
|
@ -81,64 +81,33 @@ std::string VertexLoaderBase::ToString() const
|
||||||
dest += GetName();
|
dest += GetName();
|
||||||
dest += ": ";
|
dest += ": ";
|
||||||
|
|
||||||
static constexpr std::array<const char*, 4> pos_mode{{
|
dest += fmt::format("{}b skin: {} P: {} {}-{} ", m_VertexSize, m_VtxDesc.low.PosMatIdx,
|
||||||
"Inv",
|
m_VtxAttr.PosElements, m_VtxDesc.low.Position, m_VtxAttr.PosFormat);
|
||||||
"Dir",
|
|
||||||
"I8",
|
|
||||||
"I16",
|
|
||||||
}};
|
|
||||||
static constexpr std::array<const char*, 8> pos_formats{{
|
|
||||||
"u8",
|
|
||||||
"s8",
|
|
||||||
"u16",
|
|
||||||
"s16",
|
|
||||||
"flt",
|
|
||||||
"Inv",
|
|
||||||
"Inv",
|
|
||||||
"Inv",
|
|
||||||
}};
|
|
||||||
static constexpr std::array<const char*, 8> color_format{{
|
|
||||||
"565",
|
|
||||||
"888",
|
|
||||||
"888x",
|
|
||||||
"4444",
|
|
||||||
"6666",
|
|
||||||
"8888",
|
|
||||||
"Inv",
|
|
||||||
"Inv",
|
|
||||||
}};
|
|
||||||
|
|
||||||
dest += fmt::format("{}b skin: {} P: {} {}-{} ", m_VertexSize, m_VtxDesc.PosMatIdx,
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
m_VtxAttr.PosElements ? 3 : 2, pos_mode[m_VtxDesc.Position],
|
|
||||||
pos_formats[m_VtxAttr.PosFormat]);
|
|
||||||
|
|
||||||
if (m_VtxDesc.Normal)
|
|
||||||
{
|
{
|
||||||
dest += fmt::format("Nrm: {} {}-{} ", m_VtxAttr.NormalElements, pos_mode[m_VtxDesc.Normal],
|
dest += fmt::format("Nrm: {} {}-{} ", m_VtxAttr.NormalElements, m_VtxDesc.low.Normal,
|
||||||
pos_formats[m_VtxAttr.NormalFormat]);
|
m_VtxAttr.NormalFormat);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::array<u64, 2> color_mode{{m_VtxDesc.Color0, m_VtxDesc.Color1}};
|
for (size_t i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
|
||||||
for (size_t i = 0; i < color_mode.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (color_mode[i] == 0)
|
if (g_main_cp_state.vtx_desc.low.Color[i] == VertexComponentFormat::NotPresent)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto& color = m_VtxAttr.color[i];
|
const auto& color = m_VtxAttr.color[i];
|
||||||
dest += fmt::format("C{}: {} {}-{} ", i, color.Elements, pos_mode[color_mode[i]],
|
dest += fmt::format("C{}: {} {}-{} ", i, color.Elements, g_main_cp_state.vtx_desc.low.Color[i],
|
||||||
color_format[color.Comp]);
|
color.Comp);
|
||||||
}
|
}
|
||||||
const std::array<u64, 8> tex_mode{{m_VtxDesc.Tex0Coord, m_VtxDesc.Tex1Coord, m_VtxDesc.Tex2Coord,
|
|
||||||
m_VtxDesc.Tex3Coord, m_VtxDesc.Tex4Coord, m_VtxDesc.Tex5Coord,
|
for (size_t i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
|
||||||
m_VtxDesc.Tex6Coord, m_VtxDesc.Tex7Coord}};
|
|
||||||
for (size_t i = 0; i < tex_mode.size(); i++)
|
|
||||||
{
|
{
|
||||||
if (tex_mode[i] == 0)
|
if (g_main_cp_state.vtx_desc.high.TexCoord[i] == VertexComponentFormat::NotPresent)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto& tex_coord = m_VtxAttr.texCoord[i];
|
const auto& tex_coord = m_VtxAttr.texCoord[i];
|
||||||
dest += fmt::format("T{}: {} {}-{} ", i, tex_coord.Elements, pos_mode[tex_mode[i]],
|
dest += fmt::format("T{}: {} {}-{} ", i, tex_coord.Elements,
|
||||||
pos_formats[tex_coord.Format]);
|
g_main_cp_state.vtx_desc.high.TexCoord[i], tex_coord.Format);
|
||||||
}
|
}
|
||||||
dest += fmt::format(" - {} v", m_numLoadedVertices);
|
dest += fmt::format(" - {} v", m_numLoadedVertices);
|
||||||
return dest;
|
return dest;
|
||||||
|
@ -200,8 +169,9 @@ public:
|
||||||
{
|
{
|
||||||
ERROR_LOG_FMT(VIDEO,
|
ERROR_LOG_FMT(VIDEO,
|
||||||
"The two vertex loaders have loaded different data "
|
"The two vertex loaders have loaded different data "
|
||||||
"(guru meditation {:#018x}, {:#010x}, {:#010x}, {:#010x}).",
|
"(guru meditation {:#010x}, {:#010x}, {:#010x}, {:#010x}, {:#010x}).",
|
||||||
m_VtxDesc.Hex, m_vat.g0.Hex, m_vat.g1.Hex, m_vat.g2.Hex);
|
m_VtxDesc.low.Hex, m_VtxDesc.high.Hex, m_vat.g0.Hex, m_vat.g1.Hex,
|
||||||
|
m_vat.g2.Hex);
|
||||||
}
|
}
|
||||||
|
|
||||||
memcpy(dst.GetPointer(), buffer_a.data(), count_a * m_native_vtx_decl.stride);
|
memcpy(dst.GetPointer(), buffer_a.data(), count_a * m_native_vtx_decl.stride);
|
||||||
|
|
|
@ -23,8 +23,8 @@ public:
|
||||||
VertexLoaderUID() {}
|
VertexLoaderUID() {}
|
||||||
VertexLoaderUID(const TVtxDesc& vtx_desc, const VAT& vat)
|
VertexLoaderUID(const TVtxDesc& vtx_desc, const VAT& vat)
|
||||||
{
|
{
|
||||||
vid[0] = vtx_desc.Hex & 0xFFFFFFFF;
|
vid[0] = vtx_desc.GetLegacyHex0();
|
||||||
vid[1] = vtx_desc.Hex >> 32;
|
vid[1] = vtx_desc.GetLegacyHex1();
|
||||||
vid[2] = vat.g0.Hex;
|
vid[2] = vat.g0.Hex;
|
||||||
vid[3] = vat.g1.Hex;
|
vid[3] = vat.g1.Hex;
|
||||||
vid[4] = vat.g2.Hex;
|
vid[4] = vat.g2.Hex;
|
||||||
|
|
|
@ -15,9 +15,11 @@
|
||||||
|
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Logging/Log.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
|
|
||||||
#include "VideoCommon/BPMemory.h"
|
#include "VideoCommon/BPMemory.h"
|
||||||
|
#include "VideoCommon/CPMemory.h"
|
||||||
#include "VideoCommon/CommandProcessor.h"
|
#include "VideoCommon/CommandProcessor.h"
|
||||||
#include "VideoCommon/DataReader.h"
|
#include "VideoCommon/DataReader.h"
|
||||||
#include "VideoCommon/IndexGenerator.h"
|
#include "VideoCommon/IndexGenerator.h"
|
||||||
|
@ -75,11 +77,26 @@ void UpdateVertexArrayPointers()
|
||||||
// But the vertex arrays with invalid addresses aren't actually enabled.
|
// But the vertex arrays with invalid addresses aren't actually enabled.
|
||||||
// Note: Only array bases 0 through 11 are used by the Vertex loaders.
|
// Note: Only array bases 0 through 11 are used by the Vertex loaders.
|
||||||
// 12 through 15 are used for loading data into xfmem.
|
// 12 through 15 are used for loading data into xfmem.
|
||||||
for (int i = 0; i < 12; i++)
|
// We also only update the array base if the vertex description states we are going to use it.
|
||||||
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Position))
|
||||||
|
cached_arraybases[ARRAY_POSITION] =
|
||||||
|
Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_POSITION]);
|
||||||
|
|
||||||
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Normal))
|
||||||
|
cached_arraybases[ARRAY_NORMAL] = Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_NORMAL]);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < g_main_cp_state.vtx_desc.low.Color.Size(); i++)
|
||||||
{
|
{
|
||||||
// Only update the array base if the vertex description states we are going to use it.
|
if (IsIndexed(g_main_cp_state.vtx_desc.low.Color[i]))
|
||||||
if (g_main_cp_state.vtx_desc.GetVertexArrayStatus(i) & MASK_INDEXED)
|
cached_arraybases[ARRAY_COLOR + i] =
|
||||||
cached_arraybases[i] = Memory::GetPointer(g_main_cp_state.array_bases[i]);
|
Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_COLOR + i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < g_main_cp_state.vtx_desc.high.TexCoord.Size(); i++)
|
||||||
|
{
|
||||||
|
if (IsIndexed(g_main_cp_state.vtx_desc.high.TexCoord[i]))
|
||||||
|
cached_arraybases[ARRAY_TEXCOORD0 + i] =
|
||||||
|
Memory::GetPointer(g_main_cp_state.array_bases[ARRAY_TEXCOORD0 + i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
g_main_cp_state.bases_dirty = false;
|
g_main_cp_state.bases_dirty = false;
|
||||||
|
@ -276,7 +293,7 @@ int RunVertices(int vtx_attr_group, int primitive, int count, DataReader src, bo
|
||||||
// if cull mode is CULL_ALL, tell VertexManager to skip triangles and quads.
|
// if cull mode is CULL_ALL, tell VertexManager to skip triangles and quads.
|
||||||
// They still need to go through vertex loading, because we need to calculate a zfreeze refrence
|
// They still need to go through vertex loading, because we need to calculate a zfreeze refrence
|
||||||
// slope.
|
// slope.
|
||||||
bool cullall = (bpmem.genMode.cullmode == GenMode::CULL_ALL && primitive < 5);
|
bool cullall = (bpmem.genMode.cullmode == CullMode::All && primitive < 5);
|
||||||
|
|
||||||
DataReader dst = g_vertex_manager->PrepareForAdditionalData(
|
DataReader dst = g_vertex_manager->PrepareForAdditionalData(
|
||||||
primitive, count, loader->m_native_vtx_decl.stride, cullall);
|
primitive, count, loader->m_native_vtx_decl.stride, cullall);
|
||||||
|
@ -302,79 +319,84 @@ void LoadCPReg(u32 sub_cmd, u32 value, bool is_preprocess)
|
||||||
{
|
{
|
||||||
bool update_global_state = !is_preprocess;
|
bool update_global_state = !is_preprocess;
|
||||||
CPState* state = is_preprocess ? &g_preprocess_cp_state : &g_main_cp_state;
|
CPState* state = is_preprocess ? &g_preprocess_cp_state : &g_main_cp_state;
|
||||||
switch (sub_cmd & 0xF0)
|
switch (sub_cmd & CP_COMMAND_MASK)
|
||||||
{
|
{
|
||||||
case 0x30:
|
case MATINDEX_A:
|
||||||
if (update_global_state)
|
if (update_global_state)
|
||||||
VertexShaderManager::SetTexMatrixChangedA(value);
|
VertexShaderManager::SetTexMatrixChangedA(value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x40:
|
case MATINDEX_B:
|
||||||
if (update_global_state)
|
if (update_global_state)
|
||||||
VertexShaderManager::SetTexMatrixChangedB(value);
|
VertexShaderManager::SetTexMatrixChangedB(value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x50:
|
case VCD_LO:
|
||||||
state->vtx_desc.Hex &= ~0x1FFFF; // keep the Upper bits
|
state->vtx_desc.low.Hex = value;
|
||||||
state->vtx_desc.Hex |= value;
|
state->attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
|
||||||
state->attr_dirty = BitSet32::AllTrue(8);
|
|
||||||
state->bases_dirty = true;
|
state->bases_dirty = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x60:
|
case VCD_HI:
|
||||||
state->vtx_desc.Hex &= 0x1FFFF; // keep the lower 17Bits
|
state->vtx_desc.high.Hex = value;
|
||||||
state->vtx_desc.Hex |= (u64)value << 17;
|
state->attr_dirty = BitSet32::AllTrue(CP_NUM_VAT_REG);
|
||||||
state->attr_dirty = BitSet32::AllTrue(8);
|
|
||||||
state->bases_dirty = true;
|
state->bases_dirty = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x70:
|
case CP_VAT_REG_A:
|
||||||
ASSERT((sub_cmd & 0x0F) < 8);
|
if ((sub_cmd - CP_VAT_REG_A) >= CP_NUM_VAT_REG)
|
||||||
state->vtx_attr[sub_cmd & 7].g0.Hex = value;
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_A: Invalid VAT {}", sub_cmd - CP_VAT_REG_A);
|
||||||
state->attr_dirty[sub_cmd & 7] = true;
|
state->vtx_attr[sub_cmd & CP_VAT_MASK].g0.Hex = value;
|
||||||
|
state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x80:
|
case CP_VAT_REG_B:
|
||||||
ASSERT((sub_cmd & 0x0F) < 8);
|
if ((sub_cmd - CP_VAT_REG_B) >= CP_NUM_VAT_REG)
|
||||||
state->vtx_attr[sub_cmd & 7].g1.Hex = value;
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_B: Invalid VAT {}", sub_cmd - CP_VAT_REG_B);
|
||||||
state->attr_dirty[sub_cmd & 7] = true;
|
state->vtx_attr[sub_cmd & CP_VAT_MASK].g1.Hex = value;
|
||||||
|
state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x90:
|
case CP_VAT_REG_C:
|
||||||
ASSERT((sub_cmd & 0x0F) < 8);
|
if ((sub_cmd - CP_VAT_REG_C) >= CP_NUM_VAT_REG)
|
||||||
state->vtx_attr[sub_cmd & 7].g2.Hex = value;
|
WARN_LOG_FMT(VIDEO, "CP_VAT_REG_C: Invalid VAT {}", sub_cmd - CP_VAT_REG_C);
|
||||||
state->attr_dirty[sub_cmd & 7] = true;
|
state->vtx_attr[sub_cmd & CP_VAT_MASK].g2.Hex = value;
|
||||||
|
state->attr_dirty[sub_cmd & CP_VAT_MASK] = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Pointers to vertex arrays in GC RAM
|
// Pointers to vertex arrays in GC RAM
|
||||||
case 0xA0:
|
case ARRAY_BASE:
|
||||||
state->array_bases[sub_cmd & 0xF] = value & CommandProcessor::GetPhysicalAddressMask();
|
state->array_bases[sub_cmd & CP_ARRAY_MASK] =
|
||||||
|
value & CommandProcessor::GetPhysicalAddressMask();
|
||||||
state->bases_dirty = true;
|
state->bases_dirty = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xB0:
|
case ARRAY_STRIDE:
|
||||||
state->array_strides[sub_cmd & 0xF] = value & 0xFF;
|
state->array_strides[sub_cmd & CP_ARRAY_MASK] = value & 0xFF;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
WARN_LOG_FMT(VIDEO, "Unknown CP register {:02x} set to {:08x}", sub_cmd, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FillCPMemoryArray(u32* memory)
|
void FillCPMemoryArray(u32* memory)
|
||||||
{
|
{
|
||||||
memory[0x30] = g_main_cp_state.matrix_index_a.Hex;
|
memory[MATINDEX_A] = g_main_cp_state.matrix_index_a.Hex;
|
||||||
memory[0x40] = g_main_cp_state.matrix_index_b.Hex;
|
memory[MATINDEX_B] = g_main_cp_state.matrix_index_b.Hex;
|
||||||
memory[0x50] = (u32)g_main_cp_state.vtx_desc.Hex;
|
memory[VCD_LO] = g_main_cp_state.vtx_desc.low.Hex;
|
||||||
memory[0x60] = (u32)(g_main_cp_state.vtx_desc.Hex >> 17);
|
memory[VCD_HI] = g_main_cp_state.vtx_desc.high.Hex;
|
||||||
|
|
||||||
for (int i = 0; i < 8; ++i)
|
for (int i = 0; i < CP_NUM_VAT_REG; ++i)
|
||||||
{
|
{
|
||||||
memory[0x70 + i] = g_main_cp_state.vtx_attr[i].g0.Hex;
|
memory[CP_VAT_REG_A + i] = g_main_cp_state.vtx_attr[i].g0.Hex;
|
||||||
memory[0x80 + i] = g_main_cp_state.vtx_attr[i].g1.Hex;
|
memory[CP_VAT_REG_B + i] = g_main_cp_state.vtx_attr[i].g1.Hex;
|
||||||
memory[0x90 + i] = g_main_cp_state.vtx_attr[i].g2.Hex;
|
memory[CP_VAT_REG_C + i] = g_main_cp_state.vtx_attr[i].g2.Hex;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < 16; ++i)
|
for (int i = 0; i < CP_NUM_ARRAYS; ++i)
|
||||||
{
|
{
|
||||||
memory[0xA0 + i] = g_main_cp_state.array_bases[i];
|
memory[ARRAY_BASE + i] = g_main_cp_state.array_bases[i];
|
||||||
memory[0xB0 + i] = g_main_cp_state.array_strides[i];
|
memory[ARRAY_STRIDE + i] = g_main_cp_state.array_strides[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "VideoCommon/VertexLoaderX64.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
@ -15,7 +18,6 @@
|
||||||
#include "Common/x64Emitter.h"
|
#include "Common/x64Emitter.h"
|
||||||
#include "VideoCommon/DataReader.h"
|
#include "VideoCommon/DataReader.h"
|
||||||
#include "VideoCommon/VertexLoaderManager.h"
|
#include "VideoCommon/VertexLoaderManager.h"
|
||||||
#include "VideoCommon/VertexLoaderX64.h"
|
|
||||||
|
|
||||||
using namespace Gen;
|
using namespace Gen;
|
||||||
|
|
||||||
|
@ -55,12 +57,12 @@ VertexLoaderX64::VertexLoaderX64(const TVtxDesc& vtx_desc, const VAT& vtx_att)
|
||||||
JitRegister::Register(region, GetCodePtr(), name.c_str());
|
JitRegister::Register(region, GetCodePtr(), name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
OpArg VertexLoaderX64::GetVertexAddr(int array, u64 attribute)
|
OpArg VertexLoaderX64::GetVertexAddr(int array, VertexComponentFormat attribute)
|
||||||
{
|
{
|
||||||
OpArg data = MDisp(src_reg, m_src_ofs);
|
OpArg data = MDisp(src_reg, m_src_ofs);
|
||||||
if (attribute & MASK_INDEXED)
|
if (IsIndexed(attribute))
|
||||||
{
|
{
|
||||||
int bits = attribute == INDEX8 ? 8 : 16;
|
int bits = attribute == VertexComponentFormat::Index8 ? 8 : 16;
|
||||||
LoadAndSwap(bits, scratch1, data);
|
LoadAndSwap(bits, scratch1, data);
|
||||||
m_src_ofs += bits / 8;
|
m_src_ofs += bits / 8;
|
||||||
if (array == ARRAY_POSITION)
|
if (array == ARRAY_POSITION)
|
||||||
|
@ -78,8 +80,8 @@ OpArg VertexLoaderX64::GetVertexAddr(int array, u64 attribute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count_in, int count_out,
|
int VertexLoaderX64::ReadVertex(OpArg data, VertexComponentFormat attribute, ComponentFormat format,
|
||||||
bool dequantize, u8 scaling_exponent,
|
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
AttributeFormat* native_format)
|
AttributeFormat* native_format)
|
||||||
{
|
{
|
||||||
static const __m128i shuffle_lut[5][3] = {
|
static const __m128i shuffle_lut[5][3] = {
|
||||||
|
@ -115,7 +117,7 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
|
|
||||||
X64Reg coords = XMM0;
|
X64Reg coords = XMM0;
|
||||||
|
|
||||||
int elem_size = 1 << (format / 2);
|
int elem_size = GetElementSize(format);
|
||||||
int load_bytes = elem_size * count_in;
|
int load_bytes = elem_size * count_in;
|
||||||
OpArg dest = MDisp(dst_reg, m_dst_ofs);
|
OpArg dest = MDisp(dst_reg, m_dst_ofs);
|
||||||
|
|
||||||
|
@ -127,7 +129,7 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
|
|
||||||
m_dst_ofs += sizeof(float) * count_out;
|
m_dst_ofs += sizeof(float) * count_out;
|
||||||
|
|
||||||
if (attribute == DIRECT)
|
if (attribute == VertexComponentFormat::Direct)
|
||||||
m_src_ofs += load_bytes;
|
m_src_ofs += load_bytes;
|
||||||
|
|
||||||
if (cpu_info.bSSSE3)
|
if (cpu_info.bSSSE3)
|
||||||
|
@ -139,12 +141,12 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
else
|
else
|
||||||
MOVD_xmm(coords, data);
|
MOVD_xmm(coords, data);
|
||||||
|
|
||||||
PSHUFB(coords, MPIC(&shuffle_lut[format][count_in - 1]));
|
PSHUFB(coords, MPIC(&shuffle_lut[u32(format)][count_in - 1]));
|
||||||
|
|
||||||
// Sign-extend.
|
// Sign-extend.
|
||||||
if (format == FORMAT_BYTE)
|
if (format == ComponentFormat::Byte)
|
||||||
PSRAD(coords, 24);
|
PSRAD(coords, 24);
|
||||||
if (format == FORMAT_SHORT)
|
if (format == ComponentFormat::Short)
|
||||||
PSRAD(coords, 16);
|
PSRAD(coords, 16);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -153,20 +155,20 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
X64Reg temp = XMM1;
|
X64Reg temp = XMM1;
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_UBYTE:
|
case ComponentFormat::UByte:
|
||||||
MOVD_xmm(coords, data);
|
MOVD_xmm(coords, data);
|
||||||
PXOR(temp, R(temp));
|
PXOR(temp, R(temp));
|
||||||
PUNPCKLBW(coords, R(temp));
|
PUNPCKLBW(coords, R(temp));
|
||||||
PUNPCKLWD(coords, R(temp));
|
PUNPCKLWD(coords, R(temp));
|
||||||
break;
|
break;
|
||||||
case FORMAT_BYTE:
|
case ComponentFormat::Byte:
|
||||||
MOVD_xmm(coords, data);
|
MOVD_xmm(coords, data);
|
||||||
PUNPCKLBW(coords, R(coords));
|
PUNPCKLBW(coords, R(coords));
|
||||||
PUNPCKLWD(coords, R(coords));
|
PUNPCKLWD(coords, R(coords));
|
||||||
PSRAD(coords, 24);
|
PSRAD(coords, 24);
|
||||||
break;
|
break;
|
||||||
case FORMAT_USHORT:
|
case ComponentFormat::UShort:
|
||||||
case FORMAT_SHORT:
|
case ComponentFormat::Short:
|
||||||
switch (count_in)
|
switch (count_in)
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
|
@ -185,12 +187,12 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
PSHUFLW(coords, R(coords), 0xAC); // ..Z.Y.X.
|
PSHUFLW(coords, R(coords), 0xAC); // ..Z.Y.X.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (format == FORMAT_SHORT)
|
if (format == ComponentFormat::Short)
|
||||||
PSRAD(coords, 16);
|
PSRAD(coords, 16);
|
||||||
else
|
else
|
||||||
PSRLD(coords, 16);
|
PSRLD(coords, 16);
|
||||||
break;
|
break;
|
||||||
case FORMAT_FLOAT:
|
case ComponentFormat::Float:
|
||||||
// Floats don't need to be scaled or converted,
|
// Floats don't need to be scaled or converted,
|
||||||
// so we can just load/swap/store them directly
|
// so we can just load/swap/store them directly
|
||||||
// and return early.
|
// and return early.
|
||||||
|
@ -231,7 +233,7 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (format != FORMAT_FLOAT)
|
if (format != ComponentFormat::Float)
|
||||||
{
|
{
|
||||||
CVTDQ2PS(coords, R(coords));
|
CVTDQ2PS(coords, R(coords));
|
||||||
|
|
||||||
|
@ -265,22 +267,22 @@ int VertexLoaderX64::ReadVertex(OpArg data, u64 attribute, int format, int count
|
||||||
return load_bytes;
|
return load_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VertexLoaderX64::ReadColor(OpArg data, u64 attribute, int format)
|
void VertexLoaderX64::ReadColor(OpArg data, VertexComponentFormat attribute, ColorFormat format)
|
||||||
{
|
{
|
||||||
int load_bytes = 0;
|
int load_bytes = 0;
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_24B_888:
|
case ColorFormat::RGB888:
|
||||||
case FORMAT_32B_888x:
|
case ColorFormat::RGB888x:
|
||||||
case FORMAT_32B_8888:
|
case ColorFormat::RGBA8888:
|
||||||
MOV(32, R(scratch1), data);
|
MOV(32, R(scratch1), data);
|
||||||
if (format != FORMAT_32B_8888)
|
if (format != ColorFormat::RGBA8888)
|
||||||
OR(32, R(scratch1), Imm32(0xFF000000));
|
OR(32, R(scratch1), Imm32(0xFF000000));
|
||||||
MOV(32, MDisp(dst_reg, m_dst_ofs), R(scratch1));
|
MOV(32, MDisp(dst_reg, m_dst_ofs), R(scratch1));
|
||||||
load_bytes = 3 + (format != FORMAT_24B_888);
|
load_bytes = format == ColorFormat::RGB888 ? 3 : 4;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_16B_565:
|
case ColorFormat::RGB565:
|
||||||
// RRRRRGGG GGGBBBBB
|
// RRRRRGGG GGGBBBBB
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
LoadAndSwap(16, scratch1, data);
|
LoadAndSwap(16, scratch1, data);
|
||||||
|
@ -320,7 +322,7 @@ void VertexLoaderX64::ReadColor(OpArg data, u64 attribute, int format)
|
||||||
load_bytes = 2;
|
load_bytes = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_16B_4444:
|
case ColorFormat::RGBA4444:
|
||||||
// RRRRGGGG BBBBAAAA
|
// RRRRGGGG BBBBAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
LoadAndSwap(16, scratch1, data);
|
LoadAndSwap(16, scratch1, data);
|
||||||
|
@ -348,7 +350,7 @@ void VertexLoaderX64::ReadColor(OpArg data, u64 attribute, int format)
|
||||||
load_bytes = 2;
|
load_bytes = 2;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FORMAT_24B_6666:
|
case ColorFormat::RGBA6666:
|
||||||
// RRRRRRGG GGGGBBBB BBAAAAAA
|
// RRRRRRGG GGGGBBBB BBAAAAAA
|
||||||
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
// AAAAAAAA BBBBBBBB GGGGGGGG RRRRRRRR
|
||||||
data.AddMemOffset(-1); // subtract one from address so we can use a 32bit load and bswap
|
data.AddMemOffset(-1); // subtract one from address so we can use a 32bit load and bswap
|
||||||
|
@ -380,7 +382,7 @@ void VertexLoaderX64::ReadColor(OpArg data, u64 attribute, int format)
|
||||||
load_bytes = 3;
|
load_bytes = 3;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (attribute == DIRECT)
|
if (attribute == VertexComponentFormat::Direct)
|
||||||
m_src_ofs += load_bytes;
|
m_src_ofs += load_bytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -399,14 +401,14 @@ void VertexLoaderX64::GenerateVertexLoader()
|
||||||
|
|
||||||
MOV(64, R(base_reg), R(ABI_PARAM4));
|
MOV(64, R(base_reg), R(ABI_PARAM4));
|
||||||
|
|
||||||
if (m_VtxDesc.Position & MASK_INDEXED)
|
if (IsIndexed(m_VtxDesc.low.Position))
|
||||||
XOR(32, R(skipped_reg), R(skipped_reg));
|
XOR(32, R(skipped_reg), R(skipped_reg));
|
||||||
|
|
||||||
// TODO: load constants into registers outside the main loop
|
// TODO: load constants into registers outside the main loop
|
||||||
|
|
||||||
const u8* loop_start = GetCodePtr();
|
const u8* loop_start = GetCodePtr();
|
||||||
|
|
||||||
if (m_VtxDesc.PosMatIdx)
|
if (m_VtxDesc.low.PosMatIdx)
|
||||||
{
|
{
|
||||||
MOVZX(32, 8, scratch1, MDisp(src_reg, m_src_ofs));
|
MOVZX(32, 8, scratch1, MDisp(src_reg, m_src_ofs));
|
||||||
AND(32, R(scratch1), Imm8(0x3F));
|
AND(32, R(scratch1), Imm8(0x3F));
|
||||||
|
@ -428,51 +430,47 @@ void VertexLoaderX64::GenerateVertexLoader()
|
||||||
m_dst_ofs += sizeof(u32);
|
m_dst_ofs += sizeof(u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 texmatidx_ofs[8];
|
std::array<u32, 8> texmatidx_ofs;
|
||||||
const u64 tm[8] = {
|
for (size_t i = 0; i < m_VtxDesc.low.TexMatIdx.Size(); i++)
|
||||||
m_VtxDesc.Tex0MatIdx, m_VtxDesc.Tex1MatIdx, m_VtxDesc.Tex2MatIdx, m_VtxDesc.Tex3MatIdx,
|
|
||||||
m_VtxDesc.Tex4MatIdx, m_VtxDesc.Tex5MatIdx, m_VtxDesc.Tex6MatIdx, m_VtxDesc.Tex7MatIdx,
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
{
|
||||||
if (tm[i])
|
if (m_VtxDesc.low.TexMatIdx[i])
|
||||||
texmatidx_ofs[i] = m_src_ofs++;
|
texmatidx_ofs[i] = m_src_ofs++;
|
||||||
}
|
}
|
||||||
|
|
||||||
OpArg data = GetVertexAddr(ARRAY_POSITION, m_VtxDesc.Position);
|
OpArg data = GetVertexAddr(ARRAY_POSITION, m_VtxDesc.low.Position);
|
||||||
int pos_elements = 2 + m_VtxAttr.PosElements;
|
int pos_elements = m_VtxAttr.PosElements == CoordComponentCount::XY ? 2 : 3;
|
||||||
ReadVertex(data, m_VtxDesc.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
|
ReadVertex(data, m_VtxDesc.low.Position, m_VtxAttr.PosFormat, pos_elements, pos_elements,
|
||||||
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position);
|
m_VtxAttr.ByteDequant, m_VtxAttr.PosFrac, &m_native_vtx_decl.position);
|
||||||
|
|
||||||
if (m_VtxDesc.Normal)
|
if (m_VtxDesc.low.Normal != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
static const u8 map[8] = {7, 6, 15, 14};
|
static const u8 map[8] = {7, 6, 15, 14};
|
||||||
u8 scaling_exponent = map[m_VtxAttr.NormalFormat];
|
const u8 scaling_exponent = map[u32(m_VtxAttr.NormalFormat)];
|
||||||
|
const int limit = m_VtxAttr.NormalElements == NormalComponentCount::NBT ? 3 : 1;
|
||||||
|
|
||||||
for (int i = 0; i < (m_VtxAttr.NormalElements ? 3 : 1); i++)
|
for (int i = 0; i < limit; i++)
|
||||||
{
|
{
|
||||||
if (!i || m_VtxAttr.NormalIndex3)
|
if (!i || m_VtxAttr.NormalIndex3)
|
||||||
{
|
{
|
||||||
data = GetVertexAddr(ARRAY_NORMAL, m_VtxDesc.Normal);
|
data = GetVertexAddr(ARRAY_NORMAL, m_VtxDesc.low.Normal);
|
||||||
int elem_size = 1 << (m_VtxAttr.NormalFormat / 2);
|
int elem_size = GetElementSize(m_VtxAttr.NormalFormat);
|
||||||
data.AddMemOffset(i * elem_size * 3);
|
data.AddMemOffset(i * elem_size * 3);
|
||||||
}
|
}
|
||||||
data.AddMemOffset(ReadVertex(data, m_VtxDesc.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
|
data.AddMemOffset(ReadVertex(data, m_VtxDesc.low.Normal, m_VtxAttr.NormalFormat, 3, 3, true,
|
||||||
scaling_exponent, &m_native_vtx_decl.normals[i]));
|
scaling_exponent, &m_native_vtx_decl.normals[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
m_native_components |= VB_HAS_NRM0;
|
m_native_components |= VB_HAS_NRM0;
|
||||||
if (m_VtxAttr.NormalElements)
|
if (m_VtxAttr.NormalElements == NormalComponentCount::NBT)
|
||||||
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
m_native_components |= VB_HAS_NRM1 | VB_HAS_NRM2;
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 col[2] = {m_VtxDesc.Color0, m_VtxDesc.Color1};
|
for (size_t i = 0; i < m_VtxDesc.low.Color.Size(); i++)
|
||||||
for (int i = 0; i < 2; i++)
|
|
||||||
{
|
{
|
||||||
if (col[i])
|
if (m_VtxDesc.low.Color[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
data = GetVertexAddr(ARRAY_COLOR + i, col[i]);
|
data = GetVertexAddr(ARRAY_COLOR + int(i), m_VtxDesc.low.Color[i]);
|
||||||
ReadColor(data, col[i], m_VtxAttr.color[i].Comp);
|
ReadColor(data, m_VtxDesc.low.Color[i], m_VtxAttr.color[i].Comp);
|
||||||
m_native_components |= VB_HAS_COL0 << i;
|
m_native_components |= VB_HAS_COL0 << i;
|
||||||
m_native_vtx_decl.colors[i].components = 4;
|
m_native_vtx_decl.colors[i].components = 4;
|
||||||
m_native_vtx_decl.colors[i].enable = true;
|
m_native_vtx_decl.colors[i].enable = true;
|
||||||
|
@ -483,22 +481,19 @@ void VertexLoaderX64::GenerateVertexLoader()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const u64 tc[8] = {
|
for (size_t i = 0; i < m_VtxDesc.high.TexCoord.Size(); i++)
|
||||||
m_VtxDesc.Tex0Coord, m_VtxDesc.Tex1Coord, m_VtxDesc.Tex2Coord, m_VtxDesc.Tex3Coord,
|
|
||||||
m_VtxDesc.Tex4Coord, m_VtxDesc.Tex5Coord, m_VtxDesc.Tex6Coord, m_VtxDesc.Tex7Coord,
|
|
||||||
};
|
|
||||||
for (int i = 0; i < 8; i++)
|
|
||||||
{
|
{
|
||||||
int elements = m_VtxAttr.texCoord[i].Elements + 1;
|
int elements = m_VtxAttr.texCoord[i].Elements == TexComponentCount::ST ? 2 : 1;
|
||||||
if (tc[i])
|
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
data = GetVertexAddr(ARRAY_TEXCOORD0 + i, tc[i]);
|
data = GetVertexAddr(ARRAY_TEXCOORD0 + int(i), m_VtxDesc.high.TexCoord[i]);
|
||||||
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
|
u8 scaling_exponent = m_VtxAttr.texCoord[i].Frac;
|
||||||
ReadVertex(data, tc[i], m_VtxAttr.texCoord[i].Format, elements, tm[i] ? 2 : elements,
|
ReadVertex(data, m_VtxDesc.high.TexCoord[i], m_VtxAttr.texCoord[i].Format, elements,
|
||||||
m_VtxAttr.ByteDequant, scaling_exponent, &m_native_vtx_decl.texcoords[i]);
|
m_VtxDesc.low.TexMatIdx[i] ? 2 : elements, m_VtxAttr.ByteDequant, scaling_exponent,
|
||||||
|
&m_native_vtx_decl.texcoords[i]);
|
||||||
m_native_components |= VB_HAS_UV0 << i;
|
m_native_components |= VB_HAS_UV0 << i;
|
||||||
}
|
}
|
||||||
if (tm[i])
|
if (m_VtxDesc.low.TexMatIdx[i])
|
||||||
{
|
{
|
||||||
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
|
m_native_components |= VB_HAS_TEXMTXIDX0 << i;
|
||||||
m_native_vtx_decl.texcoords[i].components = 3;
|
m_native_vtx_decl.texcoords[i].components = 3;
|
||||||
|
@ -506,7 +501,7 @@ void VertexLoaderX64::GenerateVertexLoader()
|
||||||
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
m_native_vtx_decl.texcoords[i].type = VAR_FLOAT;
|
||||||
m_native_vtx_decl.texcoords[i].integer = false;
|
m_native_vtx_decl.texcoords[i].integer = false;
|
||||||
MOVZX(64, 8, scratch1, MDisp(src_reg, texmatidx_ofs[i]));
|
MOVZX(64, 8, scratch1, MDisp(src_reg, texmatidx_ofs[i]));
|
||||||
if (tc[i])
|
if (m_VtxDesc.high.TexCoord[i] != VertexComponentFormat::NotPresent)
|
||||||
{
|
{
|
||||||
CVTSI2SS(XMM0, R(scratch1));
|
CVTSI2SS(XMM0, R(scratch1));
|
||||||
MOVSS(MDisp(dst_reg, m_dst_ofs), XMM0);
|
MOVSS(MDisp(dst_reg, m_dst_ofs), XMM0);
|
||||||
|
@ -537,7 +532,7 @@ void VertexLoaderX64::GenerateVertexLoader()
|
||||||
|
|
||||||
ABI_PopRegistersAndAdjustStack(regs, 0);
|
ABI_PopRegistersAndAdjustStack(regs, 0);
|
||||||
|
|
||||||
if (m_VtxDesc.Position & MASK_INDEXED)
|
if (IsIndexed(m_VtxDesc.low.Position))
|
||||||
{
|
{
|
||||||
SUB(32, R(ABI_RETURN), R(skipped_reg));
|
SUB(32, R(ABI_RETURN), R(skipped_reg));
|
||||||
RET();
|
RET();
|
||||||
|
|
|
@ -8,6 +8,10 @@
|
||||||
#include "Common/x64Emitter.h"
|
#include "Common/x64Emitter.h"
|
||||||
#include "VideoCommon/VertexLoaderBase.h"
|
#include "VideoCommon/VertexLoaderBase.h"
|
||||||
|
|
||||||
|
enum class VertexComponentFormat;
|
||||||
|
enum class ComponentFormat;
|
||||||
|
enum class ColorFormat;
|
||||||
|
|
||||||
class VertexLoaderX64 : public VertexLoaderBase, public Gen::X64CodeBlock
|
class VertexLoaderX64 : public VertexLoaderBase, public Gen::X64CodeBlock
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -22,9 +26,10 @@ private:
|
||||||
u32 m_src_ofs = 0;
|
u32 m_src_ofs = 0;
|
||||||
u32 m_dst_ofs = 0;
|
u32 m_dst_ofs = 0;
|
||||||
Gen::FixupBranch m_skip_vertex;
|
Gen::FixupBranch m_skip_vertex;
|
||||||
Gen::OpArg GetVertexAddr(int array, u64 attribute);
|
Gen::OpArg GetVertexAddr(int array, VertexComponentFormat attribute);
|
||||||
int ReadVertex(Gen::OpArg data, u64 attribute, int format, int count_in, int count_out,
|
int ReadVertex(Gen::OpArg data, VertexComponentFormat attribute, ComponentFormat format,
|
||||||
bool dequantize, u8 scaling_exponent, AttributeFormat* native_format);
|
int count_in, int count_out, bool dequantize, u8 scaling_exponent,
|
||||||
void ReadColor(Gen::OpArg data, u64 attribute, int format);
|
AttributeFormat* native_format);
|
||||||
|
void ReadColor(Gen::OpArg data, VertexComponentFormat attribute, ColorFormat format);
|
||||||
void GenerateVertexLoader();
|
void GenerateVertexLoader();
|
||||||
};
|
};
|
||||||
|
|
|
@ -230,12 +230,15 @@ constexpr Types InitializeTable()
|
||||||
constexpr Types s_table = InitializeTable();
|
constexpr Types s_table = InitializeTable();
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
u32 VertexLoader_Normal::GetSize(u64 type, u32 format, u32 elements, u32 index3)
|
u32 VertexLoader_Normal::GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
NormalComponentCount elements, u32 index3)
|
||||||
{
|
{
|
||||||
return s_table[type][index3][elements][format].gc_size;
|
return s_table[u32(type)][index3][u32(elements)][u32(format)].gc_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
TPipelineFunction VertexLoader_Normal::GetFunction(u64 type, u32 format, u32 elements, u32 index3)
|
TPipelineFunction VertexLoader_Normal::GetFunction(VertexComponentFormat type,
|
||||||
|
ComponentFormat format,
|
||||||
|
NormalComponentCount elements, u32 index3)
|
||||||
{
|
{
|
||||||
return s_table[type][index3][elements][format].function;
|
return s_table[u32(type)][index3][u32(elements)][u32(format)].function;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/VertexLoader.h"
|
#include "VideoCommon/VertexLoader.h"
|
||||||
|
|
||||||
|
enum class VertexComponentFormat;
|
||||||
|
enum class ComponentFormat;
|
||||||
|
enum class NormalComponentCount;
|
||||||
|
|
||||||
class VertexLoader_Normal
|
class VertexLoader_Normal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static u32 GetSize(u64 type, u32 format, u32 elements, u32 index3);
|
static u32 GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
NormalComponentCount elements, u32 index3);
|
||||||
|
|
||||||
static TPipelineFunction GetFunction(u64 type, u32 format, u32 elements, u32 index3);
|
static TPipelineFunction GetFunction(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
NormalComponentCount elements, u32 index3);
|
||||||
};
|
};
|
||||||
|
|
|
@ -200,12 +200,15 @@ constexpr u32 s_table_read_position_vertex_size[4][8][2] = {
|
||||||
};
|
};
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
u32 VertexLoader_Position::GetSize(u64 type, u32 format, u32 elements)
|
u32 VertexLoader_Position::GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
CoordComponentCount elements)
|
||||||
{
|
{
|
||||||
return s_table_read_position_vertex_size[type][format][elements];
|
return s_table_read_position_vertex_size[u32(type)][u32(format)][u32(elements)];
|
||||||
}
|
}
|
||||||
|
|
||||||
TPipelineFunction VertexLoader_Position::GetFunction(u64 type, u32 format, u32 elements)
|
TPipelineFunction VertexLoader_Position::GetFunction(VertexComponentFormat type,
|
||||||
|
ComponentFormat format,
|
||||||
|
CoordComponentCount elements)
|
||||||
{
|
{
|
||||||
return s_table_read_position[type][format][elements];
|
return s_table_read_position[u32(type)][u32(format)][u32(elements)];
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,10 +7,16 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/VertexLoader.h"
|
#include "VideoCommon/VertexLoader.h"
|
||||||
|
|
||||||
|
enum class VertexComponentFormat;
|
||||||
|
enum class ComponentFormat;
|
||||||
|
enum class CoordComponentCount;
|
||||||
|
|
||||||
class VertexLoader_Position
|
class VertexLoader_Position
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static u32 GetSize(u64 type, u32 format, u32 elements);
|
static u32 GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
CoordComponentCount elements);
|
||||||
|
|
||||||
static TPipelineFunction GetFunction(u64 type, u32 format, u32 elements);
|
static TPipelineFunction GetFunction(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
CoordComponentCount elements);
|
||||||
};
|
};
|
||||||
|
|
|
@ -191,14 +191,17 @@ constexpr u32 s_table_read_tex_coord_vertex_size[4][8][2] = {
|
||||||
};
|
};
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
u32 VertexLoader_TextCoord::GetSize(u64 type, u32 format, u32 elements)
|
u32 VertexLoader_TextCoord::GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
TexComponentCount elements)
|
||||||
{
|
{
|
||||||
return s_table_read_tex_coord_vertex_size[type][format][elements];
|
return s_table_read_tex_coord_vertex_size[u32(type)][u32(format)][u32(elements)];
|
||||||
}
|
}
|
||||||
|
|
||||||
TPipelineFunction VertexLoader_TextCoord::GetFunction(u64 type, u32 format, u32 elements)
|
TPipelineFunction VertexLoader_TextCoord::GetFunction(VertexComponentFormat type,
|
||||||
|
ComponentFormat format,
|
||||||
|
TexComponentCount elements)
|
||||||
{
|
{
|
||||||
return s_table_read_tex_coord[type][format][elements];
|
return s_table_read_tex_coord[u32(type)][u32(format)][u32(elements)];
|
||||||
}
|
}
|
||||||
|
|
||||||
TPipelineFunction VertexLoader_TextCoord::GetDummyFunction()
|
TPipelineFunction VertexLoader_TextCoord::GetDummyFunction()
|
||||||
|
|
|
@ -7,12 +7,18 @@
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "VideoCommon/VertexLoader.h"
|
#include "VideoCommon/VertexLoader.h"
|
||||||
|
|
||||||
|
enum class VertexComponentFormat;
|
||||||
|
enum class ComponentFormat;
|
||||||
|
enum class TexComponentCount;
|
||||||
|
|
||||||
class VertexLoader_TextCoord
|
class VertexLoader_TextCoord
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static u32 GetSize(u64 type, u32 format, u32 elements);
|
static u32 GetSize(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
TexComponentCount elements);
|
||||||
|
|
||||||
static TPipelineFunction GetFunction(u64 type, u32 format, u32 elements);
|
static TPipelineFunction GetFunction(VertexComponentFormat type, ComponentFormat format,
|
||||||
|
TexComponentCount elements);
|
||||||
|
|
||||||
// It is important to synchronize tcIndex.
|
// It is important to synchronize tcIndex.
|
||||||
static TPipelineFunction GetDummyFunction();
|
static TPipelineFunction GetDummyFunction();
|
||||||
|
|
|
@ -408,10 +408,10 @@ void VertexManagerBase::Flush()
|
||||||
for (u32 i = 0; i < xfmem.numTexGen.numTexGens; ++i)
|
for (u32 i = 0; i < xfmem.numTexGen.numTexGens; ++i)
|
||||||
{
|
{
|
||||||
TexMtxInfo tinfo = xfmem.texMtxInfo[i];
|
TexMtxInfo tinfo = xfmem.texMtxInfo[i];
|
||||||
if (tinfo.texgentype != XF_TEXGEN_EMBOSS_MAP)
|
if (tinfo.texgentype != TexGenType::EmbossMap)
|
||||||
tinfo.hex &= 0x7ff;
|
tinfo.hex &= 0x7ff;
|
||||||
if (tinfo.texgentype != XF_TEXGEN_REGULAR)
|
if (tinfo.texgentype != TexGenType::Regular)
|
||||||
tinfo.projection = 0;
|
tinfo.projection = TexSize::ST;
|
||||||
|
|
||||||
PRIM_LOG("txgen{}: proj={}, input={}, gentype={}, srcrow={}, embsrc={}, emblght={}, "
|
PRIM_LOG("txgen{}: proj={}, input={}, gentype={}, srcrow={}, embsrc={}, emblght={}, "
|
||||||
"postmtx={}, postnorm={}",
|
"postmtx={}, postnorm={}",
|
||||||
|
@ -430,7 +430,7 @@ void VertexManagerBase::Flush()
|
||||||
// Track some stats used elsewhere by the anamorphic widescreen heuristic.
|
// Track some stats used elsewhere by the anamorphic widescreen heuristic.
|
||||||
if (!SConfig::GetInstance().bWii)
|
if (!SConfig::GetInstance().bWii)
|
||||||
{
|
{
|
||||||
const bool is_perspective = xfmem.projection.type == GX_PERSPECTIVE;
|
const bool is_perspective = xfmem.projection.type == ProjectionType::Perspective;
|
||||||
|
|
||||||
auto& counts =
|
auto& counts =
|
||||||
is_perspective ? m_flush_statistics.perspective : m_flush_statistics.orthographic;
|
is_perspective ? m_flush_statistics.perspective : m_flush_statistics.orthographic;
|
||||||
|
|
|
@ -39,7 +39,7 @@ VertexShaderUid GetVertexShaderUid()
|
||||||
// first transformation
|
// first transformation
|
||||||
switch (texinfo.texgentype)
|
switch (texinfo.texgentype)
|
||||||
{
|
{
|
||||||
case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map
|
case TexGenType::EmbossMap: // calculate tex coords into bump map
|
||||||
if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0)
|
if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0)
|
||||||
{
|
{
|
||||||
// transform the light dir into tangent space
|
// transform the light dir into tangent space
|
||||||
|
@ -51,18 +51,19 @@ VertexShaderUid GetVertexShaderUid()
|
||||||
texinfo.embosssourceshift = xfmem.texMtxInfo[i].embosssourceshift;
|
texinfo.embosssourceshift = xfmem.texMtxInfo[i].embosssourceshift;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_COLOR_STRGBC0:
|
case TexGenType::Color0:
|
||||||
case XF_TEXGEN_COLOR_STRGBC1:
|
case TexGenType::Color1:
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_REGULAR:
|
case TexGenType::Regular:
|
||||||
default:
|
default:
|
||||||
uid_data->texMtxInfo_n_projection |= xfmem.texMtxInfo[i].projection << i;
|
uid_data->texMtxInfo_n_projection |= static_cast<u32>(xfmem.texMtxInfo[i].projection.Value())
|
||||||
|
<< i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uid_data->dualTexTrans_enabled = xfmem.dualTexTrans.enabled;
|
uid_data->dualTexTrans_enabled = xfmem.dualTexTrans.enabled;
|
||||||
// CHECKME: does this only work for regular tex gen types?
|
// CHECKME: does this only work for regular tex gen types?
|
||||||
if (uid_data->dualTexTrans_enabled && texinfo.texgentype == XF_TEXGEN_REGULAR)
|
if (uid_data->dualTexTrans_enabled && texinfo.texgentype == TexGenType::Regular)
|
||||||
{
|
{
|
||||||
auto& postInfo = uid_data->postMtxInfo[i];
|
auto& postInfo = uid_data->postMtxInfo[i];
|
||||||
postInfo.index = xfmem.postMtxInfo[i].index;
|
postInfo.index = xfmem.postMtxInfo[i].index;
|
||||||
|
@ -297,49 +298,48 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
||||||
out.Write("coord = float4(0.0, 0.0, 1.0, 1.0);\n");
|
out.Write("coord = float4(0.0, 0.0, 1.0, 1.0);\n");
|
||||||
switch (texinfo.sourcerow)
|
switch (texinfo.sourcerow)
|
||||||
{
|
{
|
||||||
case XF_SRCGEOM_INROW:
|
case SourceRow::Geom:
|
||||||
out.Write("coord.xyz = rawpos.xyz;\n");
|
out.Write("coord.xyz = rawpos.xyz;\n");
|
||||||
break;
|
break;
|
||||||
case XF_SRCNORMAL_INROW:
|
case SourceRow::Normal:
|
||||||
if ((uid_data->components & VB_HAS_NRM0) != 0)
|
if ((uid_data->components & VB_HAS_NRM0) != 0)
|
||||||
{
|
{
|
||||||
out.Write("coord.xyz = rawnorm0.xyz;\n");
|
out.Write("coord.xyz = rawnorm0.xyz;\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XF_SRCCOLORS_INROW:
|
case SourceRow::Colors:
|
||||||
ASSERT(texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC0 ||
|
ASSERT(texinfo.texgentype == TexGenType::Color0 || texinfo.texgentype == TexGenType::Color1);
|
||||||
texinfo.texgentype == XF_TEXGEN_COLOR_STRGBC1);
|
|
||||||
break;
|
break;
|
||||||
case XF_SRCBINORMAL_T_INROW:
|
case SourceRow::BinormalT:
|
||||||
if ((uid_data->components & VB_HAS_NRM1) != 0)
|
if ((uid_data->components & VB_HAS_NRM1) != 0)
|
||||||
{
|
{
|
||||||
out.Write("coord.xyz = rawnorm1.xyz;\n");
|
out.Write("coord.xyz = rawnorm1.xyz;\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case XF_SRCBINORMAL_B_INROW:
|
case SourceRow::BinormalB:
|
||||||
if ((uid_data->components & VB_HAS_NRM2) != 0)
|
if ((uid_data->components & VB_HAS_NRM2) != 0)
|
||||||
{
|
{
|
||||||
out.Write("coord.xyz = rawnorm2.xyz;\n");
|
out.Write("coord.xyz = rawnorm2.xyz;\n");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
ASSERT(texinfo.sourcerow <= XF_SRCTEX7_INROW);
|
ASSERT(texinfo.sourcerow >= SourceRow::Tex0 && texinfo.sourcerow <= SourceRow::Tex7);
|
||||||
if ((uid_data->components & (VB_HAS_UV0 << (texinfo.sourcerow - XF_SRCTEX0_INROW))) != 0)
|
u32 texnum = static_cast<u32>(texinfo.sourcerow) - static_cast<u32>(SourceRow::Tex0);
|
||||||
|
if ((uid_data->components & (VB_HAS_UV0 << (texnum))) != 0)
|
||||||
{
|
{
|
||||||
out.Write("coord = float4(rawtex{}.x, rawtex{}.y, 1.0, 1.0);\n",
|
out.Write("coord = float4(rawtex{}.x, rawtex{}.y, 1.0, 1.0);\n", texnum, texnum);
|
||||||
texinfo.sourcerow - XF_SRCTEX0_INROW, texinfo.sourcerow - XF_SRCTEX0_INROW);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
// Input form of AB11 sets z element to 1.0
|
// Input form of AB11 sets z element to 1.0
|
||||||
|
|
||||||
if (texinfo.inputform == XF_TEXINPUT_AB11)
|
if (texinfo.inputform == TexInputForm::AB11)
|
||||||
out.Write("coord.z = 1.0;\n");
|
out.Write("coord.z = 1.0;\n");
|
||||||
|
|
||||||
// first transformation
|
// first transformation
|
||||||
switch (texinfo.texgentype)
|
switch (texinfo.texgentype)
|
||||||
{
|
{
|
||||||
case XF_TEXGEN_EMBOSS_MAP: // calculate tex coords into bump map
|
case TexGenType::EmbossMap: // calculate tex coords into bump map
|
||||||
|
|
||||||
if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0)
|
if ((uid_data->components & (VB_HAS_NRM1 | VB_HAS_NRM2)) != 0)
|
||||||
{
|
{
|
||||||
|
@ -359,18 +359,18 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_COLOR_STRGBC0:
|
case TexGenType::Color0:
|
||||||
out.Write("o.tex{}.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i);
|
out.Write("o.tex{}.xyz = float3(o.colors_0.x, o.colors_0.y, 1);\n", i);
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_COLOR_STRGBC1:
|
case TexGenType::Color1:
|
||||||
out.Write("o.tex{}.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i);
|
out.Write("o.tex{}.xyz = float3(o.colors_1.x, o.colors_1.y, 1);\n", i);
|
||||||
break;
|
break;
|
||||||
case XF_TEXGEN_REGULAR:
|
case TexGenType::Regular:
|
||||||
default:
|
default:
|
||||||
if ((uid_data->components & (VB_HAS_TEXMTXIDX0 << i)) != 0)
|
if ((uid_data->components & (VB_HAS_TEXMTXIDX0 << i)) != 0)
|
||||||
{
|
{
|
||||||
out.Write("int tmp = int(rawtex{}.z);\n", i);
|
out.Write("int tmp = int(rawtex{}.z);\n", i);
|
||||||
if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ)
|
if (static_cast<TexSize>((uid_data->texMtxInfo_n_projection >> i) & 1) == TexSize::STQ)
|
||||||
{
|
{
|
||||||
out.Write("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES
|
out.Write("o.tex{}.xyz = float3(dot(coord, " I_TRANSFORMMATRICES
|
||||||
"[tmp]), dot(coord, " I_TRANSFORMMATRICES
|
"[tmp]), dot(coord, " I_TRANSFORMMATRICES
|
||||||
|
@ -386,7 +386,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (((uid_data->texMtxInfo_n_projection >> i) & 1) == XF_TEXPROJ_STQ)
|
if (static_cast<TexSize>((uid_data->texMtxInfo_n_projection >> i) & 1) == TexSize::STQ)
|
||||||
{
|
{
|
||||||
out.Write("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES
|
out.Write("o.tex{}.xyz = float3(dot(coord, " I_TEXMATRICES
|
||||||
"[{}]), dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES
|
"[{}]), dot(coord, " I_TEXMATRICES "[{}]), dot(coord, " I_TEXMATRICES
|
||||||
|
@ -404,7 +404,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
||||||
}
|
}
|
||||||
|
|
||||||
// CHECKME: does this only work for regular tex gen types?
|
// CHECKME: does this only work for regular tex gen types?
|
||||||
if (uid_data->dualTexTrans_enabled && texinfo.texgentype == XF_TEXGEN_REGULAR)
|
if (uid_data->dualTexTrans_enabled && texinfo.texgentype == TexGenType::Regular)
|
||||||
{
|
{
|
||||||
auto& postInfo = uid_data->postMtxInfo[i];
|
auto& postInfo = uid_data->postMtxInfo[i];
|
||||||
|
|
||||||
|
@ -427,7 +427,7 @@ ShaderCode GenerateVertexShaderCode(APIType api_type, const ShaderHostConfig& ho
|
||||||
// This can be seen in devkitPro's neheGX Lesson08 example for Wii
|
// This can be seen in devkitPro's neheGX Lesson08 example for Wii
|
||||||
// Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling)
|
// Makes differences in Rogue Squadron 3 (Hoth sky) and The Last Story (shadow culling)
|
||||||
// TODO: check if this only affects XF_TEXGEN_REGULAR
|
// TODO: check if this only affects XF_TEXGEN_REGULAR
|
||||||
if (texinfo.texgentype == XF_TEXGEN_REGULAR)
|
if (texinfo.texgentype == TexGenType::Regular)
|
||||||
{
|
{
|
||||||
out.Write(
|
out.Write(
|
||||||
"if(o.tex{0}.z == 0.0f)\n"
|
"if(o.tex{0}.z == 0.0f)\n"
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#include "VideoCommon/ShaderGenCommon.h"
|
#include "VideoCommon/ShaderGenCommon.h"
|
||||||
|
|
||||||
enum class APIType;
|
enum class APIType;
|
||||||
|
enum class TexInputForm : u32;
|
||||||
|
enum class TexGenType : u32;
|
||||||
|
enum class SourceRow : u32;
|
||||||
|
|
||||||
// TODO should be reordered
|
// TODO should be reordered
|
||||||
enum : int
|
enum : int
|
||||||
|
@ -47,9 +50,9 @@ struct vertex_shader_uid_data
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
u32 inputform : 2;
|
TexInputForm inputform : 2;
|
||||||
u32 texgentype : 3;
|
TexGenType texgentype : 3;
|
||||||
u32 sourcerow : 5;
|
SourceRow sourcerow : 5;
|
||||||
u32 embosssourceshift : 3;
|
u32 embosssourceshift : 3;
|
||||||
u32 embosslightshift : 3;
|
u32 embosslightshift : 3;
|
||||||
} texMtxInfo[8];
|
} texMtxInfo[8];
|
||||||
|
|
|
@ -353,7 +353,7 @@ void VertexShaderManager::SetConstants()
|
||||||
|
|
||||||
switch (xfmem.projection.type)
|
switch (xfmem.projection.type)
|
||||||
{
|
{
|
||||||
case GX_PERSPECTIVE:
|
case ProjectionType::Perspective:
|
||||||
{
|
{
|
||||||
const Common::Vec2 fov =
|
const Common::Vec2 fov =
|
||||||
g_freelook_camera.IsActive() ? g_freelook_camera.GetFieldOfView() : Common::Vec2{1, 1};
|
g_freelook_camera.IsActive() ? g_freelook_camera.GetFieldOfView() : Common::Vec2{1, 1};
|
||||||
|
@ -382,7 +382,7 @@ void VertexShaderManager::SetConstants()
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case GX_ORTHOGRAPHIC:
|
case ProjectionType::Orthographic:
|
||||||
{
|
{
|
||||||
g_fProjectionMatrix[0] = rawProjection[0];
|
g_fProjectionMatrix[0] = rawProjection[0];
|
||||||
g_fProjectionMatrix[1] = 0.0f;
|
g_fProjectionMatrix[1] = 0.0f;
|
||||||
|
@ -419,7 +419,7 @@ void VertexShaderManager::SetConstants()
|
||||||
|
|
||||||
auto corrected_matrix = s_viewportCorrection * Common::Matrix44::FromArray(g_fProjectionMatrix);
|
auto corrected_matrix = s_viewportCorrection * Common::Matrix44::FromArray(g_fProjectionMatrix);
|
||||||
|
|
||||||
if (g_freelook_camera.IsActive() && xfmem.projection.type == GX_PERSPECTIVE)
|
if (g_freelook_camera.IsActive() && xfmem.projection.type == ProjectionType::Perspective)
|
||||||
corrected_matrix *= g_freelook_camera.GetView();
|
corrected_matrix *= g_freelook_camera.GetView();
|
||||||
|
|
||||||
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
memcpy(constants.projection.data(), corrected_matrix.data.data(), 4 * sizeof(float4));
|
||||||
|
@ -618,9 +618,9 @@ void VertexShaderManager::SetVertexFormat(u32 components)
|
||||||
|
|
||||||
// The default alpha channel seems to depend on the number of components in the vertex format.
|
// The default alpha channel seems to depend on the number of components in the vertex format.
|
||||||
// If the vertex attribute has an alpha channel, zero is used, otherwise one.
|
// If the vertex attribute has an alpha channel, zero is used, otherwise one.
|
||||||
const u32 color_chan_alpha =
|
const auto g0 = g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0;
|
||||||
(g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0.Color0Elements ^ 1) |
|
const u32 color_chan_alpha = (g0.Color0Elements == ColorComponentCount::RGB ? 1 : 0) |
|
||||||
((g_main_cp_state.vtx_attr[g_main_cp_state.last_id].g0.Color1Elements ^ 1) << 1);
|
(g0.Color1Elements == ColorComponentCount::RGB ? 2 : 0);
|
||||||
if (color_chan_alpha != constants.color_chan_alpha)
|
if (color_chan_alpha != constants.color_chan_alpha)
|
||||||
{
|
{
|
||||||
constants.color_chan_alpha = color_chan_alpha;
|
constants.color_chan_alpha = color_chan_alpha;
|
||||||
|
|
|
@ -4,10 +4,14 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
// X.h defines None to be 0, which causes problems with some of the enums
|
||||||
|
#undef None
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
#include "Common/BitField.h"
|
#include "Common/BitField.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
#include "VideoCommon/CPMemory.h"
|
#include "VideoCommon/CPMemory.h"
|
||||||
|
|
||||||
class DataReader;
|
class DataReader;
|
||||||
|
@ -17,75 +21,164 @@ constexpr size_t NUM_XF_COLOR_CHANNELS = 2;
|
||||||
// Lighting
|
// Lighting
|
||||||
|
|
||||||
// Projection
|
// Projection
|
||||||
enum : u32
|
enum class TexSize : u32
|
||||||
{
|
{
|
||||||
XF_TEXPROJ_ST = 0,
|
ST = 0,
|
||||||
XF_TEXPROJ_STQ = 1
|
STQ = 1
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TexSize> : EnumFormatter<TexSize::STQ>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"ST (2x4 matrix)", "STQ (3x4 matrix)"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Input form
|
// Input form
|
||||||
enum : u32
|
enum class TexInputForm : u32
|
||||||
{
|
{
|
||||||
XF_TEXINPUT_AB11 = 0,
|
AB11 = 0,
|
||||||
XF_TEXINPUT_ABC1 = 1
|
ABC1 = 1
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TexInputForm> : EnumFormatter<TexInputForm::ABC1>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"AB11", "ABC1"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class NormalCount : u32
|
||||||
|
{
|
||||||
|
None = 0,
|
||||||
|
Normals = 1,
|
||||||
|
NormalsBinormals = 2
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<NormalCount> : EnumFormatter<NormalCount::NormalsBinormals>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"None", "Normals only", "Normals and binormals"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Texture generation type
|
// Texture generation type
|
||||||
enum : u32
|
enum class TexGenType : u32
|
||||||
{
|
{
|
||||||
XF_TEXGEN_REGULAR = 0,
|
Regular = 0,
|
||||||
XF_TEXGEN_EMBOSS_MAP = 1, // Used when bump mapping
|
EmbossMap = 1, // Used when bump mapping
|
||||||
XF_TEXGEN_COLOR_STRGBC0 = 2,
|
Color0 = 2,
|
||||||
XF_TEXGEN_COLOR_STRGBC1 = 3
|
Color1 = 3
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TexGenType> : EnumFormatter<TexGenType::Color1>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {
|
||||||
|
"Regular",
|
||||||
|
"Emboss map (used when bump mapping)",
|
||||||
|
"Color channel 0",
|
||||||
|
"Color channel 1",
|
||||||
|
};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Source row
|
// Source row
|
||||||
enum : u32
|
enum class SourceRow : u32
|
||||||
{
|
{
|
||||||
XF_SRCGEOM_INROW = 0, // Input is abc
|
Geom = 0, // Input is abc
|
||||||
XF_SRCNORMAL_INROW = 1, // Input is abc
|
Normal = 1, // Input is abc
|
||||||
XF_SRCCOLORS_INROW = 2,
|
Colors = 2,
|
||||||
XF_SRCBINORMAL_T_INROW = 3, // Input is abc
|
BinormalT = 3, // Input is abc
|
||||||
XF_SRCBINORMAL_B_INROW = 4, // Input is abc
|
BinormalB = 4, // Input is abc
|
||||||
XF_SRCTEX0_INROW = 5,
|
Tex0 = 5,
|
||||||
XF_SRCTEX1_INROW = 6,
|
Tex1 = 6,
|
||||||
XF_SRCTEX2_INROW = 7,
|
Tex2 = 7,
|
||||||
XF_SRCTEX3_INROW = 8,
|
Tex3 = 8,
|
||||||
XF_SRCTEX4_INROW = 9,
|
Tex4 = 9,
|
||||||
XF_SRCTEX5_INROW = 10,
|
Tex5 = 10,
|
||||||
XF_SRCTEX6_INROW = 11,
|
Tex6 = 11,
|
||||||
XF_SRCTEX7_INROW = 12
|
Tex7 = 12
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<SourceRow> : EnumFormatter<SourceRow::Tex7>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {
|
||||||
|
"Geometry (input is ABC1)",
|
||||||
|
"Normal (input is ABC1)",
|
||||||
|
"Colors",
|
||||||
|
"Binormal T (input is ABC1)",
|
||||||
|
"Binormal B (input is ABC1)",
|
||||||
|
"Tex 0",
|
||||||
|
"Tex 1",
|
||||||
|
"Tex 2",
|
||||||
|
"Tex 3",
|
||||||
|
"Tex 4",
|
||||||
|
"Tex 5",
|
||||||
|
"Tex 6",
|
||||||
|
"Tex 7",
|
||||||
|
};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Control source
|
enum class MatSource : u32
|
||||||
enum : u32
|
|
||||||
{
|
{
|
||||||
GX_SRC_REG = 0,
|
MatColorRegister = 0,
|
||||||
GX_SRC_VTX = 1
|
Vertex = 1,
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<MatSource> : EnumFormatter<MatSource::Vertex>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"Material color register", "Vertex color"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AmbSource : u32
|
||||||
|
{
|
||||||
|
AmbColorRegister = 0,
|
||||||
|
Vertex = 1,
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<AmbSource> : EnumFormatter<AmbSource::Vertex>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"Ambient color register", "Vertex color"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Light diffuse attenuation function
|
// Light diffuse attenuation function
|
||||||
enum : u32
|
enum class DiffuseFunc : u32
|
||||||
{
|
{
|
||||||
LIGHTDIF_NONE = 0,
|
None = 0,
|
||||||
LIGHTDIF_SIGN = 1,
|
Sign = 1,
|
||||||
LIGHTDIF_CLAMP = 2
|
Clamp = 2
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<DiffuseFunc> : EnumFormatter<DiffuseFunc::Clamp>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"None", "Sign", "Clamp"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Light attenuation function
|
// Light attenuation function
|
||||||
enum : u32
|
enum class AttenuationFunc : u32
|
||||||
{
|
{
|
||||||
LIGHTATTN_NONE = 0, // No attenuation
|
None = 0, // No attenuation
|
||||||
LIGHTATTN_SPEC = 1, // Point light attenuation
|
Spec = 1, // Point light attenuation
|
||||||
LIGHTATTN_DIR = 2, // Directional light attenuation
|
Dir = 2, // Directional light attenuation
|
||||||
LIGHTATTN_SPOT = 3 // Spot light attenuation
|
Spot = 3 // Spot light attenuation
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<AttenuationFunc> : EnumFormatter<AttenuationFunc::Spot>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {
|
||||||
|
"No attenuation",
|
||||||
|
"Point light attenuation",
|
||||||
|
"Directional light attenuation",
|
||||||
|
"Spot light attenuation",
|
||||||
|
};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Projection type
|
// Projection type
|
||||||
enum : u32
|
enum class ProjectionType : u32
|
||||||
{
|
{
|
||||||
GX_PERSPECTIVE = 0,
|
Perspective = 0,
|
||||||
GX_ORTHOGRAPHIC = 1
|
Orthographic = 1
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<ProjectionType> : EnumFormatter<ProjectionType::Orthographic>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"Perspective", "Orthographic"}) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Registers and register ranges
|
// Registers and register ranges
|
||||||
|
@ -99,6 +192,7 @@ enum
|
||||||
XFMEM_POSTMATRICES_END = 0x600,
|
XFMEM_POSTMATRICES_END = 0x600,
|
||||||
XFMEM_LIGHTS = 0x600,
|
XFMEM_LIGHTS = 0x600,
|
||||||
XFMEM_LIGHTS_END = 0x680,
|
XFMEM_LIGHTS_END = 0x680,
|
||||||
|
XFMEM_REGISTERS_START = 0x1000,
|
||||||
XFMEM_ERROR = 0x1000,
|
XFMEM_ERROR = 0x1000,
|
||||||
XFMEM_DIAG = 0x1001,
|
XFMEM_DIAG = 0x1001,
|
||||||
XFMEM_STATE0 = 0x1002,
|
XFMEM_STATE0 = 0x1002,
|
||||||
|
@ -133,16 +227,17 @@ enum
|
||||||
XFMEM_SETNUMTEXGENS = 0x103f,
|
XFMEM_SETNUMTEXGENS = 0x103f,
|
||||||
XFMEM_SETTEXMTXINFO = 0x1040,
|
XFMEM_SETTEXMTXINFO = 0x1040,
|
||||||
XFMEM_SETPOSTMTXINFO = 0x1050,
|
XFMEM_SETPOSTMTXINFO = 0x1050,
|
||||||
|
XFMEM_REGISTERS_END = 0x1058,
|
||||||
};
|
};
|
||||||
|
|
||||||
union LitChannel
|
union LitChannel
|
||||||
{
|
{
|
||||||
BitField<0, 1, u32> matsource;
|
BitField<0, 1, MatSource> matsource;
|
||||||
BitField<1, 1, u32> enablelighting;
|
BitField<1, 1, bool, u32> enablelighting;
|
||||||
BitField<2, 4, u32> lightMask0_3;
|
BitField<2, 4, u32> lightMask0_3;
|
||||||
BitField<6, 1, u32> ambsource;
|
BitField<6, 1, AmbSource> ambsource;
|
||||||
BitField<7, 2, u32> diffusefunc; // LIGHTDIF_X
|
BitField<7, 2, DiffuseFunc> diffusefunc;
|
||||||
BitField<9, 2, u32> attnfunc; // LIGHTATTN_X
|
BitField<9, 2, AttenuationFunc> attnfunc;
|
||||||
BitField<11, 4, u32> lightMask4_7;
|
BitField<11, 4, u32> lightMask4_7;
|
||||||
u32 hex;
|
u32 hex;
|
||||||
|
|
||||||
|
@ -151,64 +246,126 @@ union LitChannel
|
||||||
return enablelighting ? (lightMask0_3 | (lightMask4_7 << 4)) : 0;
|
return enablelighting ? (lightMask0_3 | (lightMask4_7 << 4)) : 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<LitChannel>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const LitChannel& chan, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(),
|
||||||
|
"Material source: {0}\nEnable lighting: {1}\nLight mask: {2:x} ({2:08b})\n"
|
||||||
|
"Ambient source: {3}\nDiffuse function: {4}\nAttenuation function: {5}",
|
||||||
|
chan.matsource, chan.enablelighting ? "Yes" : "No", chan.GetFullLightMask(),
|
||||||
|
chan.ambsource, chan.diffusefunc, chan.attnfunc);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
union ClipDisable
|
||||||
|
{
|
||||||
|
BitField<0, 1, bool, u32> disable_clipping_detection;
|
||||||
|
BitField<1, 1, bool, u32> disable_trivial_rejection;
|
||||||
|
BitField<2, 1, bool, u32> disable_cpoly_clipping_acceleration;
|
||||||
|
u32 hex;
|
||||||
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<ClipDisable>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const ClipDisable& cd, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(),
|
||||||
|
"Disable clipping detection: {}\n"
|
||||||
|
"Disable trivial rejection: {}\n"
|
||||||
|
"Disable cpoly clipping acceleration: {}",
|
||||||
|
cd.disable_clipping_detection ? "Yes" : "No",
|
||||||
|
cd.disable_trivial_rejection ? "Yes" : "No",
|
||||||
|
cd.disable_cpoly_clipping_acceleration ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
union INVTXSPEC
|
union INVTXSPEC
|
||||||
{
|
{
|
||||||
struct
|
BitField<0, 2, u32> numcolors;
|
||||||
{
|
BitField<2, 2, NormalCount> numnormals;
|
||||||
u32 numcolors : 2;
|
BitField<4, 4, u32> numtextures;
|
||||||
u32 numnormals : 2; // 0 - nothing, 1 - just normal, 2 - normals and binormals
|
|
||||||
u32 numtextures : 4;
|
|
||||||
u32 unused : 24;
|
|
||||||
};
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<INVTXSPEC>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const INVTXSPEC& spec, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(), "Num colors: {}\nNum normals: {}\nNum textures: {}", spec.numcolors,
|
||||||
|
spec.numnormals, spec.numtextures);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
union TexMtxInfo
|
union TexMtxInfo
|
||||||
{
|
{
|
||||||
BitField<0, 1, u32> unknown; //
|
BitField<0, 1, u32> unknown;
|
||||||
BitField<1, 1, u32> projection; // XF_TEXPROJ_X
|
BitField<1, 1, TexSize> projection;
|
||||||
BitField<2, 1, u32> inputform; // XF_TEXINPUT_X
|
BitField<2, 1, TexInputForm> inputform;
|
||||||
BitField<3, 1, u32> unknown2; //
|
BitField<3, 1, u32> unknown2;
|
||||||
BitField<4, 3, u32> texgentype; // XF_TEXGEN_X
|
BitField<4, 3, TexGenType> texgentype;
|
||||||
BitField<7, 5, u32> sourcerow; // XF_SRCGEOM_X
|
BitField<7, 5, SourceRow> sourcerow;
|
||||||
BitField<12, 3, u32> embosssourceshift; // what generated texcoord to use
|
BitField<12, 3, u32> embosssourceshift; // what generated texcoord to use
|
||||||
BitField<15, 3, u32> embosslightshift; // light index that is used
|
BitField<15, 3, u32> embosslightshift; // light index that is used
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TexMtxInfo>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const TexMtxInfo& i, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(),
|
||||||
|
"Projection: {}\nInput form: {}\nTex gen type: {}\n"
|
||||||
|
"Source row: {}\nEmboss source shift: {}\nEmboss light shift: {}",
|
||||||
|
i.projection, i.inputform, i.texgentype, i.sourcerow, i.embosssourceshift,
|
||||||
|
i.embosslightshift);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
union PostMtxInfo
|
union PostMtxInfo
|
||||||
{
|
{
|
||||||
BitField<0, 6, u32> index; // base row of dual transform matrix
|
BitField<0, 6, u32> index; // base row of dual transform matrix
|
||||||
BitField<6, 2, u32> unused; //
|
BitField<6, 2, u32> unused; //
|
||||||
BitField<8, 1, u32> normalize; // normalize before send operation
|
BitField<8, 1, bool, u32> normalize; // normalize before send operation
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<PostMtxInfo>
|
||||||
|
{
|
||||||
|
constexpr auto parse(format_parse_context& ctx) { return ctx.begin(); }
|
||||||
|
template <typename FormatContext>
|
||||||
|
auto format(const PostMtxInfo& i, FormatContext& ctx)
|
||||||
|
{
|
||||||
|
return format_to(ctx.out(), "Index: {}\nNormalize before send operation: {}", i.index,
|
||||||
|
i.normalize ? "Yes" : "No");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
union NumColorChannel
|
union NumColorChannel
|
||||||
{
|
{
|
||||||
struct
|
BitField<0, 2, u32> numColorChans;
|
||||||
{
|
|
||||||
u32 numColorChans : 2;
|
|
||||||
};
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
union NumTexGen
|
union NumTexGen
|
||||||
{
|
{
|
||||||
struct
|
BitField<0, 4, u32> numTexGens;
|
||||||
{
|
|
||||||
u32 numTexGens : 4;
|
|
||||||
};
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
union DualTexInfo
|
union DualTexInfo
|
||||||
{
|
{
|
||||||
struct
|
BitField<0, 1, bool, u32> enabled;
|
||||||
{
|
|
||||||
u32 enabled : 1;
|
|
||||||
};
|
|
||||||
u32 hex;
|
u32 hex;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -250,7 +407,7 @@ struct Projection
|
||||||
using Raw = std::array<float, 6>;
|
using Raw = std::array<float, 6>;
|
||||||
|
|
||||||
Raw rawProjection;
|
Raw rawProjection;
|
||||||
u32 type; // only GX_PERSPECTIVE or GX_ORTHOGRAPHIC are allowed
|
ProjectionType type;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct XFMemory
|
struct XFMemory
|
||||||
|
@ -267,7 +424,7 @@ struct XFMemory
|
||||||
u32 state0; // 0x1002
|
u32 state0; // 0x1002
|
||||||
u32 state1; // 0x1003
|
u32 state1; // 0x1003
|
||||||
u32 xfClock; // 0x1004
|
u32 xfClock; // 0x1004
|
||||||
u32 clipDisable; // 0x1005
|
ClipDisable clipDisable; // 0x1005
|
||||||
u32 perf0; // 0x1006
|
u32 perf0; // 0x1006
|
||||||
u32 perf1; // 0x1007
|
u32 perf1; // 0x1007
|
||||||
INVTXSPEC hostinfo; // 0x1008 number of textures,colors,normals from vertex input
|
INVTXSPEC hostinfo; // 0x1008 number of textures,colors,normals from vertex input
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
// Licensed under GPLv2+
|
// Licensed under GPLv2+
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "VideoCommon/XFStructs.h"
|
||||||
|
|
||||||
|
#include "Common/BitUtils.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
#include "Common/Swap.h"
|
#include "Common/Swap.h"
|
||||||
|
@ -28,7 +31,7 @@ static void XFRegWritten(int transferSize, u32 baseAddress, DataReader src)
|
||||||
u32 address = baseAddress;
|
u32 address = baseAddress;
|
||||||
u32 dataIndex = 0;
|
u32 dataIndex = 0;
|
||||||
|
|
||||||
while (transferSize > 0 && address < 0x1058)
|
while (transferSize > 0 && address < XFMEM_REGISTERS_END)
|
||||||
{
|
{
|
||||||
u32 newValue = src.Peek<u32>(dataIndex * sizeof(u32));
|
u32 newValue = src.Peek<u32>(dataIndex * sizeof(u32));
|
||||||
u32 nextAddress = address + 1;
|
u32 nextAddress = address + 1;
|
||||||
|
@ -93,7 +96,7 @@ static void XFRegWritten(int transferSize, u32 baseAddress, DataReader src)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XFMEM_DUALTEX:
|
case XFMEM_DUALTEX:
|
||||||
if (xfmem.dualTexTrans.enabled != (newValue & 1))
|
if (xfmem.dualTexTrans.enabled != bool(newValue & 1))
|
||||||
g_vertex_manager->Flush();
|
g_vertex_manager->Flush();
|
||||||
VertexShaderManager::SetTexMatrixInfoChanged(-1);
|
VertexShaderManager::SetTexMatrixInfoChanged(-1);
|
||||||
break;
|
break;
|
||||||
|
@ -205,30 +208,30 @@ static void XFRegWritten(int transferSize, u32 baseAddress, DataReader src)
|
||||||
void LoadXFReg(u32 transferSize, u32 baseAddress, DataReader src)
|
void LoadXFReg(u32 transferSize, u32 baseAddress, DataReader src)
|
||||||
{
|
{
|
||||||
// do not allow writes past registers
|
// do not allow writes past registers
|
||||||
if (baseAddress + transferSize > 0x1058)
|
if (baseAddress + transferSize > XFMEM_REGISTERS_END)
|
||||||
{
|
{
|
||||||
WARN_LOG_FMT(VIDEO, "XF load exceeds address space: {:x} {} bytes", baseAddress, transferSize);
|
WARN_LOG_FMT(VIDEO, "XF load exceeds address space: {:x} {} bytes", baseAddress, transferSize);
|
||||||
|
|
||||||
if (baseAddress >= 0x1058)
|
if (baseAddress >= XFMEM_REGISTERS_END)
|
||||||
transferSize = 0;
|
transferSize = 0;
|
||||||
else
|
else
|
||||||
transferSize = 0x1058 - baseAddress;
|
transferSize = XFMEM_REGISTERS_END - baseAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
// write to XF mem
|
// write to XF mem
|
||||||
if (baseAddress < 0x1000 && transferSize > 0)
|
if (baseAddress < XFMEM_REGISTERS_START && transferSize > 0)
|
||||||
{
|
{
|
||||||
u32 end = baseAddress + transferSize;
|
u32 end = baseAddress + transferSize;
|
||||||
|
|
||||||
u32 xfMemBase = baseAddress;
|
u32 xfMemBase = baseAddress;
|
||||||
u32 xfMemTransferSize = transferSize;
|
u32 xfMemTransferSize = transferSize;
|
||||||
|
|
||||||
if (end >= 0x1000)
|
if (end >= XFMEM_REGISTERS_START)
|
||||||
{
|
{
|
||||||
xfMemTransferSize = 0x1000 - baseAddress;
|
xfMemTransferSize = XFMEM_REGISTERS_START - baseAddress;
|
||||||
|
|
||||||
baseAddress = 0x1000;
|
baseAddress = XFMEM_REGISTERS_START;
|
||||||
transferSize = end - 0x1000;
|
transferSize = end - XFMEM_REGISTERS_START;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -300,3 +303,340 @@ void PreprocessIndexedXF(u32 val, int refarray)
|
||||||
const size_t buf_size = size * sizeof(u32);
|
const size_t buf_size = size * sizeof(u32);
|
||||||
Fifo::PushFifoAuxBuffer(new_data, buf_size);
|
Fifo::PushFifoAuxBuffer(new_data, buf_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value)
|
||||||
|
{
|
||||||
|
// Macro to set the register name and make sure it was written correctly via compile time assertion
|
||||||
|
#define RegName(reg) ((void)(reg), #reg)
|
||||||
|
#define DescriptionlessReg(reg) std::make_pair(RegName(reg), "");
|
||||||
|
|
||||||
|
switch (address)
|
||||||
|
{
|
||||||
|
case XFMEM_ERROR:
|
||||||
|
return DescriptionlessReg(XFMEM_ERROR);
|
||||||
|
case XFMEM_DIAG:
|
||||||
|
return DescriptionlessReg(XFMEM_DIAG);
|
||||||
|
case XFMEM_STATE0: // internal state 0
|
||||||
|
return std::make_pair(RegName(XFMEM_STATE0), "internal state 0");
|
||||||
|
case XFMEM_STATE1: // internal state 1
|
||||||
|
return std::make_pair(RegName(XFMEM_STATE1), "internal state 1");
|
||||||
|
case XFMEM_CLOCK:
|
||||||
|
return DescriptionlessReg(XFMEM_CLOCK);
|
||||||
|
case XFMEM_SETGPMETRIC:
|
||||||
|
return DescriptionlessReg(XFMEM_SETGPMETRIC);
|
||||||
|
|
||||||
|
case XFMEM_CLIPDISABLE:
|
||||||
|
return std::make_pair(RegName(XFMEM_CLIPDISABLE), fmt::to_string(ClipDisable{.hex = value}));
|
||||||
|
|
||||||
|
case XFMEM_VTXSPECS:
|
||||||
|
return std::make_pair(RegName(XFMEM_VTXSPECS), fmt::to_string(INVTXSPEC{.hex = value}));
|
||||||
|
|
||||||
|
case XFMEM_SETNUMCHAN:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETNUMCHAN),
|
||||||
|
fmt::format("Number of color channels: {}", value & 3));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XFMEM_SETCHAN0_AMBCOLOR:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN0_AMBCOLOR),
|
||||||
|
fmt::format("Channel 0 Ambient Color: {:06x}", value));
|
||||||
|
case XFMEM_SETCHAN1_AMBCOLOR:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN1_AMBCOLOR),
|
||||||
|
fmt::format("Channel 1 Ambient Color: {:06x}", value));
|
||||||
|
|
||||||
|
case XFMEM_SETCHAN0_MATCOLOR:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN0_MATCOLOR),
|
||||||
|
fmt::format("Channel 0 Material Color: {:06x}", value));
|
||||||
|
case XFMEM_SETCHAN1_MATCOLOR:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN1_MATCOLOR),
|
||||||
|
fmt::format("Channel 1 Material Color: {:06x}", value));
|
||||||
|
|
||||||
|
case XFMEM_SETCHAN0_COLOR: // Channel Color
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN0_COLOR),
|
||||||
|
fmt::format("Channel 0 Color config:\n{}", LitChannel{.hex = value}));
|
||||||
|
case XFMEM_SETCHAN1_COLOR:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN1_COLOR),
|
||||||
|
fmt::format("Channel 1 Color config:\n{}", LitChannel{.hex = value}));
|
||||||
|
case XFMEM_SETCHAN0_ALPHA: // Channel Alpha
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN0_ALPHA),
|
||||||
|
fmt::format("Channel 0 Alpha config:\n{}", LitChannel{.hex = value}));
|
||||||
|
case XFMEM_SETCHAN1_ALPHA:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETCHAN1_ALPHA),
|
||||||
|
fmt::format("Channel 1 Alpha config:\n{}", LitChannel{.hex = value}));
|
||||||
|
|
||||||
|
case XFMEM_DUALTEX:
|
||||||
|
return std::make_pair(RegName(XFMEM_DUALTEX),
|
||||||
|
fmt::format("Dual Tex Trans {}", (value & 1) ? "enabled" : "disabled"));
|
||||||
|
|
||||||
|
case XFMEM_SETMATRIXINDA:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETMATRIXINDA),
|
||||||
|
fmt::format("Matrix index A:\n{}", TMatrixIndexA{.Hex = value}));
|
||||||
|
case XFMEM_SETMATRIXINDB:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETMATRIXINDB),
|
||||||
|
fmt::format("Matrix index B:\n{}", TMatrixIndexB{.Hex = value}));
|
||||||
|
|
||||||
|
case XFMEM_SETVIEWPORT:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 0),
|
||||||
|
fmt::format("Viewport width: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETVIEWPORT + 1:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 1),
|
||||||
|
fmt::format("Viewport height: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETVIEWPORT + 2:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 2),
|
||||||
|
fmt::format("Viewport z range: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETVIEWPORT + 3:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 3),
|
||||||
|
fmt::format("Viewport x origin: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETVIEWPORT + 4:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 4),
|
||||||
|
fmt::format("Viewport y origin: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETVIEWPORT + 5:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETVIEWPORT + 5),
|
||||||
|
fmt::format("Viewport far z: {}", Common::BitCast<float>(value)));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case XFMEM_SETPROJECTION:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 0),
|
||||||
|
fmt::format("Projection[0]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 1:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 1),
|
||||||
|
fmt::format("Projection[1]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 2:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 2),
|
||||||
|
fmt::format("Projection[2]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 3:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 3),
|
||||||
|
fmt::format("Projection[3]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 4:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 4),
|
||||||
|
fmt::format("Projection[4]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 5:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 5),
|
||||||
|
fmt::format("Projection[5]: {}", Common::BitCast<float>(value)));
|
||||||
|
case XFMEM_SETPROJECTION + 6:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETPROJECTION + 6),
|
||||||
|
fmt::to_string(static_cast<ProjectionType>(value)));
|
||||||
|
|
||||||
|
case XFMEM_SETNUMTEXGENS:
|
||||||
|
return std::make_pair(RegName(XFMEM_SETNUMTEXGENS),
|
||||||
|
fmt::format("Number of tex gens: {}", value & 15));
|
||||||
|
|
||||||
|
case XFMEM_SETTEXMTXINFO:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 1:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 2:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 3:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 4:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 5:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 6:
|
||||||
|
case XFMEM_SETTEXMTXINFO + 7:
|
||||||
|
return std::make_pair(
|
||||||
|
fmt::format("XFMEM_SETTEXMTXINFO Matrix {}", address - XFMEM_SETTEXMTXINFO),
|
||||||
|
fmt::to_string(TexMtxInfo{.hex = value}));
|
||||||
|
|
||||||
|
case XFMEM_SETPOSTMTXINFO:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 1:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 2:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 3:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 4:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 5:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 6:
|
||||||
|
case XFMEM_SETPOSTMTXINFO + 7:
|
||||||
|
return std::make_pair(
|
||||||
|
fmt::format("XFMEM_SETPOSTMTXINFO Matrix {}", address - XFMEM_SETPOSTMTXINFO),
|
||||||
|
fmt::to_string(PostMtxInfo{.hex = value}));
|
||||||
|
|
||||||
|
// --------------
|
||||||
|
// Unknown Regs
|
||||||
|
// --------------
|
||||||
|
|
||||||
|
// Maybe these are for Normals?
|
||||||
|
case 0x1048: // xfmem.texcoords[0].nrmmtxinfo.hex = data; break; ??
|
||||||
|
case 0x1049:
|
||||||
|
case 0x104a:
|
||||||
|
case 0x104b:
|
||||||
|
case 0x104c:
|
||||||
|
case 0x104d:
|
||||||
|
case 0x104e:
|
||||||
|
case 0x104f:
|
||||||
|
return std::make_pair(
|
||||||
|
fmt::format("Possible Normal Mtx XF reg?: {:x}={:x}", address, value),
|
||||||
|
"Maybe these are for Normals? xfmem.texcoords[0].nrmmtxinfo.hex = data; break; ??");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 0x1013:
|
||||||
|
case 0x1014:
|
||||||
|
case 0x1015:
|
||||||
|
case 0x1016:
|
||||||
|
case 0x1017:
|
||||||
|
|
||||||
|
default:
|
||||||
|
return std::make_pair(fmt::format("Unknown XF Reg: {:x}={:x}", address, value), "");
|
||||||
|
}
|
||||||
|
#undef RegName
|
||||||
|
#undef DescriptionlessReg
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetXFMemName(u32 address)
|
||||||
|
{
|
||||||
|
if (address >= XFMEM_POSMATRICES && address < XFMEM_POSMATRICES_END)
|
||||||
|
{
|
||||||
|
const u32 row = (address - XFMEM_POSMATRICES) / 4;
|
||||||
|
const u32 col = (address - XFMEM_POSMATRICES) % 4;
|
||||||
|
return fmt::format("Position matrix row {:2d} col {:2d}", row, col);
|
||||||
|
}
|
||||||
|
else if (address >= XFMEM_NORMALMATRICES && address < XFMEM_NORMALMATRICES_END)
|
||||||
|
{
|
||||||
|
const u32 row = (address - XFMEM_NORMALMATRICES) / 3;
|
||||||
|
const u32 col = (address - XFMEM_NORMALMATRICES) % 3;
|
||||||
|
return fmt::format("Normal matrix row {:2d} col {:2d}", row, col);
|
||||||
|
}
|
||||||
|
else if (address >= XFMEM_POSTMATRICES && address < XFMEM_POSTMATRICES_END)
|
||||||
|
{
|
||||||
|
const u32 row = (address - XFMEM_POSMATRICES) / 4;
|
||||||
|
const u32 col = (address - XFMEM_POSMATRICES) % 4;
|
||||||
|
return fmt::format("Post matrix row {:2d} col {:2d}", row, col);
|
||||||
|
}
|
||||||
|
else if (address >= XFMEM_LIGHTS && address < XFMEM_LIGHTS_END)
|
||||||
|
{
|
||||||
|
const u32 light = (address - XFMEM_LIGHTS) / 16;
|
||||||
|
const u32 offset = (address - XFMEM_LIGHTS) % 16;
|
||||||
|
switch (offset)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
return fmt::format("Light {} unused param {}", light, offset);
|
||||||
|
case 3:
|
||||||
|
return fmt::format("Light {} color", light);
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
case 6:
|
||||||
|
return fmt::format("Light {} cosine attenuation {}", light, offset - 4);
|
||||||
|
case 7:
|
||||||
|
case 8:
|
||||||
|
case 9:
|
||||||
|
return fmt::format("Light {} distance attenuation {}", light, offset - 7);
|
||||||
|
case 10:
|
||||||
|
case 11:
|
||||||
|
case 12:
|
||||||
|
// Yagcd says light pos or "inf ldir", while dolphin has a union for dpos and sdir with only
|
||||||
|
// dpos being used nowadays. As far as I can tell only the DX9 engine once at
|
||||||
|
// Source/Plugins/Plugin_VideoDX9/Src/TransformEngine.cpp used sdir directly...
|
||||||
|
return fmt::format("Light {0} {1} position or inf ldir {1}", light, "xyz"[offset - 10]);
|
||||||
|
case 13:
|
||||||
|
case 14:
|
||||||
|
case 15:
|
||||||
|
// Yagcd says light dir or "1/2 angle", dolphin has union for ddir or shalfangle.
|
||||||
|
// It would make sense if d stood for direction and s for specular, but it's ddir and
|
||||||
|
// shalfhangle that have the comment "specular lights only", both at the same offset,
|
||||||
|
// while dpos and sdir have none...
|
||||||
|
return fmt::format("Light {0} {1} direction or half hangle {1}", light, "xyz"[offset - 13]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return fmt::format("Unknown memory {:04x}", address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string GetXFMemDescription(u32 address, u32 value)
|
||||||
|
{
|
||||||
|
if ((address >= XFMEM_POSMATRICES && address < XFMEM_POSMATRICES_END) ||
|
||||||
|
(address >= XFMEM_NORMALMATRICES && address < XFMEM_NORMALMATRICES_END) ||
|
||||||
|
(address >= XFMEM_POSTMATRICES && address < XFMEM_POSTMATRICES_END))
|
||||||
|
{
|
||||||
|
// The matrices all use floats
|
||||||
|
return fmt::format("{} = {}", GetXFMemName(address), Common::BitCast<float>(value));
|
||||||
|
}
|
||||||
|
else if (address >= XFMEM_LIGHTS && address < XFMEM_LIGHTS_END)
|
||||||
|
{
|
||||||
|
// Each light is 16 words; for this function we don't care which light it is
|
||||||
|
const u32 offset = (address - XFMEM_LIGHTS) % 16;
|
||||||
|
if (offset <= 3)
|
||||||
|
{
|
||||||
|
// The unused parameters (0, 1, 2) and the color (3) should be hex-formatted
|
||||||
|
return fmt::format("{} = {:08x}", GetXFMemName(address), value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Everything else is a float
|
||||||
|
return fmt::format("{} = {}", GetXFMemName(address), Common::BitCast<float>(value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Unknown address
|
||||||
|
return fmt::format("{} = {:08x}", GetXFMemName(address), value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> GetXFTransferInfo(const u8* data)
|
||||||
|
{
|
||||||
|
const u32 cmd = Common::swap32(data);
|
||||||
|
data += 4;
|
||||||
|
u32 base_address = cmd & 0xFFFF;
|
||||||
|
const u32 transfer_size = ((cmd >> 16) & 15) + 1;
|
||||||
|
|
||||||
|
if (base_address > XFMEM_REGISTERS_END)
|
||||||
|
{
|
||||||
|
return std::make_pair("Invalid XF Transfer", "Base address past end of address space");
|
||||||
|
}
|
||||||
|
else if (transfer_size == 1 && base_address >= XFMEM_REGISTERS_START)
|
||||||
|
{
|
||||||
|
// Write directly to a single register
|
||||||
|
const u32 value = Common::swap32(data);
|
||||||
|
return GetXFRegInfo(base_address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// More complicated cases
|
||||||
|
fmt::memory_buffer name, desc;
|
||||||
|
u32 end_address = base_address + transfer_size; // exclusive
|
||||||
|
|
||||||
|
// do not allow writes past registers
|
||||||
|
if (end_address > XFMEM_REGISTERS_END)
|
||||||
|
{
|
||||||
|
fmt::format_to(name, "Invalid XF Transfer ");
|
||||||
|
fmt::format_to(desc, "Transfer ends past end of address space\n\n");
|
||||||
|
end_address = XFMEM_REGISTERS_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to XF mem
|
||||||
|
if (base_address < XFMEM_REGISTERS_START)
|
||||||
|
{
|
||||||
|
const u32 xf_mem_base = base_address;
|
||||||
|
u32 xf_mem_transfer_size = transfer_size;
|
||||||
|
|
||||||
|
if (end_address > XFMEM_REGISTERS_START)
|
||||||
|
{
|
||||||
|
xf_mem_transfer_size = XFMEM_REGISTERS_START - base_address;
|
||||||
|
base_address = XFMEM_REGISTERS_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt::format_to(name, "Write {} XF mem words at {:04x}", xf_mem_transfer_size, xf_mem_base);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < xf_mem_transfer_size; i++)
|
||||||
|
{
|
||||||
|
const auto mem_desc = GetXFMemDescription(xf_mem_base + i, Common::swap32(data));
|
||||||
|
fmt::format_to(desc, i == 0 ? "{}" : "\n{}", mem_desc);
|
||||||
|
data += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end_address > XFMEM_REGISTERS_START)
|
||||||
|
fmt::format_to(name, "; ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// write to XF regs
|
||||||
|
if (base_address >= XFMEM_REGISTERS_START)
|
||||||
|
{
|
||||||
|
fmt::format_to(name, "Write {} XF regs at {:04x}", end_address - base_address, base_address);
|
||||||
|
|
||||||
|
for (u32 address = base_address; address < end_address; address++)
|
||||||
|
{
|
||||||
|
const u32 value = Common::swap32(data);
|
||||||
|
|
||||||
|
const auto [regname, regdesc] = GetXFRegInfo(address, value);
|
||||||
|
fmt::format_to(desc, "{}\n{}\n", regname, regdesc);
|
||||||
|
|
||||||
|
data += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_pair(fmt::to_string(name), fmt::to_string(desc));
|
||||||
|
}
|
||||||
|
|
|
@ -4,4 +4,12 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
#include "VideoCommon/XFMemory.h"
|
#include "VideoCommon/XFMemory.h"
|
||||||
|
|
||||||
|
std::pair<std::string, std::string> GetXFRegInfo(u32 address, u32 value);
|
||||||
|
std::string GetXFMemName(u32 address);
|
||||||
|
std::string GetXFMemDescription(u32 address, u32 value);
|
||||||
|
std::pair<std::string, std::string> GetXFTransferInfo(const u8* data);
|
||||||
|
|
|
@ -6,6 +6,15 @@
|
||||||
|
|
||||||
#include "Common/BitField.h"
|
#include "Common/BitField.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
|
|
||||||
|
enum class TestEnum : u64
|
||||||
|
{
|
||||||
|
A,
|
||||||
|
B,
|
||||||
|
C,
|
||||||
|
D,
|
||||||
|
};
|
||||||
|
|
||||||
union TestUnion
|
union TestUnion
|
||||||
{
|
{
|
||||||
|
@ -21,6 +30,11 @@ union TestUnion
|
||||||
BitField<30, 4, s64> at_dword_boundary; // goes over the boundary of two u32 values
|
BitField<30, 4, s64> at_dword_boundary; // goes over the boundary of two u32 values
|
||||||
|
|
||||||
BitField<15, 1, s64> signed_1bit; // allowed values: -1 and 0
|
BitField<15, 1, s64> signed_1bit; // allowed values: -1 and 0
|
||||||
|
|
||||||
|
BitField<63, 1, bool, u64> flag;
|
||||||
|
|
||||||
|
BitField<16, 2, TestEnum> enum_1;
|
||||||
|
BitField<48, 2, TestEnum> enum_2;
|
||||||
};
|
};
|
||||||
|
|
||||||
// table of raw numbers to test with
|
// table of raw numbers to test with
|
||||||
|
@ -51,6 +65,9 @@ TEST(BitField, Storage)
|
||||||
EXPECT_EQ(sizeof(TestUnion), sizeof(object.regular_field_signed));
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.regular_field_signed));
|
||||||
EXPECT_EQ(sizeof(TestUnion), sizeof(object.at_dword_boundary));
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.at_dword_boundary));
|
||||||
EXPECT_EQ(sizeof(TestUnion), sizeof(object.signed_1bit));
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.signed_1bit));
|
||||||
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.flag));
|
||||||
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.enum_1));
|
||||||
|
EXPECT_EQ(sizeof(TestUnion), sizeof(object.enum_2));
|
||||||
|
|
||||||
// Now write some values to one field and check if this reflects properly
|
// Now write some values to one field and check if this reflects properly
|
||||||
// in the others.
|
// in the others.
|
||||||
|
@ -82,6 +99,9 @@ TEST(BitField, Read)
|
||||||
EXPECT_EQ(object.regular_field_signed, (s64)object.regular_field_signed);
|
EXPECT_EQ(object.regular_field_signed, (s64)object.regular_field_signed);
|
||||||
EXPECT_EQ(object.at_dword_boundary, (s64)object.at_dword_boundary);
|
EXPECT_EQ(object.at_dword_boundary, (s64)object.at_dword_boundary);
|
||||||
EXPECT_EQ(object.signed_1bit, (s64)object.signed_1bit);
|
EXPECT_EQ(object.signed_1bit, (s64)object.signed_1bit);
|
||||||
|
EXPECT_EQ(object.flag, (bool)object.flag);
|
||||||
|
EXPECT_EQ(object.enum_1, static_cast<TestEnum>(object.enum_1));
|
||||||
|
EXPECT_EQ(object.enum_2, static_cast<TestEnum>(object.enum_2));
|
||||||
|
|
||||||
// Now make sure the value is indeed correct
|
// Now make sure the value is indeed correct
|
||||||
EXPECT_EQ(val, object.full_u64);
|
EXPECT_EQ(val, object.full_u64);
|
||||||
|
@ -91,6 +111,9 @@ TEST(BitField, Read)
|
||||||
EXPECT_EQ(((s64)(object.hex << 52)) >> 61, object.regular_field_signed);
|
EXPECT_EQ(((s64)(object.hex << 52)) >> 61, object.regular_field_signed);
|
||||||
EXPECT_EQ(((s64)(object.hex << 30)) >> 60, object.at_dword_boundary);
|
EXPECT_EQ(((s64)(object.hex << 30)) >> 60, object.at_dword_boundary);
|
||||||
EXPECT_EQ(((object.hex >> 15) & 1) ? -1 : 0, object.signed_1bit);
|
EXPECT_EQ(((object.hex >> 15) & 1) ? -1 : 0, object.signed_1bit);
|
||||||
|
EXPECT_EQ((bool)object.flag, ((object.hex >> 63) & 1));
|
||||||
|
EXPECT_EQ(static_cast<TestEnum>((object.hex >> 16) & 3), object.enum_1);
|
||||||
|
EXPECT_EQ(static_cast<TestEnum>((object.hex >> 48) & 3), object.enum_2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +145,10 @@ TEST(BitField, Assignment)
|
||||||
// Assignment from other BitField
|
// Assignment from other BitField
|
||||||
object.at_dword_boundary = object.regular_field_signed;
|
object.at_dword_boundary = object.regular_field_signed;
|
||||||
EXPECT_EQ(object.regular_field_signed, object.at_dword_boundary);
|
EXPECT_EQ(object.regular_field_signed, object.at_dword_boundary);
|
||||||
|
|
||||||
|
// Assignment to field of a type with a size smaller than the underlying type
|
||||||
|
object.flag = (val & 2);
|
||||||
|
EXPECT_EQ(object.flag, (val & 2) != 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,5 +192,354 @@ TEST(BitField, Alignment)
|
||||||
// Assignment from other BitField
|
// Assignment from other BitField
|
||||||
object.at_dword_boundary = object.regular_field_signed;
|
object.at_dword_boundary = object.regular_field_signed;
|
||||||
EXPECT_EQ(object.regular_field_signed, object.at_dword_boundary);
|
EXPECT_EQ(object.regular_field_signed, object.at_dword_boundary);
|
||||||
|
|
||||||
|
// Assignment to field of a type with a size smaller than the underlying type
|
||||||
|
object.flag = (val & 2);
|
||||||
|
EXPECT_EQ(object.flag, (val & 2) != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<TestEnum> : EnumFormatter<TestEnum::D>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"A", "B", "C", "D"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test behavior of using BitFields with fmt
|
||||||
|
TEST(BitField, Fmt)
|
||||||
|
{
|
||||||
|
TestUnion object;
|
||||||
|
|
||||||
|
for (u64 val : table)
|
||||||
|
{
|
||||||
|
object.hex = val;
|
||||||
|
|
||||||
|
// Formatting the BitField should be the same as formatting its value
|
||||||
|
EXPECT_EQ(fmt::to_string(object.full_u64), fmt::to_string(object.full_u64.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.full_s64), fmt::to_string(object.full_s64.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.regular_field_unsigned),
|
||||||
|
fmt::to_string(object.regular_field_unsigned.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.regular_field_unsigned2),
|
||||||
|
fmt::to_string(object.regular_field_unsigned2.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.regular_field_signed),
|
||||||
|
fmt::to_string(object.regular_field_signed.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.at_dword_boundary),
|
||||||
|
fmt::to_string(object.at_dword_boundary.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.signed_1bit), fmt::to_string(object.signed_1bit.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.flag), fmt::to_string(object.flag.Value()));
|
||||||
|
// The custom enum formatter should be used properly.
|
||||||
|
EXPECT_EQ(fmt::to_string(object.enum_1), fmt::to_string(object.enum_1.Value()));
|
||||||
|
EXPECT_EQ(fmt::to_string(object.enum_2), fmt::to_string(object.enum_2.Value()));
|
||||||
|
|
||||||
|
// Formatting the BitField should respect the format spec
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.full_u64),
|
||||||
|
fmt::format("{:02x}", object.full_u64.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.full_s64),
|
||||||
|
fmt::format("{:02x}", object.full_s64.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.regular_field_unsigned),
|
||||||
|
fmt::format("{:02x}", object.regular_field_unsigned.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.regular_field_unsigned2),
|
||||||
|
fmt::format("{:02x}", object.regular_field_unsigned2.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.regular_field_signed),
|
||||||
|
fmt::format("{:02x}", object.regular_field_signed.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.at_dword_boundary),
|
||||||
|
fmt::format("{:02x}", object.at_dword_boundary.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.signed_1bit),
|
||||||
|
fmt::format("{:02x}", object.signed_1bit.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:02x}", object.flag), fmt::format("{:02x}", object.flag.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", object.enum_1), fmt::format("{:s}", object.enum_1.Value()));
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", object.enum_2), fmt::format("{:s}", object.enum_2.Value()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
union TestUnion2
|
||||||
|
{
|
||||||
|
u32 hex;
|
||||||
|
BitField<0, 2, u32> a;
|
||||||
|
BitField<2, 2, u32> b;
|
||||||
|
BitField<4, 2, u32> c;
|
||||||
|
BitFieldArray<0, 2, 3, u32> arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BitFieldArray, Unsigned)
|
||||||
|
{
|
||||||
|
TestUnion2 object;
|
||||||
|
object.hex = 0;
|
||||||
|
const TestUnion2& objectc = object;
|
||||||
|
|
||||||
|
for (u32 value : object.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, 0u);
|
||||||
|
}
|
||||||
|
|
||||||
|
object.arr[0] = 2;
|
||||||
|
EXPECT_EQ(object.arr[0], 2u);
|
||||||
|
EXPECT_EQ(object.a, 2u);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'00'10u);
|
||||||
|
|
||||||
|
object.arr[1] = 3;
|
||||||
|
EXPECT_EQ(object.arr[1], 3u);
|
||||||
|
EXPECT_EQ(object.b, 3u);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'11'10u);
|
||||||
|
|
||||||
|
object.arr[2] = object.arr[1];
|
||||||
|
EXPECT_EQ(object.arr[2], 3u);
|
||||||
|
EXPECT_EQ(object.c, 3u);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'11'10u);
|
||||||
|
|
||||||
|
object.arr[1] = objectc.arr[0];
|
||||||
|
EXPECT_EQ(object.arr[1], 2u);
|
||||||
|
EXPECT_EQ(object.b, 2u);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'10'10u);
|
||||||
|
|
||||||
|
for (auto ref : object.arr)
|
||||||
|
{
|
||||||
|
ref = 1;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.a, 1u);
|
||||||
|
EXPECT_EQ(object.b, 1u);
|
||||||
|
EXPECT_EQ(object.c, 1u);
|
||||||
|
EXPECT_EQ(object.hex, 0b01'01'01u);
|
||||||
|
|
||||||
|
std::fill_n(object.arr.begin(), object.arr.Size(), 3);
|
||||||
|
EXPECT_EQ(object.arr[0], 3u);
|
||||||
|
EXPECT_EQ(object.arr[1], 3u);
|
||||||
|
EXPECT_EQ(object.arr[2], 3u);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'11'11u);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < object.arr.Size(); i++)
|
||||||
|
{
|
||||||
|
object.arr[i] = i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.hex, 0b10'01'00u);
|
||||||
|
|
||||||
|
EXPECT_EQ(objectc.arr[0], 0u);
|
||||||
|
EXPECT_EQ(objectc.arr[1], 1u);
|
||||||
|
EXPECT_EQ(objectc.arr[2], 2u);
|
||||||
|
|
||||||
|
u32 counter = 0;
|
||||||
|
for (u32 value : objectc.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, counter);
|
||||||
|
counter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ("[0, 1, 2]", fmt::format("[{}]", fmt::join(object.arr, ", ")));
|
||||||
|
EXPECT_EQ("[0b00, 0b01, 0b10]", fmt::format("[{:#04b}]", fmt::join(object.arr, ", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
union TestUnion3
|
||||||
|
{
|
||||||
|
s32 hex;
|
||||||
|
BitField<5, 2, s32> a;
|
||||||
|
BitField<7, 2, s32> b;
|
||||||
|
BitField<9, 2, s32> c;
|
||||||
|
BitFieldArray<5, 2, 3, s32> arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BitFieldArray, Signed)
|
||||||
|
{
|
||||||
|
TestUnion3 object;
|
||||||
|
object.hex = 0;
|
||||||
|
const TestUnion3& objectc = object;
|
||||||
|
|
||||||
|
for (s32 value : object.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
object.arr[0] = -2;
|
||||||
|
EXPECT_EQ(object.arr[0], -2);
|
||||||
|
EXPECT_EQ(object.a, -2);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'00'10'00000);
|
||||||
|
|
||||||
|
object.arr[1] = -1;
|
||||||
|
EXPECT_EQ(object.arr[1], -1);
|
||||||
|
EXPECT_EQ(object.b, -1);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'11'10'00000);
|
||||||
|
|
||||||
|
object.arr[2] = object.arr[1];
|
||||||
|
EXPECT_EQ(object.arr[2], -1);
|
||||||
|
EXPECT_EQ(object.c, -1);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'11'10'00000);
|
||||||
|
|
||||||
|
object.arr[1] = objectc.arr[0];
|
||||||
|
EXPECT_EQ(object.arr[1], -2);
|
||||||
|
EXPECT_EQ(object.b, -2);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'10'10'00000);
|
||||||
|
|
||||||
|
for (auto ref : object.arr)
|
||||||
|
{
|
||||||
|
ref = 1;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.a, 1);
|
||||||
|
EXPECT_EQ(object.b, 1);
|
||||||
|
EXPECT_EQ(object.c, 1);
|
||||||
|
EXPECT_EQ(object.hex, 0b01'01'01'00000);
|
||||||
|
|
||||||
|
std::fill_n(object.arr.begin(), object.arr.Size(), -1);
|
||||||
|
EXPECT_EQ(object.arr[0], -1);
|
||||||
|
EXPECT_EQ(object.arr[1], -1);
|
||||||
|
EXPECT_EQ(object.arr[2], -1);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'11'11'00000);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < object.arr.Size(); i++)
|
||||||
|
{
|
||||||
|
object.arr[i] = i;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.hex, 0b10'01'00'00000);
|
||||||
|
|
||||||
|
EXPECT_EQ(objectc.arr[0], 0);
|
||||||
|
EXPECT_EQ(objectc.arr[1], 1);
|
||||||
|
EXPECT_EQ(objectc.arr[2], -2);
|
||||||
|
|
||||||
|
u32 counter = 0;
|
||||||
|
for (s32 value : objectc.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr[counter++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ("[0, 1, -2]", fmt::format("[{}]", fmt::join(object.arr, ", ")));
|
||||||
|
EXPECT_EQ("[+0b00, +0b01, -0b10]", fmt::format("[{:+#05b}]", fmt::join(object.arr, ", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
union TestUnion4
|
||||||
|
{
|
||||||
|
u64 hex;
|
||||||
|
BitField<30, 2, TestEnum> a;
|
||||||
|
BitField<32, 2, TestEnum> b;
|
||||||
|
BitField<34, 2, TestEnum> c;
|
||||||
|
BitField<36, 2, TestEnum> d;
|
||||||
|
BitFieldArray<30, 2, 4, TestEnum> arr;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BitFieldArray, Enum)
|
||||||
|
{
|
||||||
|
TestUnion4 object;
|
||||||
|
object.hex = 0;
|
||||||
|
const TestUnion4& objectc = object;
|
||||||
|
|
||||||
|
for (TestEnum value : object.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, TestEnum::A);
|
||||||
|
}
|
||||||
|
|
||||||
|
object.arr[0] = TestEnum::B;
|
||||||
|
EXPECT_EQ(object.arr[0], TestEnum::B);
|
||||||
|
EXPECT_EQ(object.a, TestEnum::B);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'00'00'01ull << 30);
|
||||||
|
|
||||||
|
object.arr[1] = TestEnum::C;
|
||||||
|
EXPECT_EQ(object.arr[1], TestEnum::C);
|
||||||
|
EXPECT_EQ(object.b, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'00'10'01ull << 30);
|
||||||
|
|
||||||
|
object.arr[2] = object.arr[1];
|
||||||
|
EXPECT_EQ(object.arr[2], TestEnum::C);
|
||||||
|
EXPECT_EQ(object.c, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.hex, 0b00'10'10'01ull << 30);
|
||||||
|
|
||||||
|
object.arr[3] = objectc.arr[0];
|
||||||
|
EXPECT_EQ(object.arr[3], TestEnum::B);
|
||||||
|
EXPECT_EQ(object.d, TestEnum::B);
|
||||||
|
EXPECT_EQ(object.hex, 0b01'10'10'01ull << 30);
|
||||||
|
|
||||||
|
for (auto ref : object.arr)
|
||||||
|
{
|
||||||
|
ref = TestEnum::D;
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.a, TestEnum::D);
|
||||||
|
EXPECT_EQ(object.b, TestEnum::D);
|
||||||
|
EXPECT_EQ(object.c, TestEnum::D);
|
||||||
|
EXPECT_EQ(object.d, TestEnum::D);
|
||||||
|
EXPECT_EQ(object.hex, 0b11'11'11'11ull << 30);
|
||||||
|
|
||||||
|
std::fill_n(object.arr.begin(), object.arr.Size(), TestEnum::C);
|
||||||
|
EXPECT_EQ(object.a, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.b, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.c, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.d, TestEnum::C);
|
||||||
|
EXPECT_EQ(object.hex, 0b10'10'10'10ull << 30);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < object.arr.Size(); i++)
|
||||||
|
{
|
||||||
|
object.arr[i] = static_cast<TestEnum>(i);
|
||||||
|
}
|
||||||
|
EXPECT_EQ(object.hex, 0b11'10'01'00ull << 30);
|
||||||
|
|
||||||
|
EXPECT_EQ(objectc.arr[0], TestEnum::A);
|
||||||
|
EXPECT_EQ(objectc.arr[1], TestEnum::B);
|
||||||
|
EXPECT_EQ(objectc.arr[2], TestEnum::C);
|
||||||
|
EXPECT_EQ(objectc.arr[3], TestEnum::D);
|
||||||
|
|
||||||
|
u32 counter = 0;
|
||||||
|
for (TestEnum value : objectc.arr)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr[counter++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ("[A (0), B (1), C (2), D (3)]", fmt::format("[{}]", fmt::join(object.arr, ", ")));
|
||||||
|
EXPECT_EQ("[0x0u /* A */, 0x1u /* B */, 0x2u /* C */, 0x3u /* D */]",
|
||||||
|
fmt::format("[{:s}]", fmt::join(object.arr, ", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
union TestUnion5
|
||||||
|
{
|
||||||
|
u64 hex;
|
||||||
|
BitFieldArray<0, 5, 6, u8, u64> arr1;
|
||||||
|
BitFieldArray<30, 1, 4, bool, u64> arr2;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(BitFieldArray, StorageType)
|
||||||
|
{
|
||||||
|
TestUnion5 object;
|
||||||
|
const u64 arr2_hex_1 = 0b1010ull << 30;
|
||||||
|
object.hex = arr2_hex_1;
|
||||||
|
const TestUnion5& objectc = object;
|
||||||
|
|
||||||
|
EXPECT_FALSE(object.arr2[0]);
|
||||||
|
EXPECT_TRUE(object.arr2[1]);
|
||||||
|
EXPECT_FALSE(object.arr2[2]);
|
||||||
|
EXPECT_TRUE(object.arr2[3]);
|
||||||
|
|
||||||
|
object.arr1[0] = 0;
|
||||||
|
object.arr1[1] = 1;
|
||||||
|
object.arr1[2] = 2;
|
||||||
|
object.arr1[3] = 4;
|
||||||
|
object.arr1[4] = 8;
|
||||||
|
object.arr1[5] = 16;
|
||||||
|
const u64 arr1_hex = 0b10000'01000'00100'00010'00001'00000;
|
||||||
|
EXPECT_EQ(object.hex, arr1_hex | arr2_hex_1);
|
||||||
|
|
||||||
|
object.arr2[2] = object.arr2[0] = true;
|
||||||
|
object.arr2[3] = object.arr2[1] = false;
|
||||||
|
const u64 arr2_hex_2 = 0b0101ull << 30;
|
||||||
|
EXPECT_EQ(object.hex, arr1_hex | arr2_hex_2);
|
||||||
|
|
||||||
|
object.arr2[2] = object.arr2[1];
|
||||||
|
object.arr2[3] = objectc.arr2[0];
|
||||||
|
const u64 arr2_hex_3 = 0b1001ull << 30;
|
||||||
|
EXPECT_EQ(object.hex, arr1_hex | arr2_hex_3);
|
||||||
|
|
||||||
|
u32 counter = 0;
|
||||||
|
for (u8 value : object.arr1)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr1[counter++]);
|
||||||
|
}
|
||||||
|
counter = 0;
|
||||||
|
for (bool value : object.arr2)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr2[counter++]);
|
||||||
|
}
|
||||||
|
|
||||||
|
counter = 0;
|
||||||
|
for (u8 value : objectc.arr1)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr1[counter++]);
|
||||||
|
}
|
||||||
|
counter = 0;
|
||||||
|
for (bool value : objectc.arr2)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(value, object.arr2[counter++]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ add_dolphin_test(BlockingLoopTest BlockingLoopTest.cpp)
|
||||||
add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
|
add_dolphin_test(BusyLoopTest BusyLoopTest.cpp)
|
||||||
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
|
add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)
|
||||||
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
|
add_dolphin_test(CryptoEcTest Crypto/EcTest.cpp)
|
||||||
|
add_dolphin_test(EnumFormatterTest EnumFormatterTest.cpp)
|
||||||
add_dolphin_test(EventTest EventTest.cpp)
|
add_dolphin_test(EventTest EventTest.cpp)
|
||||||
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
add_dolphin_test(FixedSizeQueueTest FixedSizeQueueTest.cpp)
|
||||||
add_dolphin_test(FlagTest FlagTest.cpp)
|
add_dolphin_test(FlagTest FlagTest.cpp)
|
||||||
|
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Copyright 2021 Dolphin Emulator Project
|
||||||
|
// Licensed under GPLv2+
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/EnumFormatter.h"
|
||||||
|
|
||||||
|
enum class Enum1 : u32
|
||||||
|
{
|
||||||
|
A = 0,
|
||||||
|
B = 1,
|
||||||
|
C = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Enum1> : EnumFormatter<Enum1::C>
|
||||||
|
{
|
||||||
|
formatter() : EnumFormatter({"A", "B", "C"}) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class Enum2 : s32
|
||||||
|
{
|
||||||
|
D = 0,
|
||||||
|
E = 1,
|
||||||
|
F = 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct fmt::formatter<Enum2> : EnumFormatter<Enum2::F>
|
||||||
|
{
|
||||||
|
static constexpr array_type names = {"D", "E", nullptr, "F"};
|
||||||
|
formatter() : EnumFormatter(names) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST(EnumUtil, Enum1)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::A), "A (0)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::B), "B (1)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum1::C), "C (2)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum1>(3)), "Invalid (3)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum1>(4)), "Invalid (4)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::A), "0x0u /* A */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::B), "0x1u /* B */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum1::C), "0x2u /* C */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(3)), "0x3u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum1>(4)), "0x4u /* Invalid */");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(EnumUtil, Enum2)
|
||||||
|
{
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::D), "D (0)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::E), "E (1)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(2)), "Invalid (2)");
|
||||||
|
EXPECT_EQ(fmt::to_string(Enum2::F), "F (3)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(4)), "Invalid (4)");
|
||||||
|
EXPECT_EQ(fmt::to_string(static_cast<Enum2>(-1)), "Invalid (-1)");
|
||||||
|
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::D), "0x0u /* D */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::E), "0x1u /* E */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(2)), "0x2u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", Enum2::F), "0x3u /* F */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(4)), "0x4u /* Invalid */");
|
||||||
|
EXPECT_EQ(fmt::format("{:s}", static_cast<Enum2>(-1)), "0xffffffffu /* Invalid */");
|
||||||
|
}
|
|
@ -49,6 +49,7 @@
|
||||||
<ClCompile Include="Common\BusyLoopTest.cpp" />
|
<ClCompile Include="Common\BusyLoopTest.cpp" />
|
||||||
<ClCompile Include="Common\CommonFuncsTest.cpp" />
|
<ClCompile Include="Common\CommonFuncsTest.cpp" />
|
||||||
<ClCompile Include="Common\Crypto\EcTest.cpp" />
|
<ClCompile Include="Common\Crypto\EcTest.cpp" />
|
||||||
|
<ClCompile Include="Common\EnumFormatterTest.cpp" />
|
||||||
<ClCompile Include="Common\EventTest.cpp" />
|
<ClCompile Include="Common\EventTest.cpp" />
|
||||||
<ClCompile Include="Common\FixedSizeQueueTest.cpp" />
|
<ClCompile Include="Common\FixedSizeQueueTest.cpp" />
|
||||||
<ClCompile Include="Common\FlagTest.cpp" />
|
<ClCompile Include="Common\FlagTest.cpp" />
|
||||||
|
|
|
@ -28,7 +28,7 @@ TEST(VertexLoaderUID, UniqueEnough)
|
||||||
memset(&vat, 0, sizeof(vat));
|
memset(&vat, 0, sizeof(vat));
|
||||||
uids.insert(VertexLoaderUID(vtx_desc, vat));
|
uids.insert(VertexLoaderUID(vtx_desc, vat));
|
||||||
|
|
||||||
vtx_desc.Hex = 0xFEDCBA9876543210ull;
|
vtx_desc.SetLegacyHex(0xFEDCBA9876543210ull);
|
||||||
EXPECT_EQ(uids.end(), uids.find(VertexLoaderUID(vtx_desc, vat)));
|
EXPECT_EQ(uids.end(), uids.find(VertexLoaderUID(vtx_desc, vat)));
|
||||||
uids.insert(VertexLoaderUID(vtx_desc, vat));
|
uids.insert(VertexLoaderUID(vtx_desc, vat));
|
||||||
|
|
||||||
|
@ -106,29 +106,37 @@ protected:
|
||||||
std::unique_ptr<VertexLoaderBase> m_loader;
|
std::unique_ptr<VertexLoaderBase> m_loader;
|
||||||
};
|
};
|
||||||
|
|
||||||
class VertexLoaderParamTest : public VertexLoaderTest,
|
class VertexLoaderParamTest
|
||||||
public ::testing::WithParamInterface<std::tuple<int, int, int, int>>
|
: public VertexLoaderTest,
|
||||||
|
public ::testing::WithParamInterface<
|
||||||
|
std::tuple<VertexComponentFormat, ComponentFormat, CoordComponentCount, int>>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
INSTANTIATE_TEST_CASE_P(AllCombinations, VertexLoaderParamTest,
|
INSTANTIATE_TEST_CASE_P(
|
||||||
::testing::Combine(::testing::Values(DIRECT, INDEX8, INDEX16),
|
AllCombinations, VertexLoaderParamTest,
|
||||||
::testing::Values(FORMAT_UBYTE, FORMAT_BYTE,
|
::testing::Combine(
|
||||||
FORMAT_USHORT, FORMAT_SHORT,
|
::testing::Values(VertexComponentFormat::Direct, VertexComponentFormat::Index8,
|
||||||
FORMAT_FLOAT),
|
VertexComponentFormat::Index16),
|
||||||
::testing::Values(0, 1), // elements
|
::testing::Values(ComponentFormat::UByte, ComponentFormat::Byte, ComponentFormat::UShort,
|
||||||
::testing::Values(0, 1, 31) // frac
|
ComponentFormat::Short, ComponentFormat::Float),
|
||||||
));
|
::testing::Values(CoordComponentCount::XY, CoordComponentCount::XYZ),
|
||||||
|
::testing::Values(0, 1, 31) // frac
|
||||||
|
));
|
||||||
|
|
||||||
TEST_P(VertexLoaderParamTest, PositionAll)
|
TEST_P(VertexLoaderParamTest, PositionAll)
|
||||||
{
|
{
|
||||||
int addr, format, elements, frac;
|
VertexComponentFormat addr;
|
||||||
|
ComponentFormat format;
|
||||||
|
CoordComponentCount elements;
|
||||||
|
int frac;
|
||||||
std::tie(addr, format, elements, frac) = GetParam();
|
std::tie(addr, format, elements, frac) = GetParam();
|
||||||
this->m_vtx_desc.Position = addr;
|
this->m_vtx_desc.low.Position = addr;
|
||||||
this->m_vtx_attr.g0.PosFormat = format;
|
this->m_vtx_attr.g0.PosFormat = format;
|
||||||
this->m_vtx_attr.g0.PosElements = elements;
|
this->m_vtx_attr.g0.PosElements = elements;
|
||||||
this->m_vtx_attr.g0.PosFrac = frac;
|
this->m_vtx_attr.g0.PosFrac = frac;
|
||||||
this->m_vtx_attr.g0.ByteDequant = true;
|
this->m_vtx_attr.g0.ByteDequant = true;
|
||||||
elements += 2;
|
const u32 elem_size = GetElementSize(format);
|
||||||
|
const u32 elem_count = elements == CoordComponentCount::XY ? 2 : 3;
|
||||||
|
|
||||||
std::vector<float> values = {
|
std::vector<float> values = {
|
||||||
std::numeric_limits<float>::lowest(),
|
std::numeric_limits<float>::lowest(),
|
||||||
|
@ -153,38 +161,37 @@ TEST_P(VertexLoaderParamTest, PositionAll)
|
||||||
ASSERT_EQ(0u, values.size() % 2);
|
ASSERT_EQ(0u, values.size() % 2);
|
||||||
ASSERT_EQ(0u, values.size() % 3);
|
ASSERT_EQ(0u, values.size() % 3);
|
||||||
|
|
||||||
int count = (int)values.size() / elements;
|
int count = (int)values.size() / elem_count;
|
||||||
u32 elem_size = 1 << (format / 2);
|
size_t input_size = elem_count * elem_size;
|
||||||
size_t input_size = elements * elem_size;
|
if (IsIndexed(addr))
|
||||||
if (addr & MASK_INDEXED)
|
|
||||||
{
|
{
|
||||||
input_size = addr - 1;
|
input_size = addr == VertexComponentFormat::Index8 ? 1 : 2;
|
||||||
for (int i = 0; i < count; i++)
|
for (int i = 0; i < count; i++)
|
||||||
if (addr == INDEX8)
|
if (addr == VertexComponentFormat::Index8)
|
||||||
Input<u8>(i);
|
Input<u8>(i);
|
||||||
else
|
else
|
||||||
Input<u16>(i);
|
Input<u16>(i);
|
||||||
VertexLoaderManager::cached_arraybases[ARRAY_POSITION] = m_src.GetPointer();
|
VertexLoaderManager::cached_arraybases[ARRAY_POSITION] = m_src.GetPointer();
|
||||||
g_main_cp_state.array_strides[ARRAY_POSITION] = elements * elem_size;
|
g_main_cp_state.array_strides[ARRAY_POSITION] = elem_count * elem_size;
|
||||||
}
|
}
|
||||||
CreateAndCheckSizes(input_size, elements * sizeof(float));
|
CreateAndCheckSizes(input_size, elem_count * sizeof(float));
|
||||||
for (float value : values)
|
for (float value : values)
|
||||||
{
|
{
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_UBYTE:
|
case ComponentFormat::UByte:
|
||||||
Input((u8)value);
|
Input((u8)value);
|
||||||
break;
|
break;
|
||||||
case FORMAT_BYTE:
|
case ComponentFormat::Byte:
|
||||||
Input((s8)value);
|
Input((s8)value);
|
||||||
break;
|
break;
|
||||||
case FORMAT_USHORT:
|
case ComponentFormat::UShort:
|
||||||
Input((u16)value);
|
Input((u16)value);
|
||||||
break;
|
break;
|
||||||
case FORMAT_SHORT:
|
case ComponentFormat::Short:
|
||||||
Input((s16)value);
|
Input((s16)value);
|
||||||
break;
|
break;
|
||||||
case FORMAT_FLOAT:
|
case ComponentFormat::Float:
|
||||||
Input(value);
|
Input(value);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -192,29 +199,29 @@ TEST_P(VertexLoaderParamTest, PositionAll)
|
||||||
|
|
||||||
RunVertices(count);
|
RunVertices(count);
|
||||||
|
|
||||||
float scale = 1.f / (1u << (format == FORMAT_FLOAT ? 0 : frac));
|
float scale = 1.f / (1u << (format == ComponentFormat::Float ? 0 : frac));
|
||||||
for (auto iter = values.begin(); iter != values.end();)
|
for (auto iter = values.begin(); iter != values.end();)
|
||||||
{
|
{
|
||||||
float f, g;
|
float f, g;
|
||||||
switch (format)
|
switch (format)
|
||||||
{
|
{
|
||||||
case FORMAT_UBYTE:
|
case ComponentFormat::UByte:
|
||||||
f = (u8)*iter++;
|
f = (u8)*iter++;
|
||||||
g = (u8)*iter++;
|
g = (u8)*iter++;
|
||||||
break;
|
break;
|
||||||
case FORMAT_BYTE:
|
case ComponentFormat::Byte:
|
||||||
f = (s8)*iter++;
|
f = (s8)*iter++;
|
||||||
g = (s8)*iter++;
|
g = (s8)*iter++;
|
||||||
break;
|
break;
|
||||||
case FORMAT_USHORT:
|
case ComponentFormat::UShort:
|
||||||
f = (u16)*iter++;
|
f = (u16)*iter++;
|
||||||
g = (u16)*iter++;
|
g = (u16)*iter++;
|
||||||
break;
|
break;
|
||||||
case FORMAT_SHORT:
|
case ComponentFormat::Short:
|
||||||
f = (s16)*iter++;
|
f = (s16)*iter++;
|
||||||
g = (s16)*iter++;
|
g = (s16)*iter++;
|
||||||
break;
|
break;
|
||||||
case FORMAT_FLOAT:
|
case ComponentFormat::Float:
|
||||||
f = *iter++;
|
f = *iter++;
|
||||||
g = *iter++;
|
g = *iter++;
|
||||||
break;
|
break;
|
||||||
|
@ -228,8 +235,8 @@ TEST_P(VertexLoaderParamTest, PositionAll)
|
||||||
|
|
||||||
TEST_F(VertexLoaderTest, PositionIndex16FloatXY)
|
TEST_F(VertexLoaderTest, PositionIndex16FloatXY)
|
||||||
{
|
{
|
||||||
m_vtx_desc.Position = INDEX16;
|
m_vtx_desc.low.Position = VertexComponentFormat::Index16;
|
||||||
m_vtx_attr.g0.PosFormat = FORMAT_FLOAT;
|
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
|
||||||
CreateAndCheckSizes(sizeof(u16), 2 * sizeof(float));
|
CreateAndCheckSizes(sizeof(u16), 2 * sizeof(float));
|
||||||
Input<u16>(1);
|
Input<u16>(1);
|
||||||
Input<u16>(0);
|
Input<u16>(0);
|
||||||
|
@ -246,47 +253,49 @@ TEST_F(VertexLoaderTest, PositionIndex16FloatXY)
|
||||||
}
|
}
|
||||||
|
|
||||||
class VertexLoaderSpeedTest : public VertexLoaderTest,
|
class VertexLoaderSpeedTest : public VertexLoaderTest,
|
||||||
public ::testing::WithParamInterface<std::tuple<int, int>>
|
public ::testing::WithParamInterface<std::tuple<ComponentFormat, int>>
|
||||||
{
|
{
|
||||||
};
|
};
|
||||||
INSTANTIATE_TEST_CASE_P(FormatsAndElements, VertexLoaderSpeedTest,
|
INSTANTIATE_TEST_CASE_P(
|
||||||
::testing::Combine(::testing::Values(FORMAT_UBYTE, FORMAT_BYTE,
|
FormatsAndElements, VertexLoaderSpeedTest,
|
||||||
FORMAT_USHORT, FORMAT_SHORT,
|
::testing::Combine(::testing::Values(ComponentFormat::UByte, ComponentFormat::Byte,
|
||||||
FORMAT_FLOAT),
|
ComponentFormat::UShort, ComponentFormat::Short,
|
||||||
::testing::Values(0, 1) // elements
|
ComponentFormat::Float),
|
||||||
));
|
::testing::Values(0, 1)));
|
||||||
|
|
||||||
TEST_P(VertexLoaderSpeedTest, PositionDirectAll)
|
TEST_P(VertexLoaderSpeedTest, PositionDirectAll)
|
||||||
{
|
{
|
||||||
int format, elements;
|
ComponentFormat format;
|
||||||
std::tie(format, elements) = GetParam();
|
int elements_i;
|
||||||
const char* map[] = {"u8", "s8", "u16", "s16", "float"};
|
std::tie(format, elements_i) = GetParam();
|
||||||
printf("format: %s, elements: %d\n", map[format], elements);
|
CoordComponentCount elements = static_cast<CoordComponentCount>(elements_i);
|
||||||
m_vtx_desc.Position = DIRECT;
|
fmt::print("format: {}, elements: {}\n", format, elements);
|
||||||
|
const u32 elem_count = elements == CoordComponentCount::XY ? 2 : 3;
|
||||||
|
m_vtx_desc.low.Position = VertexComponentFormat::Direct;
|
||||||
m_vtx_attr.g0.PosFormat = format;
|
m_vtx_attr.g0.PosFormat = format;
|
||||||
m_vtx_attr.g0.PosElements = elements;
|
m_vtx_attr.g0.PosElements = elements;
|
||||||
elements += 2;
|
const size_t elem_size = GetElementSize(format);
|
||||||
size_t elem_size = static_cast<size_t>(1) << (format / 2);
|
CreateAndCheckSizes(elem_count * elem_size, elem_count * sizeof(float));
|
||||||
CreateAndCheckSizes(elements * elem_size, elements * sizeof(float));
|
|
||||||
for (int i = 0; i < 1000; ++i)
|
for (int i = 0; i < 1000; ++i)
|
||||||
RunVertices(100000);
|
RunVertices(100000);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(VertexLoaderSpeedTest, TexCoordSingleElement)
|
TEST_P(VertexLoaderSpeedTest, TexCoordSingleElement)
|
||||||
{
|
{
|
||||||
int format, elements;
|
ComponentFormat format;
|
||||||
std::tie(format, elements) = GetParam();
|
int elements_i;
|
||||||
const char* map[] = {"u8", "s8", "u16", "s16", "float"};
|
std::tie(format, elements_i) = GetParam();
|
||||||
printf("format: %s, elements: %d\n", map[format], elements);
|
TexComponentCount elements = static_cast<TexComponentCount>(elements_i);
|
||||||
m_vtx_desc.Position = DIRECT;
|
fmt::print("format: {}, elements: {}\n", format, elements);
|
||||||
m_vtx_attr.g0.PosFormat = FORMAT_BYTE;
|
const u32 elem_count = elements == TexComponentCount::S ? 1 : 2;
|
||||||
m_vtx_desc.Tex0Coord = DIRECT;
|
m_vtx_desc.low.Position = VertexComponentFormat::Direct;
|
||||||
|
m_vtx_attr.g0.PosFormat = ComponentFormat::Byte;
|
||||||
|
m_vtx_desc.high.Tex0Coord = VertexComponentFormat::Direct;
|
||||||
m_vtx_attr.g0.Tex0CoordFormat = format;
|
m_vtx_attr.g0.Tex0CoordFormat = format;
|
||||||
m_vtx_attr.g0.Tex0CoordElements = elements;
|
m_vtx_attr.g0.Tex0CoordElements = elements;
|
||||||
elements += 1;
|
const size_t elem_size = GetElementSize(format);
|
||||||
size_t elem_size = static_cast<size_t>(1) << (format / 2);
|
CreateAndCheckSizes(2 * sizeof(s8) + elem_count * elem_size,
|
||||||
CreateAndCheckSizes(2 * sizeof(s8) + elements * elem_size,
|
2 * sizeof(float) + elem_count * sizeof(float));
|
||||||
2 * sizeof(float) + elements * sizeof(float));
|
|
||||||
for (int i = 0; i < 1000; ++i)
|
for (int i = 0; i < 1000; ++i)
|
||||||
RunVertices(100000);
|
RunVertices(100000);
|
||||||
}
|
}
|
||||||
|
@ -294,52 +303,52 @@ TEST_P(VertexLoaderSpeedTest, TexCoordSingleElement)
|
||||||
TEST_F(VertexLoaderTest, LargeFloatVertexSpeed)
|
TEST_F(VertexLoaderTest, LargeFloatVertexSpeed)
|
||||||
{
|
{
|
||||||
// Enables most attributes in floating point indexed mode to test speed.
|
// Enables most attributes in floating point indexed mode to test speed.
|
||||||
m_vtx_desc.PosMatIdx = 1;
|
m_vtx_desc.low.PosMatIdx = 1;
|
||||||
m_vtx_desc.Tex0MatIdx = 1;
|
m_vtx_desc.low.Tex0MatIdx = 1;
|
||||||
m_vtx_desc.Tex1MatIdx = 1;
|
m_vtx_desc.low.Tex1MatIdx = 1;
|
||||||
m_vtx_desc.Tex2MatIdx = 1;
|
m_vtx_desc.low.Tex2MatIdx = 1;
|
||||||
m_vtx_desc.Tex3MatIdx = 1;
|
m_vtx_desc.low.Tex3MatIdx = 1;
|
||||||
m_vtx_desc.Tex4MatIdx = 1;
|
m_vtx_desc.low.Tex4MatIdx = 1;
|
||||||
m_vtx_desc.Tex5MatIdx = 1;
|
m_vtx_desc.low.Tex5MatIdx = 1;
|
||||||
m_vtx_desc.Tex6MatIdx = 1;
|
m_vtx_desc.low.Tex6MatIdx = 1;
|
||||||
m_vtx_desc.Tex7MatIdx = 1;
|
m_vtx_desc.low.Tex7MatIdx = 1;
|
||||||
m_vtx_desc.Position = INDEX16;
|
m_vtx_desc.low.Position = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Normal = INDEX16;
|
m_vtx_desc.low.Normal = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Color0 = INDEX16;
|
m_vtx_desc.low.Color0 = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Color1 = INDEX16;
|
m_vtx_desc.low.Color1 = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex0Coord = INDEX16;
|
m_vtx_desc.high.Tex0Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex1Coord = INDEX16;
|
m_vtx_desc.high.Tex1Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex2Coord = INDEX16;
|
m_vtx_desc.high.Tex2Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex3Coord = INDEX16;
|
m_vtx_desc.high.Tex3Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex4Coord = INDEX16;
|
m_vtx_desc.high.Tex4Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex5Coord = INDEX16;
|
m_vtx_desc.high.Tex5Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex6Coord = INDEX16;
|
m_vtx_desc.high.Tex6Coord = VertexComponentFormat::Index16;
|
||||||
m_vtx_desc.Tex7Coord = INDEX16;
|
m_vtx_desc.high.Tex7Coord = VertexComponentFormat::Index16;
|
||||||
|
|
||||||
m_vtx_attr.g0.PosElements = 1; // XYZ
|
m_vtx_attr.g0.PosElements = CoordComponentCount::XYZ;
|
||||||
m_vtx_attr.g0.PosFormat = FORMAT_FLOAT;
|
m_vtx_attr.g0.PosFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g0.NormalElements = 1; // NBT
|
m_vtx_attr.g0.NormalElements = NormalComponentCount::NBT;
|
||||||
m_vtx_attr.g0.NormalFormat = FORMAT_FLOAT;
|
m_vtx_attr.g0.NormalFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g0.Color0Elements = 1; // Has Alpha
|
m_vtx_attr.g0.Color0Elements = ColorComponentCount::RGBA;
|
||||||
m_vtx_attr.g0.Color0Comp = FORMAT_32B_8888;
|
m_vtx_attr.g0.Color0Comp = ColorFormat::RGBA8888;
|
||||||
m_vtx_attr.g0.Color1Elements = 1; // Has Alpha
|
m_vtx_attr.g0.Color1Elements = ColorComponentCount::RGBA;
|
||||||
m_vtx_attr.g0.Color1Comp = FORMAT_32B_8888;
|
m_vtx_attr.g0.Color1Comp = ColorFormat::RGBA8888;
|
||||||
m_vtx_attr.g0.Tex0CoordElements = 1; // ST
|
m_vtx_attr.g0.Tex0CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g0.Tex0CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g0.Tex0CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g1.Tex1CoordElements = 1; // ST
|
m_vtx_attr.g1.Tex1CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g1.Tex1CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g1.Tex1CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g1.Tex2CoordElements = 1; // ST
|
m_vtx_attr.g1.Tex2CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g1.Tex2CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g1.Tex2CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g1.Tex3CoordElements = 1; // ST
|
m_vtx_attr.g1.Tex3CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g1.Tex3CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g1.Tex3CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g1.Tex4CoordElements = 1; // ST
|
m_vtx_attr.g1.Tex4CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g1.Tex4CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g1.Tex4CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g2.Tex5CoordElements = 1; // ST
|
m_vtx_attr.g2.Tex5CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g2.Tex5CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g2.Tex5CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g2.Tex6CoordElements = 1; // ST
|
m_vtx_attr.g2.Tex6CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g2.Tex6CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g2.Tex6CoordFormat = ComponentFormat::Float;
|
||||||
m_vtx_attr.g2.Tex7CoordElements = 1; // ST
|
m_vtx_attr.g2.Tex7CoordElements = TexComponentCount::ST;
|
||||||
m_vtx_attr.g2.Tex7CoordFormat = FORMAT_FLOAT;
|
m_vtx_attr.g2.Tex7CoordFormat = ComponentFormat::Float;
|
||||||
|
|
||||||
CreateAndCheckSizes(33, 156);
|
CreateAndCheckSizes(33, 156);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue