Texture GMMU Address Resolution + Refactor Maxwell3D::CallMethod

Fixes bugs with the Texture Manager lookup, fixes `RenderTarget` address extraction (`low`/`high` were flipped prior), refactors `Maxwell3D::CallMethod` to utilize a case for the register being modified + preventing redundant method calls when no new value is being written to the register, and fixes the behavior of shadow RAM which was broken previously and would lead to incorrect arguments being utilized for methods.
This commit is contained in:
PixelyIon 2021-08-25 00:49:03 +05:30
parent 270f2db1d2
commit 54908afc44
16 changed files with 199 additions and 142 deletions

View File

@ -171,7 +171,7 @@
</inspection_tool> </inspection_tool>
<inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" /> <inspection_tool class="CheckedExceptionClass" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="ClangTidy" enabled="true" level="WARNING" enabled_by_default="true">
<option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-parentheses,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" /> <option name="clangTidyChecks" value="-*,bugprone-argument-comment,bugprone-assert-side-effect,bugprone-bad-signal-to-kill-thread,bugprone-branch-clone,bugprone-copy-constructor-init,bugprone-dangling-handle,bugprone-dynamic-static-initializers,bugprone-fold-init-type,bugprone-forward-declaration-namespace,bugprone-forwarding-reference-overload,bugprone-inaccurate-erase,bugprone-incorrect-roundings,bugprone-integer-division,bugprone-lambda-function-name,bugprone-macro-repeated-side-effects,bugprone-misplaced-operator-in-strlen-in-alloc,bugprone-misplaced-pointer-arithmetic-in-alloc,bugprone-misplaced-widening-cast,bugprone-move-forwarding-reference,bugprone-multiple-statement-macro,bugprone-no-escape,bugprone-not-null-terminated-result,bugprone-parent-virtual-call,bugprone-posix-return,bugprone-reserved-identifier,bugprone-sizeof-container,bugprone-sizeof-expression,bugprone-spuriously-wake-up-functions,bugprone-string-constructor,bugprone-string-integer-assignment,bugprone-string-literal-with-embedded-nul,bugprone-suspicious-enum-usage,bugprone-suspicious-include,bugprone-suspicious-memset-usage,bugprone-suspicious-missing-comma,bugprone-suspicious-semicolon,bugprone-suspicious-string-compare,bugprone-swapped-arguments,bugprone-terminating-continue,bugprone-throw-keyword-missing,bugprone-too-small-loop-variable,bugprone-undefined-memory-manipulation,bugprone-undelegated-constructor,bugprone-unhandled-self-assignment,bugprone-unused-raii,bugprone-unused-return-value,bugprone-use-after-move,bugprone-virtual-near-miss,cert-dcl21-cpp,cert-dcl58-cpp,cert-err34-c,cert-err52-cpp,cert-err58-cpp,cert-err60-cpp,cert-flp30-c,cert-msc50-cpp,cert-msc51-cpp,cert-str34-c,cppcoreguidelines-interfaces-global-init,cppcoreguidelines-narrowing-conversions,cppcoreguidelines-pro-type-member-init,cppcoreguidelines-pro-type-static-cast-downcast,cppcoreguidelines-slicing,google-default-arguments,google-explicit-constructor,google-runtime-operator,hicpp-exception-baseclass,hicpp-multiway-paths-covered,misc-misplaced-const,misc-new-delete-overloads,misc-no-recursion,misc-non-copyable-objects,misc-throw-by-value-catch-by-reference,misc-unconventional-assign-operator,misc-uniqueptr-reset-release,modernize-avoid-bind,modernize-concat-nested-namespaces,modernize-deprecated-ios-base-aliases,modernize-loop-convert,modernize-make-shared,modernize-make-unique,modernize-pass-by-value,modernize-raw-string-literal,modernize-redundant-void-arg,modernize-replace-auto-ptr,modernize-replace-disallow-copy-and-assign-macro,modernize-replace-random-shuffle,modernize-return-braced-init-list,modernize-shrink-to-fit,modernize-unary-static-assert,modernize-use-auto,modernize-use-bool-literals,modernize-use-emplace,modernize-use-equals-default,modernize-use-equals-delete,modernize-use-nodiscard,modernize-use-noexcept,modernize-use-nullptr,modernize-use-override,modernize-use-transparent-functors,modernize-use-uncaught-exceptions,mpi-buffer-deref,mpi-type-mismatch,openmp-use-default-none,performance-faster-string-find,performance-for-range-copy,performance-implicit-conversion-in-loop,performance-inefficient-algorithm,performance-inefficient-string-concatenation,performance-inefficient-vector-operation,performance-move-const-arg,performance-move-constructor-init,performance-no-automatic-move,performance-noexcept-move-constructor,performance-trivially-destructible,performance-type-promotion-in-math-fn,performance-unnecessary-copy-initialization,performance-unnecessary-value-param,portability-simd-intrinsics,readability-avoid-const-params-in-decls,readability-const-return-type,readability-container-size-empty,readability-convert-member-functions-to-static,readability-delete-null-pointer,readability-deleted-default,readability-inconsistent-declaration-parameter-name,readability-misleading-indentation,readability-misplaced-array-index,readability-non-const-parameter,readability-redundant-control-flow,readability-redundant-declaration,readability-redundant-function-ptr-dereference,readability-redundant-smartptr-get,readability-redundant-string-cstr,readability-redundant-string-init,readability-simplify-subscript-expr,readability-static-accessed-through-instance,readability-static-definition-in-anonymous-namespace,readability-string-compare,readability-uniqueptr-delete-release,readability-use-anyofallof" />
</inspection_tool> </inspection_tool>
<inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true"> <inspection_tool class="ClassComplexity" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="80" /> <option name="m_limit" value="80" />

