Implement QuadList support for non-indexed draws

This commit is contained in:
lynxnb 2022-04-20 11:26:06 +02:00
parent bcaf7dfe1c
commit 0d9992cb8e
6 changed files with 117 additions and 13 deletions

View File

@ -171,6 +171,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/shader_manager.cpp ${source_DIR}/skyline/gpu/shader_manager.cpp
${source_DIR}/skyline/gpu/interconnect/command_executor.cpp ${source_DIR}/skyline/gpu/interconnect/command_executor.cpp
${source_DIR}/skyline/gpu/interconnect/command_nodes.cpp ${source_DIR}/skyline/gpu/interconnect/command_nodes.cpp
${source_DIR}/skyline/gpu/interconnect/conversion/quads.cpp
${source_DIR}/skyline/soc/smmu.cpp ${source_DIR}/skyline/soc/smmu.cpp
${source_DIR}/skyline/soc/host1x/syncpoint.cpp ${source_DIR}/skyline/soc/host1x/syncpoint.cpp
${source_DIR}/skyline/soc/host1x/command_fifo.cpp ${source_DIR}/skyline/soc/host1x/command_fifo.cpp

View File

@ -87,6 +87,16 @@ namespace skyline::gpu {
return backing.vkBuffer; return backing.vkBuffer;
} }
/**
* @return A span over the backing of this buffer
* @note This operation **must** be performed only on host-only buffers since synchronization is handled internally for guest-backed buffers
*/
span<u8> GetBackingSpan() {
if (guest)
throw exception("Attempted to get a span of a guest-backed buffer");
return span<u8>(backing);
}
Buffer(GPU &gpu, GuestBuffer guest); Buffer(GPU &gpu, GuestBuffer guest);
/** /**

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include "quads.h"
namespace skyline::gpu::interconnect::conversion::quads {
void GenerateQuadListConversionBuffer(u32 *dest, u32 vertexCount) {
#pragma clang loop vectorize(enable) interleave(enable) unroll(enable)
for (u32 i{}; i < vertexCount; i += 4) {
// Given a quad ABCD, we want to generate triangles ABC & CDA
// Triangle ABC
*(dest++) = i + 0;
*(dest++) = i + 1;
*(dest++) = i + 2;
// Triangle CDA
*(dest++) = i + 2;
*(dest++) = i + 3;
*(dest++) = i + 0;
}
}
}

View File

@ -0,0 +1,33 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2022 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common/base.h>
namespace skyline::gpu::interconnect::conversion::quads {
constexpr u32 EmittedIndexCount{6}; //!< The number of indices needed to draw a quad with two triangles
constexpr u32 QuadVertexCount{4}; //!< The amount of vertices a quad is composed of
/**
* @return The amount of indices emitted converting a buffer with the supplied element count
*/
constexpr u32 GetIndexCount(u32 count) {
return (count * EmittedIndexCount) / QuadVertexCount;
}
/**
* @return The minimum size (in bytes) required to store the quad index buffer of the given type after conversion
* @tparam T The type of an element in the index buffer
*/
template<typename T>
constexpr size_t GetRequiredBufferSize(u32 count) {
return GetIndexCount(count) * sizeof(T);
}
/**
* @brief Create an index buffer that repeats quad vertices to generate a triangle list
* @note The size of the supplied buffer should be at least the size returned by GetRequiredBufferSize()
*/
void GenerateQuadListConversionBuffer(u32 *dest, u32 vertexCount);
}

View File

