image_base.cpp 10 KB

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