mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 12:41:50 +01:00
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.
This commit is contained in:
parent
57b0d6a2fb
commit
4dcf12c4c0
@ -1113,4 +1113,115 @@ namespace skyline::gpu::interconnect {
|
||||
vk::PipelineMultisampleStateCreateInfo multisampleState{
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||
};
|
||||
|
||||
/* Draws */
|
||||
private:
|
||||
vk::GraphicsPipelineCreateInfo pipelineState{
|
||||
.pVertexInputState = &vertexState.get<vk::PipelineVertexInputStateCreateInfo>(),
|
||||
.pInputAssemblyState = &inputAssemblyState,
|
||||
.pViewportState = &viewportState,
|
||||
.pRasterizationState = &rasterizerState.get<vk::PipelineRasterizationStateCreateInfo>(),
|
||||
.pMultisampleState = &multisampleState,
|
||||
.pDepthStencilState = nullptr,
|
||||
.pColorBlendState = &blendState,
|
||||
.pDynamicState = nullptr,
|
||||
};
|
||||
|
||||
public:
|
||||
void Draw(u32 vertexCount, u32 firstVertex) {
|
||||
// Render Target Setup
|
||||
boost::container::static_vector<std::scoped_lock<TextureView>, maxwell3d::RenderTargetCount> renderTargetLocks;
|
||||
std::vector<TextureView *> 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<u32>(activeRenderTargets.size());
|
||||
|
||||
// Vertex Buffer Setup
|
||||
boost::container::static_vector<std::scoped_lock<BufferView>, maxwell3d::VertexBufferCount> vertexBufferLocks;
|
||||
std::array<vk::Buffer, maxwell3d::VertexBufferCount> vertexBufferHandles{};
|
||||
std::array<vk::DeviceSize, maxwell3d::VertexBufferCount> 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<vk::PipelineVertexInputStateCreateInfo>().vertexBindingDescriptionCount = static_cast<u32>(vertexBindingDescriptions.size());
|
||||
vertexState.get<vk::PipelineVertexInputDivisorStateCreateInfoEXT>().vertexBindingDivisorCount = static_cast<u32>(vertexBindingDivisorsDescriptions.size());
|
||||
|
||||
// Vertex Attribute Setup
|
||||
vertexAttributesDescriptions.clear();
|
||||
|
||||
for (auto &vertexAttribute : vertexAttributes)
|
||||
if (vertexAttribute.enabled)
|
||||
vertexAttributesDescriptions.push_back(vertexAttribute.description);
|
||||
|
||||
vertexState.get<vk::PipelineVertexInputStateCreateInfo>().vertexAttributeDescriptionCount = static_cast<u32>(vertexAttributesDescriptions.size());
|
||||
|
||||
// Shader Setup
|
||||
auto shaderStages{GetShaderStages()};
|
||||
pipelineState.pStages = shaderStages.data();
|
||||
pipelineState.stageCount = static_cast<u32>(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<FenceCycle> &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<Storage>(std::move(pipelineLayout), vk::raii::Pipeline(vkDevice, pipeline.value)));
|
||||
}, vk::Rect2D{
|
||||
.extent = activeRenderTargets[0]->texture->dimensions,
|
||||
}, {}, activeRenderTargets);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -72,6 +72,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
|
||||
Register<0x280, std::array<type::ViewportTransform, type::ViewportCount>> viewportTransforms;
|
||||
Register<0x300, std::array<type::Viewport, type::ViewportCount>> 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<u32, 4>> clearColorValue;
|
||||
Register<0x364, u32> clearDepthValue;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user