summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-01-13 20:36:03 -0300
committerGravatar ReinUsesLisp2020-01-13 20:37:50 -0300
commit09e17fbb0ff70a62b4cff1cafae0ab579956c648 (patch)
tree3a38559009287af0f4b08ed13bc245868cf627a0 /src
parenttexture_cache/surface_params: Make GetNumLayers public (diff)
downloadyuzu-09e17fbb0ff70a62b4cff1cafae0ab579956c648.tar.gz
yuzu-09e17fbb0ff70a62b4cff1cafae0ab579956c648.tar.xz
yuzu-09e17fbb0ff70a62b4cff1cafae0ab579956c648.zip
vk_texture_cache: Implement generic texture cache on Vulkan
It currently ignores PBO linearizations since these should be dropped as soon as possible on OpenGL.
Diffstat (limited to 'src')
-rw-r--r--src/video_core/CMakeLists.txt5
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h1
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp484
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h244
4 files changed, 733 insertions, 1 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 142852082..729ee4a01 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -190,8 +190,11 @@ if (ENABLE_VULKAN)
190 renderer_vulkan/vk_stream_buffer.h 190 renderer_vulkan/vk_stream_buffer.h
191 renderer_vulkan/vk_swapchain.cpp 191 renderer_vulkan/vk_swapchain.cpp
192 renderer_vulkan/vk_swapchain.h 192 renderer_vulkan/vk_swapchain.h
193 renderer_vulkan/vk_texture_cache.cpp
194 renderer_vulkan/vk_texture_cache.h
193 renderer_vulkan/vk_update_descriptor.cpp 195 renderer_vulkan/vk_update_descriptor.cpp
194 renderer_vulkan/vk_update_descriptor.h) 196 renderer_vulkan/vk_update_descriptor.h
197 )
195 198
196 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include) 199 target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
197 target_compile_definitions(video_core PRIVATE HAS_VULKAN) 200 target_compile_definitions(video_core PRIVATE HAS_VULKAN)
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 02310375f..4d9488f49 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -13,6 +13,7 @@
13 13
14#include "video_core/renderer_vulkan/declarations.h" 14#include "video_core/renderer_vulkan/declarations.h"
15#include "video_core/renderer_vulkan/vk_memory_manager.h" 15#include "video_core/renderer_vulkan/vk_memory_manager.h"
16#include "video_core/renderer_vulkan/vk_resource_manager.h"
16 17
17namespace Vulkan { 18namespace Vulkan {
18 19
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
new file mode 100644
index 000000000..aa088f8d0
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -0,0 +1,484 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6#include <array>
7#include <cstddef>
8#include <cstring>
9#include <memory>
10#include <variant>
11#include <vector>
12
13#include "common/alignment.h"
14#include "common/assert.h"
15#include "common/common_types.h"
16#include "core/core.h"
17#include "core/memory.h"
18#include "video_core/engines/maxwell_3d.h"
19#include "video_core/morton.h"
20#include "video_core/renderer_vulkan/declarations.h"
21#include "video_core/renderer_vulkan/maxwell_to_vk.h"
22#include "video_core/renderer_vulkan/vk_device.h"
23#include "video_core/renderer_vulkan/vk_memory_manager.h"
24#include "video_core/renderer_vulkan/vk_rasterizer.h"
25#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
26#include "video_core/renderer_vulkan/vk_texture_cache.h"
27#include "video_core/surface.h"
28#include "video_core/textures/convert.h"
29
30namespace Vulkan {
31
32using VideoCore::MortonSwizzle;
33using VideoCore::MortonSwizzleMode;
34
35using Tegra::Texture::SwizzleSource;
36using VideoCore::Surface::PixelFormat;
37using VideoCore::Surface::SurfaceCompression;
38using VideoCore::Surface::SurfaceTarget;
39
40namespace {
41
42vk::ImageType SurfaceTargetToImage(SurfaceTarget target) {
43 switch (target) {
44 case SurfaceTarget::Texture1D:
45 case SurfaceTarget::Texture1DArray:
46 return vk::ImageType::e1D;
47 case SurfaceTarget::Texture2D:
48 case SurfaceTarget::Texture2DArray:
49 case SurfaceTarget::TextureCubemap:
50 case SurfaceTarget::TextureCubeArray:
51 return vk::ImageType::e2D;
52 case SurfaceTarget::Texture3D:
53 return vk::ImageType::e3D;
54 }
55 UNREACHABLE_MSG("Unknown texture target={}", static_cast<u32>(target));
56 return {};
57}
58
59vk::ImageAspectFlags PixelFormatToImageAspect(PixelFormat pixel_format) {
60 if (pixel_format < PixelFormat::MaxColorFormat) {
61 return vk::ImageAspectFlagBits::eColor;
62 } else if (pixel_format < PixelFormat::MaxDepthFormat) {
63 return vk::ImageAspectFlagBits::eDepth;
64 } else if (pixel_format < PixelFormat::MaxDepthStencilFormat) {
65 return vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil;
66 } else {
67 UNREACHABLE_MSG("Invalid pixel format={}", static_cast<u32>(pixel_format));
68 return vk::ImageAspectFlagBits::eColor;
69 }
70}
71
72vk::ImageViewType GetImageViewType(SurfaceTarget target) {
73 switch (target) {
74 case SurfaceTarget::Texture1D:
75 return vk::ImageViewType::e1D;
76 case SurfaceTarget::Texture2D:
77 return vk::ImageViewType::e2D;
78 case SurfaceTarget::Texture3D:
79 return vk::ImageViewType::e3D;
80 case SurfaceTarget::Texture1DArray:
81 return vk::ImageViewType::e1DArray;
82 case SurfaceTarget::Texture2DArray:
83 return vk::ImageViewType::e2DArray;
84 case SurfaceTarget::TextureCubemap:
85 return vk::ImageViewType::eCube;
86 case SurfaceTarget::TextureCubeArray:
87 return vk::ImageViewType::eCubeArray;
88 case SurfaceTarget::TextureBuffer:
89 break;
90 }
91 UNREACHABLE();
92 return {};
93}
94
95UniqueBuffer CreateBuffer(const VKDevice& device, const SurfaceParams& params) {
96 // TODO(Rodrigo): Move texture buffer creation to the buffer cache
97 const vk::BufferCreateInfo buffer_ci({}, params.GetHostSizeInBytes(),
98 vk::BufferUsageFlagBits::eUniformTexelBuffer |
99 vk::BufferUsageFlagBits::eTransferSrc |
100 vk::BufferUsageFlagBits::eTransferDst,
101 vk::SharingMode::eExclusive, 0, nullptr);
102 const auto dev = device.GetLogical();
103 const auto& dld = device.GetDispatchLoader();
104 return dev.createBufferUnique(buffer_ci, nullptr, dld);
105}
106
107vk::BufferViewCreateInfo GenerateBufferViewCreateInfo(const VKDevice& device,
108 const SurfaceParams& params,
109 vk::Buffer buffer) {
110 ASSERT(params.IsBuffer());
111
112 const auto format =
113 MaxwellToVK::SurfaceFormat(device, FormatType::Buffer, params.pixel_format).format;
114 return vk::BufferViewCreateInfo({}, buffer, format, 0, params.GetHostSizeInBytes());
115}
116
117vk::ImageCreateInfo GenerateImageCreateInfo(const VKDevice& device, const SurfaceParams& params) {
118 constexpr auto sample_count = vk::SampleCountFlagBits::e1;
119 constexpr auto tiling = vk::ImageTiling::eOptimal;
120
121 ASSERT(!params.IsBuffer());
122
123 const auto [format, attachable, storage] =
124 MaxwellToVK::SurfaceFormat(device, FormatType::Optimal, params.pixel_format);
125
126 auto image_usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eTransferDst |
127 vk::ImageUsageFlagBits::eTransferSrc;
128 if (attachable) {
129 image_usage |= params.IsPixelFormatZeta() ? vk::ImageUsageFlagBits::eDepthStencilAttachment
130 : vk::ImageUsageFlagBits::eColorAttachment;
131 }
132 if (storage) {
133 image_usage |= vk::ImageUsageFlagBits::eStorage;
134 }
135
136 vk::ImageCreateFlags flags;
137 vk::Extent3D extent;
138 switch (params.target) {
139 case SurfaceTarget::TextureCubemap:
140 case SurfaceTarget::TextureCubeArray:
141 flags |= vk::ImageCreateFlagBits::eCubeCompatible;
142 [[fallthrough]];
143 case SurfaceTarget::Texture1D:
144 case SurfaceTarget::Texture1DArray:
145 case SurfaceTarget::Texture2D:
146 case SurfaceTarget::Texture2DArray:
147 extent = vk::Extent3D(params.width, params.height, 1);
148 break;
149 case SurfaceTarget::Texture3D:
150 extent = vk::Extent3D(params.width, params.height, params.depth);
151 break;
152 case SurfaceTarget::TextureBuffer:
153 UNREACHABLE();
154 }
155
156 return vk::ImageCreateInfo(flags, SurfaceTargetToImage(params.target), format, extent,
157 params.num_levels, static_cast<u32>(params.GetNumLayers()),
158 sample_count, tiling, image_usage, vk::SharingMode::eExclusive, 0,
159 nullptr, vk::ImageLayout::eUndefined);
160}
161
162} // Anonymous namespace
163
164CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
165 VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
166 VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
167 GPUVAddr gpu_addr, const SurfaceParams& params)
168 : SurfaceBase<View>{gpu_addr, params}, system{system}, device{device},
169 resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
170 staging_pool{staging_pool} {
171 if (params.IsBuffer()) {
172 buffer = CreateBuffer(device, params);
173 commit = memory_manager.Commit(*buffer, false);
174
175 const auto buffer_view_ci = GenerateBufferViewCreateInfo(device, params, *buffer);
176 format = buffer_view_ci.format;
177
178 const auto dev = device.GetLogical();
179 const auto& dld = device.GetDispatchLoader();
180 buffer_view = dev.createBufferViewUnique(buffer_view_ci, nullptr, dld);
181 } else {
182 const auto image_ci = GenerateImageCreateInfo(device, params);
183 format = image_ci.format;
184
185 image.emplace(device, scheduler, image_ci, PixelFormatToImageAspect(params.pixel_format));
186 commit = memory_manager.Commit(image->GetHandle(), false);
187 }
188
189 // TODO(Rodrigo): Move this to a virtual function.
190 main_view = CreateViewInner(
191 ViewParams(params.target, 0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels),
192 true);
193}
194
195CachedSurface::~CachedSurface() = default;
196
197void CachedSurface::UploadTexture(const std::vector<u8>& staging_buffer) {
198 // To upload data we have to be outside of a renderpass
199 scheduler.RequestOutsideRenderPassOperationContext();
200
201 if (params.IsBuffer()) {
202 UploadBuffer(staging_buffer);
203 } else {
204 UploadImage(staging_buffer);
205 }
206}
207
208void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) {
209 UNIMPLEMENTED_IF(params.IsBuffer());
210
211 if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) {
212 LOG_WARNING(Render_Vulkan, "A1B5G5R5 flushing is stubbed");
213 }
214
215 // We can't copy images to buffers inside a renderpass
216 scheduler.RequestOutsideRenderPassOperationContext();
217
218 FullTransition(vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferRead,
219 vk::ImageLayout::eTransferSrcOptimal);
220
221 const auto& buffer = staging_pool.GetUnusedBuffer(host_memory_size, true);
222 // TODO(Rodrigo): Do this in a single copy
223 for (u32 level = 0; level < params.num_levels; ++level) {
224 scheduler.Record([image = image->GetHandle(), buffer = *buffer.handle,
225 copy = GetBufferImageCopy(level)](auto cmdbuf, auto& dld) {
226 cmdbuf.copyImageToBuffer(image, vk::ImageLayout::eTransferSrcOptimal, buffer, {copy},
227 dld);
228 });
229 }
230 scheduler.Finish();
231
232 // TODO(Rodrigo): Use an intern buffer for staging buffers and avoid this unnecesary memcpy.
233 std::memcpy(staging_buffer.data(), buffer.commit->Map(host_memory_size), host_memory_size);
234}
235
236void CachedSurface::DecorateSurfaceName() {
237 // TODO(Rodrigo): Add name decorations
238}
239
240View CachedSurface::CreateView(const ViewParams& params) {
241 return CreateViewInner(params, false);
242}
243
244View CachedSurface::CreateViewInner(const ViewParams& params, bool is_proxy) {
245 auto view = std::make_shared<CachedSurfaceView>(device, *this, params, is_proxy);
246 views[params] = view;
247 // TODO(Rodrigo): Add name decorations
248 return view;
249}
250
251void CachedSurface::UploadBuffer(const std::vector<u8>& staging_buffer) {
252 const auto& src_buffer = staging_pool.GetUnusedBuffer(host_memory_size, true);
253 std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size);
254
255 scheduler.Record([src_buffer = *src_buffer.handle, dst_buffer = *buffer,
256 size = params.GetHostSizeInBytes()](auto cmdbuf, auto& dld) {
257 const vk::BufferCopy copy(0, 0, size);
258 cmdbuf.copyBuffer(src_buffer, dst_buffer, {copy}, dld);
259
260 cmdbuf.pipelineBarrier(
261 vk::PipelineStageFlagBits::eTransfer, vk::PipelineStageFlagBits::eVertexShader, {}, {},
262 {vk::BufferMemoryBarrier(vk::AccessFlagBits::eTransferWrite,
263 vk::AccessFlagBits::eShaderRead, 0, 0, dst_buffer, 0, size)},
264 {}, dld);
265 });
266}
267
268void CachedSurface::UploadImage(const std::vector<u8>& staging_buffer) {
269 const auto& src_buffer = staging_pool.GetUnusedBuffer(host_memory_size, true);
270 std::memcpy(src_buffer.commit->Map(host_memory_size), staging_buffer.data(), host_memory_size);
271
272 FullTransition(vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferWrite,
273 vk::ImageLayout::eTransferDstOptimal);
274
275 for (u32 level = 0; level < params.num_levels; ++level) {
276 vk::BufferImageCopy copy = GetBufferImageCopy(level);
277 const auto& dld = device.GetDispatchLoader();
278 if (image->GetAspectMask() ==
279 (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) {
280 vk::BufferImageCopy depth = copy;
281 vk::BufferImageCopy stencil = copy;
282 depth.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eDepth;
283 stencil.imageSubresource.aspectMask = vk::ImageAspectFlagBits::eStencil;
284 scheduler.Record([buffer = *src_buffer.handle, image = image->GetHandle(), depth,
285 stencil](auto cmdbuf, auto& dld) {
286 cmdbuf.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal,
287 {depth, stencil}, dld);
288 });
289 } else {
290 scheduler.Record([buffer = *src_buffer.handle, image = image->GetHandle(),
291 copy](auto cmdbuf, auto& dld) {
292 cmdbuf.copyBufferToImage(buffer, image, vk::ImageLayout::eTransferDstOptimal,
293 {copy}, dld);
294 });
295 }
296 }
297}
298
299vk::BufferImageCopy CachedSurface::GetBufferImageCopy(u32 level) const {
300 const u32 vk_depth = params.target == SurfaceTarget::Texture3D ? params.GetMipDepth(level) : 1;
301 const auto compression_type = params.GetCompressionType();
302 const std::size_t mip_offset = compression_type == SurfaceCompression::Converted
303 ? params.GetConvertedMipmapOffset(level)
304 : params.GetHostMipmapLevelOffset(level);
305
306 return vk::BufferImageCopy(
307 mip_offset, 0, 0,
308 {image->GetAspectMask(), level, 0, static_cast<u32>(params.GetNumLayers())}, {0, 0, 0},
309 {params.GetMipWidth(level), params.GetMipHeight(level), vk_depth});
310}
311
312vk::ImageSubresourceRange CachedSurface::GetImageSubresourceRange() const {
313 return {image->GetAspectMask(), 0, params.num_levels, 0,
314 static_cast<u32>(params.GetNumLayers())};
315}
316
317CachedSurfaceView::CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
318 const ViewParams& params, bool is_proxy)
319 : VideoCommon::ViewBase{params}, params{surface.GetSurfaceParams()},
320 image{surface.GetImageHandle()}, buffer_view{surface.GetBufferViewHandle()},
321 aspect_mask{surface.GetAspectMask()}, device{device}, surface{surface},
322 base_layer{params.base_layer}, num_layers{params.num_layers}, base_level{params.base_level},
323 num_levels{params.num_levels} {
324 if (image) {
325 image_view_type = GetImageViewType(params.target);
326 }
327}
328
329CachedSurfaceView::~CachedSurfaceView() = default;
330
331vk::ImageView CachedSurfaceView::GetHandle(SwizzleSource x_source, SwizzleSource y_source,
332 SwizzleSource z_source, SwizzleSource w_source) {
333 const u32 swizzle = EncodeSwizzle(x_source, y_source, z_source, w_source);
334 if (last_image_view && last_swizzle == swizzle) {
335 return last_image_view;
336 }
337 last_swizzle = swizzle;
338
339 const auto [entry, is_cache_miss] = view_cache.try_emplace(swizzle);
340 auto& image_view = entry->second;
341 if (!is_cache_miss) {
342 return last_image_view = *image_view;
343 }
344
345 auto swizzle_x = MaxwellToVK::SwizzleSource(x_source);
346 auto swizzle_y = MaxwellToVK::SwizzleSource(y_source);
347 auto swizzle_z = MaxwellToVK::SwizzleSource(z_source);
348 auto swizzle_w = MaxwellToVK::SwizzleSource(w_source);
349
350 if (params.pixel_format == VideoCore::Surface::PixelFormat::A1B5G5R5U) {
351 // A1B5G5R5 is implemented as A1R5G5B5, we have to change the swizzle here.
352 std::swap(swizzle_x, swizzle_z);
353 }
354
355 // Games can sample depth or stencil values on textures. This is decided by the swizzle value on
356 // hardware. To emulate this on Vulkan we specify it in the aspect.
357 vk::ImageAspectFlags aspect = aspect_mask;
358 if (aspect == (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil)) {
359 UNIMPLEMENTED_IF(x_source != SwizzleSource::R && x_source != SwizzleSource::G);
360 const bool is_first = x_source == SwizzleSource::R;
361 switch (params.pixel_format) {
362 case VideoCore::Surface::PixelFormat::Z24S8:
363 case VideoCore::Surface::PixelFormat::Z32FS8:
364 aspect = is_first ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eStencil;
365 break;
366 case VideoCore::Surface::PixelFormat::S8Z24:
367 aspect = is_first ? vk::ImageAspectFlagBits::eStencil : vk::ImageAspectFlagBits::eDepth;
368 break;
369 default:
370 aspect = vk::ImageAspectFlagBits::eDepth;
371 UNIMPLEMENTED();
372 }
373
374 // Vulkan doesn't seem to understand swizzling of a depth stencil image, use identity
375 swizzle_x = vk::ComponentSwizzle::eR;
376 swizzle_y = vk::ComponentSwizzle::eG;
377 swizzle_z = vk::ComponentSwizzle::eB;
378 swizzle_w = vk::ComponentSwizzle::eA;
379 }
380
381 const vk::ImageViewCreateInfo image_view_ci(
382 {}, surface.GetImageHandle(), image_view_type, surface.GetImage().GetFormat(),
383 {swizzle_x, swizzle_y, swizzle_z, swizzle_w},
384 {aspect, base_level, num_levels, base_layer, num_layers});
385
386 const auto dev = device.GetLogical();
387 image_view = dev.createImageViewUnique(image_view_ci, nullptr, device.GetDispatchLoader());
388 return last_image_view = *image_view;
389}
390
391bool CachedSurfaceView::IsOverlapping(const View& rhs) const {
392 // TODO(Rodrigo): Also test for layer and mip level overlaps.
393 return &surface == &rhs->surface;
394}
395
396VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
397 const VKDevice& device, VKResourceManager& resource_manager,
398 VKMemoryManager& memory_manager, VKScheduler& scheduler,
399 VKStagingBufferPool& staging_pool)
400 : TextureCache(system, rasterizer), device{device}, resource_manager{resource_manager},
401 memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {}
402
403VKTextureCache::~VKTextureCache() = default;
404
405Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
406 return std::make_shared<CachedSurface>(system, device, resource_manager, memory_manager,
407 scheduler, staging_pool, gpu_addr, params);
408}
409
410void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
411 const VideoCommon::CopyParams& copy_params) {
412 const bool src_3d = src_surface->GetSurfaceParams().target == SurfaceTarget::Texture3D;
413 const bool dst_3d = dst_surface->GetSurfaceParams().target == SurfaceTarget::Texture3D;
414 UNIMPLEMENTED_IF(src_3d);
415
416 // The texture cache handles depth in OpenGL terms, we have to handle it as subresource and
417 // dimension respectively.
418 const u32 dst_base_layer = dst_3d ? 0 : copy_params.dest_z;
419 const u32 dst_offset_z = dst_3d ? copy_params.dest_z : 0;
420
421 const u32 extent_z = dst_3d ? copy_params.depth : 1;
422 const u32 num_layers = dst_3d ? 1 : copy_params.depth;
423
424 // We can't copy inside a renderpass
425 scheduler.RequestOutsideRenderPassOperationContext();
426
427 src_surface->Transition(copy_params.source_z, copy_params.depth, copy_params.source_level, 1,
428 vk::PipelineStageFlagBits::eTransfer, vk::AccessFlagBits::eTransferRead,
429 vk::ImageLayout::eTransferSrcOptimal);
430 dst_surface->Transition(
431 dst_base_layer, num_layers, copy_params.dest_level, 1, vk::PipelineStageFlagBits::eTransfer,
432 vk::AccessFlagBits::eTransferWrite, vk::ImageLayout::eTransferDstOptimal);
433
434 const auto& dld{device.GetDispatchLoader()};
435 const vk::ImageSubresourceLayers src_subresource(
436 src_surface->GetAspectMask(), copy_params.source_level, copy_params.source_z, num_layers);
437 const vk::ImageSubresourceLayers dst_subresource(
438 dst_surface->GetAspectMask(), copy_params.dest_level, dst_base_layer, num_layers);
439 const vk::Offset3D src_offset(copy_params.source_x, copy_params.source_y, 0);
440 const vk::Offset3D dst_offset(copy_params.dest_x, copy_params.dest_y, dst_offset_z);
441 const vk::Extent3D extent(copy_params.width, copy_params.height, extent_z);
442 const vk::ImageCopy copy(src_subresource, src_offset, dst_subresource, dst_offset, extent);
443 const vk::Image src_image = src_surface->GetImageHandle();
444 const vk::Image dst_image = dst_surface->GetImageHandle();
445 scheduler.Record([src_image, dst_image, copy](auto cmdbuf, auto& dld) {
446 cmdbuf.copyImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image,
447 vk::ImageLayout::eTransferDstOptimal, {copy}, dld);
448 });
449}
450
451void VKTextureCache::ImageBlit(View& src_view, View& dst_view,
452 const Tegra::Engines::Fermi2D::Config& copy_config) {
453 // We can't blit inside a renderpass
454 scheduler.RequestOutsideRenderPassOperationContext();
455
456 src_view->Transition(vk::ImageLayout::eTransferSrcOptimal, vk::PipelineStageFlagBits::eTransfer,
457 vk::AccessFlagBits::eTransferRead);
458 dst_view->Transition(vk::ImageLayout::eTransferDstOptimal, vk::PipelineStageFlagBits::eTransfer,
459 vk::AccessFlagBits::eTransferWrite);
460
461 const auto& cfg = copy_config;
462 const auto src_top_left = vk::Offset3D(cfg.src_rect.left, cfg.src_rect.top, 0);
463 const auto src_bot_right = vk::Offset3D(cfg.src_rect.right, cfg.src_rect.bottom, 1);
464 const auto dst_top_left = vk::Offset3D(cfg.dst_rect.left, cfg.dst_rect.top, 0);
465 const auto dst_bot_right = vk::Offset3D(cfg.dst_rect.right, cfg.dst_rect.bottom, 1);
466 const vk::ImageBlit blit(src_view->GetImageSubresourceLayers(), {src_top_left, src_bot_right},
467 dst_view->GetImageSubresourceLayers(), {dst_top_left, dst_bot_right});
468 const bool is_linear = copy_config.filter == Tegra::Engines::Fermi2D::Filter::Linear;
469
470 const auto& dld{device.GetDispatchLoader()};
471 scheduler.Record([src_image = src_view->GetImage(), dst_image = dst_view->GetImage(), blit,
472 is_linear](auto cmdbuf, auto& dld) {
473 cmdbuf.blitImage(src_image, vk::ImageLayout::eTransferSrcOptimal, dst_image,
474 vk::ImageLayout::eTransferDstOptimal, {blit},
475 is_linear ? vk::Filter::eLinear : vk::Filter::eNearest, dld);
476 });
477}
478
479void VKTextureCache::BufferCopy(Surface& src_surface, Surface& dst_surface) {
480 // Currently unimplemented. PBO copies should be dropped and we should use a render pass to
481 // convert from color to depth and viceversa.
482}
483
484} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
new file mode 100644
index 000000000..4bc330c9f
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -0,0 +1,244 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <memory>
8#include <tuple>
9#include <unordered_map>
10
11#include <boost/functional/hash.hpp>
12#include <boost/icl/interval_map.hpp>
13
14#include "common/assert.h"
15#include "common/common_types.h"
16#include "common/hash.h"
17#include "common/logging/log.h"
18#include "common/math_util.h"
19#include "video_core/gpu.h"
20#include "video_core/rasterizer_cache.h"
21#include "video_core/renderer_vulkan/declarations.h"
22#include "video_core/renderer_vulkan/vk_image.h"
23#include "video_core/renderer_vulkan/vk_memory_manager.h"
24#include "video_core/renderer_vulkan/vk_scheduler.h"
25#include "video_core/surface.h"
26#include "video_core/texture_cache/surface_base.h"
27#include "video_core/texture_cache/texture_cache.h"
28#include "video_core/textures/decoders.h"
29#include "video_core/textures/texture.h"
30
31namespace Core {
32class System;
33}
34
35namespace VideoCore {
36class RasterizerInterface;
37}
38
39namespace Vulkan {
40
41class RasterizerVulkan;
42class VKDevice;
43class VKResourceManager;
44class VKScheduler;
45class VKStagingBufferPool;
46
47class CachedSurfaceView;
48class CachedSurface;
49
50using Surface = std::shared_ptr<CachedSurface>;
51using View = std::shared_ptr<CachedSurfaceView>;
52using TextureCacheBase = VideoCommon::TextureCache<Surface, View>;
53
54using VideoCommon::SurfaceParams;
55using VideoCommon::ViewParams;
56
57class CachedSurface final : public VideoCommon::SurfaceBase<View> {
58 friend CachedSurfaceView;
59
60public:
61 explicit CachedSurface(Core::System& system, const VKDevice& device,
62 VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
63 VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
64 GPUVAddr gpu_addr, const SurfaceParams& params);
65 ~CachedSurface();
66
67 void UploadTexture(const std::vector<u8>& staging_buffer) override;
68 void DownloadTexture(std::vector<u8>& staging_buffer) override;
69
70 void FullTransition(vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access,
71 vk::ImageLayout new_layout) {
72 image->Transition(0, static_cast<u32>(params.GetNumLayers()), 0, params.num_levels,
73 new_stage_mask, new_access, new_layout);
74 }
75
76 void Transition(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels,
77 vk::PipelineStageFlags new_stage_mask, vk::AccessFlags new_access,
78 vk::ImageLayout new_layout) {
79 image->Transition(base_layer, num_layers, base_level, num_levels, new_stage_mask,
80 new_access, new_layout);
81 }
82
83 VKImage& GetImage() {
84 return *image;
85 }
86
87 const VKImage& GetImage() const {
88 return *image;
89 }
90
91 vk::Image GetImageHandle() const {
92 return image->GetHandle();
93 }
94
95 vk::ImageAspectFlags GetAspectMask() const {
96 return image->GetAspectMask();
97 }
98
99 vk::BufferView GetBufferViewHandle() const {
100 return *buffer_view;
101 }
102
103protected:
104 void DecorateSurfaceName();
105
106 View CreateView(const ViewParams& params) override;
107 View CreateViewInner(const ViewParams& params, bool is_proxy);
108
109private:
110 void UploadBuffer(const std::vector<u8>& staging_buffer);
111
112 void UploadImage(const std::vector<u8>& staging_buffer);
113
114 vk::BufferImageCopy GetBufferImageCopy(u32 level) const;
115
116 vk::ImageSubresourceRange GetImageSubresourceRange() const;
117
118 Core::System& system;
119 const VKDevice& device;
120 VKResourceManager& resource_manager;
121 VKMemoryManager& memory_manager;
122 VKScheduler& scheduler;
123 VKStagingBufferPool& staging_pool;
124
125 std::optional<VKImage> image;
126 UniqueBuffer buffer;
127 UniqueBufferView buffer_view;
128 VKMemoryCommit commit;
129
130 vk::Format format;
131};
132
133class CachedSurfaceView final : public VideoCommon::ViewBase {
134public:
135 explicit CachedSurfaceView(const VKDevice& device, CachedSurface& surface,
136 const ViewParams& params, bool is_proxy);
137 ~CachedSurfaceView();
138
139 vk::ImageView GetHandle(Tegra::Texture::SwizzleSource x_source,
140 Tegra::Texture::SwizzleSource y_source,
141 Tegra::Texture::SwizzleSource z_source,
142 Tegra::Texture::SwizzleSource w_source);
143
144 bool IsOverlapping(const View& rhs) const;
145
146 vk::ImageView GetHandle() {
147 return GetHandle(Tegra::Texture::SwizzleSource::R, Tegra::Texture::SwizzleSource::G,
148 Tegra::Texture::SwizzleSource::B, Tegra::Texture::SwizzleSource::A);
149 }
150
151 u32 GetWidth() const {
152 return params.GetMipWidth(base_level);
153 }
154
155 u32 GetHeight() const {
156 return params.GetMipHeight(base_level);
157 }
158
159 bool IsBufferView() const {
160 return buffer_view;
161 }
162
163 vk::Image GetImage() const {
164 return image;
165 }
166
167 vk::BufferView GetBufferView() const {
168 return buffer_view;
169 }
170
171 vk::ImageSubresourceRange GetImageSubresourceRange() const {
172 return {aspect_mask, base_level, num_levels, base_layer, num_layers};
173 }
174
175 vk::ImageSubresourceLayers GetImageSubresourceLayers() const {
176 return {surface.GetAspectMask(), base_level, base_layer, num_layers};
177 }
178
179 void Transition(vk::ImageLayout new_layout, vk::PipelineStageFlags new_stage_mask,
180 vk::AccessFlags new_access) const {
181 surface.Transition(base_layer, num_layers, base_level, num_levels, new_stage_mask,
182 new_access, new_layout);
183 }
184
185 void MarkAsModified(u64 tick) {
186 surface.MarkAsModified(true, tick);
187 }
188
189private:
190 static u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source,
191 Tegra::Texture::SwizzleSource y_source,
192 Tegra::Texture::SwizzleSource z_source,
193 Tegra::Texture::SwizzleSource w_source) {
194 return (static_cast<u32>(x_source) << 24) | (static_cast<u32>(y_source) << 16) |
195 (static_cast<u32>(z_source) << 8) | static_cast<u32>(w_source);
196 }
197
198 // Store a copy of these values to avoid double dereference when reading them
199 const SurfaceParams params;
200 const vk::Image image;
201 const vk::BufferView buffer_view;
202 const vk::ImageAspectFlags aspect_mask;
203
204 const VKDevice& device;
205 CachedSurface& surface;
206 const u32 base_layer;
207 const u32 num_layers;
208 const u32 base_level;
209 const u32 num_levels;
210 vk::ImageViewType image_view_type{};
211
212 vk::ImageView last_image_view;
213 u32 last_swizzle{};
214
215 std::unordered_map<u32, UniqueImageView> view_cache;
216};
217
218class VKTextureCache final : public TextureCacheBase {
219public:
220 explicit VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
221 const VKDevice& device, VKResourceManager& resource_manager,
222 VKMemoryManager& memory_manager, VKScheduler& scheduler,
223 VKStagingBufferPool& staging_pool);
224 ~VKTextureCache();
225
226private:
227 Surface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) override;
228
229 void ImageCopy(Surface& src_surface, Surface& dst_surface,
230 const VideoCommon::CopyParams& copy_params) override;
231
232 void ImageBlit(View& src_view, View& dst_view,
233 const Tegra::Engines::Fermi2D::Config& copy_config) override;
234
235 void BufferCopy(Surface& src_surface, Surface& dst_surface) override;
236
237 const VKDevice& device;
238 VKResourceManager& resource_manager;
239 VKMemoryManager& memory_manager;
240 VKScheduler& scheduler;
241 VKStagingBufferPool& staging_pool;
242};
243
244} // namespace Vulkan