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:
PixelyIon 2021-09-14 21:30:12 +05:30
parent 3879d573d5
commit 239d2625e2
12 changed files with 379 additions and 28 deletions

View File

@ -102,6 +102,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/gpu/command_scheduler.cpp
${source_DIR}/skyline/gpu/texture/texture.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/host1x/syncpoint.cpp
${source_DIR}/skyline/soc/gm20b/gpfifo.cpp

View File

@ -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();
}
}
}

View 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();
};
}

View 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
}

View File

@ -3,13 +3,12 @@
#pragma once
#include <vulkan/vulkan_raii.hpp>
#include <gpu.h>
#include <gpu/texture/format.h>
#include <soc/gm20b/gmmu.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;
/**
@ -20,6 +19,7 @@ namespace skyline::gpu::context {
private:
GPU &gpu;
soc::gm20b::GMMU &gmmu;
gpu::interconnect::CommandExecutor &executor;
struct RenderTarget {
bool disabled{}; //!< If this RT has been disabled and will be an unbound attachment instead
@ -50,7 +50,7 @@ namespace skyline::gpu::context {
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);
}
@ -88,8 +88,12 @@ namespace skyline::gpu::context {
switch (format) {
case maxwell3d::RenderTarget::ColorFormat::None:
return {};
case maxwell3d::RenderTarget::ColorFormat::A2B10G10R10Unorm:
return format::A2B10G10R10Unorm;
case maxwell3d::RenderTarget::ColorFormat::R8G8B8A8Unorm:
return format::RGBA8888Unorm;
return format::R8G8B8A8Unorm;
case maxwell3d::RenderTarget::ColorFormat::A8B8G8R8Srgb:
return format::A8B8G8R8Srgb;
default:
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());
}
return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest));
renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest);
return &renderTarget.view.value();
}
void UpdateRenderTargetControl(maxwell3d::RenderTargetControl control) {
@ -186,10 +191,34 @@ namespace skyline::gpu::context {
}
void ClearBuffers(maxwell3d::ClearBuffers clear) {
auto renderTarget{GetRenderTarget(renderTargetControl.Map(clear.renderTargetId))};
if (renderTarget) {
std::lock_guard lock(*renderTarget->backing);
// TODO: Clear the buffer
auto renderTargetIndex{renderTargetControl[clear.renderTargetId]};
auto renderTargetPointer{GetRenderTarget(renderTargetIndex)};
if (renderTargetPointer) {
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) {
scissors.at(index) = scissor ? vk::Rect2D{
.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,
.extent.height = scissor->horizontal.maximum,
.extent.height = static_cast<u32>(scissor->horizontal.maximum - scissor->vertical.minimum),
} : DefaultScissor;
}
void SetScissorHorizontal(size_t index, maxwell3d::Scissor::ScissorBounds bounds) {
auto &scissor{scissors.at(index)};
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) {
auto &scissor{scissors.at(index)};
scissor.offset.y = bounds.minimum;
scissor.extent.height = bounds.maximum;
scissor.extent.height = bounds.maximum - bounds.minimum;
}
};
}

View File

@ -10,8 +10,10 @@ namespace skyline::gpu::format {
using vkf = vk::Format;
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 RGB565Unorm{sizeof(u8) * 2, 1, 1, vkf::eR5G6B5UnormPack16, vka::eColor}; //!< Red channel: 5-bit, Green channel: 6-bit, Blue channel: 5-bit
constexpr Format R8G8B8A8Unorm{sizeof(u32), 1, 1, vkf::eR8G8B8A8Unorm, vka::eColor};
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
@ -19,9 +21,13 @@ namespace skyline::gpu::format {
constexpr gpu::texture::Format GetFormat(vk::Format format) {
switch (format) {
case vk::Format::eR8G8B8A8Unorm:
return RGBA8888Unorm;
return R8G8B8A8Unorm;
case vk::Format::eR5G6B5UnormPack16:
return RGB565Unorm;
return R5G6B5Unorm;
case vk::Format::eA2B10G10R10UnormPack32:
return A2B10G10R10Unorm;
case vk::Format::eA8B8G8R8SrgbPack32:
return A8B8G8R8Srgb;
default:
throw exception("Vulkan format not supported: '{}'", vk::to_string(format));
}

View File

@ -304,11 +304,11 @@ namespace skyline::service::hosbinder {
switch (handle.format) {
case AndroidPixelFormat::RGBA8888:
case AndroidPixelFormat::RGBX8888:
format = gpu::format::RGBA8888Unorm;
format = gpu::format::R8G8B8A8Unorm;
break;
case AndroidPixelFormat::RGB565:
format = gpu::format::RGB565Unorm;
format = gpu::format::R5G6B5Unorm;
break;
default:
@ -386,7 +386,7 @@ namespace skyline::service::hosbinder {
{
auto &texture{buffer.texture};
std::scoped_lock textureLock(*texture);
texture->SynchronizeHost();
// texture->SynchronizeHost();
u64 frameId;
state.gpu->presentation.Present(texture, isAutoTimestamp ? 0 : timestamp, swapInterval, crop, scalingMode, transform, frameId);
}

View File

@ -7,8 +7,9 @@ namespace skyline::soc::gm20b {
GM20B::GM20B(const DeviceState &state) :
fermi2D(state),
keplerMemory(state),
maxwell3D(state, gmmu),
maxwell3D(state, gmmu, executor),
maxwellCompute(state),
maxwellDma(state),
gpfifo(state) {}
gpfifo(state),
executor(state) {}
}

View File

@ -3,6 +3,7 @@
#pragma once
#include <gpu/interconnect/command_executor.h>
#include "gm20b/engines/maxwell_3d.h"
#include "gm20b/gpfifo.h"
#include "gm20b/gmmu.h"
@ -15,6 +16,7 @@ namespace skyline::soc::gm20b {
class GM20B {
public:
GMMU gmmu;
gpu::interconnect::CommandExecutor executor;
engine::Engine fermi2D;
engine::maxwell3d::Maxwell3D maxwell3D;
engine::Engine maxwellCompute;

View File

@ -42,7 +42,9 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
enum class ColorFormat : u32 {
None = 0x0,
A2B10G10R10Unorm = 0xD1,
R8G8B8A8Unorm = 0xD5,
A8B8G8R8Srgb = 0xD6,
} format;
struct TileMode {
@ -221,7 +223,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d::type {
u8 map6 : 3;
u8 map7 : 3;
size_t Map(size_t index) {
size_t operator[](size_t index) {
switch (index) {
case 0:
return map0;

View File

@ -6,7 +6,7 @@
#include <soc.h>
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();
}
@ -245,6 +245,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
MAXWELL3D_CASE(syncpointAction, {
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(syncpointAction.id));
state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
state.soc->gm20b.executor.Execute();
})
MAXWELL3D_CASE(clearBuffers, {

View File

@ -4,7 +4,7 @@
#pragma once
#include <gpu/context/graphics_context.h>
#include <gpu/interconnect/graphics_context.h>
#include "engine.h"
#include "maxwell/macro_interpreter.h"
@ -23,7 +23,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
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)
@ -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
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