diff options
| -rw-r--r-- | src/video_core/texture_cache/surface_params.cpp | 38 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_params.h | 14 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 89 | ||||
| -rw-r--r-- | src/video_core/textures/decoders.h | 4 |
4 files changed, 144 insertions, 1 deletions
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index a4f1edd9a..38b3a4ba8 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -392,4 +392,42 @@ std::string SurfaceParams::TargetName() const { | |||
| 392 | } | 392 | } |
| 393 | } | 393 | } |
| 394 | 394 | ||
| 395 | u32 SurfaceParams::GetBlockSize() const { | ||
| 396 | const u32 x = 64U << block_width; | ||
| 397 | const u32 y = 8U << block_height; | ||
| 398 | const u32 z = 1U << block_depth; | ||
| 399 | return x * y * z; | ||
| 400 | } | ||
| 401 | |||
| 402 | std::pair<u32, u32> SurfaceParams::GetBlockXY() const { | ||
| 403 | const u32 x_pixels = 64U / GetBytesPerPixel(); | ||
| 404 | const u32 x = x_pixels << block_width; | ||
| 405 | const u32 y = 8U << block_height; | ||
| 406 | return {x, y}; | ||
| 407 | } | ||
| 408 | |||
| 409 | std::tuple<u32, u32, u32> SurfaceParams::GetBlockOffsetXYZ(u32 offset) const { | ||
| 410 | const auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); }; | ||
| 411 | const u32 block_size = GetBlockSize(); | ||
| 412 | const u32 block_index = offset / block_size; | ||
| 413 | const u32 gob_offset = offset % block_size; | ||
| 414 | const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GetGOBSize()); | ||
| 415 | const u32 x_gob_pixels = 64U / GetBytesPerPixel(); | ||
| 416 | const u32 x_block_pixels = x_gob_pixels << block_width; | ||
| 417 | const u32 y_block_pixels = 8U << block_height; | ||
| 418 | const u32 z_block_pixels = 1U << block_depth; | ||
| 419 | const u32 x_blocks = div_ceil(width, x_block_pixels); | ||
| 420 | const u32 y_blocks = div_ceil(height, y_block_pixels); | ||
| 421 | const u32 z_blocks = div_ceil(depth, z_block_pixels); | ||
| 422 | const u32 base_x = block_index % x_blocks; | ||
| 423 | const u32 base_y = (block_index / x_blocks) % y_blocks; | ||
| 424 | const u32 base_z = (block_index / (x_blocks * y_blocks)) % z_blocks; | ||
| 425 | u32 x = base_x * x_block_pixels; | ||
| 426 | u32 y = base_y * y_block_pixels; | ||
| 427 | u32 z = base_z * z_block_pixels; | ||
| 428 | z += gob_index >> block_height; | ||
| 429 | y += (gob_index * 8U) % y_block_pixels; | ||
| 430 | return {x, y, z}; | ||
| 431 | } | ||
| 432 | |||
| 395 | } // namespace VideoCommon | 433 | } // namespace VideoCommon |
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index 129817ad3..992b5c022 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h | |||
| @@ -4,6 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <utility> | ||
| 8 | |||
| 7 | #include "common/alignment.h" | 9 | #include "common/alignment.h" |
| 8 | #include "common/bit_util.h" | 10 | #include "common/bit_util.h" |
| 9 | #include "common/cityhash.h" | 11 | #include "common/cityhash.h" |
| @@ -136,6 +138,15 @@ public: | |||
| 136 | 138 | ||
| 137 | std::size_t GetConvertedMipmapSize(u32 level) const; | 139 | std::size_t GetConvertedMipmapSize(u32 level) const; |
| 138 | 140 | ||
| 141 | /// Get this texture Tegra Block size in guest memory layout | ||
| 142 | u32 GetBlockSize() const; | ||
| 143 | |||
| 144 | /// Get X, Y coordinates max sizes of a single block. | ||
| 145 | std::pair<u32, u32> GetBlockXY() const; | ||
| 146 | |||
| 147 | /// Get the offset in x, y, z coordinates from a memory offset | ||
| 148 | std::tuple<u32, u32, u32> GetBlockOffsetXYZ(u32 offset) const; | ||
| 149 | |||
| 139 | /// Returns the size of a layer in bytes in guest memory. | 150 | /// Returns the size of a layer in bytes in guest memory. |
| 140 | std::size_t GetGuestLayerSize() const { | 151 | std::size_t GetGuestLayerSize() const { |
| 141 | return GetLayerSize(false, false); | 152 | return GetLayerSize(false, false); |
| @@ -269,7 +280,8 @@ private: | |||
| 269 | 280 | ||
| 270 | /// Returns the size of all mipmap levels and aligns as needed. | 281 | /// Returns the size of all mipmap levels and aligns as needed. |
| 271 | std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const { | 282 | std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const { |
| 272 | return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : depth); | 283 | return GetLayerSize(as_host_size, uncompressed) * |
| 284 | (layer_only ? 1U : (is_layered ? depth : 1U)); | ||
| 273 | } | 285 | } |
| 274 | 286 | ||
| 275 | /// Returns the size of a layer | 287 | /// Returns the size of a layer |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 02d2e9136..f4c015635 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -616,6 +616,86 @@ private: | |||
| 616 | } | 616 | } |
| 617 | 617 | ||
| 618 | /** | 618 | /** |
| 619 | * Takes care of managing 3D textures and its slices. Does HLE methods for reconstructing the 3D | ||
| 620 | * textures within the GPU if possible. Falls back to LLE when it isn't possible to use any of | ||
| 621 | * the HLE methods. | ||
| 622 | * | ||
| 623 | * @param overlaps The overlapping surfaces registered in the cache. | ||
| 624 | * @param params The parameters on the new surface. | ||
| 625 | * @param gpu_addr The starting address of the new surface. | ||
| 626 | * @param cache_addr The starting address of the new surface on physical memory. | ||
| 627 | * @param preserve_contents Indicates that the new surface should be loaded from memory or | ||
| 628 | * left blank. | ||
| 629 | */ | ||
| 630 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, | ||
| 631 | const SurfaceParams& params, | ||
| 632 | const GPUVAddr gpu_addr, | ||
| 633 | const CacheAddr cache_addr, | ||
| 634 | bool preserve_contents) { | ||
| 635 | if (params.target == SurfaceTarget::Texture3D) { | ||
| 636 | bool failed = false; | ||
| 637 | if (params.num_levels > 1) { | ||
| 638 | // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach | ||
| 639 | return std::nullopt; | ||
| 640 | } | ||
| 641 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 642 | bool modified = false; | ||
| 643 | for (auto& surface : overlaps) { | ||
| 644 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | ||
| 645 | if (src_params.target != SurfaceTarget::Texture2D) { | ||
| 646 | failed = true; | ||
| 647 | break; | ||
| 648 | } | ||
| 649 | if (src_params.height != params.height) { | ||
| 650 | failed = true; | ||
| 651 | break; | ||
| 652 | } | ||
| 653 | if (src_params.block_depth != params.block_depth || | ||
| 654 | src_params.block_height != params.block_height) { | ||
| 655 | failed = true; | ||
| 656 | break; | ||
| 657 | } | ||
| 658 | const u32 offset = static_cast<u32>(surface->GetCacheAddr() - cache_addr); | ||
| 659 | const auto [x, y, z] = params.GetBlockOffsetXYZ(offset); | ||
| 660 | modified |= surface->IsModified(); | ||
| 661 | const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, | ||
| 662 | 1); | ||
| 663 | ImageCopy(surface, new_surface, copy_params); | ||
| 664 | } | ||
| 665 | if (failed) { | ||
| 666 | return std::nullopt; | ||
| 667 | } | ||
| 668 | for (const auto& surface : overlaps) { | ||
| 669 | Unregister(surface); | ||
| 670 | } | ||
| 671 | new_surface->MarkAsModified(modified, Tick()); | ||
| 672 | Register(new_surface); | ||
| 673 | auto view = new_surface->GetMainView(); | ||
| 674 | return {{std::move(new_surface), view}}; | ||
| 675 | } else { | ||
| 676 | for (const auto& surface : overlaps) { | ||
| 677 | if (!surface->MatchTarget(params.target)) { | ||
| 678 | if (overlaps.size() == 1 && surface->GetCacheAddr() == cache_addr) { | ||
| 679 | if (Settings::values.use_accurate_gpu_emulation) { | ||
| 680 | return std::nullopt; | ||
| 681 | } | ||
| 682 | Unregister(surface); | ||
| 683 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 684 | } | ||
| 685 | return std::nullopt; | ||
| 686 | } | ||
| 687 | if (surface->GetCacheAddr() != cache_addr) { | ||
| 688 | continue; | ||
| 689 | } | ||
| 690 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { | ||
| 691 | return {{surface, surface->GetMainView()}}; | ||
| 692 | } | ||
| 693 | } | ||
| 694 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 695 | } | ||
| 696 | } | ||
| 697 | |||
| 698 | /** | ||
| 619 | * Gets the starting address and parameters of a candidate surface and tries | 699 | * Gets the starting address and parameters of a candidate surface and tries |
| 620 | * to find a matching surface within the cache. This is done in 3 big steps: | 700 | * to find a matching surface within the cache. This is done in 3 big steps: |
| 621 | * | 701 | * |
| @@ -687,6 +767,15 @@ private: | |||
| 687 | } | 767 | } |
| 688 | } | 768 | } |
| 689 | 769 | ||
| 770 | // Check if it's a 3D texture | ||
| 771 | if (params.block_depth > 0) { | ||
| 772 | auto surface = | ||
| 773 | Manage3DSurfaces(overlaps, params, gpu_addr, cache_addr, preserve_contents); | ||
| 774 | if (surface) { | ||
| 775 | return *surface; | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 690 | // Split cases between 1 overlap or many. | 779 | // Split cases between 1 overlap or many. |
| 691 | if (overlaps.size() == 1) { | 780 | if (overlaps.size() == 1) { |
| 692 | TSurface current_surface = overlaps[0]; | 781 | TSurface current_surface = overlaps[0]; |
diff --git a/src/video_core/textures/decoders.h b/src/video_core/textures/decoders.h index f1e3952bc..e5eac3f3b 100644 --- a/src/video_core/textures/decoders.h +++ b/src/video_core/textures/decoders.h | |||
| @@ -12,6 +12,10 @@ namespace Tegra::Texture { | |||
| 12 | 12 | ||
| 13 | // GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents | 13 | // GOBSize constant. Calculated by 64 bytes in x multiplied by 8 y coords, represents |
| 14 | // an small rect of (64/bytes_per_pixel)X8. | 14 | // an small rect of (64/bytes_per_pixel)X8. |
| 15 | inline std::size_t GetGOBSize() { | ||
| 16 | return 512; | ||
| 17 | } | ||
| 18 | |||
| 15 | inline std::size_t GetGOBSizeShift() { | 19 | inline std::size_t GetGOBSizeShift() { |
| 16 | return 9; | 20 | return 9; |
| 17 | } | 21 | } |