|
|
@ -143,7 +143,7 @@ public: |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)}; |
|
|
const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)}; |
|
|
const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, false); |
|
|
|
|
|
|
|
|
const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, true, false); |
|
|
if (guard_samplers) { |
|
|
if (guard_samplers) { |
|
|
sampled_textures.push_back(surface); |
|
|
sampled_textures.push_back(surface); |
|
|
} |
|
|
} |
|
|
@ -163,7 +163,7 @@ public: |
|
|
return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
|
|
return GetNullSurface(SurfaceParams::ExpectedTarget(entry)); |
|
|
} |
|
|
} |
|
|
const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)}; |
|
|
const auto params{SurfaceParams::CreateForImage(format_lookup_table, tic, entry)}; |
|
|
const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, false); |
|
|
|
|
|
|
|
|
const auto [surface, view] = GetSurface(gpu_addr, *cpu_addr, params, true, false); |
|
|
if (guard_samplers) { |
|
|
if (guard_samplers) { |
|
|
sampled_textures.push_back(surface); |
|
|
sampled_textures.push_back(surface); |
|
|
} |
|
|
} |
|
|
@ -178,7 +178,7 @@ public: |
|
|
return any_rt; |
|
|
return any_rt; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TView GetDepthBufferSurface() { |
|
|
|
|
|
|
|
|
TView GetDepthBufferSurface(bool preserve_contents) { |
|
|
std::lock_guard lock{mutex}; |
|
|
std::lock_guard lock{mutex}; |
|
|
auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) { |
|
|
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) { |
|
|
@ -199,7 +199,7 @@ public: |
|
|
return {}; |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; |
|
|
const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)}; |
|
|
auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, true); |
|
|
|
|
|
|
|
|
auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, preserve_contents, true); |
|
|
if (depth_buffer.target) |
|
|
if (depth_buffer.target) |
|
|
depth_buffer.target->MarkAsRenderTarget(false, NO_RT); |
|
|
depth_buffer.target->MarkAsRenderTarget(false, NO_RT); |
|
|
depth_buffer.target = surface_view.first; |
|
|
depth_buffer.target = surface_view.first; |
|
|
@ -209,7 +209,7 @@ public: |
|
|
return surface_view.second; |
|
|
return surface_view.second; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
TView GetColorBufferSurface(std::size_t index) { |
|
|
|
|
|
|
|
|
TView GetColorBufferSurface(std::size_t index, bool preserve_contents) { |
|
|
std::lock_guard lock{mutex}; |
|
|
std::lock_guard lock{mutex}; |
|
|
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); |
|
|
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); |
|
|
auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
auto& maxwell3d = system.GPU().Maxwell3D(); |
|
|
@ -239,8 +239,9 @@ public: |
|
|
return {}; |
|
|
return {}; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
auto surface_view = GetSurface(gpu_addr, *cpu_addr, |
|
|
|
|
|
SurfaceParams::CreateForFramebuffer(system, index), true); |
|
|
|
|
|
|
|
|
auto surface_view = |
|
|
|
|
|
GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index), |
|
|
|
|
|
preserve_contents, true); |
|
|
if (render_targets[index].target) { |
|
|
if (render_targets[index].target) { |
|
|
auto& surface = render_targets[index].target; |
|
|
auto& surface = render_targets[index].target; |
|
|
surface->MarkAsRenderTarget(false, NO_RT); |
|
|
surface->MarkAsRenderTarget(false, NO_RT); |
|
|
@ -300,9 +301,9 @@ public: |
|
|
const std::optional<VAddr> src_cpu_addr = |
|
|
const std::optional<VAddr> src_cpu_addr = |
|
|
system.GPU().MemoryManager().GpuToCpuAddress(src_gpu_addr); |
|
|
system.GPU().MemoryManager().GpuToCpuAddress(src_gpu_addr); |
|
|
std::pair<TSurface, TView> dst_surface = |
|
|
std::pair<TSurface, TView> dst_surface = |
|
|
GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, false); |
|
|
|
|
|
|
|
|
GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false); |
|
|
std::pair<TSurface, TView> src_surface = |
|
|
std::pair<TSurface, TView> src_surface = |
|
|
GetSurface(src_gpu_addr, *src_cpu_addr, src_params, false); |
|
|
|
|
|
|
|
|
GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false); |
|
|
ImageBlit(src_surface.second, dst_surface.second, copy_config); |
|
|
ImageBlit(src_surface.second, dst_surface.second, copy_config); |
|
|
dst_surface.first->MarkAsModified(true, Tick()); |
|
|
dst_surface.first->MarkAsModified(true, Tick()); |
|
|
} |
|
|
} |
|
|
@ -532,18 +533,22 @@ private: |
|
|
* @param overlaps The overlapping surfaces registered in the cache. |
|
|
* @param overlaps The overlapping surfaces registered in the cache. |
|
|
* @param params The parameters for the new surface. |
|
|
* @param params The parameters for the new surface. |
|
|
* @param gpu_addr The starting address of the new surface. |
|
|
* @param gpu_addr The starting address of the new surface. |
|
|
|
|
|
* @param preserve_contents Indicates that the new surface should be loaded from memory or left |
|
|
|
|
|
* blank. |
|
|
* @param untopological Indicates to the recycler that the texture has no way to match the |
|
|
* @param untopological Indicates to the recycler that the texture has no way to match the |
|
|
* overlaps due to topological reasons. |
|
|
* overlaps due to topological reasons. |
|
|
**/ |
|
|
**/ |
|
|
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, |
|
|
std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, |
|
|
const SurfaceParams& params, const GPUVAddr gpu_addr, |
|
|
const SurfaceParams& params, const GPUVAddr gpu_addr, |
|
|
|
|
|
const bool preserve_contents, |
|
|
const MatchTopologyResult untopological) { |
|
|
const MatchTopologyResult untopological) { |
|
|
|
|
|
const bool do_load = preserve_contents && Settings::IsGPULevelExtreme(); |
|
|
for (auto& surface : overlaps) { |
|
|
for (auto& surface : overlaps) { |
|
|
Unregister(surface); |
|
|
Unregister(surface); |
|
|
} |
|
|
} |
|
|
switch (PickStrategy(overlaps, params, gpu_addr, untopological)) { |
|
|
switch (PickStrategy(overlaps, params, gpu_addr, untopological)) { |
|
|
case RecycleStrategy::Ignore: { |
|
|
case RecycleStrategy::Ignore: { |
|
|
return InitializeSurface(gpu_addr, params, Settings::IsGPULevelExtreme()); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, do_load); |
|
|
} |
|
|
} |
|
|
case RecycleStrategy::Flush: { |
|
|
case RecycleStrategy::Flush: { |
|
|
std::sort(overlaps.begin(), overlaps.end(), |
|
|
std::sort(overlaps.begin(), overlaps.end(), |
|
|
@ -553,7 +558,7 @@ private: |
|
|
for (auto& surface : overlaps) { |
|
|
for (auto& surface : overlaps) { |
|
|
FlushSurface(surface); |
|
|
FlushSurface(surface); |
|
|
} |
|
|
} |
|
|
return InitializeSurface(gpu_addr, params); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, preserve_contents); |
|
|
} |
|
|
} |
|
|
case RecycleStrategy::BufferCopy: { |
|
|
case RecycleStrategy::BufferCopy: { |
|
|
auto new_surface = GetUncachedSurface(gpu_addr, params); |
|
|
auto new_surface = GetUncachedSurface(gpu_addr, params); |
|
|
@ -562,7 +567,7 @@ private: |
|
|
} |
|
|
} |
|
|
default: { |
|
|
default: { |
|
|
UNIMPLEMENTED_MSG("Unimplemented Texture Cache Recycling Strategy!"); |
|
|
UNIMPLEMENTED_MSG("Unimplemented Texture Cache Recycling Strategy!"); |
|
|
return InitializeSurface(gpu_addr, params); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, do_load); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
@ -700,11 +705,14 @@ private: |
|
|
* @param params The parameters on the new surface. |
|
|
* @param params The parameters on the new surface. |
|
|
* @param gpu_addr The starting address of the new surface. |
|
|
* @param gpu_addr The starting address of the new surface. |
|
|
* @param cpu_addr The starting address of the new surface on physical memory. |
|
|
* @param cpu_addr The starting address of the new surface on physical memory. |
|
|
|
|
|
* @param preserve_contents Indicates that the new surface should be loaded from memory or |
|
|
|
|
|
* left blank. |
|
|
*/ |
|
|
*/ |
|
|
std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, |
|
|
std::optional<std::pair<TSurface, TView>> Manage3DSurfaces(std::vector<TSurface>& overlaps, |
|
|
const SurfaceParams& params, |
|
|
const SurfaceParams& params, |
|
|
const GPUVAddr gpu_addr, |
|
|
const GPUVAddr gpu_addr, |
|
|
const VAddr cpu_addr) { |
|
|
|
|
|
|
|
|
const VAddr cpu_addr, |
|
|
|
|
|
bool preserve_contents) { |
|
|
if (params.target == SurfaceTarget::Texture3D) { |
|
|
if (params.target == SurfaceTarget::Texture3D) { |
|
|
bool failed = false; |
|
|
bool failed = false; |
|
|
if (params.num_levels > 1) { |
|
|
if (params.num_levels > 1) { |
|
|
@ -754,7 +762,7 @@ private: |
|
|
return std::nullopt; |
|
|
return std::nullopt; |
|
|
} |
|
|
} |
|
|
Unregister(surface); |
|
|
Unregister(surface); |
|
|
return InitializeSurface(gpu_addr, params); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, preserve_contents); |
|
|
} |
|
|
} |
|
|
return std::nullopt; |
|
|
return std::nullopt; |
|
|
} |
|
|
} |
|
|
@ -765,7 +773,7 @@ private: |
|
|
return {{surface, surface->GetMainView()}}; |
|
|
return {{surface, surface->GetMainView()}}; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return InitializeSurface(gpu_addr, params); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, preserve_contents); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
@ -788,10 +796,13 @@ private: |
|
|
* |
|
|
* |
|
|
* @param gpu_addr The starting address of the candidate surface. |
|
|
* @param gpu_addr The starting address of the candidate surface. |
|
|
* @param params The parameters on the candidate surface. |
|
|
* @param params The parameters on the candidate surface. |
|
|
|
|
|
* @param preserve_contents Indicates that the new surface should be loaded from memory or |
|
|
|
|
|
* left blank. |
|
|
* @param is_render Whether or not the surface is a render target. |
|
|
* @param is_render Whether or not the surface is a render target. |
|
|
**/ |
|
|
**/ |
|
|
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const VAddr cpu_addr, |
|
|
std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const VAddr cpu_addr, |
|
|
const SurfaceParams& params, bool is_render) { |
|
|
|
|
|
|
|
|
const SurfaceParams& params, bool preserve_contents, |
|
|
|
|
|
bool is_render) { |
|
|
// Step 1 |
|
|
// Step 1 |
|
|
// Check Level 1 Cache for a fast structural match. If candidate surface |
|
|
// Check Level 1 Cache for a fast structural match. If candidate surface |
|
|
// matches at certain level we are pretty much done. |
|
|
// matches at certain level we are pretty much done. |
|
|
@ -800,7 +811,8 @@ private: |
|
|
const auto topological_result = current_surface->MatchesTopology(params); |
|
|
const auto topological_result = current_surface->MatchesTopology(params); |
|
|
if (topological_result != MatchTopologyResult::FullMatch) { |
|
|
if (topological_result != MatchTopologyResult::FullMatch) { |
|
|
std::vector<TSurface> overlaps{current_surface}; |
|
|
std::vector<TSurface> overlaps{current_surface}; |
|
|
return RecycleSurface(overlaps, params, gpu_addr, topological_result); |
|
|
|
|
|
|
|
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
|
|
|
|
|
topological_result); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
const auto struct_result = current_surface->MatchesStructure(params); |
|
|
const auto struct_result = current_surface->MatchesStructure(params); |
|
|
@ -825,7 +837,7 @@ private: |
|
|
|
|
|
|
|
|
// If none are found, we are done. we just load the surface and create it. |
|
|
// If none are found, we are done. we just load the surface and create it. |
|
|
if (overlaps.empty()) { |
|
|
if (overlaps.empty()) { |
|
|
return InitializeSurface(gpu_addr, params); |
|
|
|
|
|
|
|
|
return InitializeSurface(gpu_addr, params, preserve_contents); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Step 3 |
|
|
// Step 3 |
|
|
@ -835,13 +847,15 @@ private: |
|
|
for (const auto& surface : overlaps) { |
|
|
for (const auto& surface : overlaps) { |
|
|
const auto topological_result = surface->MatchesTopology(params); |
|
|
const auto topological_result = surface->MatchesTopology(params); |
|
|
if (topological_result != MatchTopologyResult::FullMatch) { |
|
|
if (topological_result != MatchTopologyResult::FullMatch) { |
|
|
return RecycleSurface(overlaps, params, gpu_addr, topological_result); |
|
|
|
|
|
|
|
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
|
|
|
|
|
topological_result); |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Check if it's a 3D texture |
|
|
// Check if it's a 3D texture |
|
|
if (params.block_depth > 0) { |
|
|
if (params.block_depth > 0) { |
|
|
auto surface = Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr); |
|
|
|
|
|
|
|
|
auto surface = |
|
|
|
|
|
Manage3DSurfaces(overlaps, params, gpu_addr, cpu_addr, preserve_contents); |
|
|
if (surface) { |
|
|
if (surface) { |
|
|
return *surface; |
|
|
return *surface; |
|
|
} |
|
|
} |
|
|
@ -861,7 +875,8 @@ private: |
|
|
return *view; |
|
|
return *view; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
return RecycleSurface(overlaps, params, gpu_addr, MatchTopologyResult::FullMatch); |
|
|
|
|
|
|
|
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
|
|
|
|
|
MatchTopologyResult::FullMatch); |
|
|
} |
|
|
} |
|
|
// Now we check if the candidate is a mipmap/layer of the overlap |
|
|
// Now we check if the candidate is a mipmap/layer of the overlap |
|
|
std::optional<TView> view = |
|
|
std::optional<TView> view = |
|
|
@ -885,7 +900,7 @@ private: |
|
|
pair.first->EmplaceView(params, gpu_addr, candidate_size); |
|
|
pair.first->EmplaceView(params, gpu_addr, candidate_size); |
|
|
if (mirage_view) |
|
|
if (mirage_view) |
|
|
return {pair.first, *mirage_view}; |
|
|
return {pair.first, *mirage_view}; |
|
|
return RecycleSurface(overlaps, params, gpu_addr, |
|
|
|
|
|
|
|
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
|
|
MatchTopologyResult::FullMatch); |
|
|
MatchTopologyResult::FullMatch); |
|
|
} |
|
|
} |
|
|
return {current_surface, *view}; |
|
|
return {current_surface, *view}; |
|
|
@ -901,7 +916,8 @@ private: |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
// We failed all the tests, recycle the overlaps into a new texture. |
|
|
// We failed all the tests, recycle the overlaps into a new texture. |
|
|
return RecycleSurface(overlaps, params, gpu_addr, MatchTopologyResult::FullMatch); |
|
|
|
|
|
|
|
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, |
|
|
|
|
|
MatchTopologyResult::FullMatch); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
@ -1059,10 +1075,10 @@ private: |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |
|
|
std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |
|
|
bool do_load = true) { |
|
|
|
|
|
|
|
|
bool preserve_contents) { |
|
|
auto new_surface{GetUncachedSurface(gpu_addr, params)}; |
|
|
auto new_surface{GetUncachedSurface(gpu_addr, params)}; |
|
|
Register(new_surface); |
|
|
Register(new_surface); |
|
|
if (do_load) { |
|
|
|
|
|
|
|
|
if (preserve_contents) { |
|
|
LoadSurface(new_surface); |
|
|
LoadSurface(new_surface); |
|
|
} |
|
|
} |
|
|
return {new_surface, new_surface->GetMainView()}; |
|
|
return {new_surface, new_surface->GetMainView()}; |
|
|
|