Implement Maxwell3D Vertex Attributes

Translates all Maxwell3D vertex attributes to Vulkan with the exception of `isConstant` which causes the vertex attribute to return a constant value `(0,0,0,X)` which was trivial in OpenGL with `glDisableVertexAttribArray` and `glVertexAttrib4(..., 0, 0, 0, 1)` but we don't have access to this in Vulkan and might need to depend on undefined behavior or manually emulate it in a shader. This'll be revisited in the future after checking host GPU behavior.
This commit is contained in:
PixelyIon 2021-11-16 21:20:34 +05:30
parent 4b9f99bb27
commit 138f884159
4 changed files with 195 additions and 26 deletions

View File

@ -55,6 +55,10 @@ namespace skyline::gpu::interconnect {
vertexState.unlink<vk::PipelineVertexInputDivisorStateCreateInfoEXT>();
}
u32 attributeIndex{};
for (auto &vertexAttribute : vertexAttributes)
vertexAttribute.location = attributeIndex++;
if (!gpu.quirks.supportsLastProvokingVertex)
rasterizerState.unlink<vk::PipelineRasterizationProvokingVertexStateCreateInfoEXT>();
}
@ -772,10 +776,13 @@ namespace skyline::gpu::interconnect {
std::array<VertexBuffer, maxwell3d::VertexBufferCount> vertexBuffers{};
std::array<vk::VertexInputBindingDescription, maxwell3d::VertexBufferCount> vertexBindings{};
std::array<vk::VertexInputBindingDivisorDescriptionEXT, maxwell3d::VertexBufferCount> vertexBindingDivisors{};
std::array<vk::VertexInputAttributeDescription, maxwell3d::VertexAttributeCount> vertexAttributes{};
vk::StructureChain<vk::PipelineVertexInputStateCreateInfo, vk::PipelineVertexInputDivisorStateCreateInfoEXT> vertexState{
vk::PipelineVertexInputStateCreateInfo{
.pVertexBindingDescriptions = vertexBindings.data(),
.vertexBindingDescriptionCount = maxwell3d::VertexBufferCount,
.pVertexAttributeDescriptions = vertexAttributes.data(),
.vertexAttributeDescriptionCount = maxwell3d::VertexAttributeCount,
}, vk::PipelineVertexInputDivisorStateCreateInfoEXT{
.pVertexBindingDivisors = vertexBindingDivisors.data(),
.vertexBindingDivisorCount = maxwell3d::VertexBufferCount,
@ -814,5 +821,124 @@ namespace skyline::gpu::interconnect {
Logger::Warn("Cannot set vertex attribute divisor to zero without host GPU support");
vertexBindingDivisors[index].divisor = divisor;
}
vk::Format ConvertVertexBufferFormat(maxwell3d::VertexAttribute::ElementType type, maxwell3d::VertexAttribute::ElementSize size) {
using Size = maxwell3d::VertexAttribute::ElementSize;
using Type = maxwell3d::VertexAttribute::ElementType;
if (size == Size::e0 || type == Type::None)
return vk::Format::eUndefined;
switch(size | type) {
// @fmt:off
/* 8-bit components */
case Size::e1x8 | Type::Unorm: return vk::Format::eR8Unorm;
case Size::e1x8 | Type::Snorm: return vk::Format::eR8Snorm;
case Size::e1x8 | Type::Uint: return vk::Format::eR8Uint;
case Size::e1x8 | Type::Sint: return vk::Format::eR8Sint;
case Size::e1x8 | Type::Uscaled: return vk::Format::eR8Uscaled;
case Size::e1x8 | Type::Sscaled: return vk::Format::eR8Sscaled;
case Size::e2x8 | Type::Unorm: return vk::Format::eR8G8Unorm;
case Size::e2x8 | Type::Snorm: return vk::Format::eR8G8Snorm;
case Size::e2x8 | Type::Uint: return vk::Format::eR8G8Uint;
case Size::e2x8 | Type::Sint: return vk::Format::eR8G8Sint;
case Size::e2x8 | Type::Uscaled: return vk::Format::eR8G8Uscaled;
case Size::e2x8 | Type::Sscaled: return vk::Format::eR8G8Sscaled;
case Size::e3x8 | Type::Unorm: return vk::Format::eR8G8B8Unorm;
case Size::e3x8 | Type::Snorm: return vk::Format::eR8G8B8Snorm;
case Size::e3x8 | Type::Uint: return vk::Format::eR8G8B8Uint;
case Size::e3x8 | Type::Sint: return vk::Format::eR8G8B8Sint;
case Size::e3x8 | Type::Uscaled: return vk::Format::eR8G8B8Uscaled;
case Size::e3x8 | Type::Sscaled: return vk::Format::eR8G8B8Sscaled;
case Size::e4x8 | Type::Unorm: return vk::Format::eR8G8B8A8Unorm;
case Size::e4x8 | Type::Snorm: return vk::Format::eR8G8B8A8Snorm;
case Size::e4x8 | Type::Uint: return vk::Format::eR8G8B8A8Uint;
case Size::e4x8 | Type::Sint: return vk::Format::eR8G8B8A8Sint;
case Size::e4x8 | Type::Uscaled: return vk::Format::eR8G8B8A8Uscaled;
case Size::e4x8 | Type::Sscaled: return vk::Format::eR8G8B8A8Sscaled;
/* 16-bit components */
case Size::e1x16 | Type::Unorm: return vk::Format::eR16Unorm;
case Size::e1x16 | Type::Snorm: return vk::Format::eR16Snorm;
case Size::e1x16 | Type::Uint: return vk::Format::eR16Uint;
case Size::e1x16 | Type::Sint: return vk::Format::eR16Sint;
case Size::e1x16 | Type::Uscaled: return vk::Format::eR16Uscaled;
case Size::e1x16 | Type::Sscaled: return vk::Format::eR16Sscaled;
case Size::e1x16 | Type::Float: return vk::Format::eR16Sfloat;
case Size::e2x16 | Type::Unorm: return vk::Format::eR16G16Unorm;
case Size::e2x16 | Type::Snorm: return vk::Format::eR16G16Snorm;
case Size::e2x16 | Type::Uint: return vk::Format::eR16G16Uint;
case Size::e2x16 | Type::Sint: return vk::Format::eR16G16Sint;
case Size::e2x16 | Type::Uscaled: return vk::Format::eR16G16Uscaled;
case Size::e2x16 | Type::Sscaled: return vk::Format::eR16G16Sscaled;
case Size::e2x16 | Type::Float: return vk::Format::eR16G16Sfloat;
case Size::e3x16 | Type::Unorm: return vk::Format::eR16G16B16Unorm;
case Size::e3x16 | Type::Snorm: return vk::Format::eR16G16B16Snorm;
case Size::e3x16 | Type::Uint: return vk::Format::eR16G16B16Uint;
case Size::e3x16 | Type::Sint: return vk::Format::eR16G16B16Sint;
case Size::e3x16 | Type::Uscaled: return vk::Format::eR16G16B16Uscaled;
case Size::e3x16 | Type::Sscaled: return vk::Format::eR16G16B16Sscaled;
case Size::e3x16 | Type::Float: return vk::Format::eR16G16B16Sfloat;
case Size::e4x16 | Type::Unorm: return vk::Format::eR16G16B16A16Unorm;
case Size::e4x16 | Type::Snorm: return vk::Format::eR16G16B16A16Snorm;
case Size::e4x16 | Type::Uint: return vk::Format::eR16G16B16A16Uint;
case Size::e4x16 | Type::Sint: return vk::Format::eR16G16B16A16Sint;
case Size::e4x16 | Type::Uscaled: return vk::Format::eR16G16B16A16Uscaled;
case Size::e4x16 | Type::Sscaled: return vk::Format::eR16G16B16A16Sscaled;
case Size::e4x16 | Type::Float: return vk::Format::eR16G16B16A16Sfloat;
/* 32-bit components */
case Size::e1x32 | Type::Uint: return vk::Format::eR32Uint;
case Size::e1x32 | Type::Sint: return vk::Format::eR32Sint;
case Size::e1x32 | Type::Float: return vk::Format::eR32Sfloat;
case Size::e2x32 | Type::Uint: return vk::Format::eR32G32Uint;
case Size::e2x32 | Type::Sint: return vk::Format::eR32G32Sint;
case Size::e2x32 | Type::Float: return vk::Format::eR32G32Sfloat;
case Size::e3x32 | Type::Uint: return vk::Format::eR32G32B32Uint;
case Size::e3x32 | Type::Sint: return vk::Format::eR32G32B32Sint;
case Size::e3x32 | Type::Float: return vk::Format::eR32G32B32Sfloat;
case Size::e4x32 | Type::Uint: return vk::Format::eR32G32B32A32Uint;
case Size::e4x32 | Type::Sint: return vk::Format::eR32G32B32A32Sint;
case Size::e4x32 | Type::Float: return vk::Format::eR32G32B32A32Sfloat;
/* 10-bit RGB, 2-bit A */
case Size::e10_10_10_2 | Type::Unorm: return vk::Format::eA2R10G10B10UnormPack32;
case Size::e10_10_10_2 | Type::Snorm: return vk::Format::eA2R10G10B10SnormPack32;
case Size::e10_10_10_2 | Type::Uint: return vk::Format::eA2R10G10B10UintPack32;
case Size::e10_10_10_2 | Type::Sint: return vk::Format::eA2R10G10B10SintPack32;
case Size::e10_10_10_2 | Type::Uscaled: return vk::Format::eA2R10G10B10UscaledPack32;
case Size::e10_10_10_2 | Type::Sscaled: return vk::Format::eA2R10G10B10SscaledPack32;
/* Unknown */
case 0x12F: return vk::Format::eUndefined; // Issued by Maxwell3D::InitializeRegisters()
// @fmt:on
default:
throw exception("Unimplemented Vertex Buffer Format: {} | {}", maxwell3d::VertexAttribute::ToString(size), maxwell3d::VertexAttribute::ToString(type));
}
}
void SetVertexAttributeState(u32 index, maxwell3d::VertexAttribute attribute) {
auto& vkAttributes{vertexAttributes[index]};
vkAttributes.binding = attribute.bufferId;
vkAttributes.format = ConvertVertexBufferFormat(attribute.type, attribute.elementSize);
vkAttributes.offset = attribute.offset;
}
};
}

View File

@ -201,51 +201,85 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
static_assert(sizeof(Scissor) == (0x4 * sizeof(u32)));
constexpr static size_t VertexBufferCount{16}; //!< The maximum amount of vertex buffers that can be bound at once
constexpr static size_t VertexAttributeCount{32}; //!< The amount of vertex attributes that can be set
union VertexAttribute {
u32 raw;
enum class Size : u8 {
Size_1x32 = 0x12,
Size_2x32 = 0x04,
Size_3x32 = 0x02,
Size_4x32 = 0x01,
Size_1x16 = 0x1B,
Size_2x16 = 0x0F,
Size_3x16 = 0x05,
Size_4x16 = 0x03,
Size_1x8 = 0x1D,
Size_2x8 = 0x18,
Size_3x8 = 0x13,
Size_4x8 = 0x0A,
Size_10_10_10_2 = 0x30,
Size_11_11_10 = 0x31,
enum class ElementSize : u16 {
e0 = 0x0,
e1x8 = 0x1D,
e2x8 = 0x18,
e3x8 = 0x13,
e4x8 = 0x0A,
e1x16 = 0x1B,
e2x16 = 0x0F,
e3x16 = 0x05,
e4x16 = 0x03,
e1x32 = 0x12,
e2x32 = 0x04,
e3x32 = 0x02,
e4x32 = 0x01,
e10_10_10_2 = 0x30,
e11_11_10 = 0x31,
};
enum class Type : u8 {
ENUM_STRING(ElementSize, {
ENUM_CASE_PAIR(e1x8, "1x8");
ENUM_CASE_PAIR(e2x8, "2x8");
ENUM_CASE_PAIR(e3x8, "3x8");
ENUM_CASE_PAIR(e4x8, "4x8");
ENUM_CASE_PAIR(e1x16, "1x16");
ENUM_CASE_PAIR(e2x16, "2x16");
ENUM_CASE_PAIR(e3x16, "3x16");
ENUM_CASE_PAIR(e4x16, "4x16");
ENUM_CASE_PAIR(e1x32, "1x32");
ENUM_CASE_PAIR(e2x32, "2x32");
ENUM_CASE_PAIR(e3x32, "3x32");
ENUM_CASE_PAIR(e4x32, "4x32");
ENUM_CASE_PAIR(e10_10_10_2, "10_10_10_2");
ENUM_CASE_PAIR(e11_11_10, "11_11_10");
})
enum class ElementType : u16 {
None = 0,
SNorm = 1,
UNorm = 2,
SInt = 3,
UInt = 4,
UScaled = 5,
SScaled = 6,
Snorm = 1,
Unorm = 2,
Sint = 3,
Uint = 4,
Uscaled = 5,
Sscaled = 6,
Float = 7,
};
ENUM_STRING(ElementType, {
ENUM_CASE(None);
ENUM_CASE(Snorm);
ENUM_CASE(Unorm);
ENUM_CASE(Sint);
ENUM_CASE(Uint);
ENUM_CASE(Uscaled);
ENUM_CASE(Sscaled);
ENUM_CASE(Float);
})
struct {
u8 bufferId : 5;
u8 _pad0_ : 1;
bool fixed : 1;
bool isConstant : 1;
u16 offset : 14;
Size size : 6;
Type type : 3;
ElementSize elementSize : 6;
ElementType type : 3;
u8 _pad1_ : 1;
bool bgra : 1;
};
};
static_assert(sizeof(VertexAttribute) == sizeof(u32));
constexpr u16 operator|(VertexAttribute::ElementSize elementSize, VertexAttribute::ElementType type) {
return static_cast<u16>(static_cast<u16>(elementSize) | (static_cast<u16>(type) << 6));
}
/**
* @brief A descriptor that controls how the RenderTarget array (at 0x200) will be interpreted
*/

View File

@ -351,6 +351,15 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
static_assert(type::VertexBufferCount == 16 && type::VertexBufferCount < BOOST_PP_LIMIT_REPEAT);
#undef VERTEX_BUFFER_CALLBACKS
#define VERTEX_ATTRIBUTES_CALLBACKS(z, index, data) \
MAXWELL3D_ARRAY_CASE(vertexAttributeState, index, { \
context.SetVertexAttributeState(index, vertexAttributeState); \
})
BOOST_PP_REPEAT(32, VERTEX_ATTRIBUTES_CALLBACKS, 0)
static_assert(type::VertexAttributeCount == 32 && type::VertexAttributeCount < BOOST_PP_LIMIT_REPEAT);
#undef VERTEX_BUFFER_CALLBACKS
#define SET_INDEPENDENT_COLOR_BLEND_CALLBACKS(z, index, data) \
MAXWELL3D_ARRAY_STRUCT_CASE(independentBlend, index, colorOp, { \
context.SetColorBlendOp(index, colorOp); \

View File

@ -106,7 +106,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
Register<0x3E4, u32> commonColorWriteMask; //!< If enabled, the color write masks for all RTs must be set to that of the first RT
Register<0x3EB, u32> rtSeparateFragData;
Register<0x458, std::array<type::VertexAttribute, 0x20>> vertexAttributeState;
Register<0x458, std::array<type::VertexAttribute, type::VertexAttributeCount>> vertexAttributeState;
Register<0x487, type::RenderTargetControl> renderTargetControl;
Register<0x4B9, u32> independentBlendEnable;
Register<0x4BB, u32> alphaTestEnable;