summaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-05-29 23:32:41 -0300
committerGravatar ReinUsesLisp2020-06-08 05:01:00 -0300
commitc95c254f3eda75476ad221a4828033f4140a3470 (patch)
treeb06f5db8c853d3b40d82439e4e739205146cf07a /src/video_core/texture_cache
parentMerge pull request #4034 from ReinUsesLisp/storage-texels (diff)
downloadyuzu-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.cpp7
-rw-r--r--src/video_core/texture_cache/surface_base.h13
-rw-r--r--src/video_core/texture_cache/surface_params.cpp17
-rw-r--r--src/video_core/texture_cache/texture_cache.h119
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 }