View File

@ -60,7 +60,7 @@
</value> </value>
</option> </option>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="JDK" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -110,6 +110,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/soc/gm20b/gpfifo.cpp ${source_DIR}/skyline/soc/gm20b/gpfifo.cpp
${source_DIR}/skyline/soc/gm20b/engines/maxwell_3d.cpp ${source_DIR}/skyline/soc/gm20b/engines/maxwell_3d.cpp
${source_DIR}/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp ${source_DIR}/skyline/soc/gm20b/engines/maxwell/macro_interpreter.cpp
${source_DIR}/skyline/soc/gm20b/gmmu.cpp
${source_DIR}/skyline/input/npad.cpp ${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/input/npad_device.cpp ${source_DIR}/skyline/input/npad_device.cpp
${source_DIR}/skyline/input/touch.cpp ${source_DIR}/skyline/input/touch.cpp

View File

@ -372,6 +372,14 @@ namespace skyline {
void FillRandomBytes(T &object) { void FillRandomBytes(T &object) {
FillRandomBytes(std::span(reinterpret_cast<typename IntegerFor<T>::type *>(&object), IntegerFor<T>::count)); FillRandomBytes(std::span(reinterpret_cast<typename IntegerFor<T>::type *>(&object), IntegerFor<T>::count));
} }
/**
* @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier
*/
template<typename To, typename From>
To BitCast(const From& from) {
return *reinterpret_cast<const To*>(&from);
}
} }
/** /**
@ -443,7 +451,7 @@ namespace skyline {
* @return If a supplied span is located entirely inside this span and is effectively a subspan * @return If a supplied span is located entirely inside this span and is effectively a subspan
*/ */
constexpr bool contains(const span<T, Extent>& other) const { constexpr bool contains(const span<T, Extent>& other) const {
return this->begin() >= other.begin() && this->size() <= other.size(); return this->begin() <= other.begin() && this->end() >= other.end();
} }
/** Comparision operators for equality and binary searches **/ /** Comparision operators for equality and binary searches **/

View File

