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/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; +}