mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 18:11:51 +01:00
Introduce CommandExecutor
+ Implement ClearBuffers
+ More RT Formats
This commit introduces the `CommandExecutor` which is responsible for creating and orchestrating a Vulkan command graph comprising of `CommandNode`s that construct all the objects required for rendering. As a result of the infrastructure provided by `CommandExecutor`, `ClearBuffers` could be implemented and be appropriately utilized. A bug regarding scissors was also determined and fixed in the PR, the extent of them were previously inaccurate and this has now been fixed. Note: We don't synchronize any textures from the guest for now as this would override the contents on the host, this'll be fixed with the appropriate write tracking but it also results in a black screen for anything that writes to FB
This commit is contained in:
parent
3879d573d5
commit
239d2625e2
@ -102,6 +102,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
||||||
${source_DIR}/skyline/gpu/texture/texture.cpp
|
${source_DIR}/skyline/gpu/texture/texture.cpp
|
||||||
${source_DIR}/skyline/gpu/presentation_engine.cpp
|
${source_DIR}/skyline/gpu/presentation_engine.cpp
|
||||||
|
${source_DIR}/skyline/gpu/interconnect/command_executor.cpp
|
||||||
${source_DIR}/skyline/soc/gm20b.cpp
|
${source_DIR}/skyline/soc/gm20b.cpp
|
||||||
${source_DIR}/skyline/soc/host1x/syncpoint.cpp
|
${source_DIR}/skyline/soc/host1x/syncpoint.cpp
|
||||||
${source_DIR}/skyline/soc/gm20b/gpfifo.cpp
|
${source_DIR}/skyline/soc/gm20b/gpfifo.cpp
|
||||||
|
@ -0,0 +1,47 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <gpu.h>
|
||||||
|
#include "command_executor.h"
|
||||||
|
|
||||||
|
namespace skyline::gpu::interconnect {
|
||||||
|
void CommandExecutor::AddSubpass(const std::function<void(vk::raii::CommandBuffer &, const std::shared_ptr<FenceCycle> &, GPU &)> &function, vk::Rect2D renderArea, std::vector<TextureView> inputAttachments, std::vector<TextureView> colorAttachments, std::optional<TextureView> depthStencilAttachment) {
|
||||||
|
if (renderpass) { // TODO: Subpass support (&& renderpass->renderArea != renderArea)
|
||||||
|
nodes.emplace_back(std::in_place_type_t<node::RenderpassEndNode>());
|
||||||
|
renderpass = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool newRenderpass{renderpass == nullptr};
|
||||||
|
if (newRenderpass)
|
||||||
|
// We need to create a render pass if one doesn't already exist or the current one isn't compatible
|
||||||
|
renderpass = &std::get<node::RenderpassNode>(nodes.emplace_back(std::in_place_type_t<node::RenderpassNode>(), renderArea));
|
||||||
|
|
||||||
|
renderpass->AddSubpass(inputAttachments, colorAttachments, depthStencilAttachment);
|
||||||
|
if (newRenderpass)
|
||||||
|
nodes.emplace_back(std::in_place_type_t<node::FunctionNode>(), function);
|
||||||
|
else
|
||||||
|
nodes.emplace_back(std::in_place_type_t<node::NextSubpassNode>(), function);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CommandExecutor::Execute() {
|
||||||
|
if (!nodes.empty()) {
|
||||||
|
if (renderpass) {
|
||||||
|
nodes.emplace_back(std::in_place_type_t<node::RenderpassEndNode>());
|
||||||
|
renderpass = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
gpu.scheduler.SubmitWithCycle([this](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle) {
|
||||||
|
using namespace node;
|
||||||
|
for (NodeVariant &node : nodes) {
|
||||||
|
std::visit(VariantVisitor{
|
||||||
|
[&](FunctionNode &node) { node(commandBuffer, cycle, gpu); },
|
||||||
|
[&](RenderpassNode &node) { node(commandBuffer, cycle, gpu); },
|
||||||
|
[&](RenderpassEndNode &node) { node(commandBuffer, cycle, gpu); },
|
||||||
|
}, node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
nodes.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
31
app/src/main/cpp/skyline/gpu/interconnect/command_executor.h
Normal file
31
app/src/main/cpp/skyline/gpu/interconnect/command_executor.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <boost/container/stable_vector.hpp>
|
||||||
|
#include "command_nodes.h"
|
||||||
|
|
||||||
|
namespace skyline::gpu::interconnect {
|
||||||
|
/**
|
||||||
|
* @brief Assembles a Vulkan command stream with various nodes and manages execution of the produced graph
|
||||||
|
* @note This class is **NOT** thread-safe and should not be utilized by multiple threads concurrently
|
||||||
|
*/
|
||||||
|
class CommandExecutor {
|
||||||
|
private:
|
||||||
|
GPU &gpu;
|
||||||
|
boost::container::stable_vector<node::NodeVariant> nodes;
|
||||||
|
node::RenderpassNode *renderpass{};
|
||||||
|
|
||||||
|
public:
|
||||||
|
CommandExecutor(const DeviceState &state) : gpu(*state.gpu) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Adds a command that needs to be executed inside a subpass configured with certain attachments
|
||||||
|
* @note Any texture supplied to this **must** be locked by the calling thread, it should also undergo no persistent layout transitions till execution
|
||||||
|
*/
|
||||||
|
void AddSubpass(const std::function<void(vk::raii::CommandBuffer &, const std::shared_ptr<FenceCycle> &, GPU &)> &function, vk::Rect2D renderArea, std::vector<TextureView> inputAttachments = {}, std::vector<TextureView> colorAttachments = {}, std::optional<TextureView> depthStencilAttachment = {});
|
||||||
|
|
||||||
|
void Execute();
|
||||||
|
};
|
||||||
|
}
|
231
app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h
Normal file
231
app/src/main/cpp/skyline/gpu/interconnect/command_nodes.h
Normal file
@ -0,0 +1,231 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <gpu.h>
|
||||||
|
|
||||||
|
namespace skyline::gpu::interconnect::node {
|
||||||
|
/**
|
||||||
|
* @brief A generic node for simply executing a function
|
||||||
|
*/
|
||||||
|
template<typename FunctionSignature = void(vk::raii::CommandBuffer &, const std::shared_ptr<FenceCycle> &, GPU &)>
|
||||||
|
struct FunctionNodeBase {
|
||||||
|
std::function<FunctionSignature> function;
|
||||||
|
|
||||||
|
FunctionNodeBase(std::function<FunctionSignature> function) : function(function) {}
|
||||||
|
|
||||||
|
template<class... Args>
|
||||||
|
void operator()(Args &&... args) {
|
||||||
|
function(std::forward<Args>(args)...);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using FunctionNode = FunctionNodeBase<>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Creates and begins a VkRenderpass while tying lifetimes of all bound resources to a GPU fence
|
||||||
|
*/
|
||||||
|
struct RenderpassNode {
|
||||||
|
private:
|
||||||
|
struct Storage : public FenceCycleDependency {
|
||||||
|
vk::raii::Device *device{};
|
||||||
|
vk::Framebuffer framebuffer{};
|
||||||
|
vk::RenderPass renderpass{};
|
||||||
|
std::vector<std::shared_ptr<Texture>> textures;
|
||||||
|
|
||||||
|
~Storage() {
|
||||||
|
if (device) {
|
||||||
|
if (framebuffer)
|
||||||
|
(**device).destroy(framebuffer, nullptr, *device->getDispatcher());
|
||||||
|
if (renderpass)
|
||||||
|
(**device).destroy(renderpass, nullptr, *device->getDispatcher());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
std::shared_ptr<Storage> storage;
|
||||||
|
|
||||||
|
std::vector<vk::ImageView> attachments;
|
||||||
|
std::vector<vk::AttachmentDescription> attachmentDescriptions;
|
||||||
|
|
||||||
|
std::vector<vk::AttachmentReference> attachmentReferences;
|
||||||
|
std::vector<boost::container::small_vector<u32, 5>> preserveAttachmentReferences; //!< Any attachment that must be preserved to be utilized by a future subpass, these are stored per-subpass to ensure contiguity
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<vk::SubpassDescription> subpassDescriptions;
|
||||||
|
std::vector<vk::SubpassDependency> subpassDependencies;
|
||||||
|
|
||||||
|
vk::Rect2D renderArea;
|
||||||
|
std::vector<vk::ClearValue> clearValues;
|
||||||
|
|
||||||
|
RenderpassNode(vk::Rect2D renderArea) : storage(std::make_shared<Storage>()), renderArea(renderArea) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note Any preservation of attachments from previous subpasses is automatically handled by this
|
||||||
|
* @return The index of the attachment in the render pass which can be utilized with VkAttachmentReference
|
||||||
|
*/
|
||||||
|
u32 AddAttachment(TextureView &view) {
|
||||||
|
auto &textures{storage->textures};
|
||||||
|
auto texture{std::find(textures.begin(), textures.end(), view.backing)};
|
||||||
|
if (texture == textures.end())
|
||||||
|
textures.push_back(view.backing);
|
||||||
|
|
||||||
|
vk::AttachmentDescription attachmentDescription{
|
||||||
|
.format = *view.format,
|
||||||
|
.initialLayout = view.backing->layout,
|
||||||
|
.finalLayout = view.backing->layout,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto vkView{view.GetView()};
|
||||||
|
auto attachment{std::find(attachments.begin(), attachments.end(), vkView)};
|
||||||
|
if (attachment == attachments.end() || attachmentDescriptions[std::distance(attachments.begin(), attachment)] != attachmentDescription) {
|
||||||
|
// If we cannot find any matches for the specified attachment, we add it as a new one
|
||||||
|
attachments.push_back(vkView);
|
||||||
|
attachmentDescriptions.push_back(attachmentDescription);
|
||||||
|
return attachments.size() - 1;
|
||||||
|
} else {
|
||||||
|
// If we've got a match from a previous subpass, we need to preserve the attachment till the current subpass
|
||||||
|
auto attachmentIndex{std::distance(attachments.begin(), attachment)};
|
||||||
|
auto attachmentReferenceIt{std::find_if(attachmentReferences.begin(), attachmentReferences.end(), [&](const vk::AttachmentReference &reference) {
|
||||||
|
return reference.attachment == attachmentIndex;
|
||||||
|
})};
|
||||||
|
|
||||||
|
auto attachmentReferenceOffset{std::distance(attachmentReferences.begin(), attachmentReferenceIt) * sizeof(vk::AttachmentReference)};
|
||||||
|
auto subpassDescriptionIt{std::find_if(subpassDescriptions.begin(), subpassDescriptions.end(), [&](const vk::SubpassDescription &description) {
|
||||||
|
return reinterpret_cast<uintptr_t>(description.pDepthStencilAttachment) > attachmentReferenceOffset;
|
||||||
|
})};
|
||||||
|
|
||||||
|
for (ssize_t subpassIndex{std::distance(subpassDescriptions.begin(), subpassDescriptionIt)}; subpassIndex != subpassDescriptions.size(); subpassIndex++)
|
||||||
|
preserveAttachmentReferences[subpassIndex].push_back(attachmentIndex);
|
||||||
|
|
||||||
|
return std::distance(attachments.begin(), attachment);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddSubpass(std::vector<TextureView> &inputAttachments, std::vector<TextureView> &colorAttachments, std::optional<TextureView> &depthStencilAttachment) {
|
||||||
|
attachmentReferences.reserve(attachmentReferences.size() + inputAttachments.size() + colorAttachments.size() + (depthStencilAttachment ? 1 : 0));
|
||||||
|
|
||||||
|
auto inputAttachmentsOffset{attachmentReferences.size() * sizeof(vk::AttachmentReference)};
|
||||||
|
for (auto &attachment : inputAttachments) {
|
||||||
|
attachmentReferences.push_back(vk::AttachmentReference{
|
||||||
|
.attachment = AddAttachment(attachment),
|
||||||
|
.layout = attachment.backing->layout,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto colorAttachmentsOffset{attachmentReferences.size() * sizeof(vk::AttachmentReference)};
|
||||||
|
for (auto &attachment : colorAttachments) {
|
||||||
|
attachmentReferences.push_back(vk::AttachmentReference{
|
||||||
|
.attachment = AddAttachment(attachment),
|
||||||
|
.layout = attachment.backing->layout,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto depthStencilAttachmentOffset{attachmentReferences.size() * sizeof(vk::AttachmentReference)};
|
||||||
|
if (depthStencilAttachment) {
|
||||||
|
attachmentReferences.push_back(vk::AttachmentReference{
|
||||||
|
.attachment = AddAttachment(*depthStencilAttachment),
|
||||||
|
.layout = depthStencilAttachment->backing->layout,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
preserveAttachmentReferences.emplace_back(); // We need to create storage for any attachments that might need to preserved by this pass
|
||||||
|
|
||||||
|
// Note: We encode the offsets as the pointers due to vector pointer invalidation, the vector offset will be added to them prior to submission
|
||||||
|
subpassDescriptions.push_back(vk::SubpassDescription{
|
||||||
|
.pipelineBindPoint = vk::PipelineBindPoint::eGraphics,
|
||||||
|
.inputAttachmentCount = static_cast<u32>(inputAttachments.size()),
|
||||||
|
.pInputAttachments = reinterpret_cast<vk::AttachmentReference *>(inputAttachmentsOffset),
|
||||||
|
.colorAttachmentCount = static_cast<u32>(colorAttachments.size()),
|
||||||
|
.pColorAttachments = reinterpret_cast<vk::AttachmentReference *>(colorAttachmentsOffset),
|
||||||
|
.pDepthStencilAttachment = reinterpret_cast<vk::AttachmentReference *>(depthStencilAttachment ? depthStencilAttachmentOffset : std::numeric_limits<uintptr_t>::max()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
|
||||||
|
storage->device = &gpu.vkDevice;
|
||||||
|
|
||||||
|
auto preserveAttachmentIt{preserveAttachmentReferences.begin()};
|
||||||
|
auto attachmentReferenceOffset{reinterpret_cast<uintptr_t>(attachmentReferences.data())};
|
||||||
|
for (auto &subpassDescription : subpassDescriptions) {
|
||||||
|
subpassDescription.pInputAttachments = reinterpret_cast<vk::AttachmentReference *>(attachmentReferenceOffset + reinterpret_cast<uintptr_t>(subpassDescription.pInputAttachments));
|
||||||
|
subpassDescription.pColorAttachments = reinterpret_cast<vk::AttachmentReference *>(attachmentReferenceOffset + reinterpret_cast<uintptr_t>(subpassDescription.pColorAttachments));
|
||||||
|
|
||||||
|
auto depthStencilAttachmentOffset{reinterpret_cast<uintptr_t>(subpassDescription.pDepthStencilAttachment)};
|
||||||
|
if (depthStencilAttachmentOffset != std::numeric_limits<uintptr_t>::max())
|
||||||
|
subpassDescription.pDepthStencilAttachment = reinterpret_cast<vk::AttachmentReference *>(attachmentReferenceOffset + depthStencilAttachmentOffset);
|
||||||
|
else
|
||||||
|
subpassDescription.pDepthStencilAttachment = nullptr;
|
||||||
|
|
||||||
|
subpassDescription.preserveAttachmentCount = preserveAttachmentIt->size();
|
||||||
|
subpassDescription.pPreserveAttachments = preserveAttachmentIt->data();
|
||||||
|
preserveAttachmentIt++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto &texture : storage->textures) {
|
||||||
|
texture->lock();
|
||||||
|
texture->WaitOnBacking();
|
||||||
|
if (texture->cycle != cycle)
|
||||||
|
texture->WaitOnFence();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto renderpass{(*gpu.vkDevice).createRenderPass(vk::RenderPassCreateInfo{
|
||||||
|
.attachmentCount = static_cast<u32>(attachmentDescriptions.size()),
|
||||||
|
.pAttachments = attachmentDescriptions.data(),
|
||||||
|
.subpassCount = static_cast<u32>(subpassDescriptions.size()),
|
||||||
|
.pSubpasses = subpassDescriptions.data(),
|
||||||
|
.dependencyCount = static_cast<u32>(subpassDependencies.size()),
|
||||||
|
.pDependencies = subpassDependencies.data(),
|
||||||
|
}, nullptr, *gpu.vkDevice.getDispatcher())};
|
||||||
|
storage->renderpass = renderpass;
|
||||||
|
|
||||||
|
auto framebuffer{(*gpu.vkDevice).createFramebuffer(vk::FramebufferCreateInfo{
|
||||||
|
.renderPass = renderpass,
|
||||||
|
.attachmentCount = static_cast<u32>(attachments.size()),
|
||||||
|
.pAttachments = attachments.data(),
|
||||||
|
.width = renderArea.extent.width,
|
||||||
|
.height = renderArea.extent.height,
|
||||||
|
.layers = 1,
|
||||||
|
}, nullptr, *gpu.vkDevice.getDispatcher())};
|
||||||
|
storage->framebuffer = framebuffer;
|
||||||
|
|
||||||
|
commandBuffer.beginRenderPass(vk::RenderPassBeginInfo{
|
||||||
|
.renderPass = renderpass,
|
||||||
|
.framebuffer = framebuffer,
|
||||||
|
.renderArea = renderArea,
|
||||||
|
.clearValueCount = static_cast<u32>(clearValues.size()),
|
||||||
|
.pClearValues = clearValues.data(),
|
||||||
|
}, vk::SubpassContents::eInline);
|
||||||
|
|
||||||
|
cycle->AttachObjects(storage);
|
||||||
|
|
||||||
|
for (auto &texture : storage->textures) {
|
||||||
|
texture->unlock();
|
||||||
|
texture->cycle = cycle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A FunctionNode which progresses to the next subpass prior to calling the function
|
||||||
|
*/
|
||||||
|
struct NextSubpassNode : FunctionNode {
|
||||||
|
using FunctionNode::FunctionNode;
|
||||||
|
|
||||||
|
void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
|
||||||
|
commandBuffer.nextSubpass(vk::SubpassContents::eInline);
|
||||||
|
FunctionNode::operator()(commandBuffer, cycle, gpu);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Ends a VkRenderpass that would be created prior with RenderpassNode
|
||||||
|
*/
|
||||||
|
struct RenderpassEndNode {
|
||||||
|
void operator()(vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &cycle, GPU &gpu) {
|
||||||
|
commandBuffer.endRenderPass();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using NodeVariant = std::variant<FunctionNode, RenderpassNode, NextSubpassNode, RenderpassEndNode>; //!< A variant encompassing all command nodes types
|
||||||
|
}
|
@ -3,13 +3,12 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vulkan/vulkan_raii.hpp>
|
|
||||||
#include <gpu.h>
|
|
||||||
#include <gpu/texture/format.h>
|
#include <gpu/texture/format.h>
|
||||||
#include <soc/gm20b/gmmu.h>
|
#include <soc/gm20b/gmmu.h>
|
||||||
#include <soc/gm20b/engines/maxwell/types.h>
|
#include <soc/gm20b/engines/maxwell/types.h>
|
||||||
|
#include "command_executor.h"
|
||||||
|
|
||||||
namespace skyline::gpu::context {
|
namespace skyline::gpu::interconnect {
|
||||||
namespace maxwell3d = soc::gm20b::engine::maxwell3d::type;
|
namespace maxwell3d = soc::gm20b::engine::maxwell3d::type;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -20,6 +19,7 @@ namespace skyline::gpu::context {
|
|||||||
private:
|
private:
|
||||||
GPU &gpu;
|
GPU &gpu;
|
||||||
soc::gm20b::GMMU &gmmu;
|
soc::gm20b::GMMU &gmmu;
|
||||||
|
gpu::interconnect::CommandExecutor &executor;
|
||||||
|
|
||||||
struct RenderTarget {
|
struct RenderTarget {
|
||||||
bool disabled{}; //!< If this RT has been disabled and will be an unbound attachment instead
|
bool disabled{}; //!< If this RT has been disabled and will be an unbound attachment instead
|
||||||
@ -50,7 +50,7 @@ namespace skyline::gpu::context {
|
|||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GraphicsContext(GPU &gpu, soc::gm20b::GMMU &gmmu) : gpu(gpu), gmmu(gmmu) {
|
GraphicsContext(GPU &gpu, soc::gm20b::GMMU &gmmu, gpu::interconnect::CommandExecutor &executor) : gpu(gpu), gmmu(gmmu), executor(executor) {
|
||||||
scissors.fill(DefaultScissor);
|
scissors.fill(DefaultScissor);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,8 +88,12 @@ namespace skyline::gpu::context {
|
|||||||
switch (format) {
|
switch (format) {
|
||||||
case maxwell3d::RenderTarget::ColorFormat::None:
|
case maxwell3d::RenderTarget::ColorFormat::None:
|
||||||
return {};
|
return {};
|
||||||
|
case maxwell3d::RenderTarget::ColorFormat::A2B10G10R10Unorm:
|
||||||
|
return format::A2B10G10R10Unorm;
|
||||||
case maxwell3d::RenderTarget::ColorFormat::R8G8B8A8Unorm:
|
case maxwell3d::RenderTarget::ColorFormat::R8G8B8A8Unorm:
|
||||||
return format::RGBA8888Unorm;
|
return format::R8G8B8A8Unorm;
|
||||||
|
case maxwell3d::RenderTarget::ColorFormat::A8B8G8R8Srgb:
|
||||||
|
return format::A8B8G8R8Srgb;
|
||||||
default:
|
default:
|
||||||
throw exception("Cannot translate the supplied RT format: 0x{:X}", static_cast<u32>(format));
|
throw exception("Cannot translate the supplied RT format: 0x{:X}", static_cast<u32>(format));
|
||||||
}
|
}
|
||||||
@ -148,7 +152,8 @@ namespace skyline::gpu::context {
|
|||||||
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
|
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest));
|
renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest);
|
||||||
|
return &renderTarget.view.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
void UpdateRenderTargetControl(maxwell3d::RenderTargetControl control) {
|
void UpdateRenderTargetControl(maxwell3d::RenderTargetControl control) {
|
||||||
@ -186,10 +191,34 @@ namespace skyline::gpu::context {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ClearBuffers(maxwell3d::ClearBuffers clear) {
|
void ClearBuffers(maxwell3d::ClearBuffers clear) {
|
||||||
auto renderTarget{GetRenderTarget(renderTargetControl.Map(clear.renderTargetId))};
|
auto renderTargetIndex{renderTargetControl[clear.renderTargetId]};
|
||||||
if (renderTarget) {
|
auto renderTargetPointer{GetRenderTarget(renderTargetIndex)};
|
||||||
std::lock_guard lock(*renderTarget->backing);
|
if (renderTargetPointer) {
|
||||||
// TODO: Clear the buffer
|
auto renderTarget{*renderTargetPointer};
|
||||||
|
std::lock_guard lock(*renderTarget.backing);
|
||||||
|
|
||||||
|
vk::ImageAspectFlags aspect{};
|
||||||
|
if (clear.depth)
|
||||||
|
aspect |= vk::ImageAspectFlagBits::eDepth;
|
||||||
|
if (clear.stencil)
|
||||||
|
aspect |= vk::ImageAspectFlagBits::eStencil;
|
||||||
|
if (clear.red || clear.green || clear.blue || clear.alpha)
|
||||||
|
aspect |= vk::ImageAspectFlagBits::eColor;
|
||||||
|
aspect &= renderTarget.format->vkAspect;
|
||||||
|
|
||||||
|
executor.AddSubpass([aspect = aspect, clearColorValue = clearColorValue, layerId = clear.layerId, scissor = scissors.at(renderTargetIndex)](vk::raii::CommandBuffer &commandBuffer, const std::shared_ptr<FenceCycle> &, GPU &) {
|
||||||
|
commandBuffer.clearAttachments(vk::ClearAttachment{
|
||||||
|
.aspectMask = aspect,
|
||||||
|
.colorAttachment = 0,
|
||||||
|
.clearValue = clearColorValue,
|
||||||
|
}, vk::ClearRect{
|
||||||
|
.rect = scissor,
|
||||||
|
.baseArrayLayer = layerId,
|
||||||
|
.layerCount = 1,
|
||||||
|
});
|
||||||
|
}, vk::Rect2D{
|
||||||
|
.extent = renderTarget.backing->dimensions,
|
||||||
|
}, {}, {renderTarget});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -198,22 +227,22 @@ namespace skyline::gpu::context {
|
|||||||
void SetScissor(size_t index, std::optional<maxwell3d::Scissor> scissor) {
|
void SetScissor(size_t index, std::optional<maxwell3d::Scissor> scissor) {
|
||||||
scissors.at(index) = scissor ? vk::Rect2D{
|
scissors.at(index) = scissor ? vk::Rect2D{
|
||||||
.offset.x = scissor->horizontal.minimum,
|
.offset.x = scissor->horizontal.minimum,
|
||||||
.extent.width = scissor->horizontal.maximum,
|
.extent.width = static_cast<u32>(scissor->horizontal.maximum - scissor->horizontal.minimum),
|
||||||
.offset.y = scissor->vertical.minimum,
|
.offset.y = scissor->vertical.minimum,
|
||||||
.extent.height = scissor->horizontal.maximum,
|
.extent.height = static_cast<u32>(scissor->horizontal.maximum - scissor->vertical.minimum),
|
||||||
} : DefaultScissor;
|
} : DefaultScissor;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetScissorHorizontal(size_t index, maxwell3d::Scissor::ScissorBounds bounds) {
|
void SetScissorHorizontal(size_t index, maxwell3d::Scissor::ScissorBounds bounds) {
|
||||||
auto &scissor{scissors.at(index)};
|
auto &scissor{scissors.at(index)};
|
||||||
scissor.offset.x = bounds.minimum;
|
scissor.offset.x = bounds.minimum;
|
||||||
scissor.extent.width = bounds.maximum;
|
scissor.extent.width = bounds.maximum - bounds.minimum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetScissorVertical(size_t index, maxwell3d::Scissor::ScissorBounds bounds) {
|
void SetScissorVertical(size_t index, maxwell3d::Scissor::ScissorBounds bounds) {
|
||||||
auto &scissor{scissors.at(index)};
|
auto &scissor{scissors.at(index)};
|
||||||
scissor.offset.y = bounds.minimum;
|
scissor.offset.y = bounds.minimum;
|
||||||
scissor.extent.height = bounds.maximum;
|
scissor.extent.height = bounds.maximum - bounds.minimum;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -10,8 +10,10 @@ namespace skyline::gpu::format {
|
|||||||
using vkf = vk::Format;
|
using vkf = vk::Format;
|
||||||
using vka = vk::ImageAspectFlagBits;
|
using vka = vk::ImageAspectFlagBits;
|
||||||
|
|
||||||
constexpr Format RGBA8888Unorm{sizeof(u8) * 4, 1, 1, vkf::eR8G8B8A8Unorm, vka::eColor}; //!< 8-bits per channel 4-channel pixels
|
constexpr Format R8G8B8A8Unorm{sizeof(u32), 1, 1, vkf::eR8G8B8A8Unorm, vka::eColor};
|
||||||
constexpr Format RGB565Unorm{sizeof(u8) * 2, 1, 1, vkf::eR5G6B5UnormPack16, vka::eColor}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit
|
constexpr Format R5G6B5Unorm{sizeof(u16), 1, 1, vkf::eR5G6B5UnormPack16, vka::eColor};
|
||||||
|
constexpr Format A2B10G10R10Unorm{sizeof(u32), 1, 1, vkf::eA2B10G10R10UnormPack32, vka::eColor};
|
||||||
|
constexpr Format A8B8G8R8Srgb{sizeof(u32), 1, 1, vkf::eA8B8G8R8SrgbPack32, vka::eColor};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Converts a Vulkan format to a Skyline format
|
* @brief Converts a Vulkan format to a Skyline format
|
||||||
@ -19,9 +21,13 @@ namespace skyline::gpu::format {
|
|||||||
constexpr gpu::texture::Format GetFormat(vk::Format format) {
|
constexpr gpu::texture::Format GetFormat(vk::Format format) {
|
||||||
switch (format) {
|
switch (format) {
|
||||||
case vk::Format::eR8G8B8A8Unorm:
|
case vk::Format::eR8G8B8A8Unorm:
|
||||||
return RGBA8888Unorm;
|
return R8G8B8A8Unorm;
|
||||||
case vk::Format::eR5G6B5UnormPack16:
|
case vk::Format::eR5G6B5UnormPack16:
|
||||||
return RGB565Unorm;
|
return R5G6B5Unorm;
|
||||||
|
case vk::Format::eA2B10G10R10UnormPack32:
|
||||||
|
return A2B10G10R10Unorm;
|
||||||
|
case vk::Format::eA8B8G8R8SrgbPack32:
|
||||||
|
return A8B8G8R8Srgb;
|
||||||
default:
|
default:
|
||||||
throw exception("Vulkan format not supported: '{}'", vk::to_string(format));
|
throw exception("Vulkan format not supported: '{}'", vk::to_string(format));
|
||||||
}
|
}
|
||||||
|
@ -304,11 +304,11 @@ namespace skyline::service::hosbinder {
|
|||||||
switch (handle.format) {
|
switch (handle.format) {
|
||||||
case AndroidPixelFormat::RGBA8888:
|
case AndroidPixelFormat::RGBA8888:
|
||||||
case AndroidPixelFormat::RGBX8888:
|
case AndroidPixelFormat::RGBX8888:
|
||||||
format = gpu::format::RGBA8888Unorm;
|
format = gpu::format::R8G8B8A8Unorm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AndroidPixelFormat::RGB565:
|
case AndroidPixelFormat::RGB565:
|
||||||
format = gpu::format::RGB565Unorm;
|
format = gpu::format::R5G6B5Unorm;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -386,7 +386,7 @@ namespace skyline::service::hosbinder {
|
|||||||
{
|
{
|
||||||
auto &texture{buffer.texture};
|
auto &texture{buffer.texture};
|
||||||
std::scoped_lock textureLock(*texture);
|
std::scoped_lock textureLock(*texture);
|
||||||
texture->SynchronizeHost();
|
// texture->SynchronizeHost();
|
||||||
u64 frameId;
|
u64 frameId;
|
||||||
state.gpu->presentation.Present(texture, isAutoTimestamp ? 0 : timestamp, swapInterval, crop, scalingMode, transform, frameId);
|
state.gpu->presentation.Present(texture, isAutoTimestamp ? 0 : timestamp, swapInterval, crop, scalingMode, transform, frameId);
|
||||||
}
|
}
|
||||||
|
@ -7,8 +7,9 @@ namespace skyline::soc::gm20b {
|
|||||||
GM20B::GM20B(const DeviceState &state) :
|
GM20B::GM20B(const DeviceState &state) :
|
||||||
fermi2D(state),
|
fermi2D(state),
|
||||||
keplerMemory(state),
|
keplerMemory(state),
|
||||||
maxwell3D(state, gmmu),
|
maxwell3D(state, gmmu, executor),
|
||||||
maxwellCompute(state),
|
maxwellCompute(state),
|
||||||
maxwellDma(state),
|
maxwellDma(state),
|
||||||
gpfifo(state) {}
|
gpfifo(state),
|
||||||
|
executor(state) {}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <gpu/interconnect/command_executor.h>
|
||||||
#include "gm20b/engines/maxwell_3d.h"
|
#include "gm20b/engines/maxwell_3d.h"
|
||||||
#include "gm20b/gpfifo.h"
|
#include "gm20b/gpfifo.h"
|
||||||
#include "gm20b/gmmu.h"
|
#include "gm20b/gmmu.h"
|
||||||
@ -15,6 +16,7 @@ namespace skyline::soc::gm20b {
|
|||||||
class GM20B {
|
class GM20B {
|
||||||
public:
|
public:
|
||||||
GMMU gmmu;
|
GMMU gmmu;
|
||||||
|
gpu::interconnect::CommandExecutor executor;
|
||||||
engine::Engine fermi2D;
|
engine::Engine fermi2D;
|
||||||
engine::maxwell3d::Maxwell3D maxwell3D;
|
engine::maxwell3d::Maxwell3D maxwell3D;
|
||||||
engine::Engine maxwellCompute;
|
engine::Engine maxwellCompute;
|
||||||
|
@ -42,7 +42,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
|
|||||||
|
|
||||||
enum class ColorFormat : u32 {
|
enum class ColorFormat : u32 {
|
||||||
None = 0x0,
|
None = 0x0,
|
||||||
|
A2B10G10R10Unorm = 0xD1,
|
||||||
R8G8B8A8Unorm = 0xD5,
|
R8G8B8A8Unorm = 0xD5,
|
||||||
|
A8B8G8R8Srgb = 0xD6,
|
||||||
} format;
|
} format;
|
||||||
|
|
||||||
struct TileMode {
|
struct TileMode {
|
||||||
@ -221,7 +223,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
|
|||||||
u8 map6 : 3;
|
u8 map6 : 3;
|
||||||
u8 map7 : 3;
|
u8 map7 : 3;
|
||||||
|
|
||||||
size_t Map(size_t index) {
|
size_t operator[](size_t index) {
|
||||||
switch (index) {
|
switch (index) {
|
||||||
case 0:
|
case 0:
|
||||||
return map0;
|
return map0;
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
#include <soc.h>
|
#include <soc.h>
|
||||||
|
|
||||||
namespace skyline::soc::gm20b::engine::maxwell3d {
|
namespace skyline::soc::gm20b::engine::maxwell3d {
|
||||||
Maxwell3D::Maxwell3D(const DeviceState &state, GMMU &gmmu) : Engine(state), macroInterpreter(*this), context(*state.gpu, gmmu) {
|
Maxwell3D::Maxwell3D(const DeviceState &state, GMMU &gmmu, gpu::interconnect::CommandExecutor& executor) : Engine(state), macroInterpreter(*this), context(*state.gpu, gmmu, executor) {
|
||||||
ResetRegs();
|
ResetRegs();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -245,6 +245,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
|
|||||||
MAXWELL3D_CASE(syncpointAction, {
|
MAXWELL3D_CASE(syncpointAction, {
|
||||||
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(syncpointAction.id));
|
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(syncpointAction.id));
|
||||||
state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
|
state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
|
||||||
|
state.soc->gm20b.executor.Execute();
|
||||||
})
|
})
|
||||||
|
|
||||||
MAXWELL3D_CASE(clearBuffers, {
|
MAXWELL3D_CASE(clearBuffers, {
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <gpu/context/graphics_context.h>
|
#include <gpu/interconnect/graphics_context.h>
|
||||||
#include "engine.h"
|
#include "engine.h"
|
||||||
#include "maxwell/macro_interpreter.h"
|
#include "maxwell/macro_interpreter.h"
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
|
|||||||
|
|
||||||
MacroInterpreter macroInterpreter;
|
MacroInterpreter macroInterpreter;
|
||||||
|
|
||||||
gpu::context::GraphicsContext context;
|
gpu::interconnect::GraphicsContext context;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Writes back a semaphore result to the guest with an auto-generated timestamp (if required)
|
* @brief Writes back a semaphore result to the guest with an auto-generated timestamp (if required)
|
||||||
@ -241,7 +241,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
|
|||||||
|
|
||||||
std::array<u32, 0x2000> macroCode{}; //!< Stores GPU macros, writes to it will wraparound on overflow
|
std::array<u32, 0x2000> macroCode{}; //!< Stores GPU macros, writes to it will wraparound on overflow
|
||||||
|
|
||||||
Maxwell3D(const DeviceState &state, GMMU &gmmu);
|
Maxwell3D(const DeviceState &state, GMMU &gmmu, gpu::interconnect::CommandExecutor& executor);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resets the Maxwell 3D registers to their default values
|
* @brief Resets the Maxwell 3D registers to their default values
|
||||||
|
Loading…
Reference in New Issue
Block a user