@ -15,6 +15,7 @@
#include "command_executor.h" #include "command_executor.h"
#include "types/tsc.h" #include "types/tsc.h"
#include "types/tic.h" #include "types/tic.h"
#include "conversion/quads.h"
namespace skyline::gpu::interconnect { namespace skyline::gpu::interconnect {
namespace maxwell3d = soc::gm20b::engine::maxwell3d::type; namespace maxwell3d = soc::gm20b::engine::maxwell3d::type;
@ -1581,6 +1582,25 @@ namespace skyline::gpu::interconnect {
}; };
std::array<VertexAttribute, maxwell3d::VertexAttributeCount> vertexAttributes{}; std::array<VertexAttribute, maxwell3d::VertexAttributeCount> vertexAttributes{};
public:
bool needsQuadConversion{}; //!< Whether the current primitive topology is quads and needs conversion to triangles
private:
std::shared_ptr<Buffer> quadListConversionBuffer{}; //!< Index buffer used for QuadList conversion
/**
* @brief Retrieves an index buffer for converting a non-indexed quad list to a triangle list
* @result A tuple containing a view over the index buffer, the index type and the index count
*/
std::tuple<BufferView, vk::IndexType, u32> GetQuadListConversionBuffer(u32 count) {
vk::DeviceSize size{conversion::quads::GetRequiredBufferSize<u32>(count)};
if (!quadListConversionBuffer || quadListConversionBuffer->GetBackingSpan().size_bytes() < size) {
quadListConversionBuffer = std::make_shared<Buffer>(gpu, size);
conversion::quads::GenerateQuadListConversionBuffer(quadListConversionBuffer->GetBackingSpan().cast<u32>().data(), count);
}
return {quadListConversionBuffer->GetView(0, size), vk::IndexType::eUint32, conversion::quads::GetIndexCount(count)};
}
public: public:
void SetVertexBufferStride(u32 index, u32 stride) { void SetVertexBufferStride(u32 index, u32 stride) {
vertexBuffers[index].bindingDescription.stride = stride; vertexBuffers[index].bindingDescription.stride = stride;
@ -1774,27 +1794,29 @@ namespace skyline::gpu::interconnect {
public: public:
void SetPrimitiveTopology(maxwell3d::PrimitiveTopology topology) { void SetPrimitiveTopology(maxwell3d::PrimitiveTopology topology) {
auto[vkTopology, shaderTopology] = [topology]() -> std::tuple<vk::PrimitiveTopology, ShaderCompiler::InputTopology> { auto[vkTopology, shaderTopology, isQuad] = [topology]() -> std::tuple<vk::PrimitiveTopology, ShaderCompiler::InputTopology, bool> {
using MaxwellTopology = maxwell3d::PrimitiveTopology; using MaxwellTopology = maxwell3d::PrimitiveTopology;
using VkTopology = vk::PrimitiveTopology; using VkTopology = vk::PrimitiveTopology;
using ShaderTopology = ShaderCompiler::InputTopology; using ShaderTopology = ShaderCompiler::InputTopology;
switch (topology) { switch (topology) {
// @fmt:off // @fmt:off
case MaxwellTopology::PointList: return {VkTopology::ePointList, ShaderTopology::Points}; case MaxwellTopology::PointList: return {VkTopology::ePointList, ShaderTopology::Points, false};
case MaxwellTopology::LineList: return {VkTopology::eLineList, ShaderTopology::Lines}; case MaxwellTopology::LineList: return {VkTopology::eLineList, ShaderTopology::Lines, false};
case MaxwellTopology::LineStrip: return {VkTopology::eLineStrip, ShaderTopology::Lines}; case MaxwellTopology::LineStrip: return {VkTopology::eLineStrip, ShaderTopology::Lines, false};
case MaxwellTopology::LineListWithAdjacency: return {VkTopology::eLineListWithAdjacency, ShaderTopology::LinesAdjacency}; case MaxwellTopology::LineListWithAdjacency: return {VkTopology::eLineListWithAdjacency, ShaderTopology::LinesAdjacency, false};
case MaxwellTopology::LineStripWithAdjacency: return {VkTopology::eLineStripWithAdjacency, ShaderTopology::LinesAdjacency}; case MaxwellTopology::LineStripWithAdjacency: return {VkTopology::eLineStripWithAdjacency, ShaderTopology::LinesAdjacency, false};
case MaxwellTopology::TriangleList: return {VkTopology::eTriangleList, ShaderTopology::Triangles}; case MaxwellTopology::TriangleList: return {VkTopology::eTriangleList, ShaderTopology::Triangles, false};
case MaxwellTopology::TriangleStrip: return {VkTopology::eTriangleStrip, ShaderTopology::Triangles}; case MaxwellTopology::TriangleStrip: return {VkTopology::eTriangleStrip, ShaderTopology::Triangles, false};
case MaxwellTopology::TriangleFan: return {VkTopology::eTriangleFan, ShaderTopology::Triangles}; case MaxwellTopology::TriangleFan: return {VkTopology::eTriangleFan, ShaderTopology::Triangles, false};
case MaxwellTopology::TriangleListWithAdjacency: return {VkTopology::eTriangleListWithAdjacency, ShaderTopology::TrianglesAdjacency}; case MaxwellTopology::TriangleListWithAdjacency: return {VkTopology::eTriangleListWithAdjacency, ShaderTopology::TrianglesAdjacency, false};
case MaxwellTopology::TriangleStripWithAdjacency: return {VkTopology::eTriangleStripWithAdjacency, ShaderTopology::TrianglesAdjacency}; case MaxwellTopology::TriangleStripWithAdjacency: return {VkTopology::eTriangleStripWithAdjacency, ShaderTopology::TrianglesAdjacency, false};
case MaxwellTopology::PatchList: return {VkTopology::ePatchList, ShaderTopology::Triangles}; case MaxwellTopology::QuadList: return {VkTopology::eTriangleList, ShaderTopology::Triangles, true};
case MaxwellTopology::PatchList: return {VkTopology::ePatchList, ShaderTopology::Triangles, false};
// @fmt:on // @fmt:on
@ -1804,6 +1826,7 @@ namespace skyline::gpu::interconnect {
}(); }();
inputAssemblyState.topology = vkTopology; inputAssemblyState.topology = vkTopology;
needsQuadConversion = isQuad;
UpdateRuntimeInformation(runtimeInfo.input_topology, shaderTopology, maxwell3d::PipelineStage::Geometry); UpdateRuntimeInformation(runtimeInfo.input_topology, shaderTopology, maxwell3d::PipelineStage::Geometry);
} }
@ -2595,6 +2618,18 @@ namespace skyline::gpu::interconnect {
auto boundIndexBuffer{std::make_shared<BoundIndexBuffer>()}; auto boundIndexBuffer{std::make_shared<BoundIndexBuffer>()};
if constexpr (IsIndexed) { if constexpr (IsIndexed) {
auto indexBufferView{GetIndexBuffer(count)}; auto indexBufferView{GetIndexBuffer(count)};
if (needsQuadConversion) {
if (indexBufferView) {
throw exception("Indexed quad conversion is not supported");
} else {
auto[bufferView, indexType, indexCount] = GetQuadListConversionBuffer(count);
indexBufferView = bufferView;
indexBuffer.type = indexType;
count = indexCount;
}
}
{ {
std::scoped_lock lock(indexBufferView); std::scoped_lock lock(indexBufferView);

View File

@ -618,6 +618,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
}) })
ENGINE_CASE(drawVertexCount, { ENGINE_CASE(drawVertexCount, {
if (context.needsQuadConversion)
context.DrawIndexed(drawVertexCount, *registers.drawVertexFirst, 0);
else
context.DrawVertex(drawVertexCount, *registers.drawVertexFirst); context.DrawVertex(drawVertexCount, *registers.drawVertexFirst);
}) })