Implement Maxwell3D Index Buffers

Adds support for index buffers including U8 index buffers via the `VK_EXT_index_type_uint8` extension which has been added as an optional quirk but an exception will be thrown if the guest utilizes it but the host doesn't support it.
This commit is contained in:
PixelyIon 2021-12-24 23:20:30 +05:30
parent a4041364e1
commit 23cdfe2139
6 changed files with 111 additions and 4 deletions

View File

@ -1256,7 +1256,6 @@ namespace skyline::gpu::interconnect {
/* Vertex Buffers */ /* Vertex Buffers */
private: private:
struct VertexBuffer { struct VertexBuffer {
bool disabled{};
vk::VertexInputBindingDescription bindingDescription{}; vk::VertexInputBindingDescription bindingDescription{};
vk::VertexInputBindingDivisorDescriptionEXT bindingDivisorDescription{}; vk::VertexInputBindingDivisorDescriptionEXT bindingDivisorDescription{};
IOVA start{}, end{}; //!< IOVAs covering a contiguous region in GPU AS with the vertex buffer IOVA start{}, end{}; //!< IOVAs covering a contiguous region in GPU AS with the vertex buffer
@ -1472,10 +1471,10 @@ namespace skyline::gpu::interconnect {
BufferView *GetVertexBuffer(size_t index) { BufferView *GetVertexBuffer(size_t index) {
auto &vertexBuffer{vertexBuffers.at(index)}; auto &vertexBuffer{vertexBuffers.at(index)};
if (vertexBuffer.disabled || vertexBuffer.start > vertexBuffer.end || vertexBuffer.start == 0 || vertexBuffer.end == 0) if (vertexBuffer.start > vertexBuffer.end || vertexBuffer.start == 0 || vertexBuffer.end == 0)
return nullptr; return nullptr;
else if (vertexBuffer.view) else if (vertexBuffer.view)
return &*vertexBuffer.view; return vertexBuffer.view.get();
if (vertexBuffer.guest.mappings.empty()) { if (vertexBuffer.guest.mappings.empty()) {
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(vertexBuffer.start, (vertexBuffer.end + 1) - vertexBuffer.start)}; auto mappings{channelCtx.asCtx->gmmu.TranslateRange(vertexBuffer.start, (vertexBuffer.end + 1) - vertexBuffer.start)};
@ -1525,6 +1524,85 @@ namespace skyline::gpu::interconnect {
UpdateRuntimeInformation(runtimeInfo.input_topology, shaderTopology, maxwell3d::PipelineStage::Geometry); UpdateRuntimeInformation(runtimeInfo.input_topology, shaderTopology, maxwell3d::PipelineStage::Geometry);
} }
/* Index Buffer */
private:
struct IndexBuffer {
IOVA start{}, end{}; //!< IOVAs covering a contiguous region in GPU AS containing the index buffer (end does not represent the true extent of the index buffers, just a maximum possible extent and is set to extremely high values which cannot be used to create a buffer)
vk::IndexType type{};
vk::DeviceSize viewSize{}; //!< The size of the cached view
std::shared_ptr<BufferView> view{}; //!< A cached view tied to the IOVAs and size to allow for a faster lookup
vk::DeviceSize GetIndexBufferSize(u32 elementCount) {
switch (type) {
case vk::IndexType::eUint8EXT:
return sizeof(u8) * elementCount;
case vk::IndexType::eUint16:
return sizeof(u16) * elementCount;
case vk::IndexType::eUint32:
return sizeof(u32) * elementCount;
default:
throw exception("Unsupported Vulkan Index Type: {}", vk::to_string(type));
}
}
} indexBuffer{};
public:
void SetIndexBufferStartIovaHigh(u32 high) {
indexBuffer.start.high = high;
indexBuffer.view.reset();
}
void SetIndexBufferStartIovaLow(u32 low) {
indexBuffer.start.low = low;
indexBuffer.view.reset();
}
void SetIndexBufferEndIovaHigh(u32 high) {
indexBuffer.end.high = high;
indexBuffer.view.reset();
}
void SetIndexBufferEndIovaLow(u32 low) {
indexBuffer.end.low = low;
indexBuffer.view.reset();
}
void SetIndexBufferFormat(maxwell3d::IndexBuffer::Format format) {
indexBuffer.type = [format]() {
using MaxwellFormat = maxwell3d::IndexBuffer::Format;
using VkFormat = vk::IndexType;
switch (format) {
case MaxwellFormat::Uint8:
return VkFormat::eUint8EXT;
case MaxwellFormat::Uint16:
return VkFormat::eUint16;
case MaxwellFormat::Uint32:
return VkFormat::eUint32;
}
}();
if (indexBuffer.type == vk::IndexType::eUint8EXT && !gpu.quirks.supportsUint8Indices)
throw exception("Cannot use U8 index buffer without host GPU support");
indexBuffer.view.reset();
}
BufferView *GetIndexBuffer(u32 elementCount) {
auto size{indexBuffer.GetIndexBufferSize(elementCount)};
if (indexBuffer.start > indexBuffer.end || indexBuffer.start == 0 || indexBuffer.end == 0 || size == 0)
return nullptr;
else if (indexBuffer.view && size == indexBuffer.viewSize)
return indexBuffer.view.get();
GuestBuffer guestBuffer;
auto mappings{channelCtx.asCtx->gmmu.TranslateRange(indexBuffer.start, size)};
guestBuffer.mappings.assign(mappings.begin(), mappings.end());
indexBuffer.view = gpu.buffer.FindOrCreate(guestBuffer);
return indexBuffer.view.get();
}
/* Depth */ /* Depth */
vk::PipelineDepthStencilStateCreateInfo depthState{}; vk::PipelineDepthStencilStateCreateInfo depthState{};

View File

@ -27,6 +27,7 @@ namespace skyline::gpu {
std::string_view extensionName{extension.extensionName}; std::string_view extensionName{extension.extensionName};
auto extensionVersion{extension.specVersion}; auto extensionVersion{extension.specVersion};
switch (util::Hash(extensionName)) { switch (util::Hash(extensionName)) {
EXT_SET("VK_EXT_index_type_uint8", supportsUint8Indices);
EXT_SET("VK_EXT_provoking_vertex", supportsLastProvokingVertex); EXT_SET("VK_EXT_provoking_vertex", supportsLastProvokingVertex);
EXT_SET("VK_EXT_vertex_attribute_divisor", supportsVertexAttributeDivisor); EXT_SET("VK_EXT_vertex_attribute_divisor", supportsVertexAttributeDivisor);
EXT_SET("VK_EXT_shader_viewport_index_layer", supportsShaderViewportIndexLayer); EXT_SET("VK_EXT_shader_viewport_index_layer", supportsShaderViewportIndexLayer);
@ -84,6 +85,6 @@ namespace skyline::gpu {
} }
std::string QuirkManager::Summary() { std::string QuirkManager::Summary() {
return fmt::format("\n* Supports Last Provoking Vertex: {}\n* Supports Logical Operations: {}\n* Supports Vertex Attribute Divisor: {}\n* Supports Vertex Attribute Zero Divisor: {}\n* Supports Multiple Viewports: {}\n* Supports Shader Viewport Index: {}\n* Supports SPIR-V 1.4: {}\n* Supports 16-bit FP: {}\n* Supports 8-bit Integers: {}\n* Supports 16-bit Integers: {}\n* Supports 64-bit Integers: {}\n* Supports Atomic 64-bit Integers: {}\n* Supports Floating Point Behavior Control: {}\n* Supports Image Read Without Format: {}\n* Supports Subgroup Vote: {}\n* Subgroup Size: {}", supportsLastProvokingVertex, supportsLogicOp, supportsVertexAttributeDivisor, supportsVertexAttributeZeroDivisor, supportsMultipleViewports, supportsShaderViewportIndexLayer, supportsSpirv14, supportsFloat16, supportsInt8, supportsInt16, supportsInt64, supportsAtomicInt64, supportsFloatControls, supportsImageReadWithoutFormat, supportsSubgroupVote, subgroupSize); return fmt::format("\n* Supports U8 Indices: {}\n* Supports Last Provoking Vertex: {}\n* Supports Logical Operations: {}\n* Supports Vertex Attribute Divisor: {}\n* Supports Vertex Attribute Zero Divisor: {}\n* Supports Multiple Viewports: {}\n* Supports Shader Viewport Index: {}\n* Supports SPIR-V 1.4: {}\n* Supports 16-bit FP: {}\n* Supports 8-bit Integers: {}\n* Supports 16-bit Integers: {}\n* Supports 64-bit Integers: {}\n* Supports Atomic 64-bit Integers: {}\n* Supports Floating Point Behavior Control: {}\n* Supports Image Read Without Format: {}\n* Supports Subgroup Vote: {}\n* Subgroup Size: {}", supportsUint8Indices, supportsLastProvokingVertex, supportsLogicOp, supportsVertexAttributeDivisor, supportsVertexAttributeZeroDivisor, supportsMultipleViewports, supportsShaderViewportIndexLayer, supportsSpirv14, supportsFloat16, supportsInt8, supportsInt16, supportsInt64, supportsAtomicInt64, supportsFloatControls, supportsImageReadWithoutFormat, supportsSubgroupVote, subgroupSize);
} }
} }

View File

@ -12,6 +12,7 @@ namespace skyline::gpu {
*/ */
class QuirkManager { class QuirkManager {
public: public:
bool supportsUint8Indices{}; //!< If the device supports using uint8 indices in index buffers
bool supportsLastProvokingVertex{}; //!< If the device supports setting the last vertex as the provoking vertex (with VK_EXT_provoking_vertex) bool supportsLastProvokingVertex{}; //!< If the device supports setting the last vertex as the provoking vertex (with VK_EXT_provoking_vertex)
bool supportsLogicOp{}; //!< If the device supports framebuffer logical operations during blending bool supportsLogicOp{}; //!< If the device supports framebuffer logical operations during blending
bool supportsVertexAttributeDivisor{}; //!< If the device supports a divisor for instance-rate vertex attributes (with VK_EXT_vertex_attribute_divisor) bool supportsVertexAttributeDivisor{}; //!< If the device supports a divisor for instance-rate vertex attributes (with VK_EXT_vertex_attribute_divisor)

View File

@ -715,5 +715,14 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
}; };
static_assert(sizeof(SetProgramInfo) == (sizeof(u32) * 0x10)); static_assert(sizeof(SetProgramInfo) == (sizeof(u32) * 0x10));
struct IndexBuffer {
Address start, limit; //!< The IOVA bounds of the index buffer
enum class Format : u32 {
Uint8 = 0,
Uint16 = 1,
Uint32 = 2,
} format;
};
#pragma pack(pop) #pragma pack(pop)
} }

View File

@ -447,6 +447,22 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
context.SetConstantBufferSelectorIovaLow(low); context.SetConstantBufferSelectorIovaLow(low);
}) })
MAXWELL3D_STRUCT_STRUCT_CASE(indexBuffer, start, high, {
context.SetIndexBufferStartIovaHigh(high);
})
MAXWELL3D_STRUCT_STRUCT_CASE(indexBuffer, start, low, {
context.SetIndexBufferStartIovaLow(low);
})
MAXWELL3D_STRUCT_STRUCT_CASE(indexBuffer, limit, high, {
context.SetIndexBufferEndIovaHigh(high);
})
MAXWELL3D_STRUCT_STRUCT_CASE(indexBuffer, limit, low, {
context.SetIndexBufferEndIovaLow(low);
})
MAXWELL3D_STRUCT_CASE(indexBuffer, format, {
context.SetIndexBufferFormat(format);
})
default: default:
break; break;
} }

View File

@ -222,6 +222,8 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
Register<0x5A1, u32> provokingVertexIsLast; Register<0x5A1, u32> provokingVertexIsLast;
Register<0x5F2, type::IndexBuffer> indexBuffer;
Register<0x61F, float> depthBiasClamp; Register<0x61F, float> depthBiasClamp;
Register<0x620, std::array<u32, type::VertexBufferCount>> isVertexInputRatePerInstance; //!< A per-VBO boolean denoting if the vertex input rate should be per vertex or per instance Register<0x620, std::array<u32, type::VertexBufferCount>> isVertexInputRatePerInstance; //!< A per-VBO boolean denoting if the vertex input rate should be per vertex or per instance