From 4dcf12c4c0d2efadd480f99cf578cd5138f68633 Mon Sep 17 00:00:00 2001 From: PixelyIon Date: Tue, 7 Dec 2021 03:18:25 +0530 Subject: [PATCH] Implement Maxwell3D Draws Uses all Maxwell3D state converted into Vulkan state to do an equivalent draw on the host GPU, it sets up RT/Vertex Buffer/Vertex Attribute/Shader state and creates a stubbed out `VkPipelineLayout` for the draw. Any descriptor state isn't currently handled and is yet to be implemented, currently there's no Vulkan pipeline cache supplied which will be implemented subsequently. --- .../gpu/interconnect/graphics_context.h | 111 ++++++++++++++++++ .../skyline/soc/gm20b/engines/maxwell_3d.cpp | 6 +- .../skyline/soc/gm20b/engines/maxwell_3d.h | 3 + 3 files changed, 119 insertions(+), 1 deletion(-) 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 e4a5027a..17b5135a 100644 --- a/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h +++ b/app/src/main/cpp/skyline/gpu/interconnect/graphics_context.h @@ -1113,4 +1113,115 @@ namespace skyline::gpu::interconnect { vk::PipelineMultisampleStateCreateInfo multisampleState{ .rasterizationSamples = vk::SampleCountFlagBits::e1, }; + + /* Draws */ + private: + vk::GraphicsPipelineCreateInfo pipelineState{ + .pVertexInputState = &vertexState.get(), + .pInputAssemblyState = &inputAssemblyState, + .pViewportState = &viewportState, + .pRasterizationState = &rasterizerState.get(), + .pMultisampleState = &multisampleState, + .pDepthStencilState = nullptr, + .pColorBlendState = &blendState, + .pDynamicState = nullptr, + }; + + public: + void Draw(u32 vertexCount, u32 firstVertex) { + // Render Target Setup + boost::container::static_vector, maxwell3d::RenderTargetCount> renderTargetLocks; + std::vector activeRenderTargets; + + for (u32 index{}; index < maxwell3d::RenderTargetCount; index++) { + auto renderTarget{GetRenderTarget(index)}; + if (renderTarget) { + renderTargetLocks.emplace_back(*renderTarget); + activeRenderTargets.push_back(renderTarget); + } + } + + blendState.attachmentCount = static_cast(activeRenderTargets.size()); + + // Vertex Buffer Setup + boost::container::static_vector, maxwell3d::VertexBufferCount> vertexBufferLocks; + std::array vertexBufferHandles{}; + std::array vertexBufferOffsets{}; + + vertexBindingDescriptions.clear(); + vertexBindingDivisorsDescriptions.clear(); + + for (u32 index{}; index < maxwell3d::VertexBufferCount; index++) { + auto vertexBufferView{GetVertexBuffer(index)}; + if (vertexBufferView) { + auto &vertexBuffer{vertexBuffers[index]}; + vertexBindingDescriptions.push_back(vertexBuffer.bindingDescription); + vertexBindingDivisorsDescriptions.push_back(vertexBuffer.bindingDivisorDescription); + + vertexBufferLocks.emplace_back(*vertexBufferView); + executor.AttachBuffer(vertexBufferView->buffer); + vertexBufferHandles[index] = vertexBufferView->buffer->GetBacking(); + vertexBufferOffsets[index] = vertexBufferView->offset; + } + } + + vertexState.get().vertexBindingDescriptionCount = static_cast(vertexBindingDescriptions.size()); + vertexState.get().vertexBindingDivisorCount = static_cast(vertexBindingDivisorsDescriptions.size()); + + // Vertex Attribute Setup + vertexAttributesDescriptions.clear(); + + for (auto &vertexAttribute : vertexAttributes) + if (vertexAttribute.enabled) + vertexAttributesDescriptions.push_back(vertexAttribute.description); + + vertexState.get().vertexAttributeDescriptionCount = static_cast(vertexAttributesDescriptions.size()); + + // Shader Setup + auto shaderStages{GetShaderStages()}; + pipelineState.pStages = shaderStages.data(); + pipelineState.stageCount = static_cast(shaderStages.size()); + + // Submit Draw + executor.AddSubpass([vertexCount, firstVertex, &vkDevice = gpu.vkDevice, pipelineCreateInfo = pipelineState, vertexBufferHandles = std::move(vertexBufferHandles), vertexBufferOffsets = std::move(vertexBufferOffsets), pipelineCache = *pipelineCache](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr &cycle, GPU &, vk::RenderPass renderPass, u32 subpassIndex) mutable { + vk::PipelineLayoutCreateInfo layoutCreateInfo{}; + vk::raii::PipelineLayout pipelineLayout(vkDevice, layoutCreateInfo); + pipelineCreateInfo.layout = *pipelineLayout; + + pipelineCreateInfo.renderPass = renderPass; + pipelineCreateInfo.subpass = subpassIndex; + + auto pipeline{(*vkDevice).createGraphicsPipeline(nullptr, pipelineCreateInfo, nullptr, *vkDevice.getDispatcher())}; + if (pipeline.result != vk::Result::eSuccess) + vk::throwResultException(pipeline.result, __builtin_FUNCTION()); + + commandBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.value); + + for (u32 bindingIndex{}; bindingIndex != vertexBufferHandles.size(); bindingIndex++) { + // We need to bind all non-null vertex buffers while skipping any null ones + if (vertexBufferHandles[bindingIndex]) { + u32 bindingEndIndex{bindingIndex + 1}; + while (bindingEndIndex < vertexBufferHandles.size() && vertexBufferHandles[bindingEndIndex]) + bindingEndIndex++; + + u32 bindingCount{bindingEndIndex - bindingIndex}; + commandBuffer.bindVertexBuffers(bindingIndex, span(vertexBufferHandles.data() + bindingIndex, bindingCount), span(vertexBufferOffsets.data() + bindingIndex, bindingCount)); + } + } + + commandBuffer.draw(vertexCount, 1, firstVertex, 0); + + struct Storage : FenceCycleDependency { + vk::raii::PipelineLayout pipelineLayout; + vk::raii::Pipeline pipeline; + + Storage(vk::raii::PipelineLayout&& pipelineLayout, vk::raii::Pipeline&& pipeline) : pipelineLayout(std::move(pipelineLayout)), pipeline(std::move(pipeline)) {} + }; + + cycle->AttachObject(std::make_shared(std::move(pipelineLayout), vk::raii::Pipeline(vkDevice, pipeline.value))); + }, vk::Rect2D{ + .extent = activeRenderTargets[0]->texture->dimensions, + }, {}, activeRenderTargets); + } + }; } 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 c0592f54..6e67f87e 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 @@ -12,7 +12,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d { } __attribute__((always_inline)) void Maxwell3D::CallMethod(u32 method, u32 argument, bool lastCall) { - Logger::Error("Called method in Maxwell 3D: 0x{:X} args: 0x{:X}", method, argument); + Logger::Debug("Called method in Maxwell 3D: 0x{:X} args: 0x{:X}", method, argument); // Methods that are greater than the register size are for macro control if (method >= RegisterCount) [[unlikely]] { @@ -430,6 +430,10 @@ namespace skyline::soc::gm20b::engine::maxwell3d { context.ClearBuffers(clearBuffers); }) + MAXWELL3D_CASE(drawVertexCount, { + context.Draw(drawVertexCount, *registers.drawVertexFirst); + }) + MAXWELL3D_STRUCT_CASE(semaphore, info, { switch (info.op) { case type::SemaphoreInfo::Op::Release: 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 a940aba3..87782a5a 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 @@ -72,6 +72,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d { Register<0x280, std::array> viewportTransforms; Register<0x300, std::array> viewports; + Register<0x35D, u32> drawVertexFirst; //!< The index of the first vertex to draw + Register<0x35E, u32> drawVertexCount; //!< The amount of vertices to draw, calling this method triggers drawing + Register<0x360, std::array> clearColorValue; Register<0x364, u32> clearDepthValue;