diff --git a/app/src/main/cpp/skyline/gpu.cpp b/app/src/main/cpp/skyline/gpu.cpp index f41220a2..5bdcddb2 100644 --- a/app/src/main/cpp/skyline/gpu.cpp +++ b/app/src/main/cpp/skyline/gpu.cpp @@ -12,7 +12,7 @@ extern skyline::u16 fps; extern skyline::u32 frametime; namespace skyline::gpu { - GPU::GPU(const DeviceState &state) : state(state), window(ANativeWindow_fromSurface(state.jvm->GetEnv(), Surface)), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { + GPU::GPU(const DeviceState &state) : state(state), memoryManager(state), window(ANativeWindow_fromSurface(state.jvm->GetEnv(), Surface)), vsyncEvent(std::make_shared(state)), bufferEvent(std::make_shared(state)) { ANativeWindow_acquire(window); resolution.width = static_cast(ANativeWindow_getWidth(window)); resolution.height = static_cast(ANativeWindow_getHeight(window)); diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp index fb36bbf7..6e6f8b27 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp @@ -1,10 +1,11 @@ // SPDX-License-Identifier: MPL-2.0 // Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/) +#include #include "memory_manager.h" namespace skyline::gpu::vmm { - MemoryManager::MemoryManager() { + MemoryManager::MemoryManager(const DeviceState &state) : state(state) { constexpr u64 GpuAddressSpaceSize = 1ul << 40; //!< The size of the GPU address space constexpr u64 GpuAddressSpaceBase = 0x100000; //!< The base of the GPU address space - must be non-zero @@ -14,7 +15,7 @@ namespace skyline::gpu::vmm { } std::optional MemoryManager::FindChunk(u64 size, ChunkState state) { - auto chunk = std::find_if(chunkList.begin(), chunkList.end(), [size, state] (const ChunkDescriptor& chunk) -> bool { + auto chunk = std::find_if(chunkList.begin(), chunkList.end(), [size, state](const ChunkDescriptor &chunk) -> bool { return chunk.size > size && chunk.state == state; }); @@ -41,10 +42,10 @@ namespace skyline::gpu::vmm { } if (extension) - chunkList.insert(std::next(chunk), ChunkDescriptor(newChunk.address + newChunk.size, extension, oldChunk.cpuAddress + oldChunk.size + newChunk.size, oldChunk.state)); + chunkList.insert(std::next(chunk), ChunkDescriptor(newChunk.address + newChunk.size, extension, (oldChunk.state == ChunkState::Mapped) ? (oldChunk.cpuAddress + oldChunk.size + newChunk.size) : 0, oldChunk.state)); return newChunk.address; - } else if (chunk->address + chunk->size >= newChunk.address) { + } else if (chunk->address + chunk->size > newChunk.address) { chunk->size = (newChunk.address - chunk->address); // Deletes all chunks that are within the chunk being inserted and split the final one @@ -116,7 +117,33 @@ namespace skyline::gpu::vmm { size = util::AlignUp(size, constant::GpuPageSize); - return InsertChunk(ChunkDescriptor(address, size, cpuAddress, ChunkState::Mapped)); } + + void MemoryManager::Read(u8 *destination, u64 address, u64 size) const { + auto chunk = --std::upper_bound(chunkList.begin(), chunkList.end(), address, [](const u64 address, const ChunkDescriptor &chunk) -> bool { + return address < chunk.address; + }); + + if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) + throw exception("Failed to read region in GPU address space - address: {#:X} size: {#:X}", address, size); + + u64 chunkOffset = address - chunk->address; + u64 destinationOffset{}; + + // A continuous region in the GPU address space may be made up of several discontinuous regions in physical memory so we have to iterate over all chunks + while (size != 0) { + if (chunk == chunkList.end() || chunk->state != ChunkState::Mapped) + throw exception("Failed to read region in GPU address space - address: {#:X} size: {#:X}", address, size); + + u64 readSize = std::min(chunk->size - chunkOffset, size); + state.process->ReadMemory(destination + destinationOffset, chunk->cpuAddress + chunkOffset, readSize); + + // After the first read all further reads will start from the base of the chunk + chunkOffset = 0; + size -= readSize; + destinationOffset += readSize; + chunk++; + } + } } diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.h b/app/src/main/cpp/skyline/gpu/memory_manager.h index 4dfc17b2..08df7eb1 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.h +++ b/app/src/main/cpp/skyline/gpu/memory_manager.h @@ -35,7 +35,7 @@ namespace skyline { * @param chunk The chunk to check * @return If the given chunk can be contained wholly within this chunk */ - inline bool CanContain(const ChunkDescriptor& chunk) { + inline bool CanContain(const ChunkDescriptor &chunk) { return (chunk.address >= this->address) && ((this->size + this->address) >= (chunk.size + chunk.address)); } }; @@ -45,6 +45,7 @@ namespace skyline { */ class MemoryManager { private: + const DeviceState &state; std::vector chunkList; //!< This vector holds all the chunk descriptors /** @@ -63,7 +64,7 @@ namespace skyline { u64 InsertChunk(const ChunkDescriptor &newChunk); public: - MemoryManager(); + MemoryManager(const DeviceState &state); /** * @brief This reserves a region of the GPU address space so it can be automatically used when mapping @@ -96,6 +97,11 @@ namespace skyline { * @return The virtual address of the region base */ u64 MapFixed(u64 address, u64 cpuAddress, u64 size); + + /** + * @brief Reads in a buffer from a region of the GPU virtual address space + */ + void Read(u8 *destination, u64 address, u64 size) const; }; } }