summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/hle/kernel/kernel.cpp41
-rw-r--r--src/core/hle/kernel/process.cpp24
-rw-r--r--src/video_core/engines/engine_interface.h3
-rw-r--r--src/video_core/engines/fermi_2d.h2
-rw-r--r--src/video_core/engines/kepler_memory.h2
-rw-r--r--src/video_core/engines/maxwell_dma.h2
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp11
-rw-r--r--src/video_core/texture_cache/util.cpp21
-rw-r--r--src/video_core/texture_cache/util.h5
10 files changed, 55 insertions, 60 deletions
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index 8fd990577..f7d3f218a 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -67,8 +67,13 @@ struct KernelCore::Impl {
67 is_phantom_mode_for_singlecore = false; 67 is_phantom_mode_for_singlecore = false;
68 68
69 InitializePhysicalCores(); 69 InitializePhysicalCores();
70 InitializeSystemResourceLimit(kernel, system); 70
71 InitializeMemoryLayout(); 71 // Derive the initial memory layout from the emulated board
72 KMemoryLayout memory_layout;
73 DeriveInitialMemoryLayout(memory_layout);
74 InitializeMemoryLayout(memory_layout);
75 InitializeSystemResourceLimit(kernel, system, memory_layout);
76 InitializeSlabHeaps();
72 InitializeSchedulers(); 77 InitializeSchedulers();
73 InitializeSuspendThreads(); 78 InitializeSuspendThreads();
74 InitializePreemption(kernel); 79 InitializePreemption(kernel);
@@ -137,27 +142,32 @@ struct KernelCore::Impl {
137 } 142 }
138 143
139 // Creates the default system resource limit 144 // Creates the default system resource limit
140 void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) { 145 void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system,
146 const KMemoryLayout& memory_layout) {
141 system_resource_limit = std::make_shared<KResourceLimit>(kernel, system); 147 system_resource_limit = std::make_shared<KResourceLimit>(kernel, system);
148 const auto [total_size, kernel_size] = memory_layout.GetTotalAndKernelMemorySizes();
142 149
143 // If setting the default system values fails, then something seriously wrong has occurred. 150 // If setting the default system values fails, then something seriously wrong has occurred.
144 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000) 151 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, total_size)
145 .IsSuccess()); 152 .IsSuccess());
146 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); 153 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess());
147 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess()); 154 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 900).IsSuccess());
148 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) 155 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200)
149 .IsSuccess()); 156 .IsSuccess());
150 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess()); 157 ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 1133).IsSuccess());
158 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size);
151 159
152 // Derived from recent software updates. The kernel reserves 27MB
153 constexpr u64 kernel_size{0x1b00000};
154 if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, kernel_size)) {
155 UNREACHABLE();
156 }
157 // Reserve secure applet memory, introduced in firmware 5.0.0 160 // Reserve secure applet memory, introduced in firmware 5.0.0
158 constexpr u64 secure_applet_memory_size{0x400000}; 161 constexpr u64 secure_applet_memory_size{Common::Size_4_MB};
159 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 162 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
160 secure_applet_memory_size)); 163 secure_applet_memory_size));
164
165 // This memory seems to be reserved on hardware, but is not reserved/used by yuzu.
166 // Likely Horizon OS reserved memory
167 // TODO(ameerj): Derive the memory rather than hardcode it.
168 constexpr u64 unknown_reserved_memory{0x2f896000};
169 ASSERT(system_resource_limit->Reserve(LimitableResource::PhysicalMemory,
170 unknown_reserved_memory));
161 } 171 }
162 172
163 void InitializePreemption(KernelCore& kernel) { 173 void InitializePreemption(KernelCore& kernel) {
@@ -531,11 +541,7 @@ struct KernelCore::Impl {
531 linear_region_start); 541 linear_region_start);
532 } 542 }
533 543
534 void InitializeMemoryLayout() { 544 void InitializeMemoryLayout(const KMemoryLayout& memory_layout) {
535 // Derive the initial memory layout from the emulated board
536 KMemoryLayout memory_layout;
537 DeriveInitialMemoryLayout(memory_layout);
538
539 const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents(); 545 const auto system_pool = memory_layout.GetKernelSystemPoolRegionPhysicalExtents();
540 const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents(); 546 const auto applet_pool = memory_layout.GetKernelAppletPoolRegionPhysicalExtents();
541 const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents(); 547 const auto application_pool = memory_layout.GetKernelApplicationPoolRegionPhysicalExtents();
@@ -578,11 +584,14 @@ struct KernelCore::Impl {
578 system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize}, 584 system.Kernel(), system.DeviceMemory(), nullptr, {time_phys_addr, time_size / PageSize},
579 KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size, 585 KMemoryPermission::None, KMemoryPermission::Read, time_phys_addr, time_size,
580 "Time:SharedMemory"); 586 "Time:SharedMemory");
587 }
581 588
589 void InitializeSlabHeaps() {
582 // Allocate slab heaps 590 // Allocate slab heaps
583 user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>(); 591 user_slab_heap_pages = std::make_unique<KSlabHeap<Page>>();
584 592
585 constexpr u64 user_slab_heap_size{0x1ef000}; 593 // TODO(ameerj): This should be derived, not hardcoded within the kernel
594 constexpr u64 user_slab_heap_size{0x3de000};
586 // Reserve slab heaps 595 // Reserve slab heaps
587 ASSERT( 596 ASSERT(
588 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size)); 597 system_resource_limit->Reserve(LimitableResource::PhysicalMemory, user_slab_heap_size));
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index 9d5956ead..dd01f3924 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -120,9 +120,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
120 std::shared_ptr<Process> process = std::make_shared<Process>(system); 120 std::shared_ptr<Process> process = std::make_shared<Process>(system);
121 process->name = std::move(name); 121 process->name = std::move(name);
122 122
123 // TODO: This is inaccurate 123 process->resource_limit = kernel.GetSystemResourceLimit();
124 // The process should hold a reference to the kernel-wide resource limit.
125 process->resource_limit = std::make_shared<KResourceLimit>(kernel, system);
126 process->status = ProcessStatus::Created; 124 process->status = ProcessStatus::Created;
127 process->program_id = 0; 125 process->program_id = 0;
128 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() 126 process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID()
@@ -160,17 +158,13 @@ void Process::DecrementThreadCount() {
160} 158}
161 159
162u64 Process::GetTotalPhysicalMemoryAvailable() const { 160u64 Process::GetTotalPhysicalMemoryAvailable() const {
163 // TODO: This is expected to always return the application memory pool size after accurately
164 // reserving kernel resources. The current workaround uses a process-local resource limit of
165 // application memory pool size, which is inaccurate.
166 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + 161 const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) +
167 page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + 162 page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size +
168 main_thread_stack_size}; 163 main_thread_stack_size};
169 164 ASSERT(capacity == kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
170 if (capacity < memory_usage_capacity) { 165 if (capacity < memory_usage_capacity) {
171 return capacity; 166 return capacity;
172 } 167 }
173
174 return memory_usage_capacity; 168 return memory_usage_capacity;
175} 169}
176 170
@@ -272,10 +266,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
272 system_resource_size = metadata.GetSystemResourceSize(); 266 system_resource_size = metadata.GetSystemResourceSize();
273 image_size = code_size; 267 image_size = code_size;
274 268
275 // Set initial resource limits
276 resource_limit->SetLimitValue(
277 LimitableResource::PhysicalMemory,
278 kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
279 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory, 269 KScopedResourceReservation memory_reservation(resource_limit, LimitableResource::PhysicalMemory,
280 code_size + system_resource_size); 270 code_size + system_resource_size);
281 if (!memory_reservation.Succeeded()) { 271 if (!memory_reservation.Succeeded()) {
@@ -324,16 +314,6 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata,
324 UNREACHABLE(); 314 UNREACHABLE();
325 } 315 }
326 316
327 // Set initial resource limits
328 resource_limit->SetLimitValue(
329 LimitableResource::PhysicalMemory,
330 kernel.MemoryManager().GetSize(KMemoryManager::Pool::Application));
331
332 resource_limit->SetLimitValue(LimitableResource::Threads, 608);
333 resource_limit->SetLimitValue(LimitableResource::Events, 700);
334 resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128);
335 resource_limit->SetLimitValue(LimitableResource::Sessions, 894);
336
337 // Create TLS region 317 // Create TLS region
338 tls_region_address = CreateTLSRegion(); 318 tls_region_address = CreateTLSRegion();
339 memory_reservation.Commit(); 319 memory_reservation.Commit();
diff --git a/src/video_core/engines/engine_interface.h b/src/video_core/engines/engine_interface.h
index 18a9db7e6..c7ffd68c5 100644
--- a/src/video_core/engines/engine_interface.h
+++ b/src/video_core/engines/engine_interface.h
@@ -4,13 +4,14 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <type_traits>
8#include "common/common_types.h" 7#include "common/common_types.h"
9 8
10namespace Tegra::Engines { 9namespace Tegra::Engines {
11 10
12class EngineInterface { 11class EngineInterface {
13public: 12public:
13 virtual ~EngineInterface() = default;
14
14 /// Write the value to the register identified by method. 15 /// Write the value to the register identified by method.
15 virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0; 16 virtual void CallMethod(u32 method, u32 method_argument, bool is_last_call) = 0;
16 17
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index c808a577d..a4170ffff 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -35,7 +35,7 @@ namespace Tegra::Engines {
35class Fermi2D final : public EngineInterface { 35class Fermi2D final : public EngineInterface {
36public: 36public:
37 explicit Fermi2D(); 37 explicit Fermi2D();
38 ~Fermi2D(); 38 ~Fermi2D() override;
39 39
40 /// Binds a rasterizer to this engine. 40 /// Binds a rasterizer to this engine.
41 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer); 41 void BindRasterizer(VideoCore::RasterizerInterface* rasterizer);
diff --git a/src/video_core/engines/kepler_memory.h b/src/video_core/engines/kepler_memory.h
index 19808a5c6..0d8ea09a9 100644
--- a/src/video_core/engines/kepler_memory.h
+++ b/src/video_core/engines/kepler_memory.h
@@ -36,7 +36,7 @@ namespace Tegra::Engines {
36class KeplerMemory final : public EngineInterface { 36class KeplerMemory final : public EngineInterface {
37public: 37public:
38 explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager); 38 explicit KeplerMemory(Core::System& system_, MemoryManager& memory_manager);
39 ~KeplerMemory(); 39 ~KeplerMemory() override;
40 40
41 /// Write the value to the register identified by method. 41 /// Write the value to the register identified by method.
42 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; 42 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
diff --git a/src/video_core/engines/maxwell_dma.h b/src/video_core/engines/maxwell_dma.h
index 3c59eeb13..c77f02a22 100644
--- a/src/video_core/engines/maxwell_dma.h
+++ b/src/video_core/engines/maxwell_dma.h
@@ -188,7 +188,7 @@ public:
188 static_assert(sizeof(RemapConst) == 12); 188 static_assert(sizeof(RemapConst) == 12);
189 189
190 explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_); 190 explicit MaxwellDMA(Core::System& system_, MemoryManager& memory_manager_);
191 ~MaxwellDMA(); 191 ~MaxwellDMA() override;
192 192
193 /// Write the value to the register identified by method. 193 /// Write the value to the register identified by method.
194 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override; 194 void CallMethod(u32 method, u32 method_argument, bool is_last_call) override;
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
index 2c7ed654d..4b6d64daa 100644
--- a/src/video_core/renderer_vulkan/vk_master_semaphore.h
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -35,8 +35,8 @@ public:
35 } 35 }
36 36
37 /// Returns true when a tick has been hit by the GPU. 37 /// Returns true when a tick has been hit by the GPU.
38 [[nodiscard]] bool IsFree(u64 tick) { 38 [[nodiscard]] bool IsFree(u64 tick) const noexcept {
39 return gpu_tick.load(std::memory_order_relaxed) >= tick; 39 return KnownGpuTick() >= tick;
40 } 40 }
41 41
42 /// Advance to the logical tick. 42 /// Advance to the logical tick.
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 18155e449..bc2a53841 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -7,6 +7,8 @@
7#include <span> 7#include <span>
8#include <vector> 8#include <vector>
9 9
10#include "common/bit_cast.h"
11
10#include "video_core/engines/fermi_2d.h" 12#include "video_core/engines/fermi_2d.h"
11#include "video_core/renderer_vulkan/blit_image.h" 13#include "video_core/renderer_vulkan/blit_image.h"
12#include "video_core/renderer_vulkan/maxwell_to_vk.h" 14#include "video_core/renderer_vulkan/maxwell_to_vk.h"
@@ -1062,14 +1064,13 @@ vk::ImageView ImageView::MakeDepthStencilView(VkImageAspectFlags aspect_mask) {
1062Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) { 1064Sampler::Sampler(TextureCacheRuntime& runtime, const Tegra::Texture::TSCEntry& tsc) {
1063 const auto& device = runtime.device; 1065 const auto& device = runtime.device;
1064 const bool arbitrary_borders = runtime.device.IsExtCustomBorderColorSupported(); 1066 const bool arbitrary_borders = runtime.device.IsExtCustomBorderColorSupported();
1065 const std::array<float, 4> color = tsc.BorderColor(); 1067 const auto color = tsc.BorderColor();
1066 // C++20 bit_cast 1068
1067 VkClearColorValue border_color;
1068 std::memcpy(&border_color, &color, sizeof(color));
1069 const VkSamplerCustomBorderColorCreateInfoEXT border_ci{ 1069 const VkSamplerCustomBorderColorCreateInfoEXT border_ci{
1070 .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT, 1070 .sType = VK_STRUCTURE_TYPE_SAMPLER_CUSTOM_BORDER_COLOR_CREATE_INFO_EXT,
1071 .pNext = nullptr, 1071 .pNext = nullptr,
1072 .customBorderColor = border_color, 1072 // TODO: Make use of std::bit_cast once libc++ supports it.
1073 .customBorderColor = Common::BitCast<VkClearColorValue>(color),
1073 .format = VK_FORMAT_UNDEFINED, 1074 .format = VK_FORMAT_UNDEFINED,
1074 }; 1075 };
1075 const void* pnext = nullptr; 1076 const void* pnext = nullptr;
diff --git a/src/video_core/texture_cache/util.cpp b/src/video_core/texture_cache/util.cpp
index c22dd0148..0ab297413 100644
--- a/src/video_core/texture_cache/util.cpp
+++ b/src/video_core/texture_cache/util.cpp
@@ -268,16 +268,19 @@ template <u32 GOB_EXTENT>
268 return num_tiles << shift; 268 return num_tiles << shift;
269} 269}
270 270
271[[nodiscard]] constexpr std::array<u32, MAX_MIP_LEVELS> CalculateLevelSizes(const LevelInfo& info, 271[[nodiscard]] constexpr LevelArray CalculateLevelSizes(const LevelInfo& info, u32 num_levels) {
272 u32 num_levels) {
273 ASSERT(num_levels <= MAX_MIP_LEVELS); 272 ASSERT(num_levels <= MAX_MIP_LEVELS);
274 std::array<u32, MAX_MIP_LEVELS> sizes{}; 273 LevelArray sizes{};
275 for (u32 level = 0; level < num_levels; ++level) { 274 for (u32 level = 0; level < num_levels; ++level) {
276 sizes[level] = CalculateLevelSize(info, level); 275 sizes[level] = CalculateLevelSize(info, level);
277 } 276 }
278 return sizes; 277 return sizes;
279} 278}
280 279
280[[nodiscard]] u32 CalculateLevelBytes(const LevelArray& sizes, u32 num_levels) {
281 return std::reduce(sizes.begin(), sizes.begin() + num_levels, 0U);
282}
283
281[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block, 284[[nodiscard]] constexpr LevelInfo MakeLevelInfo(PixelFormat format, Extent3D size, Extent3D block,
282 u32 num_samples, u32 tile_width_spacing) { 285 u32 num_samples, u32 tile_width_spacing) {
283 const auto [samples_x, samples_y] = Samples(num_samples); 286 const auto [samples_x, samples_y] = Samples(num_samples);
@@ -566,10 +569,10 @@ void SwizzleBlockLinearImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr
566 569
567 const u32 num_levels = info.resources.levels; 570 const u32 num_levels = info.resources.levels;
568 const std::array sizes = CalculateLevelSizes(level_info, num_levels); 571 const std::array sizes = CalculateLevelSizes(level_info, num_levels);
569 size_t guest_offset = std::reduce(sizes.begin(), sizes.begin() + level, 0); 572 size_t guest_offset = CalculateLevelBytes(sizes, level);
570 const size_t layer_stride = 573 const size_t layer_stride =
571 AlignLayerSize(std::reduce(sizes.begin(), sizes.begin() + num_levels, 0), size, 574 AlignLayerSize(CalculateLevelBytes(sizes, num_levels), size, level_info.block,
572 level_info.block, tile_size.height, info.tile_width_spacing); 575 tile_size.height, info.tile_width_spacing);
573 const size_t subresource_size = sizes[level]; 576 const size_t subresource_size = sizes[level];
574 577
575 const auto dst_data = std::make_unique<u8[]>(subresource_size); 578 const auto dst_data = std::make_unique<u8[]>(subresource_size);
@@ -643,10 +646,10 @@ u32 CalculateLayerSize(const ImageInfo& info) noexcept {
643 info.tile_width_spacing, info.resources.levels); 646 info.tile_width_spacing, info.resources.levels);
644} 647}
645 648
646std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(const ImageInfo& info) noexcept { 649LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept {
647 ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS)); 650 ASSERT(info.resources.levels <= static_cast<s32>(MAX_MIP_LEVELS));
648 const LevelInfo level_info = MakeLevelInfo(info); 651 const LevelInfo level_info = MakeLevelInfo(info);
649 std::array<u32, MAX_MIP_LEVELS> offsets{}; 652 LevelArray offsets{};
650 u32 offset = 0; 653 u32 offset = 0;
651 for (s32 level = 0; level < info.resources.levels; ++level) { 654 for (s32 level = 0; level < info.resources.levels; ++level) {
652 offsets[level] = offset; 655 offsets[level] = offset;
@@ -812,7 +815,7 @@ std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory, GP
812 const Extent2D tile_size = DefaultBlockSize(info.format); 815 const Extent2D tile_size = DefaultBlockSize(info.format);
813 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels); 816 const std::array level_sizes = CalculateLevelSizes(level_info, num_levels);
814 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing); 817 const Extent2D gob = GobSize(bpp_log2, info.block.height, info.tile_width_spacing);
815 const u32 layer_size = std::reduce(level_sizes.begin(), level_sizes.begin() + num_levels, 0); 818 const u32 layer_size = CalculateLevelBytes(level_sizes, num_levels);
816 const u32 layer_stride = AlignLayerSize(layer_size, size, level_info.block, tile_size.height, 819 const u32 layer_stride = AlignLayerSize(layer_size, size, level_info.block, tile_size.height,
817 info.tile_width_spacing); 820 info.tile_width_spacing);
818 size_t guest_offset = 0; 821 size_t guest_offset = 0;
diff --git a/src/video_core/texture_cache/util.h b/src/video_core/texture_cache/util.h
index 4d0072867..cdc5cbc75 100644
--- a/src/video_core/texture_cache/util.h
+++ b/src/video_core/texture_cache/util.h
@@ -20,6 +20,8 @@ namespace VideoCommon {
20 20
21using Tegra::Texture::TICEntry; 21using Tegra::Texture::TICEntry;
22 22
23using LevelArray = std::array<u32, MAX_MIP_LEVELS>;
24
23struct OverlapResult { 25struct OverlapResult {
24 GPUVAddr gpu_addr; 26 GPUVAddr gpu_addr;
25 VAddr cpu_addr; 27 VAddr cpu_addr;
@@ -36,8 +38,7 @@ struct OverlapResult {
36 38
37[[nodiscard]] u32 CalculateLayerSize(const ImageInfo& info) noexcept; 39[[nodiscard]] u32 CalculateLayerSize(const ImageInfo& info) noexcept;
38 40
39[[nodiscard]] std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets( 41[[nodiscard]] LevelArray CalculateMipLevelOffsets(const ImageInfo& info) noexcept;
40 const ImageInfo& info) noexcept;
41 42
42[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info); 43[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info);
43 44