From 138f884159d4c20142fbc553402f5a28f1bfe328 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Tue, 16 Nov 2021 21:20:34 +0530 Subject: [PATCH] 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. --- .../gpu/interconnect/graphics_context.h | 126 ++++++++++++++++++ .../skyline/soc/gm20b/engines/maxwell/types.h | 84 ++++++++---- .../skyline/soc/gm20b/engines/maxwell_3d.cpp | 9 ++ .../skyline/soc/gm20b/engines/maxwell_3d.h | 2 +- 4 files changed, 195 insertions(+), 26 deletions(-) diff --git a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h index 37ccb322..607ae665 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -55,6 +55,10 @@ namespace skyline::gpu::interconnect { vertexState.unlink(); } + u32 attributeIndex{}; + for (auto &vertexAttribute : vertexAttributes) + vertexAttribute.location = attributeIndex++; + if (!gpu.quirks.supportsLastProvokingVertex) rasterizerState.unlink(); } @@ -772,10 +776,13 @@ namespace skyline::gpu::interconnect { std::array vertexBuffers{}; std::array vertexBindings{}; std::array vertexBindingDivisors{}; + std::array vertexAttributes{}; vk::StructureChain 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; + } }; } diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h index b66cb33e..4e3b07e6 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell/types.h @@ -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(static_cast(elementSize) | (static_cast(type) << 6)); + } + /** * @brief A descriptor that controls how the RenderTarget array (at 0x200) will be interpreted */ diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp index 270dca15..d5b36064 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp @@ -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); \ diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h index 42f41f88..c65ac30d 100644 --- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h +++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h @@ -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> vertexAttributeState; + Register<0x458, std::array> vertexAttributeState; Register<0x487, type::RenderTargetControl> renderTargetControl; Register<0x4B9, u32> independentBlendEnable; Register<0x4BB, u32> alphaTestEnable;