diff options
25 files changed, 523 insertions, 270 deletions
| diff --git a/src/core/hle/kernel/k_code_memory.cpp b/src/core/hle/kernel/k_code_memory.cpp index d9da1e600..884eba001 100644 --- a/src/core/hle/kernel/k_code_memory.cpp +++ b/src/core/hle/kernel/k_code_memory.cpp @@ -74,7 +74,7 @@ Result KCodeMemory::Map(VAddr address, size_t size) {      R_UNLESS(!m_is_mapped, ResultInvalidState);      // Map the memory. -    R_TRY(kernel.CurrentProcess()->PageTable().MapPages( +    R_TRY(kernel.CurrentProcess()->PageTable().MapPageGroup(          address, *m_page_group, KMemoryState::CodeOut, KMemoryPermission::UserReadWrite));      // Mark ourselves as mapped. @@ -91,8 +91,8 @@ Result KCodeMemory::Unmap(VAddr address, size_t size) {      KScopedLightLock lk(m_lock);      // Unmap the memory. -    R_TRY(kernel.CurrentProcess()->PageTable().UnmapPages(address, *m_page_group, -                                                          KMemoryState::CodeOut)); +    R_TRY(kernel.CurrentProcess()->PageTable().UnmapPageGroup(address, *m_page_group, +                                                              KMemoryState::CodeOut));      // Mark ourselves as unmapped.      m_is_mapped = false; @@ -125,8 +125,8 @@ Result KCodeMemory::MapToOwner(VAddr address, size_t size, Svc::MemoryPermission      }      // Map the memory. -    R_TRY( -        m_owner->PageTable().MapPages(address, *m_page_group, KMemoryState::GeneratedCode, k_perm)); +    R_TRY(m_owner->PageTable().MapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode, +                                            k_perm));      // Mark ourselves as mapped.      m_is_owner_mapped = true; @@ -142,7 +142,7 @@ Result KCodeMemory::UnmapFromOwner(VAddr address, size_t size) {      KScopedLightLock lk(m_lock);      // Unmap the memory. -    R_TRY(m_owner->PageTable().UnmapPages(address, *m_page_group, KMemoryState::GeneratedCode)); +    R_TRY(m_owner->PageTable().UnmapPageGroup(address, *m_page_group, KMemoryState::GeneratedCode));      // Mark ourselves as unmapped.      m_is_owner_mapped = false; diff --git a/src/core/hle/kernel/k_page_table.cpp b/src/core/hle/kernel/k_page_table.cpp index 9c7ac22dc..fbd28f5f3 100644 --- a/src/core/hle/kernel/k_page_table.cpp +++ b/src/core/hle/kernel/k_page_table.cpp @@ -435,6 +435,9 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si          KPageGroup pg{m_kernel, m_block_info_manager};          AddRegionToPages(src_address, num_pages, pg); +        // We're going to perform an update, so create a helper. +        KScopedPageTableUpdater updater(this); +          // Reprotect the source as kernel-read/not mapped.          const auto new_perm = static_cast<KMemoryPermission>(KMemoryPermission::KernelRead |                                                               KMemoryPermission::NotMapped); @@ -447,7 +450,10 @@ Result KPageTable::MapCodeMemory(VAddr dst_address, VAddr src_address, size_t si          });          // Map the alias pages. -        R_TRY(MapPages(dst_address, pg, new_perm)); +        const KPageProperties dst_properties = {new_perm, false, false, +                                                DisableMergeAttribute::DisableHead}; +        R_TRY( +            this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_properties, false));          // We successfully mapped the alias pages, so we don't need to unprotect the src pages on          // failure. @@ -1881,7 +1887,8 @@ Result KPageTable::UnmapPhysicalMemory(VAddr address, size_t size) {      R_SUCCEED();  } -Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size) { +Result KPageTable::MapMemory(KProcessAddress dst_address, KProcessAddress src_address, +                             size_t size) {      // Lock the table.      KScopedLightLock lk(m_general_lock); @@ -1902,53 +1909,73 @@ Result KPageTable::MapMemory(VAddr dst_address, VAddr src_address, size_t size)                                   KMemoryAttribute::None));      // Create an update allocator for the source. -    Result src_allocator_result{ResultSuccess}; +    Result src_allocator_result;      KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),                                                       m_memory_block_slab_manager,                                                       num_src_allocator_blocks);      R_TRY(src_allocator_result);      // Create an update allocator for the destination. -    Result dst_allocator_result{ResultSuccess}; +    Result dst_allocator_result;      KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),                                                       m_memory_block_slab_manager,                                                       num_dst_allocator_blocks);      R_TRY(dst_allocator_result);      // Map the memory. -    KPageGroup page_linked_list{m_kernel, m_block_info_manager}; -    const size_t num_pages{size / PageSize}; -    const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( -        KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); -    const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; - -    AddRegionToPages(src_address, num_pages, page_linked_list);      { +        // Determine the number of pages being operated on. +        const size_t num_pages = size / PageSize; + +        // Create page groups for the memory being unmapped. +        KPageGroup pg{m_kernel, m_block_info_manager}; + +        // Create the page group representing the source. +        R_TRY(this->MakePageGroup(pg, src_address, num_pages)); + +        // We're going to perform an update, so create a helper. +        KScopedPageTableUpdater updater(this); +          // Reprotect the source as kernel-read/not mapped. -        auto block_guard = detail::ScopeExit([&] { -            Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, -                    OperationType::ChangePermissions); -        }); -        R_TRY(Operate(src_address, num_pages, new_src_perm, OperationType::ChangePermissions)); -        R_TRY(MapPages(dst_address, page_linked_list, KMemoryPermission::UserReadWrite)); +        const KMemoryPermission new_src_perm = static_cast<KMemoryPermission>( +            KMemoryPermission::KernelRead | KMemoryPermission::NotMapped); +        const KMemoryAttribute new_src_attr = KMemoryAttribute::Locked; +        const KPageProperties src_properties = {new_src_perm, false, false, +                                                DisableMergeAttribute::DisableHeadBodyTail}; +        R_TRY(this->Operate(src_address, num_pages, src_properties.perm, +                            OperationType::ChangePermissions)); -        block_guard.Cancel(); -    } +        // Ensure that we unprotect the source pages on failure. +        ON_RESULT_FAILURE { +            const KPageProperties unprotect_properties = { +                KMemoryPermission::UserReadWrite, false, false, +                DisableMergeAttribute::EnableHeadBodyTail}; +            ASSERT(this->Operate(src_address, num_pages, unprotect_properties.perm, +                                 OperationType::ChangePermissions) == ResultSuccess); +        }; -    // Apply the memory block updates. -    m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, -                                  new_src_perm, new_src_attr, -                                  KMemoryBlockDisableMergeAttribute::Locked, -                                  KMemoryBlockDisableMergeAttribute::None); -    m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, -                                  KMemoryState::Stack, KMemoryPermission::UserReadWrite, -                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, -                                  KMemoryBlockDisableMergeAttribute::None); +        // Map the alias pages. +        const KPageProperties dst_map_properties = {KMemoryPermission::UserReadWrite, false, false, +                                                    DisableMergeAttribute::DisableHead}; +        R_TRY(this->MapPageGroupImpl(updater.GetPageList(), dst_address, pg, dst_map_properties, +                                     false)); + +        // Apply the memory block updates. +        m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, +                                      src_state, new_src_perm, new_src_attr, +                                      KMemoryBlockDisableMergeAttribute::Locked, +                                      KMemoryBlockDisableMergeAttribute::None); +        m_memory_block_manager.Update( +            std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::Stack, +            KMemoryPermission::UserReadWrite, KMemoryAttribute::None, +            KMemoryBlockDisableMergeAttribute::Normal, KMemoryBlockDisableMergeAttribute::None); +    }      R_SUCCEED();  } -Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size) { +Result KPageTable::UnmapMemory(KProcessAddress dst_address, KProcessAddress src_address, +                               size_t size) {      // Lock the table.      KScopedLightLock lk(m_general_lock); @@ -1970,108 +1997,208 @@ Result KPageTable::UnmapMemory(VAddr dst_address, VAddr src_address, size_t size          KMemoryPermission::None, KMemoryAttribute::All, KMemoryAttribute::None));      // Create an update allocator for the source. -    Result src_allocator_result{ResultSuccess}; +    Result src_allocator_result;      KMemoryBlockManagerUpdateAllocator src_allocator(std::addressof(src_allocator_result),                                                       m_memory_block_slab_manager,                                                       num_src_allocator_blocks);      R_TRY(src_allocator_result);      // Create an update allocator for the destination. -    Result dst_allocator_result{ResultSuccess}; +    Result dst_allocator_result;      KMemoryBlockManagerUpdateAllocator dst_allocator(std::addressof(dst_allocator_result),                                                       m_memory_block_slab_manager,                                                       num_dst_allocator_blocks);      R_TRY(dst_allocator_result); -    KPageGroup src_pages{m_kernel, m_block_info_manager}; -    KPageGroup dst_pages{m_kernel, m_block_info_manager}; -    const size_t num_pages{size / PageSize}; +    // Unmap the memory. +    { +        // Determine the number of pages being operated on. +        const size_t num_pages = size / PageSize; -    AddRegionToPages(src_address, num_pages, src_pages); -    AddRegionToPages(dst_address, num_pages, dst_pages); +        // Create page groups for the memory being unmapped. +        KPageGroup pg{m_kernel, m_block_info_manager}; -    R_UNLESS(dst_pages.IsEquivalentTo(src_pages), ResultInvalidMemoryRegion); +        // Create the page group representing the destination. +        R_TRY(this->MakePageGroup(pg, dst_address, num_pages)); -    { -        auto block_guard = detail::ScopeExit([&] { MapPages(dst_address, dst_pages, dst_perm); }); +        // Ensure the page group is the valid for the source. +        R_UNLESS(this->IsValidPageGroup(pg, src_address, num_pages), ResultInvalidMemoryRegion); -        R_TRY(Operate(dst_address, num_pages, KMemoryPermission::None, OperationType::Unmap)); -        R_TRY(Operate(src_address, num_pages, KMemoryPermission::UserReadWrite, -                      OperationType::ChangePermissions)); +        // We're going to perform an update, so create a helper. +        KScopedPageTableUpdater updater(this); -        block_guard.Cancel(); -    } +        // Unmap the aliased copy of the pages. +        const KPageProperties dst_unmap_properties = {KMemoryPermission::None, false, false, +                                                      DisableMergeAttribute::None}; +        R_TRY( +            this->Operate(dst_address, num_pages, dst_unmap_properties.perm, OperationType::Unmap)); + +        // Ensure that we re-map the aliased pages on failure. +        ON_RESULT_FAILURE { +            this->RemapPageGroup(updater.GetPageList(), dst_address, size, pg); +        }; -    // Apply the memory block updates. -    m_memory_block_manager.Update(std::addressof(src_allocator), src_address, num_pages, src_state, -                                  KMemoryPermission::UserReadWrite, KMemoryAttribute::None, -                                  KMemoryBlockDisableMergeAttribute::None, -                                  KMemoryBlockDisableMergeAttribute::Locked); -    m_memory_block_manager.Update(std::addressof(dst_allocator), dst_address, num_pages, -                                  KMemoryState::None, KMemoryPermission::None, -                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::None, -                                  KMemoryBlockDisableMergeAttribute::Normal); +        // Try to set the permissions for the source pages back to what they should be. +        const KPageProperties src_properties = {KMemoryPermission::UserReadWrite, false, false, +                                                DisableMergeAttribute::EnableAndMergeHeadBodyTail}; +        R_TRY(this->Operate(src_address, num_pages, src_properties.perm, +                            OperationType::ChangePermissions)); + +        // Apply the memory block updates. +        m_memory_block_manager.Update( +            std::addressof(src_allocator), src_address, num_pages, src_state, +            KMemoryPermission::UserReadWrite, KMemoryAttribute::None, +            KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Locked); +        m_memory_block_manager.Update( +            std::addressof(dst_allocator), dst_address, num_pages, KMemoryState::None, +            KMemoryPermission::None, KMemoryAttribute::None, +            KMemoryBlockDisableMergeAttribute::None, KMemoryBlockDisableMergeAttribute::Normal); +    }      R_SUCCEED();  } -Result KPageTable::MapPages(VAddr addr, const KPageGroup& page_linked_list, -                            KMemoryPermission perm) { +Result KPageTable::AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, +                                           size_t num_pages, KMemoryPermission perm) {      ASSERT(this->IsLockedByCurrentThread()); -    VAddr cur_addr{addr}; +    // Create a page group to hold the pages we allocate. +    KPageGroup pg{m_kernel, m_block_info_manager}; -    for (const auto& node : page_linked_list) { -        if (const auto result{ -                Operate(cur_addr, node.GetNumPages(), perm, OperationType::Map, node.GetAddress())}; -            result.IsError()) { -            const size_t num_pages{(addr - cur_addr) / PageSize}; +    // Allocate the pages. +    R_TRY( +        m_kernel.MemoryManager().AllocateAndOpen(std::addressof(pg), num_pages, m_allocate_option)); -            ASSERT(Operate(addr, num_pages, KMemoryPermission::None, OperationType::Unmap) -                       .IsSuccess()); +    // Ensure that the page group is closed when we're done working with it. +    SCOPE_EXIT({ pg.Close(); }); -            R_RETURN(result); +    // Clear all pages. +    for (const auto& it : pg) { +        std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), m_heap_fill_value, +                    it.GetSize()); +    } + +    // Map the pages. +    R_RETURN(this->Operate(address, num_pages, pg, OperationType::MapGroup)); +} + +Result KPageTable::MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, +                                    const KPageGroup& pg, const KPageProperties properties, +                                    bool reuse_ll) { +    ASSERT(this->IsLockedByCurrentThread()); + +    // Note the current address, so that we can iterate. +    const KProcessAddress start_address = address; +    KProcessAddress cur_address = address; + +    // Ensure that we clean up on failure. +    ON_RESULT_FAILURE { +        ASSERT(!reuse_ll); +        if (cur_address != start_address) { +            const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, +                                                      DisableMergeAttribute::None}; +            ASSERT(this->Operate(start_address, (cur_address - start_address) / PageSize, +                                 unmap_properties.perm, OperationType::Unmap) == ResultSuccess);          } +    }; -        cur_addr += node.GetNumPages() * PageSize; +    // Iterate, mapping all pages in the group. +    for (const auto& block : pg) { +        // Map and advance. +        const KPageProperties cur_properties = +            (cur_address == start_address) +                ? properties +                : KPageProperties{properties.perm, properties.io, properties.uncached, +                                  DisableMergeAttribute::None}; +        this->Operate(cur_address, block.GetNumPages(), cur_properties.perm, OperationType::Map, +                      block.GetAddress()); +        cur_address += block.GetSize();      } +    // We succeeded!      R_SUCCEED();  } -Result KPageTable::MapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state, -                            KMemoryPermission perm) { -    // Check that the map is in range. -    const size_t num_pages{page_linked_list.GetNumPages()}; -    const size_t size{num_pages * PageSize}; -    R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); +void KPageTable::RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, +                                const KPageGroup& pg) { +    ASSERT(this->IsLockedByCurrentThread()); -    // Lock the table. -    KScopedLightLock lk(m_general_lock); +    // Note the current address, so that we can iterate. +    const KProcessAddress start_address = address; +    const KProcessAddress last_address = start_address + size - 1; +    const KProcessAddress end_address = last_address + 1; -    // Check the memory state. -    R_TRY(this->CheckMemoryState(address, size, KMemoryState::All, KMemoryState::Free, -                                 KMemoryPermission::None, KMemoryPermission::None, -                                 KMemoryAttribute::None, KMemoryAttribute::None)); +    // Iterate over the memory. +    auto pg_it = pg.begin(); +    ASSERT(pg_it != pg.end()); -    // Create an update allocator. -    Result allocator_result{ResultSuccess}; -    KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), -                                                 m_memory_block_slab_manager); +    KPhysicalAddress pg_phys_addr = pg_it->GetAddress(); +    size_t pg_pages = pg_it->GetNumPages(); -    // Map the pages. -    R_TRY(MapPages(address, page_linked_list, perm)); +    auto it = m_memory_block_manager.FindIterator(start_address); +    while (true) { +        // Check that the iterator is valid. +        ASSERT(it != m_memory_block_manager.end()); -    // Update the blocks. -    m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, -                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, -                                  KMemoryBlockDisableMergeAttribute::None); +        // Get the memory info. +        const KMemoryInfo info = it->GetMemoryInfo(); -    R_SUCCEED(); +        // Determine the range to map. +        KProcessAddress map_address = std::max(info.GetAddress(), start_address); +        const KProcessAddress map_end_address = std::min(info.GetEndAddress(), end_address); +        ASSERT(map_end_address != map_address); + +        // Determine if we should disable head merge. +        const bool disable_head_merge = +            info.GetAddress() >= start_address && +            True(info.GetDisableMergeAttribute() & KMemoryBlockDisableMergeAttribute::Normal); +        const KPageProperties map_properties = { +            info.GetPermission(), false, false, +            disable_head_merge ? DisableMergeAttribute::DisableHead : DisableMergeAttribute::None}; + +        // While we have pages to map, map them. +        size_t map_pages = (map_end_address - map_address) / PageSize; +        while (map_pages > 0) { +            // Check if we're at the end of the physical block. +            if (pg_pages == 0) { +                // Ensure there are more pages to map. +                ASSERT(pg_it != pg.end()); + +                // Advance our physical block. +                ++pg_it; +                pg_phys_addr = pg_it->GetAddress(); +                pg_pages = pg_it->GetNumPages(); +            } + +            // Map whatever we can. +            const size_t cur_pages = std::min(pg_pages, map_pages); +            ASSERT(this->Operate(map_address, map_pages, map_properties.perm, OperationType::Map, +                                 pg_phys_addr) == ResultSuccess); + +            // Advance. +            map_address += cur_pages * PageSize; +            map_pages -= cur_pages; + +            pg_phys_addr += cur_pages * PageSize; +            pg_pages -= cur_pages; +        } + +        // Check if we're done. +        if (last_address <= info.GetLastAddress()) { +            break; +        } + +        // Advance. +        ++it; +    } + +    // Check that we re-mapped precisely the page group. +    ASSERT((++pg_it) == pg.end());  } -Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, -                            bool is_pa_valid, VAddr region_start, size_t region_num_pages, +Result KPageTable::MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, +                            KPhysicalAddress phys_addr, bool is_pa_valid, +                            KProcessAddress region_start, size_t region_num_pages,                              KMemoryState state, KMemoryPermission perm) {      ASSERT(Common::IsAligned(alignment, PageSize) && alignment >= PageSize); @@ -2084,26 +2211,30 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,      KScopedLightLock lk(m_general_lock);      // Find a random address to map at. -    VAddr addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, 0, -                                    this->GetNumGuardPages()); +    KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, alignment, +                                              0, this->GetNumGuardPages());      R_UNLESS(addr != 0, ResultOutOfMemory);      ASSERT(Common::IsAligned(addr, alignment));      ASSERT(this->CanContain(addr, num_pages * PageSize, state));      ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free,                                    KMemoryPermission::None, KMemoryPermission::None, -                                  KMemoryAttribute::None, KMemoryAttribute::None) -               .IsSuccess()); +                                  KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess);      // Create an update allocator. -    Result allocator_result{ResultSuccess}; +    Result allocator_result;      KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),                                                   m_memory_block_slab_manager); +    R_TRY(allocator_result); + +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this);      // Perform mapping operation.      if (is_pa_valid) { -        R_TRY(this->Operate(addr, num_pages, perm, OperationType::Map, phys_addr)); +        const KPageProperties properties = {perm, false, false, DisableMergeAttribute::DisableHead}; +        R_TRY(this->Operate(addr, num_pages, properties.perm, OperationType::Map, phys_addr));      } else { -        UNIMPLEMENTED(); +        R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), addr, num_pages, perm));      }      // Update the blocks. @@ -2116,28 +2247,45 @@ Result KPageTable::MapPages(VAddr* out_addr, size_t num_pages, size_t alignment,      R_SUCCEED();  } -Result KPageTable::UnmapPages(VAddr addr, const KPageGroup& page_linked_list) { -    ASSERT(this->IsLockedByCurrentThread()); +Result KPageTable::MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, +                            KMemoryPermission perm) { +    // Check that the map is in range. +    const size_t size = num_pages * PageSize; +    R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); -    VAddr cur_addr{addr}; +    // Lock the table. +    KScopedLightLock lk(m_general_lock); -    for (const auto& node : page_linked_list) { -        if (const auto result{Operate(cur_addr, node.GetNumPages(), KMemoryPermission::None, -                                      OperationType::Unmap)}; -            result.IsError()) { -            R_RETURN(result); -        } +    // Check the memory state. +    size_t num_allocator_blocks; +    R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size, +                                 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, +                                 KMemoryPermission::None, KMemoryAttribute::None, +                                 KMemoryAttribute::None)); -        cur_addr += node.GetNumPages() * PageSize; -    } +    // Create an update allocator. +    Result allocator_result; +    KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), +                                                 m_memory_block_slab_manager, num_allocator_blocks); +    R_TRY(allocator_result); + +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this); + +    // Map the pages. +    R_TRY(this->AllocateAndMapPagesImpl(updater.GetPageList(), address, num_pages, perm)); + +    // Update the blocks. +    m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, state, perm, +                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, +                                  KMemoryBlockDisableMergeAttribute::None);      R_SUCCEED();  } -Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemoryState state) { +Result KPageTable::UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state) {      // Check that the unmap is in range. -    const size_t num_pages{page_linked_list.GetNumPages()}; -    const size_t size{num_pages * PageSize}; +    const size_t size = num_pages * PageSize;      R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory);      // Lock the table. @@ -2151,13 +2299,18 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo                                   KMemoryAttribute::None));      // Create an update allocator. -    Result allocator_result{ResultSuccess}; +    Result allocator_result;      KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),                                                   m_memory_block_slab_manager, num_allocator_blocks);      R_TRY(allocator_result); +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this); +      // Perform the unmap. -    R_TRY(UnmapPages(address, page_linked_list)); +    const KPageProperties unmap_properties = {KMemoryPermission::None, false, false, +                                              DisableMergeAttribute::None}; +    R_TRY(this->Operate(address, num_pages, unmap_properties.perm, OperationType::Unmap));      // Update the blocks.      m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, @@ -2168,29 +2321,130 @@ Result KPageTable::UnmapPages(VAddr address, KPageGroup& page_linked_list, KMemo      R_SUCCEED();  } -Result KPageTable::UnmapPages(VAddr address, size_t num_pages, KMemoryState state) { -    // Check that the unmap is in range. +Result KPageTable::MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, +                                KProcessAddress region_start, size_t region_num_pages, +                                KMemoryState state, KMemoryPermission perm) { +    ASSERT(!this->IsLockedByCurrentThread()); + +    // Ensure this is a valid map request. +    const size_t num_pages = pg.GetNumPages(); +    R_UNLESS(this->CanContain(region_start, region_num_pages * PageSize, state), +             ResultInvalidCurrentMemory); +    R_UNLESS(num_pages < region_num_pages, ResultOutOfMemory); + +    // Lock the table. +    KScopedLightLock lk(m_general_lock); + +    // Find a random address to map at. +    KProcessAddress addr = this->FindFreeArea(region_start, region_num_pages, num_pages, PageSize, +                                              0, this->GetNumGuardPages()); +    R_UNLESS(addr != 0, ResultOutOfMemory); +    ASSERT(this->CanContain(addr, num_pages * PageSize, state)); +    ASSERT(this->CheckMemoryState(addr, num_pages * PageSize, KMemoryState::All, KMemoryState::Free, +                                  KMemoryPermission::None, KMemoryPermission::None, +                                  KMemoryAttribute::None, KMemoryAttribute::None) == ResultSuccess); + +    // Create an update allocator. +    Result allocator_result; +    KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), +                                                 m_memory_block_slab_manager); +    R_TRY(allocator_result); + +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this); + +    // Perform mapping operation. +    const KPageProperties properties = {perm, state == KMemoryState::Io, false, +                                        DisableMergeAttribute::DisableHead}; +    R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); + +    // Update the blocks. +    m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, +                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, +                                  KMemoryBlockDisableMergeAttribute::None); + +    // We successfully mapped the pages. +    *out_addr = addr; +    R_SUCCEED(); +} + +Result KPageTable::MapPageGroup(KProcessAddress addr, const KPageGroup& pg, KMemoryState state, +                                KMemoryPermission perm) { +    ASSERT(!this->IsLockedByCurrentThread()); + +    // Ensure this is a valid map request. +    const size_t num_pages = pg.GetNumPages();      const size_t size = num_pages * PageSize; -    R_UNLESS(this->Contains(address, size), ResultInvalidCurrentMemory); +    R_UNLESS(this->CanContain(addr, size, state), ResultInvalidCurrentMemory);      // Lock the table.      KScopedLightLock lk(m_general_lock); -    // Check the memory state. -    size_t num_allocator_blocks{}; +    // Check if state allows us to map. +    size_t num_allocator_blocks; +    R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), addr, size, +                                 KMemoryState::All, KMemoryState::Free, KMemoryPermission::None, +                                 KMemoryPermission::None, KMemoryAttribute::None, +                                 KMemoryAttribute::None)); + +    // Create an update allocator. +    Result allocator_result; +    KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), +                                                 m_memory_block_slab_manager, num_allocator_blocks); +    R_TRY(allocator_result); + +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this); + +    // Perform mapping operation. +    const KPageProperties properties = {perm, state == KMemoryState::Io, false, +                                        DisableMergeAttribute::DisableHead}; +    R_TRY(this->MapPageGroupImpl(updater.GetPageList(), addr, pg, properties, false)); + +    // Update the blocks. +    m_memory_block_manager.Update(std::addressof(allocator), addr, num_pages, state, perm, +                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, +                                  KMemoryBlockDisableMergeAttribute::None); + +    // We successfully mapped the pages. +    R_SUCCEED(); +} + +Result KPageTable::UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, +                                  KMemoryState state) { +    ASSERT(!this->IsLockedByCurrentThread()); + +    // Ensure this is a valid unmap request. +    const size_t num_pages = pg.GetNumPages(); +    const size_t size = num_pages * PageSize; +    R_UNLESS(this->CanContain(address, size, state), ResultInvalidCurrentMemory); + +    // Lock the table. +    KScopedLightLock lk(m_general_lock); + +    // Check if state allows us to unmap. +    size_t num_allocator_blocks;      R_TRY(this->CheckMemoryState(std::addressof(num_allocator_blocks), address, size,                                   KMemoryState::All, state, KMemoryPermission::None,                                   KMemoryPermission::None, KMemoryAttribute::All,                                   KMemoryAttribute::None)); +    // Check that the page group is valid. +    R_UNLESS(this->IsValidPageGroup(pg, address, num_pages), ResultInvalidCurrentMemory); +      // Create an update allocator. -    Result allocator_result{ResultSuccess}; +    Result allocator_result;      KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result),                                                   m_memory_block_slab_manager, num_allocator_blocks);      R_TRY(allocator_result); -    // Perform the unmap. -    R_TRY(Operate(address, num_pages, KMemoryPermission::None, OperationType::Unmap)); +    // We're going to perform an update, so create a helper. +    KScopedPageTableUpdater updater(this); + +    // Perform unmapping operation. +    const KPageProperties properties = {KMemoryPermission::None, false, false, +                                        DisableMergeAttribute::None}; +    R_TRY(this->Operate(address, num_pages, properties.perm, OperationType::Unmap));      // Update the blocks.      m_memory_block_manager.Update(std::addressof(allocator), address, num_pages, KMemoryState::Free, @@ -2550,54 +2804,6 @@ Result KPageTable::SetHeapSize(VAddr* out, size_t size) {      }  } -ResultVal<VAddr> KPageTable::AllocateAndMapMemory(size_t needed_num_pages, size_t align, -                                                  bool is_map_only, VAddr region_start, -                                                  size_t region_num_pages, KMemoryState state, -                                                  KMemoryPermission perm, PAddr map_addr) { -    KScopedLightLock lk(m_general_lock); - -    R_UNLESS(CanContain(region_start, region_num_pages * PageSize, state), -             ResultInvalidCurrentMemory); -    R_UNLESS(region_num_pages > needed_num_pages, ResultOutOfMemory); -    const VAddr addr{ -        AllocateVirtualMemory(region_start, region_num_pages, needed_num_pages, align)}; -    R_UNLESS(addr, ResultOutOfMemory); - -    // Create an update allocator. -    Result allocator_result{ResultSuccess}; -    KMemoryBlockManagerUpdateAllocator allocator(std::addressof(allocator_result), -                                                 m_memory_block_slab_manager); - -    if (is_map_only) { -        R_TRY(Operate(addr, needed_num_pages, perm, OperationType::Map, map_addr)); -    } else { -        // Create a page group tohold the pages we allocate. -        KPageGroup pg{m_kernel, m_block_info_manager}; - -        R_TRY(m_system.Kernel().MemoryManager().AllocateAndOpen( -            &pg, needed_num_pages, -            KMemoryManager::EncodeOption(m_memory_pool, m_allocation_option))); - -        // Ensure that the page group is closed when we're done working with it. -        SCOPE_EXIT({ pg.Close(); }); - -        // Clear all pages. -        for (const auto& it : pg) { -            std::memset(m_system.DeviceMemory().GetPointer<void>(it.GetAddress()), -                        m_heap_fill_value, it.GetSize()); -        } - -        R_TRY(Operate(addr, needed_num_pages, pg, OperationType::MapGroup)); -    } - -    // Update the blocks. -    m_memory_block_manager.Update(std::addressof(allocator), addr, needed_num_pages, state, perm, -                                  KMemoryAttribute::None, KMemoryBlockDisableMergeAttribute::Normal, -                                  KMemoryBlockDisableMergeAttribute::None); - -    return addr; -} -  Result KPageTable::LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,                                                  KMemoryPermission perm, bool is_aligned,                                                  bool check_heap) { diff --git a/src/core/hle/kernel/k_page_table.h b/src/core/hle/kernel/k_page_table.h index 0a454b05b..367dab613 100644 --- a/src/core/hle/kernel/k_page_table.h +++ b/src/core/hle/kernel/k_page_table.h @@ -24,12 +24,36 @@ class System;  namespace Kernel { +enum class DisableMergeAttribute : u8 { +    None = (0U << 0), +    DisableHead = (1U << 0), +    DisableHeadAndBody = (1U << 1), +    EnableHeadAndBody = (1U << 2), +    DisableTail = (1U << 3), +    EnableTail = (1U << 4), +    EnableAndMergeHeadBodyTail = (1U << 5), +    EnableHeadBodyTail = EnableHeadAndBody | EnableTail, +    DisableHeadBodyTail = DisableHeadAndBody | DisableTail, +}; + +struct KPageProperties { +    KMemoryPermission perm; +    bool io; +    bool uncached; +    DisableMergeAttribute disable_merge_attributes; +}; +static_assert(std::is_trivial_v<KPageProperties>); +static_assert(sizeof(KPageProperties) == sizeof(u32)); +  class KBlockInfoManager;  class KMemoryBlockManager;  class KResourceLimit;  class KSystemResource;  class KPageTable final { +protected: +    struct PageLinkedList; +  public:      enum class ICacheInvalidationStrategy : u32 { InvalidateRange, InvalidateAll }; @@ -57,27 +81,12 @@ public:      Result UnmapPhysicalMemory(VAddr addr, size_t size);      Result MapMemory(VAddr dst_addr, VAddr src_addr, size_t size);      Result UnmapMemory(VAddr dst_addr, VAddr src_addr, size_t size); -    Result MapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state, -                    KMemoryPermission perm); -    Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, -                    KMemoryState state, KMemoryPermission perm) { -        R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, -                                this->GetRegionAddress(state), -                                this->GetRegionSize(state) / PageSize, state, perm)); -    } -    Result UnmapPages(VAddr addr, KPageGroup& page_linked_list, KMemoryState state); -    Result UnmapPages(VAddr address, size_t num_pages, KMemoryState state);      Result SetProcessMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission svc_perm);      KMemoryInfo QueryInfo(VAddr addr);      Result SetMemoryPermission(VAddr addr, size_t size, Svc::MemoryPermission perm);      Result SetMemoryAttribute(VAddr addr, size_t size, u32 mask, u32 attr);      Result SetMaxHeapSize(size_t size);      Result SetHeapSize(VAddr* out, size_t size); -    ResultVal<VAddr> AllocateAndMapMemory(size_t needed_num_pages, size_t align, bool is_map_only, -                                          VAddr region_start, size_t region_num_pages, -                                          KMemoryState state, KMemoryPermission perm, -                                          PAddr map_addr = 0); -      Result LockForMapDeviceAddressSpace(bool* out_is_io, VAddr address, size_t size,                                          KMemoryPermission perm, bool is_aligned, bool check_heap);      Result LockForUnmapDeviceAddressSpace(VAddr address, size_t size, bool check_heap); @@ -113,6 +122,40 @@ public:      bool CanContain(VAddr addr, size_t size, KMemoryState state) const; +    Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, +                    KPhysicalAddress phys_addr, KProcessAddress region_start, +                    size_t region_num_pages, KMemoryState state, KMemoryPermission perm) { +        R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, region_start, +                                region_num_pages, state, perm)); +    } + +    Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, +                    KPhysicalAddress phys_addr, KMemoryState state, KMemoryPermission perm) { +        R_RETURN(this->MapPages(out_addr, num_pages, alignment, phys_addr, true, +                                this->GetRegionAddress(state), +                                this->GetRegionSize(state) / PageSize, state, perm)); +    } + +    Result MapPages(KProcessAddress* out_addr, size_t num_pages, KMemoryState state, +                    KMemoryPermission perm) { +        R_RETURN(this->MapPages(out_addr, num_pages, PageSize, 0, false, +                                this->GetRegionAddress(state), +                                this->GetRegionSize(state) / PageSize, state, perm)); +    } + +    Result MapPages(KProcessAddress address, size_t num_pages, KMemoryState state, +                    KMemoryPermission perm); +    Result UnmapPages(KProcessAddress address, size_t num_pages, KMemoryState state); + +    Result MapPageGroup(KProcessAddress* out_addr, const KPageGroup& pg, +                        KProcessAddress region_start, size_t region_num_pages, KMemoryState state, +                        KMemoryPermission perm); +    Result MapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state, +                        KMemoryPermission perm); +    Result UnmapPageGroup(KProcessAddress address, const KPageGroup& pg, KMemoryState state); +    void RemapPageGroup(PageLinkedList* page_list, KProcessAddress address, size_t size, +                        const KPageGroup& pg); +  protected:      struct PageLinkedList {      private: @@ -166,11 +209,9 @@ private:      static constexpr KMemoryAttribute DefaultMemoryIgnoreAttr =          KMemoryAttribute::IpcLocked | KMemoryAttribute::DeviceShared; -    Result MapPages(VAddr addr, const KPageGroup& page_linked_list, KMemoryPermission perm); -    Result MapPages(VAddr* out_addr, size_t num_pages, size_t alignment, PAddr phys_addr, -                    bool is_pa_valid, VAddr region_start, size_t region_num_pages, -                    KMemoryState state, KMemoryPermission perm); -    Result UnmapPages(VAddr addr, const KPageGroup& page_linked_list); +    Result MapPages(KProcessAddress* out_addr, size_t num_pages, size_t alignment, +                    KPhysicalAddress phys_addr, bool is_pa_valid, KProcessAddress region_start, +                    size_t region_num_pages, KMemoryState state, KMemoryPermission perm);      bool IsRegionContiguous(VAddr addr, u64 size) const;      void AddRegionToPages(VAddr start, size_t num_pages, KPageGroup& page_linked_list);      KMemoryInfo QueryInfoImpl(VAddr addr); @@ -265,6 +306,11 @@ private:      void CleanupForIpcClientOnServerSetupFailure(PageLinkedList* page_list, VAddr address,                                                   size_t size, KMemoryPermission prot_perm); +    Result AllocateAndMapPagesImpl(PageLinkedList* page_list, KProcessAddress address, +                                   size_t num_pages, KMemoryPermission perm); +    Result MapPageGroupImpl(PageLinkedList* page_list, KProcessAddress address, +                            const KPageGroup& pg, const KPageProperties properties, bool reuse_ll); +      mutable KLightLock m_general_lock;      mutable KLightLock m_map_physical_memory_lock; diff --git a/src/core/hle/kernel/k_process.cpp b/src/core/hle/kernel/k_process.cpp index a1abf5d68..e201bb0cd 100644 --- a/src/core/hle/kernel/k_process.cpp +++ b/src/core/hle/kernel/k_process.cpp @@ -417,9 +417,8 @@ Result KProcess::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, std:  }  void KProcess::Run(s32 main_thread_priority, u64 stack_size) { -    AllocateMainThreadStack(stack_size); +    ASSERT(AllocateMainThreadStack(stack_size) == ResultSuccess);      resource_limit->Reserve(LimitableResource::ThreadCountMax, 1); -    resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, main_thread_stack_size);      const std::size_t heap_capacity{memory_usage_capacity - (main_thread_stack_size + image_size)};      ASSERT(!page_table.SetMaxHeapSize(heap_capacity).IsError()); @@ -675,20 +674,31 @@ void KProcess::ChangeState(State new_state) {  }  Result KProcess::AllocateMainThreadStack(std::size_t stack_size) { -    ASSERT(stack_size); - -    // The kernel always ensures that the given stack size is page aligned. -    main_thread_stack_size = Common::AlignUp(stack_size, PageSize); - -    const VAddr start{page_table.GetStackRegionStart()}; -    const std::size_t size{page_table.GetStackRegionEnd() - start}; - -    CASCADE_RESULT(main_thread_stack_top, -                   page_table.AllocateAndMapMemory( -                       main_thread_stack_size / PageSize, PageSize, false, start, size / PageSize, -                       KMemoryState::Stack, KMemoryPermission::UserReadWrite)); +    // Ensure that we haven't already allocated stack. +    ASSERT(main_thread_stack_size == 0); + +    // Ensure that we're allocating a valid stack. +    stack_size = Common::AlignUp(stack_size, PageSize); +    // R_UNLESS(stack_size + image_size <= m_max_process_memory, ResultOutOfMemory); +    R_UNLESS(stack_size + image_size >= image_size, ResultOutOfMemory); + +    // Place a tentative reservation of memory for our new stack. +    KScopedResourceReservation mem_reservation(this, Svc::LimitableResource::PhysicalMemoryMax, +                                               stack_size); +    R_UNLESS(mem_reservation.Succeeded(), ResultLimitReached); + +    // Allocate and map our stack. +    if (stack_size) { +        KProcessAddress stack_bottom; +        R_TRY(page_table.MapPages(std::addressof(stack_bottom), stack_size / PageSize, +                                  KMemoryState::Stack, KMemoryPermission::UserReadWrite)); + +        main_thread_stack_top = stack_bottom + stack_size; +        main_thread_stack_size = stack_size; +    } -    main_thread_stack_top += main_thread_stack_size; +    // We succeeded! Commit our memory reservation. +    mem_reservation.Commit();      R_SUCCEED();  } diff --git a/src/core/hle/kernel/k_shared_memory.cpp b/src/core/hle/kernel/k_shared_memory.cpp index 3cf2b5d91..df505edfe 100644 --- a/src/core/hle/kernel/k_shared_memory.cpp +++ b/src/core/hle/kernel/k_shared_memory.cpp @@ -94,15 +94,15 @@ Result KSharedMemory::Map(KProcess& target_process, VAddr address, std::size_t m          R_UNLESS(map_perm == test_perm, ResultInvalidNewMemoryPermission);      } -    return target_process.PageTable().MapPages(address, *page_group, KMemoryState::Shared, -                                               ConvertToKMemoryPermission(map_perm)); +    return target_process.PageTable().MapPageGroup(address, *page_group, KMemoryState::Shared, +                                                   ConvertToKMemoryPermission(map_perm));  }  Result KSharedMemory::Unmap(KProcess& target_process, VAddr address, std::size_t unmap_size) {      // Validate the size.      R_UNLESS(size == unmap_size, ResultInvalidSize); -    return target_process.PageTable().UnmapPages(address, *page_group, KMemoryState::Shared); +    return target_process.PageTable().UnmapPageGroup(address, *page_group, KMemoryState::Shared);  }  } // namespace Kernel diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp index aca442196..67fa5d71c 100644 --- a/src/core/hle/kernel/svc.cpp +++ b/src/core/hle/kernel/svc.cpp @@ -1492,8 +1492,8 @@ static Result MapProcessMemory(Core::System& system, VAddr dst_address, Handle p          KMemoryAttribute::All, KMemoryAttribute::None));      // Map the group. -    R_TRY(dst_pt.MapPages(dst_address, pg, KMemoryState::SharedCode, -                          KMemoryPermission::UserReadWrite)); +    R_TRY(dst_pt.MapPageGroup(dst_address, pg, KMemoryState::SharedCode, +                              KMemoryPermission::UserReadWrite));      return ResultSuccess;  } diff --git a/src/core/memory.cpp b/src/core/memory.cpp index 4e605fae4..af9660b55 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp @@ -440,7 +440,7 @@ struct Memory::Impl {          }          if (Settings::IsFastmemEnabled()) { -            const bool is_read_enable = !Settings::IsGPULevelExtreme() || !cached; +            const bool is_read_enable = Settings::IsGPULevelHigh() || !cached;              system.DeviceMemory().buffer.Protect(vaddr, size, is_read_enable, !cached);          } diff --git a/src/input_common/drivers/joycon.cpp b/src/input_common/drivers/joycon.cpp index 7122093c6..40cda400d 100644 --- a/src/input_common/drivers/joycon.cpp +++ b/src/input_common/drivers/joycon.cpp @@ -4,6 +4,7 @@  #include <fmt/format.h>  #include "common/param_package.h" +#include "common/polyfill_ranges.h"  #include "common/settings.h"  #include "common/thread.h"  #include "input_common/drivers/joycon.h" diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp index fb5799c42..c898ce12f 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_image.cpp @@ -436,6 +436,10 @@ Id EmitImageFetch(EmitContext& ctx, IR::Inst* inst, const IR::Value& index, Id c      if (info.type == TextureType::Buffer) {          lod = Id{};      } +    if (Sirit::ValidId(ms)) { +        // This image is multisampled, lod must be implicit +        lod = Id{}; +    }      const ImageOperands operands(offset, lod, ms);      return Emit(&EmitContext::OpImageSparseFetch, &EmitContext::OpImageFetch, ctx, inst, ctx.F32[4],                  TextureImage(ctx, info, index), coords, operands.MaskOptional(), operands.Span()); diff --git a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp index a0c155fdb..3b97721e1 100644 --- a/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/spirv_emit_context.cpp @@ -35,6 +35,7 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {      const spv::ImageFormat format{spv::ImageFormat::Unknown};      const Id type{ctx.F32[1]};      const bool depth{desc.is_depth}; +    const bool ms{desc.is_multisample};      switch (desc.type) {      case TextureType::Color1D:          return ctx.TypeImage(type, spv::Dim::Dim1D, depth, false, false, 1, format); @@ -42,9 +43,9 @@ Id ImageType(EmitContext& ctx, const TextureDescriptor& desc) {          return ctx.TypeImage(type, spv::Dim::Dim1D, depth, true, false, 1, format);      case TextureType::Color2D:      case TextureType::Color2DRect: -        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, false, 1, format); +        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, false, ms, 1, format);      case TextureType::ColorArray2D: -        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, false, 1, format); +        return ctx.TypeImage(type, spv::Dim::Dim2D, depth, true, ms, 1, format);      case TextureType::Color3D:          return ctx.TypeImage(type, spv::Dim::Dim3D, depth, false, false, 1, format);      case TextureType::ColorCube: diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index f5c86fcb1..9718c6921 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp @@ -524,6 +524,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo          const auto& cbuf{texture_inst.cbuf};          auto flags{inst->Flags<IR::TextureInstInfo>()}; +        bool is_multisample{false};          switch (inst->GetOpcode()) {          case IR::Opcode::ImageQueryDimensions:              flags.type.Assign(ReadTextureType(env, cbuf)); @@ -538,6 +539,12 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo              }              break;          case IR::Opcode::ImageFetch: +            if (flags.type == TextureType::Color2D || flags.type == TextureType::Color2DRect || +                flags.type == TextureType::ColorArray2D) { +                is_multisample = !inst->Arg(4).IsEmpty(); +            } else { +                inst->SetArg(4, IR::U32{}); +            }              if (flags.type != TextureType::Color1D) {                  break;              } @@ -613,6 +620,7 @@ void TexturePass(Environment& env, IR::Program& program, const HostTranslateInfo                  index = descriptors.Add(TextureDescriptor{                      .type = flags.type,                      .is_depth = flags.is_depth != 0, +                    .is_multisample = is_multisample,                      .has_secondary = cbuf.has_secondary,                      .cbuf_index = cbuf.index,                      .cbuf_offset = cbuf.offset, diff --git a/src/shader_recompiler/shader_info.h b/src/shader_recompiler/shader_info.h index f93181e1e..d308db942 100644 --- a/src/shader_recompiler/shader_info.h +++ b/src/shader_recompiler/shader_info.h @@ -109,6 +109,7 @@ using ImageBufferDescriptors = boost::container::small_vector<ImageBufferDescrip  struct TextureDescriptor {      TextureType type;      bool is_depth; +    bool is_multisample;      bool has_secondary;      u32 cbuf_index;      u32 cbuf_offset; diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp index 3bcae3503..83924475b 100644 --- a/src/video_core/memory_manager.cpp +++ b/src/video_core/memory_manager.cpp @@ -6,7 +6,6 @@  #include "common/alignment.h"  #include "common/assert.h"  #include "common/logging/log.h" -#include "common/settings.h"  #include "core/core.h"  #include "core/device_memory.h"  #include "core/hle/kernel/k_page_table.h" @@ -46,11 +45,6 @@ MemoryManager::MemoryManager(Core::System& system_, u64 address_space_bits_, u64      big_page_table_cpu.resize(big_page_table_size);      big_page_continous.resize(big_page_table_size / continous_bits, 0);      entries.resize(page_table_size / 32, 0); -    if (!Settings::IsGPULevelExtreme() && Settings::IsFastmemEnabled()) { -        fastmem_arena = system.DeviceMemory().buffer.VirtualBasePointer(); -    } else { -        fastmem_arena = nullptr; -    }  }  MemoryManager::~MemoryManager() = default; @@ -360,7 +354,7 @@ inline void MemoryManager::MemoryOperation(GPUVAddr gpu_src_addr, std::size_t si      }  } -template <bool is_safe, bool use_fastmem> +template <bool is_safe>  void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,                                    [[maybe_unused]] VideoCommon::CacheType which) const {      auto set_to_zero = [&]([[maybe_unused]] std::size_t page_index, @@ -374,12 +368,8 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:          if constexpr (is_safe) {              rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);          } -        if constexpr (use_fastmem) { -            std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); -        } else { -            u8* physical = memory.GetPointer(cpu_addr_base); -            std::memcpy(dest_buffer, physical, copy_amount); -        } +        u8* physical = memory.GetPointer(cpu_addr_base); +        std::memcpy(dest_buffer, physical, copy_amount);          dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;      };      auto mapped_big = [&](std::size_t page_index, std::size_t offset, std::size_t copy_amount) { @@ -388,15 +378,11 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:          if constexpr (is_safe) {              rasterizer->FlushRegion(cpu_addr_base, copy_amount, which);          } -        if constexpr (use_fastmem) { -            std::memcpy(dest_buffer, &fastmem_arena[cpu_addr_base], copy_amount); +        if (!IsBigPageContinous(page_index)) [[unlikely]] { +            memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount);          } else { -            if (!IsBigPageContinous(page_index)) [[unlikely]] { -                memory.ReadBlockUnsafe(cpu_addr_base, dest_buffer, copy_amount); -            } else { -                u8* physical = memory.GetPointer(cpu_addr_base); -                std::memcpy(dest_buffer, physical, copy_amount); -            } +            u8* physical = memory.GetPointer(cpu_addr_base); +            std::memcpy(dest_buffer, physical, copy_amount);          }          dest_buffer = static_cast<u8*>(dest_buffer) + copy_amount;      }; @@ -410,20 +396,12 @@ void MemoryManager::ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std:  void MemoryManager::ReadBlock(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,                                VideoCommon::CacheType which) const { -    if (fastmem_arena) [[likely]] { -        ReadBlockImpl<true, true>(gpu_src_addr, dest_buffer, size, which); -        return; -    } -    ReadBlockImpl<true, false>(gpu_src_addr, dest_buffer, size, which); +    ReadBlockImpl<true>(gpu_src_addr, dest_buffer, size, which);  }  void MemoryManager::ReadBlockUnsafe(GPUVAddr gpu_src_addr, void* dest_buffer,                                      const std::size_t size) const { -    if (fastmem_arena) [[likely]] { -        ReadBlockImpl<false, true>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); -        return; -    } -    ReadBlockImpl<false, false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None); +    ReadBlockImpl<false>(gpu_src_addr, dest_buffer, size, VideoCommon::CacheType::None);  }  template <bool is_safe> diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h index 2936364f0..9ebfb6179 100644 --- a/src/video_core/memory_manager.h +++ b/src/video_core/memory_manager.h @@ -141,7 +141,7 @@ private:      inline void MemoryOperation(GPUVAddr gpu_src_addr, std::size_t size, FuncMapped&& func_mapped,                                  FuncReserved&& func_reserved, FuncUnmapped&& func_unmapped) const; -    template <bool is_safe, bool use_fastmem> +    template <bool is_safe>      void ReadBlockImpl(GPUVAddr gpu_src_addr, void* dest_buffer, std::size_t size,                         VideoCommon::CacheType which) const; @@ -215,7 +215,6 @@ private:      std::vector<u64> big_page_continous;      std::vector<std::pair<VAddr, std::size_t>> page_stash{}; -    u8* fastmem_arena{};      constexpr static size_t continous_bits = 64; diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp index 4301313cf..2aaefcc05 100644 --- a/src/yuzu/configuration/configure_dialog.cpp +++ b/src/yuzu/configuration/configure_dialog.cpp @@ -66,7 +66,6 @@ ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry_,      web_tab->SetWebServiceConfigEnabled(enable_web_config);      hotkeys_tab->Populate(registry); -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      input_tab->Initialize(input_subsystem); diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp index d1b870c72..fb1292f07 100644 --- a/src/yuzu/configuration/configure_motion_touch.cpp +++ b/src/yuzu/configuration/configure_motion_touch.cpp @@ -89,7 +89,6 @@ ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,             "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "             "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>")); -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      SetConfiguration();      UpdateUiDisplay();      ConnectEvents(); diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index 93db47cfd..7e757eafd 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp @@ -66,8 +66,6 @@ ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id_, const std::st      setFocusPolicy(Qt::ClickFocus);      setWindowTitle(tr("Properties")); -    // remove Help question mark button from the title bar -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      addons_tab->SetTitleId(title_id); diff --git a/src/yuzu/configuration/configure_tas.cpp b/src/yuzu/configuration/configure_tas.cpp index 1edc5f1f3..5a545aa70 100644 --- a/src/yuzu/configuration/configure_tas.cpp +++ b/src/yuzu/configuration/configure_tas.cpp @@ -17,7 +17,6 @@ ConfigureTasDialog::ConfigureTasDialog(QWidget* parent)      setFocusPolicy(Qt::ClickFocus);      setWindowTitle(tr("TAS Configuration")); -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      connect(ui->tas_path_button, &QToolButton::pressed, this,              [this] { SetDirectory(DirectoryTarget::TAS, ui->tas_path_edit); }); diff --git a/src/yuzu/debugger/controller.cpp b/src/yuzu/debugger/controller.cpp index 19f3775a3..e2f55ebae 100644 --- a/src/yuzu/debugger/controller.cpp +++ b/src/yuzu/debugger/controller.cpp @@ -20,9 +20,8 @@ ControllerDialog::ControllerDialog(Core::HID::HIDCore& hid_core_,      setWindowTitle(tr("Controller P1"));      resize(500, 350);      setMinimumSize(500, 350); -    // Remove the "?" button from the titlebar and enable the maximize button -    setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | -                   Qt::WindowMaximizeButtonHint); +    // Enable the maximize button +    setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);      widget = new PlayerControlPreview(this);      refreshConfiguration(); diff --git a/src/yuzu/debugger/profiler.cpp b/src/yuzu/debugger/profiler.cpp index d3e2d3c12..493ee0b17 100644 --- a/src/yuzu/debugger/profiler.cpp +++ b/src/yuzu/debugger/profiler.cpp @@ -49,9 +49,8 @@ MicroProfileDialog::MicroProfileDialog(QWidget* parent) : QWidget(parent, Qt::Di      setObjectName(QStringLiteral("MicroProfile"));      setWindowTitle(tr("&MicroProfile"));      resize(1000, 600); -    // Remove the "?" button from the titlebar and enable the maximize button -    setWindowFlags((windowFlags() & ~Qt::WindowContextHelpButtonHint) | -                   Qt::WindowMaximizeButtonHint); +    // Enable the maximize button +    setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);  #if MICROPROFILE_ENABLED diff --git a/src/yuzu/install_dialog.cpp b/src/yuzu/install_dialog.cpp index 84ec4fe13..673bbaa83 100644 --- a/src/yuzu/install_dialog.cpp +++ b/src/yuzu/install_dialog.cpp @@ -46,7 +46,6 @@ InstallDialog::InstallDialog(QWidget* parent, const QStringList& files) : QDialo      vbox_layout->addLayout(hbox_layout);      setLayout(vbox_layout); -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      setWindowTitle(tr("Install Files to NAND"));  } diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 571eacf9f..62aaf41bf 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -2758,8 +2758,7 @@ void GMainWindow::OnMenuInstallToNAND() {      ui->action_Install_File_NAND->setEnabled(false);      install_progress = new QProgressDialog(QString{}, tr("Cancel"), 0, total_size, this); -    install_progress->setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint & -                                     ~Qt::WindowMaximizeButtonHint); +    install_progress->setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);      install_progress->setAttribute(Qt::WA_DeleteOnClose, true);      install_progress->setFixedWidth(installDialog.GetMinimumWidth() + 40);      install_progress->show(); @@ -4456,6 +4455,11 @@ int main(int argc, char* argv[]) {      }  #endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +    // Disables the "?" button on all dialogs. Disabled by default on Qt6. +    QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); +#endif +      // Enables the core to make the qt created contexts current on std::threads      QCoreApplication::setAttribute(Qt::AA_DontCheckOpenGLContextThreadAffinity);      QApplication app(argc, argv); diff --git a/src/yuzu/util/limitable_input_dialog.cpp b/src/yuzu/util/limitable_input_dialog.cpp index bbb370595..5f6a9c193 100644 --- a/src/yuzu/util/limitable_input_dialog.cpp +++ b/src/yuzu/util/limitable_input_dialog.cpp @@ -16,8 +16,6 @@ LimitableInputDialog::LimitableInputDialog(QWidget* parent) : QDialog{parent} {  LimitableInputDialog::~LimitableInputDialog() = default;  void LimitableInputDialog::CreateUI() { -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); -      text_label = new QLabel(this);      text_entry = new QLineEdit(this);      text_label_invalid = new QLabel(this); diff --git a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp index 4b10fa517..1670aa596 100644 --- a/src/yuzu/util/sequence_dialog/sequence_dialog.cpp +++ b/src/yuzu/util/sequence_dialog/sequence_dialog.cpp @@ -8,7 +8,6 @@  SequenceDialog::SequenceDialog(QWidget* parent) : QDialog(parent) {      setWindowTitle(tr("Enter a hotkey")); -    setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);      key_sequence = new QKeySequenceEdit; diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 6fcf04e1b..67d230462 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h @@ -5,8 +5,8 @@  namespace DefaultINI { -const char* sdl2_config_file = R"( - +const char* sdl2_config_file = +    R"(  [ControlsP0]  # The input devices and parameters for each Switch native input  # The config section determines the player number where the config will be applied on. For example "ControlsP0", "ControlsP1", ... @@ -143,6 +143,8 @@ mouse_enabled =  # 0 (default): Disabled, 1: Enabled  keyboard_enabled = +)" +    R"(  [Core]  # Whether to use multi-core for CPU emulation  # 0: Disabled, 1 (default): Enabled @@ -242,6 +244,8 @@ cpuopt_unsafe_fastmem_check =  # 0: Disabled, 1 (default): Enabled  cpuopt_unsafe_ignore_global_monitor = +)" +    R"(  [Renderer]  # Which backend API to use.  # 0: OpenGL, 1 (default): Vulkan @@ -360,6 +364,8 @@ bg_red =  bg_blue =  bg_green = +)" +    R"(  [Audio]  # Which audio output engine to use.  # auto (default): Auto-select | 