@ -6,6 +6,7 @@
#include <vulkan/vulkan_raii.hpp> #include <vulkan/vulkan_raii.hpp>
#include <gpu.h> #include <gpu.h>
#include <gpu/texture/format.h> #include <gpu/texture/format.h>
#include <soc/gm20b/gmmu.h>
#include <soc/gm20b/engines/maxwell/types.h> #include <soc/gm20b/engines/maxwell/types.h>
namespace skyline::gpu::context { namespace skyline::gpu::context {
@ -18,18 +19,23 @@ namespace skyline::gpu::context {
class GraphicsContext { class GraphicsContext {
private: private:
GPU &gpu; GPU &gpu;
soc::gm20b::GMMU &gmmu;
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
union { union {
u64 gpuAddress; u64 gpuAddress;
struct { struct {
u32 gpuAddressHigh;
u32 gpuAddressLow; u32 gpuAddressLow;
u32 gpuAddressHigh;
}; };
}; };
GuestTexture guest; GuestTexture guest;
std::optional<TextureView> view; std::optional<TextureView> view;
RenderTarget() {
guest.dimensions = texture::Dimensions(1, 1, 1); // We want the depth to be 1 by default (It cannot be set by the application)
}
}; };
std::array<RenderTarget, maxwell3d::RenderTargetCount> renderTargets{}; //!< The target textures to render into as color attachments std::array<RenderTarget, maxwell3d::RenderTargetCount> renderTargets{}; //!< The target textures to render into as color attachments
@ -44,7 +50,7 @@ namespace skyline::gpu::context {
public: public:
GraphicsContext(GPU &gpu) : gpu(gpu) { GraphicsContext(GPU &gpu, soc::gm20b::GMMU &gmmu) : gpu(gpu), gmmu(gmmu) {
scissors.fill(DefaultScissor); scissors.fill(DefaultScissor);
} }
@ -64,19 +70,19 @@ namespace skyline::gpu::context {
renderTarget.view.reset(); renderTarget.view.reset();
} }
void SetRenderTargetAddressWidth(size_t index, u32 value) { void SetRenderTargetWidth(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)}; auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.width = value; renderTarget.guest.dimensions.width = value;
renderTarget.view.reset(); renderTarget.view.reset();
} }
void SetRenderTargetAddressHeight(size_t index, u32 value) { void SetRenderTargetHeight(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)}; auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.height = value; renderTarget.guest.dimensions.height = value;
renderTarget.view.reset(); renderTarget.view.reset();
} }
void SetRenderTargetAddressFormat(size_t index, maxwell3d::RenderTarget::ColorFormat format) { void SetRenderTargetFormat(size_t index, maxwell3d::RenderTarget::ColorFormat format) {
auto &renderTarget{renderTargets.at(index)}; auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.format = [&]() -> texture::Format { renderTarget.guest.format = [&]() -> texture::Format {
switch (format) { switch (format) {
@ -137,8 +143,9 @@ namespace skyline::gpu::context {
return &*renderTarget.view; return &*renderTarget.view;
if (renderTarget.guest.mappings.empty()) { if (renderTarget.guest.mappings.empty()) {
// TODO: Fill in mappings auto size{std::max<u64>(renderTarget.guest.layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer), renderTarget.guest.format->GetSize(renderTarget.guest.dimensions))};
return nullptr; auto mappings{gmmu.TranslateRange(renderTarget.gpuAddress, size)};
renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
} }
return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest)); return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest));

View File

@ -111,7 +111,6 @@ namespace skyline::gpu::memory {
Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) { Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) {
VmaAllocationCreateInfo allocationCreateInfo{ VmaAllocationCreateInfo allocationCreateInfo{
.flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_UNKNOWN, .usage = VMA_MEMORY_USAGE_UNKNOWN,
.memoryTypeBits = static_cast<u32>(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal), .memoryTypeBits = static_cast<u32>(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal),
}; };

View File

@ -31,6 +31,7 @@ namespace skyline::gpu::memory {
/** /**
* @brief A Vulkan image which VMA allocates and manages the backing memory for * @brief A Vulkan image which VMA allocates and manages the backing memory for
* @note Any images created with VMA_ALLOCATION_CREATE_MAPPED_BIT must not be utilized with this since it'll unconditionally unmap when a pointer is present which is illegal when an image was created with that flag as unmapping will be automatically performed on image deletion
*/ */
struct Image { struct Image {
private: private:

View File

@ -21,23 +21,26 @@ namespace skyline::gpu {
// 4.2) If they aren't, we delete them from the map // 4.2) If they aren't, we delete them from the map
// 5) Create a new texture and insert it in the map then return it // 5) Create a new texture and insert it in the map then return it
std::scoped_lock lock(mutex);
std::shared_ptr<Texture> match{}; std::shared_ptr<Texture> match{};
auto mappingEnd{std::upper_bound(textures.begin(), textures.end(), guestMapping)}, hostMapping{mappingEnd}; auto mappingEnd{std::upper_bound(textures.begin(), textures.end(), guestMapping)}, hostMapping{mappingEnd};
while (hostMapping != textures.begin() && std::prev(hostMapping)->end() > guestMapping.begin()) { while (hostMapping != textures.begin() && (--hostMapping)->end() > guestMapping.begin()) {
auto &hostMappings{hostMapping->texture->guest->mappings}; auto &hostMappings{hostMapping->texture->guest->mappings};
if (!hostMapping->contains(guestMapping))
continue;
// We need to check that all corresponding mappings in the candidate texture and the guest texture match up // We need to check that all corresponding mappings in the candidate texture and the guest texture match up
// Only the start of the first matched mapping and the end of the last mapping can not match up as this is the case for views // Only the start of the first matched mapping and the end of the last mapping can not match up as this is the case for views
auto firstHostMapping{hostMapping->iterator}; auto firstHostMapping{hostMapping->iterator};
auto lastGuestMapping{guestTexture.mappings.back()}; auto lastGuestMapping{guestTexture.mappings.back()};
auto lastHostMapping{std::find_if(firstHostMapping, hostMappings.end(), [&lastGuestMapping](const span<u8> &it) { auto lastHostMapping{std::find_if(firstHostMapping, hostMappings.end(), [&lastGuestMapping](const span<u8> &it) {
return lastGuestMapping.begin() >= it.begin() && lastGuestMapping.size() <= it.size(); return lastGuestMapping.begin() > it.begin() && lastGuestMapping.end() > it.end();
})}; })}; //!< A past-the-end iterator for the last host mapping, the final valid mapping is prior to this iterator
bool mappingMatch{std::equal(firstHostMapping, lastHostMapping, guestTexture.mappings.begin(), guestTexture.mappings.end(), [](const span<u8> &lhs, const span<u8> &rhs) { bool mappingMatch{std::equal(firstHostMapping, lastHostMapping, guestTexture.mappings.begin(), guestTexture.mappings.end(), [](const span<u8> &lhs, const span<u8> &rhs) {
return lhs.end() == rhs.end(); // We check end() here to implicitly ignore any offset from the first mapping return lhs.end() == rhs.end(); // We check end() here to implicitly ignore any offset from the first mapping
})}; })};
if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == std::prev(hostMappings.end()) && lastGuestMapping.end() == lastHostMapping->end()) { if (firstHostMapping == hostMappings.begin() && firstHostMapping->begin() == guestMapping.begin() && mappingMatch && lastHostMapping == hostMappings.end() && lastGuestMapping.end() == std::prev(lastHostMapping)->end()) {
// We've gotten a perfect 1:1 match for *all* mappings from the start to end, we just need to check for compatibility aside from this // We've gotten a perfect 1:1 match for *all* mappings from the start to end, we just need to check for compatibility aside from this
auto &matchGuestTexture{*hostMapping->texture->guest}; auto &matchGuestTexture{*hostMapping->texture->guest};
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && matchGuestTexture.dimensions == guestTexture.dimensions && matchGuestTexture.tileConfig == guestTexture.tileConfig) { if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && matchGuestTexture.dimensions == guestTexture.dimensions && matchGuestTexture.tileConfig == guestTexture.tileConfig) {

View File

@ -27,10 +27,6 @@ namespace skyline::gpu {
std::mutex mutex; //!< Synchronizes access to the texture mappings std::mutex mutex; //!< Synchronizes access to the texture mappings
std::vector<TextureMapping> textures; //!< A sorted vector of all texture mappings std::vector<TextureMapping> textures; //!< A sorted vector of all texture mappings
bool IsSizeCompatible(texture::Dimensions lhsDimension, texture::TileConfig lhsConfig, texture::Dimensions rhsDimension, texture::TileConfig rhsConfig) {
return lhsDimension == rhsDimension && lhsConfig == rhsConfig;
}
public: public:
TextureManager(GPU &gpu); TextureManager(GPU &gpu);

View File

@ -12,7 +12,7 @@ namespace skyline {
} }
namespace skyline::service::nvdrv::device::nvhost { namespace skyline::service::nvdrv::device::nvhost {
using GMMU = soc::gm20b::GM20B::GMMU; using GMMU = soc::gm20b::GMMU;
AsGpu::AsGpu(const DeviceState &state, Core &core, const SessionContext &ctx) : NvDevice(state, core, ctx) {} AsGpu::AsGpu(const DeviceState &state, Core &core, const SessionContext &ctx) : NvDevice(state, core, ctx) {}
@ -320,7 +320,7 @@ namespace skyline::service::nvdrv::device::nvhost {
} }
if (!entry.handle) { if (!entry.handle) {
state.soc->gm20b.gmmu.Map(virtAddr, soc::gm20b::GM20B::GMMU::SparsePlaceholderAddress(), size, {true}); state.soc->gm20b.gmmu.Map(virtAddr, soc::gm20b::GMMU::SparsePlaceholderAddress(), size, {true});
} else { } else {
auto h{core.nvMap.GetHandle(entry.handle)}; auto h{core.nvMap.GetHandle(entry.handle)};
if (!h) if (!h)

View File

@ -1,19 +1,13 @@
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/) // Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/address_space.inc>
#include "gm20b.h" #include "gm20b.h"
namespace skyline {
template class FlatAddressSpaceMap<u64, 0, u8 *, nullptr, true, soc::gm20b::GM20B::AddressSpaceBits>;
template class FlatMemoryManager<u64, 0, soc::gm20b::GM20B::AddressSpaceBits>;
}
namespace skyline::soc::gm20b { namespace skyline::soc::gm20b {
GM20B::GM20B(const DeviceState &state) : GM20B::GM20B(const DeviceState &state) :
fermi2D(state), fermi2D(state),
keplerMemory(state), keplerMemory(state),
maxwell3D(state), maxwell3D(state, gmmu),
maxwellCompute(state), maxwellCompute(state),
maxwellDma(state), maxwellDma(state),
gpfifo(state) {} gpfifo(state) {}

View File

@ -3,9 +3,9 @@
#pragma once #pragma once
#include <common/address_space.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"
namespace skyline::soc::gm20b { namespace skyline::soc::gm20b {
/** /**
@ -14,16 +14,13 @@ namespace skyline::soc::gm20b {
*/ */
class GM20B { class GM20B {
public: public:
static constexpr u8 AddressSpaceBits{40}; //!< The width of the GMMU AS GMMU gmmu;
using GMMU = FlatMemoryManager<u64, 0, AddressSpaceBits>;
engine::Engine fermi2D; engine::Engine fermi2D;
engine::maxwell3d::Maxwell3D maxwell3D; engine::maxwell3d::Maxwell3D maxwell3D;
engine::Engine maxwellCompute; engine::Engine maxwellCompute;
engine::Engine maxwellDma; engine::Engine maxwellDma;
engine::Engine keplerMemory; engine::Engine keplerMemory;
GPFIFO gpfifo; GPFIFO gpfifo;
GMMU gmmu;
GM20B(const DeviceState &state); GM20B(const DeviceState &state);
}; };

View File

@ -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) : Engine(state), macroInterpreter(*this), context(*state.gpu) { Maxwell3D::Maxwell3D(const DeviceState &state, GMMU &gmmu) : Engine(state), macroInterpreter(*this), context(*state.gpu, gmmu) {
ResetRegs(); ResetRegs();
} }
@ -104,161 +104,182 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
return; return;
} }
registers.raw[method] = argument;
if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrack || shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrackWithFilter)
shadowRegisters.raw[method] = argument;
else if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodReplay)
argument = shadowRegisters.raw[method];
#define MAXWELL3D_OFFSET(field) U32_OFFSET(Registers, field) #define MAXWELL3D_OFFSET(field) U32_OFFSET(Registers, field)
#define MAXWELL3D_STRUCT_OFFSET(field, member) U32_OFFSET(Registers, field) + offsetof(typeof(Registers::field), member) #define MAXWELL3D_STRUCT_OFFSET(field, member) U32_OFFSET(Registers, field) + U32_OFFSET(typeof(Registers::field), member)
#define MAXWELL3D_ARRAY_OFFSET(field, index) U32_OFFSET(Registers, field) + ((sizeof(typeof(Registers::field[0])) / sizeof(u32)) * index) #define MAXWELL3D_ARRAY_OFFSET(field, index) U32_OFFSET(Registers, field) + ((sizeof(typeof(Registers::field[0])) / sizeof(u32)) * index)
#define MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) MAXWELL3D_ARRAY_OFFSET(field, index) + U32_OFFSET(typeof(Registers::field[0]), member) #define MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) MAXWELL3D_ARRAY_OFFSET(field, index) + U32_OFFSET(typeof(Registers::field[0]), member)
#define MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(field, index, member, submember) MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) + U32_OFFSET(typeof(Registers::field[0].member), submember) #define MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(field, index, member, submember) MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member) + U32_OFFSET(typeof(Registers::field[0].member), submember)
#define MAXWELL3D_CASE_BASE(fieldName, fieldAccessor, offset, content) case offset: { \
auto fieldName{util::BitCast<typeof(registers.fieldAccessor)>(argument)}; \
content \
return; \
}
#define MAXWELL3D_CASE(field, content) MAXWELL3D_CASE_BASE(field, field, MAXWELL3D_OFFSET(field), content)
#define MAXWELL3D_STRUCT_CASE(field, member, content) MAXWELL3D_CASE_BASE(member, field.member, MAXWELL3D_STRUCT_OFFSET(field, member), content)
#define MAXWELL3D_ARRAY_CASE(field, index, content) MAXWELL3D_CASE_BASE(field, field[index], MAXWELL3D_ARRAY_OFFSET(field, index), content)
#define MAXWELL3D_ARRAY_STRUCT_CASE(field, index, member, content) MAXWELL3D_CASE_BASE(member, field[index].member, MAXWELL3D_ARRAY_STRUCT_OFFSET(field, index, member), content)
#define MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(field, index, member, submember, content) MAXWELL3D_CASE_BASE(submember, field[index].member.submember, MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(field, index, member, submember), content)
if (method != MAXWELL3D_OFFSET(mme.shadowRamControl)) {
if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrack || shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodTrackWithFilter)
shadowRegisters.raw[method] = argument;
else if (shadowRegisters.mme.shadowRamControl == type::MmeShadowRamControl::MethodReplay)
argument = shadowRegisters.raw[method];
}
bool redundant{registers.raw[method] == argument};
registers.raw[method] = argument;
if (!redundant) {
switch (method) { switch (method) {
case MAXWELL3D_OFFSET(mme.instructionRamLoad): MAXWELL3D_STRUCT_CASE(mme, shadowRamControl, {
if (registers.mme.instructionRamPointer >= macroCode.size()) shadowRegisters.mme.shadowRamControl = shadowRamControl;
throw exception("Macro memory is full!"); })
macroCode[registers.mme.instructionRamPointer++] = argument;
// Wraparound writes
registers.mme.instructionRamPointer %= macroCode.size();
break;
case MAXWELL3D_OFFSET(mme.startAddressRamLoad):
if (registers.mme.startAddressRamPointer >= macroPositions.size())
throw exception("Maximum amount of macros reached!");
macroPositions[registers.mme.startAddressRamPointer++] = argument;
break;
case MAXWELL3D_OFFSET(mme.shadowRamControl):
shadowRegisters.mme.shadowRamControl = static_cast<type::MmeShadowRamControl>(argument);
break;
case MAXWELL3D_OFFSET(syncpointAction):
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(registers.syncpointAction.id));
state.soc->host1x.syncpoints.at(registers.syncpointAction.id).Increment();
break;
#define RENDER_TARGET_ARRAY(z, index, data) \ #define RENDER_TARGET_ARRAY(z, index, data) \
case MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(renderTargets, index, address, high): \ MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(renderTargets, index, address, high, { \
context.SetRenderTargetAddressHigh(index, argument); \ context.SetRenderTargetAddressHigh(index, high); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(renderTargets, index, address, low): \ MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(renderTargets, index, address, low, { \
context.SetRenderTargetAddressLow(index, argument); \ context.SetRenderTargetAddressLow(index, low); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, width): \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, width, { \
context.SetRenderTargetAddressWidth(index, argument); \ context.SetRenderTargetWidth(index, width); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, height): \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, height, { \
context.SetRenderTargetAddressHeight(index, argument); \ context.SetRenderTargetHeight(index, height); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, format): \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, format, { \
context.SetRenderTargetAddressFormat(index, \ context.SetRenderTargetFormat(index, format); \
static_cast<type::RenderTarget::ColorFormat>(argument)); \ }) \
break; \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, tileMode, { \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, tileMode): \ context.SetRenderTargetTileMode(index, tileMode); \
context.SetRenderTargetTileMode(index, \ }) \
*reinterpret_cast<type::RenderTarget::TileMode*>(&argument)); \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, arrayMode, { \
break; \ context.SetRenderTargetArrayMode(index, arrayMode); \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, arrayMode): \ }) \
context.SetRenderTargetArrayMode(index, \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, layerStrideLsr2, { \
*reinterpret_cast<type::RenderTarget::ArrayMode*>(&argument)); \ context.SetRenderTargetLayerStride(index, layerStrideLsr2); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, layerStrideLsr2): \ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, baseLayer, { \
context.SetRenderTargetLayerStride(index, argument); \ context.SetRenderTargetBaseLayer(index, baseLayer); \
break; \ })
case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, baseLayer): \
context.SetRenderTargetBaseLayer(index, argument); \
break;
BOOST_PP_REPEAT(8, RENDER_TARGET_ARRAY, 0) BOOST_PP_REPEAT(8, RENDER_TARGET_ARRAY, 0)
static_assert(type::RenderTargetCount == 8 && type::RenderTargetCount < BOOST_PP_LIMIT_REPEAT); static_assert(type::RenderTargetCount == 8 && type::RenderTargetCount < BOOST_PP_LIMIT_REPEAT);
#undef RENDER_TARGET_ARRAY #undef RENDER_TARGET_ARRAY
#define VIEWPORT_TRANSFORM_CALLBACKS(z, index, data) \ #define VIEWPORT_TRANSFORM_CALLBACKS(z, index, data) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleX): \ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, scaleX, { \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateX): \ context.SetViewportX(index, scaleX, registers.viewportTransforms[index].translateX); \
context.SetViewportX(index, registers.viewportTransforms[index].scaleX, registers.viewportTransforms[index].translateX); \ }) \
break; \ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, translateX, { \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleY): \ context.SetViewportX(index, registers.viewportTransforms[index].scaleX, translateX); \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateY): \ }) \
context.SetViewportY(index, registers.viewportTransforms[index].scaleY, registers.viewportTransforms[index].translateY); \ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, scaleY, { \
break; \ context.SetViewportY(index, scaleY, registers.viewportTransforms[index].translateY); \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleZ): \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateZ): \ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, translateY, { \
context.SetViewportZ(index, registers.viewportTransforms[index].scaleY, registers.viewportTransforms[index].translateY); \ context.SetViewportY(index, registers.viewportTransforms[index].scaleY, translateY); \
break; }) \
MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, scaleZ, { \
context.SetViewportZ(index, scaleZ, registers.viewportTransforms[index].translateZ); \
}) \
MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, translateZ, { \
context.SetViewportZ(index, registers.viewportTransforms[index].scaleZ, translateZ); \
})
BOOST_PP_REPEAT(16, VIEWPORT_TRANSFORM_CALLBACKS, 0) BOOST_PP_REPEAT(16, VIEWPORT_TRANSFORM_CALLBACKS, 0)
static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT); static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT);
#undef VIEWPORT_TRANSFORM_CALLBACKS #undef VIEWPORT_TRANSFORM_CALLBACKS
#define COLOR_CLEAR_CALLBACKS(z, index, data) \ #define COLOR_CLEAR_CALLBACKS(z, index, data) \
case MAXWELL3D_ARRAY_OFFSET(clearColorValue, index): \ MAXWELL3D_ARRAY_CASE(clearColorValue, index, { \
context.UpdateClearColorValue(index, argument); \ context.UpdateClearColorValue(index, clearColorValue); \
break; })
BOOST_PP_REPEAT(4, COLOR_CLEAR_CALLBACKS, 0) BOOST_PP_REPEAT(4, COLOR_CLEAR_CALLBACKS, 0)
static_assert(4 < BOOST_PP_LIMIT_REPEAT); static_assert(4 < BOOST_PP_LIMIT_REPEAT);
#undef COLOR_CLEAR_CALLBACKS #undef COLOR_CLEAR_CALLBACKS
#define SCISSOR_CALLBACKS(z, index, data) \ #define SCISSOR_CALLBACKS(z, index, data) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, enable): \ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, enable, { \
context.SetScissor(index, argument ? registers.scissors[index] : std::optional<type::Scissor>{}); \ context.SetScissor(index, enable ? registers.scissors[index] : std::optional<type::Scissor>{}); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, horizontal): \ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, horizontal, { \
context.SetScissorHorizontal(index, registers.scissors[index].horizontal); \ context.SetScissorHorizontal(index, horizontal); \
break; \ }) \
case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, vertical): \ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, vertical, { \
context.SetScissorVertical(index, registers.scissors[index].vertical); \ context.SetScissorVertical(index, vertical); \
break; })
BOOST_PP_REPEAT(16, SCISSOR_CALLBACKS, 0) BOOST_PP_REPEAT(16, SCISSOR_CALLBACKS, 0)
static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT); static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT);
#undef SCISSOR_CALLBACKS #undef SCISSOR_CALLBACKS
case MAXWELL3D_OFFSET(renderTargetControl): MAXWELL3D_CASE(renderTargetControl, {
context.UpdateRenderTargetControl(registers.renderTargetControl); context.UpdateRenderTargetControl(registers.renderTargetControl);
break; })
}
}
case MAXWELL3D_OFFSET(clearBuffers): switch (method) {
MAXWELL3D_STRUCT_CASE(mme, instructionRamLoad, {
if (registers.mme.instructionRamPointer >= macroCode.size())
throw exception("Macro memory is full!");
macroCode[registers.mme.instructionRamPointer++] = instructionRamLoad;
// Wraparound writes
registers.mme.instructionRamPointer %= macroCode.size();
})
MAXWELL3D_STRUCT_CASE(mme, startAddressRamLoad, {
if (registers.mme.startAddressRamPointer >= macroPositions.size())
throw exception("Maximum amount of macros reached!");
macroPositions[registers.mme.startAddressRamPointer++] = startAddressRamLoad;
})
MAXWELL3D_CASE(syncpointAction, {
state.logger->Debug("Increment syncpoint: {}", static_cast<u16>(syncpointAction.id));
state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
})
MAXWELL3D_CASE(clearBuffers, {
context.ClearBuffers(registers.clearBuffers); context.ClearBuffers(registers.clearBuffers);
break; })
case MAXWELL3D_OFFSET(semaphore.info): MAXWELL3D_STRUCT_CASE(semaphore, info, {
switch (registers.semaphore.info.op) { switch (info.op) {
case type::SemaphoreInfo::Op::Release: case type::SemaphoreInfo::Op::Release:
WriteSemaphoreResult(registers.semaphore.payload); WriteSemaphoreResult(registers.semaphore.payload);
break; break;
case type::SemaphoreInfo::Op::Counter: { case type::SemaphoreInfo::Op::Counter: {
switch (registers.semaphore.info.counterType) { switch (info.counterType) {
case type::SemaphoreInfo::CounterType::Zero: case type::SemaphoreInfo::CounterType::Zero:
WriteSemaphoreResult(0); WriteSemaphoreResult(0);
break; break;
default: default:
state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast<u8>(registers.semaphore.info.counterType)); state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast<u8>(info.counterType));
break; break;
} }
break; break;
} }
default: default:
state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast<u8>(registers.semaphore.info.op)); state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast<u8>(info.op));
break; break;
} }
break; })
case MAXWELL3D_OFFSET(firmwareCall[4]): MAXWELL3D_ARRAY_CASE(firmwareCall, 4, {
registers.raw[0xD00] = 1; registers.raw[0xD00] = 1;
break; })
default: default:
break; break;
} }
@ -268,6 +289,13 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
#undef MAXWELL3D_ARRAY_OFFSET #undef MAXWELL3D_ARRAY_OFFSET
#undef MAXWELL3D_ARRAY_STRUCT_OFFSET #undef MAXWELL3D_ARRAY_STRUCT_OFFSET
#undef MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET #undef MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET
#undef MAXWELL3D_CASE_BASE
#undef MAXWELL3D_CASE
#undef MAXWELL3D_STRUCT_CASE
#undef MAXWELL3D_ARRAY_CASE
#undef MAXWELL3D_ARRAY_STRUCT_CASE
#undef MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE
} }
void Maxwell3D::WriteSemaphoreResult(u64 result) { void Maxwell3D::WriteSemaphoreResult(u64 result) {

View File

@ -8,10 +8,6 @@
#include "engine.h" #include "engine.h"
#include "maxwell/macro_interpreter.h" #include "maxwell/macro_interpreter.h"
namespace skyline::gpu::context {
class GraphicsContext;
}
namespace skyline::soc::gm20b::engine::maxwell3d { namespace skyline::soc::gm20b::engine::maxwell3d {
/** /**
* @brief The Maxwell 3D engine handles processing 3D graphics * @brief The Maxwell 3D engine handles processing 3D graphics
@ -245,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); Maxwell3D(const DeviceState &state, GMMU &gmmu);
/** /**
* @brief Resets the Maxwell 3D registers to their default values * @brief Resets the Maxwell 3D registers to their default values

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#include <common/address_space.inc>
#include "gmmu.h"
namespace skyline {
template class FlatAddressSpaceMap<u64, 0, u8 *, nullptr, true, soc::gm20b::GmmuAddressSpaceBits>;
template class FlatMemoryManager<u64, 0, soc::gm20b::GmmuAddressSpaceBits>;
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
#pragma once
#include <common/address_space.h>
namespace skyline::soc::gm20b {
static constexpr u8 GmmuAddressSpaceBits{40}; //!< The size of the GMMU AS in bits
/**
* @brief The GMMU (Graphics Memory Management Unit) class handles mapping between a Maxwell GPU virtual address space and an application's address space and is meant to roughly emulate the GMMU on the X1
* @note This is not accurate to the X1 as it would have an SMMU between the GMMU and physical memory but we don't emulate this abstraction at the moment
* @note The GMMU is implemented entirely as a template specialization over FlatMemoryManager
*/
using GMMU = FlatMemoryManager<u64, 0, GmmuAddressSpaceBits>;
}