mirror of
				https://github.com/PabloMK7/citra.git
				synced 2025-10-31 05:40:04 +00:00 
			
		
		
		
	Implement RomFS cache and async reads. (#7089)
* Implement RomFS cache and async reads. * Suggestions and fix compilation. * Apply suggestions
This commit is contained in:
		
							parent
							
								
									79ea06b226
								
							
						
					
					
						commit
						4284893044
					
				
					 10 changed files with 404 additions and 22 deletions
				
			
		|  | @ -124,6 +124,7 @@ add_library(citra_common STATIC | |||
|     serialization/boost_flat_set.h | ||||
|     serialization/boost_small_vector.hpp | ||||
|     serialization/boost_vector.hpp | ||||
|     static_lru_cache.h | ||||
|     string_literal.h | ||||
|     string_util.cpp | ||||
|     string_util.h | ||||
|  |  | |||
|  | @ -1155,6 +1155,43 @@ std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_si | |||
|     return std::fread(data, data_size, length, m_file); | ||||
| } | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
| static std::size_t pread(int fd, void* buf, size_t count, uint64_t offset) { | ||||
|     long unsigned int read_bytes = 0; | ||||
|     OVERLAPPED overlapped = {0}; | ||||
|     HANDLE file = reinterpret_cast<HANDLE>(_get_osfhandle(fd)); | ||||
| 
 | ||||
|     overlapped.OffsetHigh = static_cast<uint32_t>(offset >> 32); | ||||
|     overlapped.Offset = static_cast<uint32_t>(offset & 0xFFFF'FFFFLL); | ||||
|     SetLastError(0); | ||||
|     bool ret = ReadFile(file, buf, static_cast<uint32_t>(count), &read_bytes, &overlapped); | ||||
| 
 | ||||
|     if (!ret && GetLastError() != ERROR_HANDLE_EOF) { | ||||
|         errno = GetLastError(); | ||||
|         return std::numeric_limits<std::size_t>::max(); | ||||
|     } | ||||
|     return read_bytes; | ||||
| } | ||||
| #else | ||||
| #define pread ::pread | ||||
| #endif | ||||
| 
 | ||||
| std::size_t IOFile::ReadAtImpl(void* data, std::size_t length, std::size_t data_size, | ||||
|                                std::size_t offset) { | ||||
|     if (!IsOpen()) { | ||||
|         m_good = false; | ||||
|         return std::numeric_limits<std::size_t>::max(); | ||||
|     } | ||||
| 
 | ||||
|     if (length == 0) { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     DEBUG_ASSERT(data != nullptr); | ||||
| 
 | ||||
|     return pread(fileno(m_file), data, data_size * length, offset); | ||||
| } | ||||
| 
 | ||||
| std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { | ||||
|     if (!IsOpen()) { | ||||
|         m_good = false; | ||||
|  |  | |||
|  | @ -294,6 +294,18 @@ public: | |||
|         return items_read; | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     std::size_t ReadAtArray(T* data, std::size_t length, std::size_t offset) { | ||||
|         static_assert(std::is_trivially_copyable_v<T>, | ||||
|                       "Given array does not consist of trivially copyable objects"); | ||||
| 
 | ||||
|         std::size_t items_read = ReadAtImpl(data, length, sizeof(T), offset); | ||||
|         if (items_read != length) | ||||
|             m_good = false; | ||||
| 
 | ||||
|         return items_read; | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     std::size_t WriteArray(const T* data, std::size_t length) { | ||||
|         static_assert(std::is_trivially_copyable_v<T>, | ||||
|  | @ -312,6 +324,12 @@ public: | |||
|         return ReadArray(reinterpret_cast<char*>(data), length); | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     std::size_t ReadAtBytes(T* data, std::size_t length, std::size_t offset) { | ||||
|         static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||||
|         return ReadAtArray(reinterpret_cast<char*>(data), length, offset); | ||||
|     } | ||||
| 
 | ||||
|     template <typename T> | ||||
|     std::size_t WriteBytes(const T* data, std::size_t length) { | ||||
|         static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||||
|  | @ -363,6 +381,8 @@ public: | |||
| 
 | ||||
| private: | ||||
|     std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size); | ||||
|     std::size_t ReadAtImpl(void* data, std::size_t length, std::size_t data_size, | ||||
|                            std::size_t offset); | ||||
|     std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size); | ||||
| 
 | ||||
|     bool Open(); | ||||
|  |  | |||
							
								
								
									
										113
									
								
								src/common/static_lru_cache.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								src/common/static_lru_cache.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,113 @@ | |||
| // Modified version of: https://www.boost.org/doc/libs/1_79_0/boost/compute/detail/lru_cache.hpp
 | ||||
| // Most important change is the use of an array instead of a map, so that elements are
 | ||||
| // statically allocated. The insert and get methods have been merged into the request method.
 | ||||
| // Original license:
 | ||||
| //
 | ||||
| //---------------------------------------------------------------------------//
 | ||||
| // Copyright (c) 2013 Kyle Lutz <kyle.r.lutz@gmail.com>
 | ||||
| //
 | ||||
| // Distributed under the Boost Software License, Version 1.0
 | ||||
| // See accompanying file LICENSE_1_0.txt or copy at
 | ||||
| // http://www.boost.org/LICENSE_1_0.txt
 | ||||
| //
 | ||||
| // See http://boostorg.github.com/compute for more information.
 | ||||
| //---------------------------------------------------------------------------//
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <array> | ||||
| #include <list> | ||||
| #include <tuple> | ||||
| #include <utility> | ||||
| 
 | ||||
| namespace Common { | ||||
| 
 | ||||
| // a cache which evicts the least recently used item when it is full
 | ||||
| // the cache elements are statically allocated.
 | ||||
| template <class Key, class Value, size_t Size> | ||||
| class StaticLRUCache { | ||||
| public: | ||||
|     using key_type = Key; | ||||
|     using value_type = Value; | ||||
|     using list_type = std::list<std::pair<Key, size_t>>; | ||||
|     using array_type = std::array<Value, Size>; | ||||
| 
 | ||||
|     StaticLRUCache() = default; | ||||
| 
 | ||||
|     ~StaticLRUCache() = default; | ||||
| 
 | ||||
|     size_t size() const { | ||||
|         return m_list.size(); | ||||
|     } | ||||
| 
 | ||||
|     constexpr size_t capacity() const { | ||||
|         return m_array.size(); | ||||
|     } | ||||
| 
 | ||||
|     bool empty() const { | ||||
|         return m_list.empty(); | ||||
|     } | ||||
| 
 | ||||
|     bool contains(const key_type& key) const { | ||||
|         return find(key) != m_list.end(); | ||||
|     } | ||||
| 
 | ||||
|     // Requests an element from the cache. If it is not found,
 | ||||
|     // the element is inserted using its key.
 | ||||
|     // Returns whether the element was present in the cache
 | ||||
|     // and a reference to the element itself.
 | ||||
|     std::pair<bool, value_type&> request(const key_type& key) { | ||||
|         // lookup value in the cache
 | ||||
|         auto i = find(key); | ||||
|         if (i == m_list.cend()) { | ||||
|             size_t next_index = size(); | ||||
|             // insert item into the cache, but first check if it is full
 | ||||
|             if (next_index >= capacity()) { | ||||
|                 // cache is full, evict the least recently used item
 | ||||
|                 next_index = evict(); | ||||
|             } | ||||
| 
 | ||||
|             // insert the new item
 | ||||
|             m_list.push_front(std::make_pair(key, next_index)); | ||||
|             return std::pair<bool, value_type&>(false, m_array[next_index]); | ||||
|         } | ||||
|         // return the value, but first update its place in the most
 | ||||
|         // recently used list
 | ||||
|         if (i != m_list.cbegin()) { | ||||
|             // move item to the front of the most recently used list
 | ||||
|             auto backup = *i; | ||||
|             m_list.erase(i); | ||||
|             m_list.push_front(backup); | ||||
| 
 | ||||
|             // return the value
 | ||||
|             return std::pair<bool, value_type&>(true, m_array[backup.second]); | ||||
|         } else { | ||||
|             // the item is already at the front of the most recently
 | ||||
|             // used list so just return it
 | ||||
|             return std::pair<bool, value_type&>(true, m_array[i->second]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void clear() { | ||||
|         m_list.clear(); | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     typename list_type::const_iterator find(const key_type& key) const { | ||||
|         return std::find_if(m_list.cbegin(), m_list.cend(), | ||||
|                             [&key](const auto& el) { return el.first == key; }); | ||||
|     } | ||||
| 
 | ||||
|     size_t evict() { | ||||
|         // evict item from the end of most recently used list
 | ||||
|         typename list_type::iterator i = --m_list.end(); | ||||
|         size_t evicted_index = i->second; | ||||
|         m_list.erase(i); | ||||
|         return evicted_index; | ||||
|     } | ||||
| 
 | ||||
| private: | ||||
|     array_type m_array; | ||||
|     list_type m_list; | ||||
| }; | ||||
| 
 | ||||
| } // namespace Common
 | ||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue