diff options
Diffstat (limited to '')
23 files changed, 443 insertions, 168 deletions
diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 8760d17a8..8cad070b4 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -129,7 +129,8 @@ public: }; std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { - auto** const page_table = Core::CurrentProcess()->vm_manager.page_table.pointers.data(); + auto& current_process = Core::CurrentProcess(); + auto** const page_table = current_process->vm_manager.page_table.pointers.data(); Dynarmic::A64::UserConfig config; @@ -138,7 +139,7 @@ std::unique_ptr<Dynarmic::A64::Jit> ARM_Dynarmic::MakeJit() const { // Memory config.page_table = reinterpret_cast<void**>(page_table); - config.page_table_address_space_bits = Memory::ADDRESS_SPACE_BITS; + config.page_table_address_space_bits = current_process->vm_manager.GetAddressSpaceWidth(); config.silently_mirror_page_table = false; // Multi-process state diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 02319ce0f..8903ed1d3 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp @@ -83,10 +83,12 @@ void ProgramMetadata::Print() const { auto address_space = "Unknown"; switch (npdm_header.address_space_type) { - case ProgramAddressSpaceType::Is64Bit: + case ProgramAddressSpaceType::Is36Bit: + case ProgramAddressSpaceType::Is39Bit: address_space = "64-bit"; break; case ProgramAddressSpaceType::Is32Bit: + case ProgramAddressSpaceType::Is32BitNoMap: address_space = "32-bit"; break; } diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 1143e36c4..e4470d6f0 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h @@ -17,8 +17,10 @@ enum class ResultStatus : u16; namespace FileSys { enum class ProgramAddressSpaceType : u8 { - Is64Bit = 1, - Is32Bit = 2, + Is32Bit = 0, + Is36Bit = 1, + Is32BitNoMap = 2, + Is39Bit = 3, }; enum class ProgramFilePermission : u64 { diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp index 0ecdd9f82..d8c7b3492 100644 --- a/src/core/gdbstub/gdbstub.cpp +++ b/src/core/gdbstub/gdbstub.cpp @@ -37,7 +37,9 @@ #include "core/core.h" #include "core/core_cpu.h" #include "core/gdbstub/gdbstub.h" +#include "core/hle/kernel/process.h" #include "core/hle/kernel/scheduler.h" +#include "core/hle/kernel/vm_manager.h" #include "core/loader/loader.h" #include "core/memory.h" @@ -585,7 +587,8 @@ static void HandleQuery() { strlen("Xfer:features:read:target.xml:")) == 0) { SendReply(target_xml); } else if (strncmp(query, "Offsets", strlen("Offsets")) == 0) { - std::string buffer = fmt::format("TextSeg={:0x}", Memory::PROCESS_IMAGE_VADDR); + const VAddr base_address = Core::CurrentProcess()->vm_manager.GetCodeRegionBaseAddress(); + std::string buffer = fmt::format("TextSeg={:0x}", base_address); SendReply(buffer.c_str()); } else if (strncmp(query, "fThreadInfo", strlen("fThreadInfo")) == 0) { std::string val = "m"; @@ -893,11 +896,11 @@ static void ReadMemory() { static u8 reply[GDB_BUFFER_SIZE - 4]; auto start_offset = command_buffer + 1; - auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); - VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); + const auto addr_pos = std::find(start_offset, command_buffer + command_length, ','); + const VAddr addr = HexToLong(start_offset, static_cast<u64>(addr_pos - start_offset)); start_offset = addr_pos + 1; - u64 len = + const u64 len = HexToLong(start_offset, static_cast<u64>((command_buffer + command_length) - start_offset)); LOG_DEBUG(Debug_GDBStub, "gdb: addr: {:016X} len: {:016X}", addr, len); @@ -906,7 +909,9 @@ static void ReadMemory() { SendReply("E01"); } - if (addr < Memory::PROCESS_IMAGE_VADDR || addr >= Memory::MAP_REGION_VADDR_END) { + const auto& vm_manager = Core::CurrentProcess()->vm_manager; + if (addr < vm_manager.GetCodeRegionBaseAddress() || + addr >= vm_manager.GetMapRegionEndAddress()) { return SendReply("E00"); } diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp index 121f741fd..a8e3098ca 100644 --- a/src/core/hle/kernel/process.cpp +++ b/src/core/hle/kernel/process.cpp @@ -8,6 +8,7 @@ #include "common/common_funcs.h" #include "common/logging/log.h" #include "core/core.h" +#include "core/file_sys/program_metadata.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" @@ -34,14 +35,21 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) { process->name = std::move(name); process->flags.raw = 0; process->flags.memory_region.Assign(MemoryRegion::APPLICATION); + process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION); process->status = ProcessStatus::Created; process->program_id = 0; process->process_id = kernel.CreateNewProcessID(); + process->svc_access_mask.set(); kernel.AppendNewProcess(process); return process; } +void Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) { + program_id = metadata.GetTitleID(); + vm_manager.Reset(metadata.GetAddressSpaceType()); +} + void Process::ParseKernelCaps(const u32* kernel_caps, std::size_t len) { for (std::size_t i = 0; i < len; ++i) { u32 descriptor = kernel_caps[i]; @@ -119,7 +127,7 @@ void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) { // TODO(bunnei): This is heap area that should be allocated by the kernel and not mapped as part // of the user address space. vm_manager - .MapMemoryBlock(Memory::STACK_AREA_VADDR_END - stack_size, + .MapMemoryBlock(vm_manager.GetTLSIORegionEndAddress() - stack_size, std::make_shared<std::vector<u8>>(stack_size, 0), 0, stack_size, MemoryState::Mapped) .Unwrap(); @@ -185,6 +193,7 @@ static std::tuple<std::size_t, std::size_t, bool> FindFreeThreadLocalSlot( VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { auto [available_page, available_slot, needs_allocation] = FindFreeThreadLocalSlot(tls_slots); + const VAddr tls_begin = vm_manager.GetTLSIORegionBaseAddress(); if (needs_allocation) { tls_slots.emplace_back(0); // The page is completely available at the start @@ -197,18 +206,17 @@ VAddr Process::MarkNextAvailableTLSSlotAsUsed(Thread& thread) { vm_manager.RefreshMemoryBlockMappings(tls_memory.get()); - vm_manager.MapMemoryBlock(Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE, - tls_memory, 0, Memory::PAGE_SIZE, MemoryState::ThreadLocal); + vm_manager.MapMemoryBlock(tls_begin + available_page * Memory::PAGE_SIZE, tls_memory, 0, + Memory::PAGE_SIZE, MemoryState::ThreadLocal); } tls_slots[available_page].set(available_slot); - return Memory::TLS_AREA_VADDR + available_page * Memory::PAGE_SIZE + - available_slot * Memory::TLS_ENTRY_SIZE; + return tls_begin + available_page * Memory::PAGE_SIZE + available_slot * Memory::TLS_ENTRY_SIZE; } void Process::FreeTLSSlot(VAddr tls_address) { - const VAddr tls_base = tls_address - Memory::TLS_AREA_VADDR; + const VAddr tls_base = tls_address - vm_manager.GetTLSIORegionBaseAddress(); const VAddr tls_page = tls_base / Memory::PAGE_SIZE; const VAddr tls_slot = (tls_base % Memory::PAGE_SIZE) / Memory::TLS_ENTRY_SIZE; @@ -232,8 +240,8 @@ void Process::LoadModule(SharedPtr<CodeSet> module_, VAddr base_addr) { } ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission perms) { - if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || - target + size < target) { + if (target < vm_manager.GetHeapRegionBaseAddress() || + target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { return ERR_INVALID_ADDRESS; } @@ -268,8 +276,8 @@ ResultVal<VAddr> Process::HeapAllocate(VAddr target, u64 size, VMAPermission per } ResultCode Process::HeapFree(VAddr target, u32 size) { - if (target < Memory::HEAP_VADDR || target + size > Memory::HEAP_VADDR_END || - target + size < target) { + if (target < vm_manager.GetHeapRegionBaseAddress() || + target + size > vm_manager.GetHeapRegionEndAddress() || target + size < target) { return ERR_INVALID_ADDRESS; } diff --git a/src/core/hle/kernel/process.h b/src/core/hle/kernel/process.h index 04d74e572..adb03c228 100644 --- a/src/core/hle/kernel/process.h +++ b/src/core/hle/kernel/process.h @@ -17,6 +17,10 @@ #include "core/hle/kernel/thread.h" #include "core/hle/kernel/vm_manager.h" +namespace FileSys { +class ProgramMetadata; +} + namespace Kernel { class KernelCore; @@ -141,6 +145,14 @@ public: return process_id; } + /** + * Loads process-specifics configuration info with metadata provided + * by an executable. + * + * @param metadata The provided metadata to load process specific info. + */ + void LoadFromMetadata(const FileSys::ProgramMetadata& metadata); + /// Title ID corresponding to the process u64 program_id; diff --git a/src/core/hle/kernel/shared_memory.cpp b/src/core/hle/kernel/shared_memory.cpp index abb1d09cd..9b78c8cb5 100644 --- a/src/core/hle/kernel/shared_memory.cpp +++ b/src/core/hle/kernel/shared_memory.cpp @@ -8,6 +8,7 @@ #include "common/logging/log.h" #include "core/core.h" #include "core/hle/kernel/errors.h" +#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/shared_memory.h" #include "core/memory.h" @@ -71,7 +72,8 @@ SharedPtr<SharedMemory> SharedMemory::CreateForApplet( shared_memory->other_permissions = other_permissions; shared_memory->backing_block = std::move(heap_block); shared_memory->backing_block_offset = offset; - shared_memory->base_address = Memory::HEAP_VADDR + offset; + shared_memory->base_address = + kernel.CurrentProcess()->vm_manager.GetHeapRegionBaseAddress() + offset; return shared_memory; } diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index c9d212a4c..44bbaf0c8 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -51,8 +51,9 @@ static ResultCode SetHeapSize(VAddr* heap_addr, u64 heap_size) { } auto& process = *Core::CurrentProcess(); + const VAddr heap_base = process.vm_manager.GetHeapRegionBaseAddress(); CASCADE_RESULT(*heap_addr, - process.HeapAllocate(Memory::HEAP_VADDR, heap_size, VMAPermission::ReadWrite)); + process.HeapAllocate(heap_base, heap_size, VMAPermission::ReadWrite)); return RESULT_SUCCESS; } @@ -325,26 +326,27 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) LOG_TRACE(Kernel_SVC, "called info_id=0x{:X}, info_sub_id=0x{:X}, handle=0x{:08X}", info_id, info_sub_id, handle); - const auto& vm_manager = Core::CurrentProcess()->vm_manager; + const auto& current_process = Core::CurrentProcess(); + const auto& vm_manager = current_process->vm_manager; switch (static_cast<GetInfoType>(info_id)) { case GetInfoType::AllowedCpuIdBitmask: - *result = Core::CurrentProcess()->allowed_processor_mask; + *result = current_process->allowed_processor_mask; break; case GetInfoType::AllowedThreadPrioBitmask: - *result = Core::CurrentProcess()->allowed_thread_priority_mask; + *result = current_process->allowed_thread_priority_mask; break; case GetInfoType::MapRegionBaseAddr: - *result = Memory::MAP_REGION_VADDR; + *result = vm_manager.GetMapRegionBaseAddress(); break; case GetInfoType::MapRegionSize: - *result = Memory::MAP_REGION_SIZE; + *result = vm_manager.GetMapRegionSize(); break; case GetInfoType::HeapRegionBaseAddr: - *result = Memory::HEAP_VADDR; + *result = vm_manager.GetHeapRegionBaseAddress(); break; case GetInfoType::HeapRegionSize: - *result = Memory::HEAP_SIZE; + *result = vm_manager.GetHeapRegionSize(); break; case GetInfoType::TotalMemoryUsage: *result = vm_manager.GetTotalMemoryUsage(); @@ -359,22 +361,35 @@ static ResultCode GetInfo(u64* result, u64 info_id, u64 handle, u64 info_sub_id) *result = 0; break; case GetInfoType::AddressSpaceBaseAddr: - *result = vm_manager.GetAddressSpaceBaseAddr(); + *result = vm_manager.GetCodeRegionBaseAddress(); break; - case GetInfoType::AddressSpaceSize: - *result = vm_manager.GetAddressSpaceSize(); + case GetInfoType::AddressSpaceSize: { + const u64 width = vm_manager.GetAddressSpaceWidth(); + + switch (width) { + case 32: + *result = 0xFFE00000; + break; + case 36: + *result = 0xFF8000000; + break; + case 39: + *result = 0x7FF8000000; + break; + } break; + } case GetInfoType::NewMapRegionBaseAddr: - *result = Memory::NEW_MAP_REGION_VADDR; + *result = vm_manager.GetNewMapRegionBaseAddress(); break; case GetInfoType::NewMapRegionSize: - *result = Memory::NEW_MAP_REGION_SIZE; + *result = vm_manager.GetNewMapRegionSize(); break; case GetInfoType::IsVirtualAddressMemoryEnabled: - *result = Core::CurrentProcess()->is_virtual_address_memory_enabled; + *result = current_process->is_virtual_address_memory_enabled; break; case GetInfoType::TitleId: - *result = Core::CurrentProcess()->program_id; + *result = current_process->program_id; break; case GetInfoType::PrivilegedProcessId: LOG_WARNING(Kernel_SVC, diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 315f65338..064ed908d 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -262,8 +262,9 @@ SharedPtr<Thread> SetupMainThread(KernelCore& kernel, VAddr entry_point, u32 pri SetCurrentPageTable(&owner_process.vm_manager.page_table); // Initialize new "main" thread + const VAddr stack_top = owner_process.vm_manager.GetTLSIORegionEndAddress(); auto thread_res = Thread::Create(kernel, "main", entry_point, priority, 0, THREADPROCESSORID_0, - Memory::STACK_AREA_VADDR_END, &owner_process); + stack_top, &owner_process); SharedPtr<Thread> thread = std::move(thread_res).Unwrap(); diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 608cbd57b..e412309fd 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp @@ -9,6 +9,7 @@ #include "common/logging/log.h" #include "core/arm/arm_interface.h" #include "core/core.h" +#include "core/file_sys/program_metadata.h" #include "core/hle/kernel/errors.h" #include "core/hle/kernel/vm_manager.h" #include "core/memory.h" @@ -54,30 +55,32 @@ bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { } VMManager::VMManager() { - Reset(); + // Default to assuming a 39-bit address space. This way we have a sane + // starting point with executables that don't provide metadata. + Reset(FileSys::ProgramAddressSpaceType::Is39Bit); } VMManager::~VMManager() { - Reset(); + Reset(FileSys::ProgramAddressSpaceType::Is39Bit); } -void VMManager::Reset() { - vma_map.clear(); +void VMManager::Reset(FileSys::ProgramAddressSpaceType type) { + Clear(); + + InitializeMemoryRegionRanges(type); + + page_table.Resize(address_space_width); // Initialize the map with a single free region covering the entire managed space. VirtualMemoryArea initial_vma; - initial_vma.size = MAX_ADDRESS; + initial_vma.size = address_space_end; vma_map.emplace(initial_vma.base, initial_vma); - page_table.pointers.fill(nullptr); - page_table.special_regions.clear(); - page_table.attributes.fill(Memory::PageType::Unmapped); - UpdatePageTableForVMA(initial_vma); } VMManager::VMAHandle VMManager::FindVMA(VAddr target) const { - if (target >= MAX_ADDRESS) { + if (target >= address_space_end) { return vma_map.end(); } else { return std::prev(vma_map.upper_bound(target)); @@ -291,7 +294,7 @@ ResultVal<VMManager::VMAIter> VMManager::CarveVMARange(VAddr target, u64 size) { const VAddr target_end = target + size; ASSERT(target_end >= target); - ASSERT(target_end <= MAX_ADDRESS); + ASSERT(target_end <= address_space_end); ASSERT(size > 0); VMAIter begin_vma = StripIterConstness(FindVMA(target)); @@ -382,6 +385,85 @@ void VMManager::UpdatePageTableForVMA(const VirtualMemoryArea& vma) { } } +void VMManager::InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type) { + u64 map_region_size = 0; + u64 heap_region_size = 0; + u64 new_map_region_size = 0; + u64 tls_io_region_size = 0; + + switch (type) { + case FileSys::ProgramAddressSpaceType::Is32Bit: + address_space_width = 32; + code_region_base = 0x200000; + code_region_end = code_region_base + 0x3FE00000; + map_region_size = 0x40000000; + heap_region_size = 0x40000000; + break; + case FileSys::ProgramAddressSpaceType::Is36Bit: + address_space_width = 36; + code_region_base = 0x8000000; + code_region_end = code_region_base + 0x78000000; + map_region_size = 0x180000000; + heap_region_size = 0x180000000; + break; + case FileSys::ProgramAddressSpaceType::Is32BitNoMap: + address_space_width = 32; + code_region_base = 0x200000; + code_region_end = code_region_base + 0x3FE00000; + map_region_size = 0; + heap_region_size = 0x80000000; + break; + case FileSys::ProgramAddressSpaceType::Is39Bit: + address_space_width = 39; + code_region_base = 0x8000000; + code_region_end = code_region_base + 0x80000000; + map_region_size = 0x1000000000; + heap_region_size = 0x180000000; + new_map_region_size = 0x80000000; + tls_io_region_size = 0x1000000000; + break; + default: + UNREACHABLE_MSG("Invalid address space type specified: {}", static_cast<u32>(type)); + return; + } + + address_space_base = 0; + address_space_end = 1ULL << address_space_width; + + map_region_base = code_region_end; + map_region_end = map_region_base + map_region_size; + + heap_region_base = map_region_end; + heap_region_end = heap_region_base + heap_region_size; + + new_map_region_base = heap_region_end; + new_map_region_end = new_map_region_base + new_map_region_size; + + tls_io_region_base = new_map_region_end; + tls_io_region_end = tls_io_region_base + tls_io_region_size; + + if (new_map_region_size == 0) { + new_map_region_base = address_space_base; + new_map_region_end = address_space_end; + } +} + +void VMManager::Clear() { + ClearVMAMap(); + ClearPageTable(); +} + +void VMManager::ClearVMAMap() { + vma_map.clear(); +} + +void VMManager::ClearPageTable() { + std::fill(page_table.pointers.begin(), page_table.pointers.end(), nullptr); + page_table.special_regions.clear(); + std::fill(page_table.attributes.begin(), page_table.attributes.end(), + Memory::PageType::Unmapped); +} + u64 VMManager::GetTotalMemoryUsage() const { LOG_WARNING(Kernel, "(STUBBED) called"); return 0xF8000000; @@ -392,14 +474,80 @@ u64 VMManager::GetTotalHeapUsage() const { return 0x0; } -VAddr VMManager::GetAddressSpaceBaseAddr() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return 0x8000000; +VAddr VMManager::GetAddressSpaceBaseAddress() const { + return address_space_base; +} + +VAddr VMManager::GetAddressSpaceEndAddress() const { + return address_space_end; } u64 VMManager::GetAddressSpaceSize() const { - LOG_WARNING(Kernel, "(STUBBED) called"); - return MAX_ADDRESS; + return address_space_end - address_space_base; +} + +u64 VMManager::GetAddressSpaceWidth() const { + return address_space_width; +} + +VAddr VMManager::GetCodeRegionBaseAddress() const { + return code_region_base; +} + +VAddr VMManager::GetCodeRegionEndAddress() const { + return code_region_end; +} + +u64 VMManager::GetCodeRegionSize() const { + return code_region_end - code_region_base; +} + +VAddr VMManager::GetHeapRegionBaseAddress() const { + return heap_region_base; +} + +VAddr VMManager::GetHeapRegionEndAddress() const { + return heap_region_end; +} + +u64 VMManager::GetHeapRegionSize() const { + return heap_region_end - heap_region_base; +} + +VAddr VMManager::GetMapRegionBaseAddress() const { + return map_region_base; +} + +VAddr VMManager::GetMapRegionEndAddress() const { + return map_region_end; +} + +u64 VMManager::GetMapRegionSize() const { + return map_region_end - map_region_base; +} + +VAddr VMManager::GetNewMapRegionBaseAddress() const { + return new_map_region_base; +} + +VAddr VMManager::GetNewMapRegionEndAddress() const { + return new_map_region_end; +} + +u64 VMManager::GetNewMapRegionSize() const { + return new_map_region_end - new_map_region_base; +} + +VAddr VMManager::GetTLSIORegionBaseAddress() const { + return tls_io_region_base; +} + +VAddr VMManager::GetTLSIORegionEndAddress() const { + return tls_io_region_end; +} + +u64 VMManager::GetTLSIORegionSize() const { + return tls_io_region_end - tls_io_region_base; } } // namespace Kernel diff --git a/src/core/hle/kernel/vm_manager.h b/src/core/hle/kernel/vm_manager.h index de75036c0..015559a64 100644 --- a/src/core/hle/kernel/vm_manager.h +++ b/src/core/hle/kernel/vm_manager.h @@ -12,6 +12,10 @@ #include "core/memory.h" #include "core/memory_hook.h" +namespace FileSys { +enum class ProgramAddressSpaceType : u8; +} + namespace Kernel { enum class VMAType : u8 { @@ -111,12 +115,6 @@ struct VirtualMemoryArea { class VMManager final { public: /** - * The maximum amount of address space managed by the kernel. - * @todo This was selected arbitrarily, and should be verified for Switch OS. - */ - static constexpr VAddr MAX_ADDRESS{0x1000000000ULL}; - - /** * A map covering the entirety of the managed address space, keyed by the `base` field of each * VMA. It must always be modified by splitting or merging VMAs, so that the invariant * `elem.base + elem.size == next.base` is preserved, and mergeable regions must always be @@ -130,7 +128,7 @@ public: ~VMManager(); /// Clears the address space map, re-initializing with a single free area. - void Reset(); + void Reset(FileSys::ProgramAddressSpaceType type); /// Finds the VMA in which the given address is included in, or `vma_map.end()`. VMAHandle FindVMA(VAddr target) const; @@ -195,12 +193,63 @@ public: /// Gets the total heap usage, used by svcGetInfo u64 GetTotalHeapUsage() const; - /// Gets the total address space base address, used by svcGetInfo - VAddr GetAddressSpaceBaseAddr() const; + /// Gets the address space base address + VAddr GetAddressSpaceBaseAddress() const; - /// Gets the total address space address size, used by svcGetInfo + /// Gets the address space end address + VAddr GetAddressSpaceEndAddress() const; + + /// Gets the total address space address size in bytes u64 GetAddressSpaceSize() const; + /// Gets the address space width in bits. + u64 GetAddressSpaceWidth() const; + + /// Gets the base address of the code region. + VAddr GetCodeRegionBaseAddress() const; + + /// Gets the end address of the code region. + VAddr GetCodeRegionEndAddress() const; + + /// Gets the total size of the code region in bytes. + u64 GetCodeRegionSize() const; + + /// Gets the base address of the heap region. + VAddr GetHeapRegionBaseAddress() const; + + /// Gets the end address of the heap region; + VAddr GetHeapRegionEndAddress() const; + + /// Gets the total size of the heap region in bytes. + u64 GetHeapRegionSize() const; + + /// Gets the base address of the map region. + VAddr GetMapRegionBaseAddress() const; + + /// Gets the end address of the map region. + VAddr GetMapRegionEndAddress() const; + + /// Gets the total size of the map region in bytes. + u64 GetMapRegionSize() const; + + /// Gets the base address of the new map region. + VAddr GetNewMapRegionBaseAddress() const; + + /// Gets the end address of the new map region. + VAddr GetNewMapRegionEndAddress() const; + + /// Gets the total size of the new map region in bytes. + u64 GetNewMapRegionSize() const; + + /// Gets the base address of the TLS IO region. + VAddr GetTLSIORegionBaseAddress() const; + + /// Gets the end address of the TLS IO region. + VAddr GetTLSIORegionEndAddress() const; + + /// Gets the total size of the TLS IO region in bytes. + u64 GetTLSIORegionSize() const; + /// Each VMManager has its own page table, which is set as the main one when the owning process /// is scheduled. Memory::PageTable page_table; @@ -240,5 +289,36 @@ private: /// Updates the pages corresponding to this VMA so they match the VMA's attributes. void UpdatePageTableForVMA(const VirtualMemoryArea& vma); + + /// Initializes memory region ranges to adhere to a given address space type. + void InitializeMemoryRegionRanges(FileSys::ProgramAddressSpaceType type); + + /// Clears the underlying map and page table. + void Clear(); + + /// Clears out the VMA map, unmapping any previously mapped ranges. + void ClearVMAMap(); + + /// Clears out the page table + void ClearPageTable(); + + u32 address_space_width = 0; + VAddr address_space_base = 0; + VAddr address_space_end = 0; + + VAddr code_region_base = 0; + VAddr code_region_end = 0; + + VAddr heap_region_base = 0; + VAddr heap_region_end = 0; + + VAddr map_region_base = 0; + VAddr map_region_end = 0; + + VAddr new_map_region_base = 0; + VAddr new_map_region_end = 0; + + VAddr tls_io_region_base = 0; + VAddr tls_io_region_end = 0; }; } // namespace Kernel diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 2b8f78136..7e8035d0f 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -14,11 +14,9 @@ #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/deconstructed_rom_directory.h" #include "core/loader/nso.h" -#include "core/memory.h" namespace Loader { @@ -127,12 +125,16 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( metadata.Print(); const FileSys::ProgramAddressSpaceType arch_bits{metadata.GetAddressSpaceType()}; - if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit) { + if (arch_bits == FileSys::ProgramAddressSpaceType::Is32Bit || + arch_bits == FileSys::ProgramAddressSpaceType::Is32BitNoMap) { return ResultStatus::Error32BitISA; } + process->LoadFromMetadata(metadata); + // Load NSO modules - VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; + const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress(); + VAddr next_load_addr = base_address; for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { const FileSys::VirtualFile module_file = dir->GetFile(module); @@ -145,13 +147,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( } } - auto& kernel = Core::System::GetInstance().Kernel(); - process->program_id = metadata.GetTitleID(); - process->svc_access_mask.set(); - process->resource_limit = - kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), - metadata.GetMainThreadStackSize()); + process->Run(base_address, metadata.GetMainThreadPriority(), metadata.GetMainThreadStackSize()); // Find the RomFS by searching for a ".romfs" file in this directory const auto& files = dir->GetFiles(); diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 0e2af20b4..ff1221574 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp @@ -12,7 +12,7 @@ #include "core/core.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/vm_manager.h" #include "core/loader/elf.h" #include "core/memory.h" @@ -189,7 +189,7 @@ private: u32* sectionAddrs; bool relocate; - u32 entryPoint; + VAddr entryPoint; public: explicit ElfReader(void* ptr); @@ -205,13 +205,13 @@ public: ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } - u32 GetEntryPoint() const { + VAddr GetEntryPoint() const { return entryPoint; } u32 GetFlags() const { return (u32)(header->e_flags); } - SharedPtr<CodeSet> LoadInto(u32 vaddr); + SharedPtr<CodeSet> LoadInto(VAddr vaddr); int GetNumSegments() const { return (int)(header->e_phnum); @@ -274,7 +274,7 @@ const char* ElfReader::GetSectionName(int section) const { return nullptr; } -SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { +SharedPtr<CodeSet> ElfReader::LoadInto(VAddr vaddr) { LOG_DEBUG(Loader, "String section: {}", header->e_shstrndx); // Should we relocate? @@ -289,11 +289,11 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { LOG_DEBUG(Loader, "{} segments:", header->e_phnum); // First pass : Get the bits into RAM - u32 base_addr = relocate ? vaddr : 0; + const VAddr base_addr = relocate ? vaddr : 0; - u32 total_image_size = 0; + u64 total_image_size = 0; for (unsigned int i = 0; i < header->e_phnum; ++i) { - Elf32_Phdr* p = &segments[i]; + const Elf32_Phdr* p = &segments[i]; if (p->p_type == PT_LOAD) { total_image_size += (p->p_memsz + 0xFFF) & ~0xFFF; } @@ -306,7 +306,7 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { SharedPtr<CodeSet> codeset = CodeSet::Create(kernel, ""); for (unsigned int i = 0; i < header->e_phnum; ++i) { - Elf32_Phdr* p = &segments[i]; + const Elf32_Phdr* p = &segments[i]; LOG_DEBUG(Loader, "Type: {} Vaddr: {:08X} Filesz: {:08X} Memsz: {:08X} ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); @@ -333,8 +333,8 @@ SharedPtr<CodeSet> ElfReader::LoadInto(u32 vaddr) { continue; } - u32 segment_addr = base_addr + p->p_vaddr; - u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; + const VAddr segment_addr = base_addr + p->p_vaddr; + const u32 aligned_size = (p->p_memsz + 0xFFF) & ~0xFFF; codeset_segment->offset = current_image_position; codeset_segment->addr = segment_addr; @@ -395,18 +395,12 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { if (buffer.size() != file->GetSize()) return ResultStatus::ErrorIncorrectELFFileSize; + const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress(); ElfReader elf_reader(&buffer[0]); - SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); + SharedPtr<CodeSet> codeset = elf_reader.LoadInto(base_address); codeset->name = file->GetName(); process->LoadModule(codeset, codeset->entrypoint); - process->svc_access_mask.set(); - - // Attach the default resource limit (APPLICATION) to the process - auto& kernel = Core::System::GetInstance().Kernel(); - process->resource_limit = - kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(codeset->entrypoint, 48, Memory::DEFAULT_STACK_SIZE); is_loaded = true; diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index c49ec34ab..b72871efa 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp @@ -16,7 +16,7 @@ #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/vm_manager.h" #include "core/loader/nro.h" #include "core/memory.h" @@ -181,17 +181,13 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { } // Load NRO - static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; + const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress(); - if (!LoadNro(file, base_addr)) { + if (!LoadNro(file, base_address)) { return ResultStatus::ErrorLoadingNRO; } - auto& kernel = Core::System::GetInstance().Kernel(); - process->svc_access_mask.set(); - process->resource_limit = - kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(base_addr, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); + process->Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 78a4438c4..1a6876a22 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp @@ -13,7 +13,7 @@ #include "core/gdbstub/gdbstub.h" #include "core/hle/kernel/kernel.h" #include "core/hle/kernel/process.h" -#include "core/hle/kernel/resource_limit.h" +#include "core/hle/kernel/vm_manager.h" #include "core/loader/nso.h" #include "core/memory.h" @@ -159,15 +159,11 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { } // Load module - LoadModule(file, Memory::PROCESS_IMAGE_VADDR); - LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); + const VAddr base_address = process->vm_manager.GetCodeRegionBaseAddress(); + LoadModule(file, base_address); + LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), base_address); - auto& kernel = Core::System::GetInstance().Kernel(); - process->svc_access_mask.set(); - process->resource_limit = - kernel.ResourceLimitForCategory(Kernel::ResourceLimitCategory::APPLICATION); - process->Run(Memory::PROCESS_IMAGE_VADDR, Kernel::THREADPRIO_DEFAULT, - Memory::DEFAULT_STACK_SIZE); + process->Run(base_address, Kernel::THREADPRIO_DEFAULT, Memory::DEFAULT_STACK_SIZE); is_loaded = true; return ResultStatus::Success; diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 316b46820..6430daad4 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -3,7 +3,6 @@ // Refer to the license.txt file included. #include <algorithm> -#include <array> #include <cstring> #include <utility> @@ -15,11 +14,11 @@ #include "core/arm/arm_interface.h" #include "core/core.h" #include "core/hle/kernel/process.h" +#include "core/hle/kernel/vm_manager.h" #include "core/hle/lock.h" #include "core/memory.h" #include "core/memory_setup.h" #include "video_core/renderer_base.h" -#include "video_core/video_core.h" namespace Memory { @@ -41,6 +40,21 @@ PageTable* GetCurrentPageTable() { return current_page_table; } +PageTable::PageTable() = default; + +PageTable::PageTable(std::size_t address_space_width_in_bits) { + Resize(address_space_width_in_bits); +} + +PageTable::~PageTable() = default; + +void PageTable::Resize(std::size_t address_space_width_in_bits) { + const std::size_t num_page_table_entries = 1ULL << (address_space_width_in_bits - PAGE_BITS); + + pointers.resize(num_page_table_entries); + attributes.resize(num_page_table_entries); +} + static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, PageType type) { LOG_DEBUG(HW_Memory, "Mapping {} onto {:016X}-{:016X}", fmt::ptr(memory), base * PAGE_SIZE, (base + size) * PAGE_SIZE); @@ -50,7 +64,7 @@ static void MapPages(PageTable& page_table, VAddr base, u64 size, u8* memory, Pa VAddr end = base + size; while (base != end) { - ASSERT_MSG(base < PAGE_TABLE_NUM_ENTRIES, "out of range mapping at {:016X}", base); + ASSERT_MSG(base < page_table.pointers.size(), "out of range mapping at {:016X}", base); page_table.attributes[base] = type; page_table.pointers[base] = memory; @@ -323,7 +337,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { return; } - VAddr end = start + size; + const VAddr end = start + size; const auto CheckRegion = [&](VAddr region_start, VAddr region_end) { if (start >= region_end || end <= region_start) { @@ -333,7 +347,7 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { const VAddr overlap_start = std::max(start, region_start); const VAddr overlap_end = std::min(end, region_end); - const u64 overlap_size = overlap_end - overlap_start; + const VAddr overlap_size = overlap_end - overlap_start; auto& rasterizer = system_instance.Renderer().Rasterizer(); switch (mode) { @@ -349,8 +363,10 @@ void RasterizerFlushVirtualRegion(VAddr start, u64 size, FlushMode mode) { } }; - CheckRegion(PROCESS_IMAGE_VADDR, PROCESS_IMAGE_VADDR_END); - CheckRegion(HEAP_VADDR, HEAP_VADDR_END); + const auto& vm_manager = Core::CurrentProcess()->vm_manager; + + CheckRegion(vm_manager.GetCodeRegionBaseAddress(), vm_manager.GetCodeRegionEndAddress()); + CheckRegion(vm_manager.GetHeapRegionBaseAddress(), vm_manager.GetHeapRegionEndAddress()); } u8 Read8(const VAddr addr) { diff --git a/src/core/memory.h b/src/core/memory.h index 2a27c0251..1acf5ce8c 100644 --- a/src/core/memory.h +++ b/src/core/memory.h @@ -4,10 +4,10 @@ #pragma once -#include <array> #include <cstddef> #include <string> #include <tuple> +#include <vector> #include <boost/icl/interval_map.hpp> #include "common/common_types.h" #include "core/memory_hook.h" @@ -23,10 +23,8 @@ namespace Memory { * be mapped. */ constexpr std::size_t PAGE_BITS = 12; -constexpr u64 PAGE_SIZE = 1 << PAGE_BITS; +constexpr u64 PAGE_SIZE = 1ULL << PAGE_BITS; constexpr u64 PAGE_MASK = PAGE_SIZE - 1; -constexpr std::size_t ADDRESS_SPACE_BITS = 36; -constexpr std::size_t PAGE_TABLE_NUM_ENTRIES = 1ULL << (ADDRESS_SPACE_BITS - PAGE_BITS); enum class PageType : u8 { /// Page is unmapped and should cause an access error. @@ -62,32 +60,39 @@ struct SpecialRegion { * mimics the way a real CPU page table works. */ struct PageTable { + explicit PageTable(); + explicit PageTable(std::size_t address_space_width_in_bits); + ~PageTable(); + + /** + * Resizes the page table to be able to accomodate enough pages within + * a given address space. + * + * @param address_space_width_in_bits The address size width in bits. + */ + void Resize(std::size_t address_space_width_in_bits); + /** - * Array of memory pointers backing each page. An entry can only be non-null if the - * corresponding entry in the `attributes` array is of type `Memory`. + * Vector of memory pointers backing each page. An entry can only be non-null if the + * corresponding entry in the `attributes` vector is of type `Memory`. */ - std::array<u8*, PAGE_TABLE_NUM_ENTRIES> pointers; + std::vector<u8*> pointers; /** - * Contains MMIO handlers that back memory regions whose entries in the `attribute` array is of - * type `Special`. + * Contains MMIO handlers that back memory regions whose entries in the `attribute` vector is + * of type `Special`. */ boost::icl::interval_map<VAddr, std::set<SpecialRegion>> special_regions; /** - * Array of fine grained page attributes. If it is set to any value other than `Memory`, then + * Vector of fine grained page attributes. If it is set to any value other than `Memory`, then * the corresponding entry in `pointers` MUST be set to null. */ - std::array<PageType, PAGE_TABLE_NUM_ENTRIES> attributes; + std::vector<PageType> attributes; }; /// Virtual user-space memory regions enum : VAddr { - /// Where the application text, data and bss reside. - PROCESS_IMAGE_VADDR = 0x08000000, - PROCESS_IMAGE_MAX_SIZE = 0x08000000, - PROCESS_IMAGE_VADDR_END = PROCESS_IMAGE_VADDR + PROCESS_IMAGE_MAX_SIZE, - /// Read-only page containing kernel and system configuration values. CONFIG_MEMORY_VADDR = 0x1FF80000, CONFIG_MEMORY_SIZE = 0x00001000, @@ -98,36 +103,12 @@ enum : VAddr { SHARED_PAGE_SIZE = 0x00001000, SHARED_PAGE_VADDR_END = SHARED_PAGE_VADDR + SHARED_PAGE_SIZE, - /// Area where TLS (Thread-Local Storage) buffers are allocated. - TLS_AREA_VADDR = 0x40000000, + /// TLS (Thread-Local Storage) related. TLS_ENTRY_SIZE = 0x200, - TLS_AREA_SIZE = 0x10000000, - TLS_AREA_VADDR_END = TLS_AREA_VADDR + TLS_AREA_SIZE, /// Application stack - STACK_AREA_VADDR = TLS_AREA_VADDR_END, - STACK_AREA_SIZE = 0x10000000, - STACK_AREA_VADDR_END = STACK_AREA_VADDR + STACK_AREA_SIZE, DEFAULT_STACK_SIZE = 0x100000, - /// Application heap - /// Size is confirmed to be a static value on fw 3.0.0 - HEAP_VADDR = 0x108000000, - HEAP_SIZE = 0x180000000, - HEAP_VADDR_END = HEAP_VADDR + HEAP_SIZE, - - /// New map region - /// Size is confirmed to be a static value on fw 3.0.0 - NEW_MAP_REGION_VADDR = HEAP_VADDR_END, - NEW_MAP_REGION_SIZE = 0x80000000, - NEW_MAP_REGION_VADDR_END = NEW_MAP_REGION_VADDR + NEW_MAP_REGION_SIZE, - - /// Map region - /// Size is confirmed to be a static value on fw 3.0.0 - MAP_REGION_VADDR = NEW_MAP_REGION_VADDR_END, - MAP_REGION_SIZE = 0x1000000000, - MAP_REGION_VADDR_END = MAP_REGION_VADDR + MAP_REGION_SIZE, - /// Kernel Virtual Address Range KERNEL_REGION_VADDR = 0xFFFFFF8000000000, KERNEL_REGION_SIZE = 0x7FFFE00000, diff --git a/src/tests/core/arm/arm_test_common.cpp b/src/tests/core/arm/arm_test_common.cpp index 7c69fc26e..c17a122cd 100644 --- a/src/tests/core/arm/arm_test_common.cpp +++ b/src/tests/core/arm/arm_test_common.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include <algorithm> + #include "core/core.h" #include "core/hle/kernel/process.h" #include "core/memory.h" @@ -16,9 +18,10 @@ TestEnvironment::TestEnvironment(bool mutable_memory_) Core::CurrentProcess() = Kernel::Process::Create(kernel, ""); page_table = &Core::CurrentProcess()->vm_manager.page_table; - page_table->pointers.fill(nullptr); + std::fill(page_table->pointers.begin(), page_table->pointers.end(), nullptr); page_table->special_regions.clear(); - page_table->attributes.fill(Memory::PageType::Unmapped); + std::fill(page_table->attributes.begin(), page_table->attributes.end(), + Memory::PageType::Unmapped); Memory::MapIoRegion(*page_table, 0x00000000, 0x80000000, test_memory); Memory::MapIoRegion(*page_table, 0x80000000, 0x80000000, test_memory); diff --git a/src/video_core/engines/maxwell_3d.h b/src/video_core/engines/maxwell_3d.h index 63dec5a83..9f5581045 100644 --- a/src/video_core/engines/maxwell_3d.h +++ b/src/video_core/engines/maxwell_3d.h @@ -41,6 +41,7 @@ public: static constexpr std::size_t NumCBData = 16; static constexpr std::size_t NumVertexArrays = 32; static constexpr std::size_t NumVertexAttributes = 32; + static constexpr std::size_t NumTextureSamplers = 32; static constexpr std::size_t MaxShaderProgram = 6; static constexpr std::size_t MaxShaderStage = 5; // Maximum number of const buffers per shader stage. diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index db6ff191c..4c8ecbd1c 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -187,7 +187,7 @@ private: OGLVertexArray> vertex_array_cache; - std::array<SamplerInfo, GLShader::NumTextureSamplers> texture_samplers; + std::array<SamplerInfo, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_samplers; static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLBufferCache buffer_cache; diff --git a/src/video_core/renderer_opengl/gl_shader_manager.h b/src/video_core/renderer_opengl/gl_shader_manager.h index b86cd96e8..3de15ba9b 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.h +++ b/src/video_core/renderer_opengl/gl_shader_manager.h @@ -11,9 +11,6 @@ namespace OpenGL::GLShader { -/// Number of OpenGL texture samplers that can be used in the fragment shader -static constexpr std::size_t NumTextureSamplers = 32; - using Tegra::Engines::Maxwell3D; /// Uniform structure for the Uniform Buffer Object, all vectors must be 16-byte aligned diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 9c3b3b546..1fe26a2a9 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -207,9 +207,6 @@ void OpenGLState::Apply() const { glActiveTexture(TextureUnits::MaxwellTexture(static_cast<int>(i)).Enum()); glBindTexture(texture_unit.target, texture_unit.texture); } - if (texture_unit.sampler != cur_state_texture_unit.sampler) { - glBindSampler(static_cast<GLuint>(i), texture_unit.sampler); - } // Update the texture swizzle if (texture_unit.swizzle.r != cur_state_texture_unit.swizzle.r || texture_unit.swizzle.g != cur_state_texture_unit.swizzle.g || @@ -221,6 +218,27 @@ void OpenGLState::Apply() const { } } + // Samplers + { + bool has_delta{}; + std::size_t first{}, last{}; + std::array<GLuint, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> samplers; + for (std::size_t i = 0; i < std::size(samplers); ++i) { + samplers[i] = texture_units[i].sampler; + if (samplers[i] != cur_state.texture_units[i].sampler) { + if (!has_delta) { + first = i; + has_delta = true; + } + last = i; + } + } + if (has_delta) { + glBindSamplers(static_cast<GLuint>(first), static_cast<GLsizei>(last - first + 1), + samplers.data()); + } + } + // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index ac4bb0555..dc21a2ee3 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -6,6 +6,7 @@ #include <array> #include <glad/glad.h> +#include "video_core/engines/maxwell_3d.h" namespace OpenGL { @@ -114,7 +115,7 @@ public: target = GL_TEXTURE_2D; } }; - std::array<TextureUnit, 32> texture_units; + std::array<TextureUnit, Tegra::Engines::Maxwell3D::Regs::NumTextureSamplers> texture_units; struct { GLuint read_framebuffer; // GL_READ_FRAMEBUFFER_BINDING |