image_base.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <optional>
  5. #include <utility>
  6. #include <vector>
  7. #include "common/common_types.h"
  8. #include "video_core/surface.h"
  9. #include "video_core/texture_cache/formatter.h"
  10. #include "video_core/texture_cache/image_base.h"
  11. #include "video_core/texture_cache/image_view_info.h"
  12. #include "video_core/texture_cache/util.h"
  13. namespace VideoCommon {
  14. using VideoCore::Surface::DefaultBlockHeight;
  15. using VideoCore::Surface::DefaultBlockWidth;
  16. namespace {
  17. /// Returns the base layer and mip level offset
  18. [[nodiscard]] std::pair<s32, s32> LayerMipOffset(s32 diff, u32 layer_stride) {
  19. if (layer_stride == 0) {
  20. return {0, diff};
  21. } else {
  22. return {diff / layer_stride, diff % layer_stride};
  23. }
  24. }
  25. [[nodiscard]] bool ValidateLayers(const SubresourceLayers& layers, const ImageInfo& info) {
  26. return layers.base_level < info.resources.levels &&
  27. layers.base_layer + layers.num_layers <= info.resources.layers;
  28. }
  29. [[nodiscard]] bool ValidateCopy(const ImageCopy& copy, const ImageInfo& dst, const ImageInfo& src) {
  30. const Extent3D src_size = MipSize(src.size, copy.src_subresource.base_level);
  31. const Extent3D dst_size = MipSize(dst.size, copy.dst_subresource.base_level);
  32. if (!ValidateLayers(copy.src_subresource, src)) {
  33. return false;
  34. }
  35. if (!ValidateLayers(copy.dst_subresource, dst)) {
  36. return false;
  37. }
  38. if (copy.src_offset.x + copy.extent.width > src_size.width ||
  39. copy.src_offset.y + copy.extent.height > src_size.height ||
  40. copy.src_offset.z + copy.extent.depth > src_size.depth) {
  41. return false;
  42. }
  43. if (copy.dst_offset.x + copy.extent.width > dst_size.width ||
  44. copy.dst_offset.y + copy.extent.height > dst_size.height ||
  45. copy.dst_offset.z + copy.extent.depth > dst_size.depth) {
  46. return false;
  47. }
  48. return true;
  49. }
  50. } // Anonymous namespace
  51. ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_)
  52. : info{info_}, guest_size_bytes{CalculateGuestSizeInBytes(info)},
  53. unswizzled_size_bytes{CalculateUnswizzledSizeBytes(info)},
  54. converted_size_bytes{CalculateConvertedSizeBytes(info)}, scale_rating{}, scale_tick{},
  55. has_scaled{}, gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_},
  56. cpu_addr_end{cpu_addr + guest_size_bytes}, mip_level_offsets{CalculateMipLevelOffsets(info)} {
  57. if (info.type == ImageType::e3D) {
  58. slice_offsets = CalculateSliceOffsets(info);
  59. slice_subresources = CalculateSliceSubresources(info);
  60. }
  61. }
  62. ImageBase::ImageBase(const NullImageParams&) {}
  63. ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_)
  64. : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {}
  65. std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const noexcept {
  66. if (other_addr < gpu_addr) {
  67. // Subresource address can't be lower than the base
  68. return std::nullopt;
  69. }
  70. const u32 diff = static_cast<u32>(other_addr - gpu_addr);
  71. if (diff > guest_size_bytes) {
  72. // This can happen when two CPU addresses are used for different GPU addresses
  73. return std::nullopt;
  74. }
  75. if (info.type != ImageType::e3D) {
  76. const auto [layer, mip_offset] = LayerMipOffset(diff, info.layer_stride);
  77. const auto end = mip_level_offsets.begin() + info.resources.levels;
  78. const auto it = std::find(mip_level_offsets.begin(), end, static_cast<u32>(mip_offset));
  79. if (layer > info.resources.layers || it == end) {
  80. return std::nullopt;
  81. }
  82. return SubresourceBase{
  83. .level = static_cast<s32>(std::distance(mip_level_offsets.begin(), it)),
  84. .layer = layer,
  85. };
  86. } else {
  87. // TODO: Consider using binary_search after a threshold
  88. const auto it = std::ranges::find(slice_offsets, diff);
  89. if (it == slice_offsets.cend()) {
  90. return std::nullopt;
  91. }
  92. return slice_subresources[std::distance(slice_offsets.begin(), it)];
  93. }
  94. }
  95. ImageViewId ImageBase::FindView(const ImageViewInfo& view_info) const noexcept {
  96. const auto it = std::ranges::find(image_view_infos, view_info);
  97. if (it == image_view_infos.end()) {
  98. return ImageViewId{};
  99. }
  100. return image_view_ids[std::distance(image_view_infos.begin(), it)];
  101. }
  102. void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id) {
  103. image_view_infos.push_back(view_info);
  104. image_view_ids.push_back(image_view_id);
  105. }
  106. bool ImageBase::IsSafeDownload() const noexcept {
  107. // Skip images that were not modified from the GPU
  108. if (False(flags & ImageFlagBits::GpuModified)) {
  109. return false;
  110. }
  111. // Skip images that .are. modified from the CPU
  112. // We don't want to write sensitive data from the guest
  113. if (True(flags & ImageFlagBits::CpuModified)) {
  114. return false;
  115. }
  116. if (info.num_samples > 1) {
  117. LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
  118. return false;
  119. }
  120. return true;
  121. }
  122. void ImageBase::CheckBadOverlapState() {
  123. if (False(flags & ImageFlagBits::BadOverlap)) {
  124. return;
  125. }
  126. if (!overlapping_images.empty()) {
  127. return;
  128. }
  129. flags &= ~ImageFlagBits::BadOverlap;
  130. }
  131. void ImageBase::CheckAliasState() {
  132. if (False(flags & ImageFlagBits::Alias)) {
  133. return;
  134. }
  135. if (!aliased_images.empty()) {
  136. return;
  137. }
  138. flags &= ~ImageFlagBits::Alias;
  139. }
  140. void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id) {
  141. static constexpr auto OPTIONS = RelaxedOptions::Size | RelaxedOptions::Format;
  142. ASSERT(lhs.info.type == rhs.info.type);
  143. std::optional<SubresourceBase> base;
  144. if (lhs.info.type == ImageType::Linear) {
  145. base = SubresourceBase{.level = 0, .layer = 0};
  146. } else {
  147. // We are passing relaxed formats as an option, having broken views/bgr or not won't matter
  148. static constexpr bool broken_views = false;
  149. static constexpr bool native_bgr = true;
  150. base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
  151. }
  152. if (!base) {
  153. LOG_ERROR(HW_GPU, "Image alias should have been flipped");
  154. return;
  155. }
  156. const PixelFormat lhs_format = lhs.info.format;
  157. const PixelFormat rhs_format = rhs.info.format;
  158. const Extent2D lhs_block{
  159. .width = DefaultBlockWidth(lhs_format),
  160. .height = DefaultBlockHeight(lhs_format),
  161. };
  162. const Extent2D rhs_block{
  163. .width = DefaultBlockWidth(rhs_format),
  164. .height = DefaultBlockHeight(rhs_format),
  165. };
  166. const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1;
  167. const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1;
  168. if (is_lhs_compressed && is_rhs_compressed) {
  169. LOG_ERROR(HW_GPU, "Compressed to compressed image aliasing is not implemented");
  170. return;
  171. }
  172. const s32 lhs_mips = lhs.info.resources.levels;
  173. const s32 rhs_mips = rhs.info.resources.levels;
  174. const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips);
  175. AliasedImage lhs_alias;
  176. AliasedImage rhs_alias;
  177. lhs_alias.id = rhs_id;
  178. rhs_alias.id = lhs_id;
  179. lhs_alias.copies.reserve(num_mips);
  180. rhs_alias.copies.reserve(num_mips);
  181. for (s32 mip_level = 0; mip_level < num_mips; ++mip_level) {
  182. Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level);
  183. Extent3D rhs_size = MipSize(rhs.info.size, mip_level);
  184. if (is_lhs_compressed) {
  185. lhs_size.width /= lhs_block.width;
  186. lhs_size.height /= lhs_block.height;
  187. }
  188. if (is_rhs_compressed) {
  189. rhs_size.width /= rhs_block.width;
  190. rhs_size.height /= rhs_block.height;
  191. }
  192. const Extent3D copy_size{
  193. .width = std::min(lhs_size.width, rhs_size.width),
  194. .height = std::min(lhs_size.height, rhs_size.height),
  195. .depth = std::min(lhs_size.depth, rhs_size.depth),
  196. };
  197. if (copy_size.width == 0 || copy_size.height == 0) {
  198. LOG_WARNING(HW_GPU, "Copy size is smaller than block size. Mip cannot be aliased.");
  199. continue;
  200. }
  201. const bool is_lhs_3d = lhs.info.type == ImageType::e3D;
  202. const bool is_rhs_3d = rhs.info.type == ImageType::e3D;
  203. const Offset3D lhs_offset{0, 0, 0};
  204. const Offset3D rhs_offset{0, 0, is_rhs_3d ? base->layer : 0};
  205. const s32 lhs_layers = is_lhs_3d ? 1 : lhs.info.resources.layers - base->layer;
  206. const s32 rhs_layers = is_rhs_3d ? 1 : rhs.info.resources.layers;
  207. const s32 num_layers = std::min(lhs_layers, rhs_layers);
  208. const SubresourceLayers lhs_subresource{
  209. .base_level = mip_level,
  210. .base_layer = 0,
  211. .num_layers = num_layers,
  212. };
  213. const SubresourceLayers rhs_subresource{
  214. .base_level = base->level + mip_level,
  215. .base_layer = is_rhs_3d ? 0 : base->layer,
  216. .num_layers = num_layers,
  217. };
  218. [[maybe_unused]] const ImageCopy& to_lhs_copy = lhs_alias.copies.emplace_back(ImageCopy{
  219. .src_subresource = lhs_subresource,
  220. .dst_subresource = rhs_subresource,
  221. .src_offset = lhs_offset,
  222. .dst_offset = rhs_offset,
  223. .extent = copy_size,
  224. });
  225. [[maybe_unused]] const ImageCopy& to_rhs_copy = rhs_alias.copies.emplace_back(ImageCopy{
  226. .src_subresource = rhs_subresource,
  227. .dst_subresource = lhs_subresource,
  228. .src_offset = rhs_offset,
  229. .dst_offset = lhs_offset,
  230. .extent = copy_size,
  231. });
  232. ASSERT_MSG(ValidateCopy(to_lhs_copy, lhs.info, rhs.info), "Invalid RHS to LHS copy");
  233. ASSERT_MSG(ValidateCopy(to_rhs_copy, rhs.info, lhs.info), "Invalid LHS to RHS copy");
  234. }
  235. ASSERT(lhs_alias.copies.empty() == rhs_alias.copies.empty());
  236. if (lhs_alias.copies.empty()) {
  237. return;
  238. }
  239. lhs.aliased_images.push_back(std::move(lhs_alias));
  240. rhs.aliased_images.push_back(std::move(rhs_alias));
  241. lhs.flags &= ~ImageFlagBits::IsRescalable;
  242. rhs.flags &= ~ImageFlagBits::IsRescalable;
  243. }
  244. } // namespace VideoCommon