image_base.cpp 9.8 KB

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