diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 4243facf..1a8db5ac 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -171,7 +171,7 @@
-
+
diff --git a/.idea/misc.xml b/.idea/misc.xml
index a9a1b1da..7689fac6 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -60,7 +60,7 @@
-
+
diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt
index 323c7a4c..f735619e 100644
--- a/app/CMakeLists.txt
+++ b/app/CMakeLists.txt
@@ -110,6 +110,7 @@ add_library(skyline SHARED
${source_DIR}/skyline/soc/gm20b/gpfifo.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/gmmu.cpp
${source_DIR}/skyline/input/npad.cpp
${source_DIR}/skyline/input/npad_device.cpp
${source_DIR}/skyline/input/touch.cpp
diff --git a/app/src/main/cpp/skyline/common.h b/app/src/main/cpp/skyline/common.h
index cbf621f6..8cacb426 100644
--- a/app/src/main/cpp/skyline/common.h
+++ b/app/src/main/cpp/skyline/common.h
@@ -372,6 +372,14 @@ namespace skyline {
void FillRandomBytes(T &object) {
FillRandomBytes(std::span(reinterpret_cast::type *>(&object), IntegerFor::count));
}
+
+ /**
+ * @brief A temporary shim for C++ 20's bit_cast to make transitioning to it easier
+ */
+ template
+ To BitCast(const From& from) {
+ return *reinterpret_cast(&from);
+ }
}
/**
@@ -443,7 +451,7 @@ namespace skyline {
* @return If a supplied span is located entirely inside this span and is effectively a subspan
*/
constexpr bool contains(const span& 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 **/
diff --git a/app/src/main/cpp/skyline/gpu/context/graphics_context.h b/app/src/main/cpp/skyline/gpu/context/graphics_context.h
index bd7e7273..40c07dba 100644
--- a/app/src/main/cpp/skyline/gpu/context/graphics_context.h
+++ b/app/src/main/cpp/skyline/gpu/context/graphics_context.h
@@ -6,6 +6,7 @@
#include
#include
#include
+#include
#include
namespace skyline::gpu::context {
@@ -18,18 +19,23 @@ namespace skyline::gpu::context {
class GraphicsContext {
private:
GPU &gpu;
+ soc::gm20b::GMMU &gmmu;
struct RenderTarget {
bool disabled{}; //!< If this RT has been disabled and will be an unbound attachment instead
union {
u64 gpuAddress;
struct {
- u32 gpuAddressHigh;
u32 gpuAddressLow;
+ u32 gpuAddressHigh;
};
};
GuestTexture guest;
std::optional 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 renderTargets{}; //!< The target textures to render into as color attachments
@@ -44,7 +50,7 @@ namespace skyline::gpu::context {
public:
- GraphicsContext(GPU &gpu) : gpu(gpu) {
+ GraphicsContext(GPU &gpu, soc::gm20b::GMMU &gmmu) : gpu(gpu), gmmu(gmmu) {
scissors.fill(DefaultScissor);
}
@@ -64,19 +70,19 @@ namespace skyline::gpu::context {
renderTarget.view.reset();
}
- void SetRenderTargetAddressWidth(size_t index, u32 value) {
+ void SetRenderTargetWidth(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.width = value;
renderTarget.view.reset();
}
- void SetRenderTargetAddressHeight(size_t index, u32 value) {
+ void SetRenderTargetHeight(size_t index, u32 value) {
auto &renderTarget{renderTargets.at(index)};
renderTarget.guest.dimensions.height = value;
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)};
renderTarget.guest.format = [&]() -> texture::Format {
switch (format) {
@@ -137,8 +143,9 @@ namespace skyline::gpu::context {
return &*renderTarget.view;
if (renderTarget.guest.mappings.empty()) {
- // TODO: Fill in mappings
- return nullptr;
+ auto size{std::max(renderTarget.guest.layerStride * (renderTarget.guest.layerCount - renderTarget.guest.baseArrayLayer), renderTarget.guest.format->GetSize(renderTarget.guest.dimensions))};
+ auto mappings{gmmu.TranslateRange(renderTarget.gpuAddress, size)};
+ renderTarget.guest.mappings.assign(mappings.begin(), mappings.end());
}
return &*(renderTarget.view = gpu.texture.FindOrCreate(renderTarget.guest));
diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp
index 6663899d..56df83f1 100644
--- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp
+++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp
@@ -111,7 +111,6 @@ namespace skyline::gpu::memory {
Image MemoryManager::AllocateMappedImage(const vk::ImageCreateInfo &createInfo) {
VmaAllocationCreateInfo allocationCreateInfo{
- .flags = VMA_ALLOCATION_CREATE_MAPPED_BIT,
.usage = VMA_MEMORY_USAGE_UNKNOWN,
.memoryTypeBits = static_cast(vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent | vk::MemoryPropertyFlagBits::eDeviceLocal),
};
diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.h b/app/src/main/cpp/skyline/gpu/memory_manager.h
index c4207dec..0596e808 100644
--- a/app/src/main/cpp/skyline/gpu/memory_manager.h
+++ b/app/src/main/cpp/skyline/gpu/memory_manager.h
@@ -31,6 +31,7 @@ namespace skyline::gpu::memory {
/**
* @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 {
private:
diff --git a/app/src/main/cpp/skyline/gpu/texture_manager.cpp b/app/src/main/cpp/skyline/gpu/texture_manager.cpp
index a31161c6..c22244c5 100644
--- a/app/src/main/cpp/skyline/gpu/texture_manager.cpp
+++ b/app/src/main/cpp/skyline/gpu/texture_manager.cpp
@@ -21,23 +21,26 @@ namespace skyline::gpu {
// 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
+ std::scoped_lock lock(mutex);
std::shared_ptr match{};
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};
+ if (!hostMapping->contains(guestMapping))
+ continue;
// 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
auto firstHostMapping{hostMapping->iterator};
auto lastGuestMapping{guestTexture.mappings.back()};
auto lastHostMapping{std::find_if(firstHostMapping, hostMappings.end(), [&lastGuestMapping](const span &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 &lhs, const span &rhs) {
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
auto &matchGuestTexture{*hostMapping->texture->guest};
if (matchGuestTexture.format->IsCompatible(*guestTexture.format) && matchGuestTexture.dimensions == guestTexture.dimensions && matchGuestTexture.tileConfig == guestTexture.tileConfig) {
diff --git a/app/src/main/cpp/skyline/gpu/texture_manager.h b/app/src/main/cpp/skyline/gpu/texture_manager.h
index 5bb12f1d..d80814f9 100644
--- a/app/src/main/cpp/skyline/gpu/texture_manager.h
+++ b/app/src/main/cpp/skyline/gpu/texture_manager.h
@@ -27,10 +27,6 @@ namespace skyline::gpu {
std::mutex mutex; //!< Synchronizes access to the texture mappings
std::vector 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:
TextureManager(GPU &gpu);
diff --git a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp
index cecdb9e2..3b294273 100644
--- a/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp
+++ b/app/src/main/cpp/skyline/services/nvdrv/devices/nvhost/as_gpu.cpp
@@ -12,7 +12,7 @@ namespace skyline {
}
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) {}
@@ -320,7 +320,7 @@ namespace skyline::service::nvdrv::device::nvhost {
}
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 {
auto h{core.nvMap.GetHandle(entry.handle)};
if (!h)
diff --git a/app/src/main/cpp/skyline/soc/gm20b.cpp b/app/src/main/cpp/skyline/soc/gm20b.cpp
index 46c4ce9d..e945202e 100644
--- a/app/src/main/cpp/skyline/soc/gm20b.cpp
+++ b/app/src/main/cpp/skyline/soc/gm20b.cpp
@@ -1,19 +1,13 @@
// SPDX-License-Identifier: MPL-2.0
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
-#include
#include "gm20b.h"
-namespace skyline {
- template class FlatAddressSpaceMap;
- template class FlatMemoryManager;
-}
-
namespace skyline::soc::gm20b {
GM20B::GM20B(const DeviceState &state) :
fermi2D(state),
keplerMemory(state),
- maxwell3D(state),
+ maxwell3D(state, gmmu),
maxwellCompute(state),
maxwellDma(state),
gpfifo(state) {}
diff --git a/app/src/main/cpp/skyline/soc/gm20b.h b/app/src/main/cpp/skyline/soc/gm20b.h
index 7ad39445..e23d40e7 100644
--- a/app/src/main/cpp/skyline/soc/gm20b.h
+++ b/app/src/main/cpp/skyline/soc/gm20b.h
@@ -3,9 +3,9 @@
#pragma once
-#include
#include "gm20b/engines/maxwell_3d.h"
#include "gm20b/gpfifo.h"
+#include "gm20b/gmmu.h"
namespace skyline::soc::gm20b {
/**
@@ -14,16 +14,13 @@ namespace skyline::soc::gm20b {
*/
class GM20B {
public:
- static constexpr u8 AddressSpaceBits{40}; //!< The width of the GMMU AS
- using GMMU = FlatMemoryManager;
-
+ GMMU gmmu;
engine::Engine fermi2D;
engine::maxwell3d::Maxwell3D maxwell3D;
engine::Engine maxwellCompute;
engine::Engine maxwellDma;
engine::Engine keplerMemory;
GPFIFO gpfifo;
- GMMU gmmu;
GM20B(const DeviceState &state);
};
diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp
index 29afa5ca..949b6eb8 100644
--- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp
+++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.cpp
@@ -6,7 +6,7 @@
#include
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();
}
@@ -104,161 +104,182 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
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_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_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)
- switch (method) {
- case MAXWELL3D_OFFSET(mme.instructionRamLoad):
- if (registers.mme.instructionRamPointer >= macroCode.size())
- throw exception("Macro memory is full!");
+ #define MAXWELL3D_CASE_BASE(fieldName, fieldAccessor, offset, content) case offset: { \
+ auto fieldName{util::BitCast(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)
- macroCode[registers.mme.instructionRamPointer++] = argument;
+ 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];
+ }
- // Wraparound writes
- registers.mme.instructionRamPointer %= macroCode.size();
+ bool redundant{registers.raw[method] == argument};
+ registers.raw[method] = argument;
- break;
+ if (!redundant) {
+ switch (method) {
+ MAXWELL3D_STRUCT_CASE(mme, shadowRamControl, {
+ shadowRegisters.mme.shadowRamControl = shadowRamControl;
+ })
- 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(argument);
- break;
-
- case MAXWELL3D_OFFSET(syncpointAction):
- state.logger->Debug("Increment syncpoint: {}", static_cast(registers.syncpointAction.id));
- state.soc->host1x.syncpoints.at(registers.syncpointAction.id).Increment();
- break;
-
- #define RENDER_TARGET_ARRAY(z, index, data) \
- case MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(renderTargets, index, address, high): \
- context.SetRenderTargetAddressHigh(index, argument); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_STRUCT_OFFSET(renderTargets, index, address, low): \
- context.SetRenderTargetAddressLow(index, argument); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, width): \
- context.SetRenderTargetAddressWidth(index, argument); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, height): \
- context.SetRenderTargetAddressHeight(index, argument); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, format): \
- context.SetRenderTargetAddressFormat(index, \
- static_cast(argument)); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, tileMode): \
- context.SetRenderTargetTileMode(index, \
- *reinterpret_cast(&argument)); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, arrayMode): \
- context.SetRenderTargetArrayMode(index, \
- *reinterpret_cast(&argument)); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, layerStrideLsr2): \
- context.SetRenderTargetLayerStride(index, argument); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(renderTargets, index, baseLayer): \
- context.SetRenderTargetBaseLayer(index, argument); \
- break;
+ #define RENDER_TARGET_ARRAY(z, index, data) \
+ MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(renderTargets, index, address, high, { \
+ context.SetRenderTargetAddressHigh(index, high); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_STRUCT_CASE(renderTargets, index, address, low, { \
+ context.SetRenderTargetAddressLow(index, low); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, width, { \
+ context.SetRenderTargetWidth(index, width); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, height, { \
+ context.SetRenderTargetHeight(index, height); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, format, { \
+ context.SetRenderTargetFormat(index, format); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, tileMode, { \
+ context.SetRenderTargetTileMode(index, tileMode); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, arrayMode, { \
+ context.SetRenderTargetArrayMode(index, arrayMode); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, layerStrideLsr2, { \
+ context.SetRenderTargetLayerStride(index, layerStrideLsr2); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(renderTargets, index, baseLayer, { \
+ context.SetRenderTargetBaseLayer(index, baseLayer); \
+ })
BOOST_PP_REPEAT(8, RENDER_TARGET_ARRAY, 0)
static_assert(type::RenderTargetCount == 8 && type::RenderTargetCount < BOOST_PP_LIMIT_REPEAT);
#undef RENDER_TARGET_ARRAY
- #define VIEWPORT_TRANSFORM_CALLBACKS(z, index, data) \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleX): \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateX): \
- context.SetViewportX(index, registers.viewportTransforms[index].scaleX, registers.viewportTransforms[index].translateX); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleY): \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateY): \
- context.SetViewportY(index, registers.viewportTransforms[index].scaleY, registers.viewportTransforms[index].translateY); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, scaleZ): \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(viewportTransforms, index, translateZ): \
- context.SetViewportZ(index, registers.viewportTransforms[index].scaleY, registers.viewportTransforms[index].translateY); \
- break;
+ #define VIEWPORT_TRANSFORM_CALLBACKS(z, index, data) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, scaleX, { \
+ context.SetViewportX(index, scaleX, registers.viewportTransforms[index].translateX); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, translateX, { \
+ context.SetViewportX(index, registers.viewportTransforms[index].scaleX, translateX); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, scaleY, { \
+ context.SetViewportY(index, scaleY, registers.viewportTransforms[index].translateY); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(viewportTransforms, index, translateY, { \
+ context.SetViewportY(index, registers.viewportTransforms[index].scaleY, translateY); \
+ }) \
+ 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)
static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT);
#undef VIEWPORT_TRANSFORM_CALLBACKS
- #define COLOR_CLEAR_CALLBACKS(z, index, data) \
- case MAXWELL3D_ARRAY_OFFSET(clearColorValue, index): \
- context.UpdateClearColorValue(index, argument); \
- break;
+ #define COLOR_CLEAR_CALLBACKS(z, index, data) \
+ MAXWELL3D_ARRAY_CASE(clearColorValue, index, { \
+ context.UpdateClearColorValue(index, clearColorValue); \
+ })
BOOST_PP_REPEAT(4, COLOR_CLEAR_CALLBACKS, 0)
static_assert(4 < BOOST_PP_LIMIT_REPEAT);
#undef COLOR_CLEAR_CALLBACKS
- #define SCISSOR_CALLBACKS(z, index, data) \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, enable): \
- context.SetScissor(index, argument ? registers.scissors[index] : std::optional{}); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, horizontal): \
- context.SetScissorHorizontal(index, registers.scissors[index].horizontal); \
- break; \
- case MAXWELL3D_ARRAY_STRUCT_OFFSET(scissors, index, vertical): \
- context.SetScissorVertical(index, registers.scissors[index].vertical); \
- break;
+ #define SCISSOR_CALLBACKS(z, index, data) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, enable, { \
+ context.SetScissor(index, enable ? registers.scissors[index] : std::optional{}); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, horizontal, { \
+ context.SetScissorHorizontal(index, horizontal); \
+ }) \
+ MAXWELL3D_ARRAY_STRUCT_CASE(scissors, index, vertical, { \
+ context.SetScissorVertical(index, vertical); \
+ })
BOOST_PP_REPEAT(16, SCISSOR_CALLBACKS, 0)
static_assert(type::ViewportCount == 16 && type::ViewportCount < BOOST_PP_LIMIT_REPEAT);
#undef SCISSOR_CALLBACKS
- case MAXWELL3D_OFFSET(renderTargetControl):
- context.UpdateRenderTargetControl(registers.renderTargetControl);
- break;
+ MAXWELL3D_CASE(renderTargetControl, {
+ context.UpdateRenderTargetControl(registers.renderTargetControl);
+ })
+ }
+ }
- 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(syncpointAction.id));
+ state.soc->host1x.syncpoints.at(syncpointAction.id).Increment();
+ })
+
+ MAXWELL3D_CASE(clearBuffers, {
context.ClearBuffers(registers.clearBuffers);
- break;
+ })
- case MAXWELL3D_OFFSET(semaphore.info):
- switch (registers.semaphore.info.op) {
+ MAXWELL3D_STRUCT_CASE(semaphore, info, {
+ switch (info.op) {
case type::SemaphoreInfo::Op::Release:
WriteSemaphoreResult(registers.semaphore.payload);
break;
case type::SemaphoreInfo::Op::Counter: {
- switch (registers.semaphore.info.counterType) {
+ switch (info.counterType) {
case type::SemaphoreInfo::CounterType::Zero:
WriteSemaphoreResult(0);
break;
default:
- state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast(registers.semaphore.info.counterType));
+ state.logger->Warn("Unsupported semaphore counter type: 0x{:X}", static_cast(info.counterType));
break;
}
break;
}
default:
- state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast(registers.semaphore.info.op));
+ state.logger->Warn("Unsupported semaphore operation: 0x{:X}", static_cast(info.op));
break;
}
- break;
+ })
- case MAXWELL3D_OFFSET(firmwareCall[4]):
+ MAXWELL3D_ARRAY_CASE(firmwareCall, 4, {
registers.raw[0xD00] = 1;
- break;
+ })
+
default:
break;
}
@@ -268,6 +289,13 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
#undef MAXWELL3D_ARRAY_OFFSET
#undef MAXWELL3D_ARRAY_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) {
diff --git a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h
index 7e2fefe5..05a68319 100644
--- a/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h
+++ b/app/src/main/cpp/skyline/soc/gm20b/engines/maxwell_3d.h
@@ -8,10 +8,6 @@
#include "engine.h"
#include "maxwell/macro_interpreter.h"
-namespace skyline::gpu::context {
- class GraphicsContext;
-}
-
namespace skyline::soc::gm20b::engine::maxwell3d {
/**
* @brief The Maxwell 3D engine handles processing 3D graphics
@@ -245,7 +241,7 @@ namespace skyline::soc::gm20b::engine::maxwell3d {
std::array 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
diff --git a/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp b/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp
new file mode 100644
index 00000000..778ef733
--- /dev/null
+++ b/app/src/main/cpp/skyline/soc/gm20b/gmmu.cpp
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#include
+#include "gmmu.h"
+
+namespace skyline {
+ template class FlatAddressSpaceMap;
+ template class FlatMemoryManager;
+}
diff --git a/app/src/main/cpp/skyline/soc/gm20b/gmmu.h b/app/src/main/cpp/skyline/soc/gm20b/gmmu.h
new file mode 100644
index 00000000..00eb02a5
--- /dev/null
+++ b/app/src/main/cpp/skyline/soc/gm20b/gmmu.h
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: MPL-2.0
+// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
+
+#pragma once
+
+#include
+
+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;
+}