diff options
| author | 2020-05-29 23:32:41 -0300 | |
|---|---|---|
| committer | 2020-06-08 05:01:00 -0300 | |
| commit | c95c254f3eda75476ad221a4828033f4140a3470 (patch) | |
| tree | b06f5db8c853d3b40d82439e4e739205146cf07a /src/video_core/texture_cache | |
| parent | Merge pull request #4034 from ReinUsesLisp/storage-texels (diff) | |
| download | yuzu-c95c254f3eda75476ad221a4828033f4140a3470.tar.gz yuzu-c95c254f3eda75476ad221a4828033f4140a3470.tar.xz yuzu-c95c254f3eda75476ad221a4828033f4140a3470.zip | |
texture_cache: Implement rendering to 3D textures
This allows rendering to 3D textures with more than one slice.
Applications are allowed to render to more than one slice of a texture
using gl_Layer from a VTG shader.
This also requires reworking how 3D texture collisions are handled, for
now, this commit allows rendering to slices but not to miplevels. When a
render target attempts to write to a mipmap, we fallback to the previous
implementation (copying or flushing as needed).
- Fixes color correction 3D textures on UE4 games (rainbow effects).
- Allows Xenoblade games to render to 3D textures directly.
Diffstat (limited to 'src/video_core/texture_cache')
| -rw-r--r-- | src/video_core/texture_cache/surface_base.cpp | 7 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_base.h | 13 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_params.cpp | 17 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 119 |
4 files changed, 76 insertions, 80 deletions
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp index 715f39d0d..94d3a6ae5 100644 --- a/src/video_core/texture_cache/surface_base.cpp +++ b/src/video_core/texture_cache/surface_base.cpp | |||
| @@ -248,12 +248,11 @@ void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager, | |||
| 248 | 248 | ||
| 249 | // Use an extra temporal buffer | 249 | // Use an extra temporal buffer |
| 250 | auto& tmp_buffer = staging_cache.GetBuffer(1); | 250 | auto& tmp_buffer = staging_cache.GetBuffer(1); |
| 251 | // Special case for 3D Texture Segments | ||
| 252 | const bool must_read_current_data = | ||
| 253 | params.block_depth > 0 && params.target == VideoCore::Surface::SurfaceTarget::Texture2D; | ||
| 254 | tmp_buffer.resize(guest_memory_size); | 251 | tmp_buffer.resize(guest_memory_size); |
| 255 | host_ptr = tmp_buffer.data(); | 252 | host_ptr = tmp_buffer.data(); |
| 256 | if (must_read_current_data) { | 253 | |
| 254 | if (params.target == SurfaceTarget::Texture3D) { | ||
| 255 | // Special case for 3D texture segments | ||
| 257 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); | 256 | memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size); |
| 258 | } | 257 | } |
| 259 | 258 | ||
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index 79e10ffbb..173f2edba 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h | |||
| @@ -217,8 +217,8 @@ public: | |||
| 217 | } | 217 | } |
| 218 | 218 | ||
| 219 | bool IsProtected() const { | 219 | bool IsProtected() const { |
| 220 | // Only 3D Slices are to be protected | 220 | // Only 3D slices are to be protected |
| 221 | return is_target && params.block_depth > 0; | 221 | return is_target && params.target == SurfaceTarget::Texture3D; |
| 222 | } | 222 | } |
| 223 | 223 | ||
| 224 | bool IsRenderTarget() const { | 224 | bool IsRenderTarget() const { |
| @@ -250,6 +250,11 @@ public: | |||
| 250 | return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels)); | 250 | return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels)); |
| 251 | } | 251 | } |
| 252 | 252 | ||
| 253 | TView Emplace3DView(u32 slice, u32 depth, u32 base_level, u32 num_levels) { | ||
| 254 | return GetView(ViewParams(VideoCore::Surface::SurfaceTarget::Texture3D, slice, depth, | ||
| 255 | base_level, num_levels)); | ||
| 256 | } | ||
| 257 | |||
| 253 | std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params, | 258 | std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params, |
| 254 | const GPUVAddr view_addr, | 259 | const GPUVAddr view_addr, |
| 255 | const std::size_t candidate_size, const u32 mipmap, | 260 | const std::size_t candidate_size, const u32 mipmap, |
| @@ -272,8 +277,8 @@ public: | |||
| 272 | std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr, | 277 | std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr, |
| 273 | const std::size_t candidate_size) { | 278 | const std::size_t candidate_size) { |
| 274 | if (params.target == SurfaceTarget::Texture3D || | 279 | if (params.target == SurfaceTarget::Texture3D || |
| 275 | (params.num_levels == 1 && !params.is_layered) || | 280 | view_params.target == SurfaceTarget::Texture3D || |
| 276 | view_params.target == SurfaceTarget::Texture3D) { | 281 | (params.num_levels == 1 && !params.is_layered)) { |
| 277 | return {}; | 282 | return {}; |
| 278 | } | 283 | } |
| 279 | const auto layer_mipmap{GetLayerMipmap(view_addr)}; | 284 | const auto layer_mipmap{GetLayerMipmap(view_addr)}; |
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index 884fabffe..642eeb850 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -215,10 +215,19 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz | |||
| 215 | params.num_levels = 1; | 215 | params.num_levels = 1; |
| 216 | params.emulated_levels = 1; | 216 | params.emulated_levels = 1; |
| 217 | 217 | ||
| 218 | const bool is_layered = config.layers > 1 && params.block_depth == 0; | 218 | if (config.memory_layout.is_3d != 0) { |
| 219 | params.is_layered = is_layered; | 219 | params.depth = config.layers.Value(); |
| 220 | params.depth = is_layered ? config.layers.Value() : 1; | 220 | params.is_layered = false; |
| 221 | params.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D; | 221 | params.target = SurfaceTarget::Texture3D; |
| 222 | } else if (config.layers > 1) { | ||
| 223 | params.depth = config.layers.Value(); | ||
| 224 | params.is_layered = true; | ||
| 225 | params.target = SurfaceTarget::Texture2DArray; | ||
| 226 | } else { | ||
| 227 | params.depth = 1; | ||
| 228 | params.is_layered = false; | ||
| 229 | params.target = SurfaceTarget::Texture2D; | ||
| 230 | } | ||
| 222 | return params; | 231 | return params; |
| 223 | } | 232 | } |
| 224 | 233 | ||
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index 6f63217a2..4ee0d76b9 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -508,12 +508,12 @@ private: | |||
| 508 | return RecycleStrategy::Flush; | 508 | return RecycleStrategy::Flush; |
| 509 | } | 509 | } |
| 510 | // 3D Textures decision | 510 | // 3D Textures decision |
| 511 | if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) { | 511 | if (params.target == SurfaceTarget::Texture3D) { |
| 512 | return RecycleStrategy::Flush; | 512 | return RecycleStrategy::Flush; |
| 513 | } | 513 | } |
| 514 | for (const auto& s : overlaps) { | 514 | for (const auto& s : overlaps) { |
| 515 | const auto& s_params = s->GetSurfaceParams(); | 515 | const auto& s_params = s->GetSurfaceParams(); |
| 516 | if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) { | 516 | if (s_params.target == SurfaceTarget::Texture3D) { |
| 517 | return RecycleStrategy::Flush; | 517 | return RecycleStrategy::Flush; |
| 518 | } | 518 | } |
| 519 | } | 519 | } |
| @@ -726,76 +726,60 @@ private: | |||
| 726 | * @param params The parameters on the new surface. | 726 | * @param params The parameters on the new surface. |
| 727 | * @param gpu_addr The starting address of the new surface. | 727 | * @param gpu_addr The starting address of the new surface. |
| 728 | * @param cpu_addr The starting address of the new surface on physical memory. | 728 | * @param cpu_addr The starting address of the new surface on physical memory. |
| 729 | * @param preserve_contents Indicates that the new surface should be loaded from memory or | ||
| 730 | * left blank. | ||
| 731 | */ | 729 | */ |
| 732 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps, | 730 | std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(VectorSurface& overlaps, |
| 733 | const SurfaceParams& params, | 731 | const SurfaceParams& params, |
| 734 | const GPUVAddr gpu_addr, | 732 | GPUVAddr gpu_addr, VAddr cpu_addr) { |
| 735 | const VAddr cpu_addr, | 733 | if (params.num_levels > 1) { |
| 736 | bool preserve_contents) { | 734 | // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach |
| 737 | if (params.target == SurfaceTarget::Texture3D) { | 735 | return std::nullopt; |
| 738 | bool failed = false; | 736 | } |
| 739 | if (params.num_levels > 1) { | 737 | |
| 740 | // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach | 738 | if (overlaps.size() == 1) { |
| 741 | return std::nullopt; | 739 | const auto& surface = overlaps[0]; |
| 742 | } | 740 | const SurfaceParams& overlap_params = surface->GetSurfaceParams(); |
| 743 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | 741 | // Don't attempt to render to textures with more than one level for now |
| 744 | bool modified = false; | 742 | // The texture has to be to the right or the sample address if we want to render to it |
| 745 | for (auto& surface : overlaps) { | 743 | if (overlap_params.num_levels == 1 && cpu_addr >= surface->GetCpuAddr()) { |
| 746 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | 744 | const u32 offset = static_cast<u32>(cpu_addr - surface->GetCpuAddr()); |
| 747 | if (src_params.target != SurfaceTarget::Texture2D) { | 745 | const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset)); |
| 748 | failed = true; | 746 | if (slice < overlap_params.depth) { |
| 749 | break; | 747 | auto view = surface->Emplace3DView(slice, params.depth, 0, 1); |
| 750 | } | 748 | return std::make_pair(std::move(surface), std::move(view)); |
| 751 | if (src_params.height != params.height) { | ||
| 752 | failed = true; | ||
| 753 | break; | ||
| 754 | } | ||
| 755 | if (src_params.block_depth != params.block_depth || | ||
| 756 | src_params.block_height != params.block_height) { | ||
| 757 | failed = true; | ||
| 758 | break; | ||
| 759 | } | 749 | } |
| 760 | const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr); | ||
| 761 | const auto offsets = params.GetBlockOffsetXYZ(offset); | ||
| 762 | const auto z = std::get<2>(offsets); | ||
| 763 | modified |= surface->IsModified(); | ||
| 764 | const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height, | ||
| 765 | 1); | ||
| 766 | ImageCopy(surface, new_surface, copy_params); | ||
| 767 | } | 750 | } |
| 768 | if (failed) { | 751 | } |
| 752 | |||
| 753 | if (params.depth == 1) { | ||
| 754 | return std::nullopt; | ||
| 755 | } | ||
| 756 | |||
| 757 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 758 | bool modified = false; | ||
| 759 | for (auto& surface : overlaps) { | ||
| 760 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | ||
| 761 | if (src_params.height != params.height || | ||
| 762 | src_params.block_depth != params.block_depth || | ||
| 763 | src_params.block_height != params.block_height) { | ||
| 769 | return std::nullopt; | 764 | return std::nullopt; |
| 770 | } | 765 | } |
| 771 | for (const auto& surface : overlaps) { | 766 | modified |= surface->IsModified(); |
| 772 | Unregister(surface); | 767 | |
| 773 | } | 768 | const u32 offset = static_cast<u32>(surface->GetCpuAddr() - cpu_addr); |
| 774 | new_surface->MarkAsModified(modified, Tick()); | 769 | const u32 slice = std::get<2>(params.GetBlockOffsetXYZ(offset)); |
| 775 | Register(new_surface); | 770 | const u32 width = params.width; |
| 776 | auto view = new_surface->GetMainView(); | 771 | const u32 height = params.height; |
| 777 | return {{std::move(new_surface), view}}; | 772 | const CopyParams copy_params(0, 0, 0, 0, 0, slice, 0, 0, width, height, 1); |
| 778 | } else { | 773 | ImageCopy(surface, new_surface, copy_params); |
| 779 | for (const auto& surface : overlaps) { | ||
| 780 | if (!surface->MatchTarget(params.target)) { | ||
| 781 | if (overlaps.size() == 1 && surface->GetCpuAddr() == cpu_addr) { | ||
| 782 | if (Settings::IsGPULevelExtreme()) { | ||
| 783 | return std::nullopt; | ||
| 784 | } | ||
| 785 | Unregister(surface); | ||
| 786 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 787 | } | ||
| 788 | return std::nullopt; | ||
| 789 | } | ||
| 790 | if (surface->GetCpuAddr() != cpu_addr) { | ||
| 791 | continue; | ||
| 792 | } | ||
| 793 | if (surface->MatchesStructure(params) == MatchStructureResult::FullMatch) { | ||
| 794 | return {{surface, surface->GetMainView()}}; | ||
| 795 | } | ||
| 796 | } | ||
| 797 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 798 | } | 774 | } |
| 775 | for (const auto& surface : overlaps) { | ||
| 776 | Unregister(surface); | ||
| 777 | } | ||
| 778 | new_surface->MarkAsModified(modified, Tick()); | ||
| 779 | Register(new_surface); | ||
| 780 | |||
| 781 | auto view = new_surface->GetMainView(); | ||
| 782 | return std::make_pair(std::move(new_surface), std::move(view)); | ||
| 799 | } | 783 | } |
| 800 | 784 | ||
| 801 | /** | 785 | /** |
| @@ -873,10 +857,9 @@ private: | |||
| 873 | } | 857 | } |
| 874 | } | 858 | } |
| 875 | 859 | ||
| 876 | // Check if it's a 3D texture | 860 | // Manage 3D textures |
| 877 | if (params.block_depth > 0) { | 861 | if (params.target == SurfaceTarget::Texture3D) { |
| 878 | auto surface = | 862 | auto surface = Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr); |
| 879 | Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); | ||
| 880 | if (surface) { | 863 | if (surface) { |
| 881 | return *surface; | 864 | return *surface; |
| 882 | } | 865 | } |