texture_cache: Handle overlaps with multiple subresources
Implement more surface reconstruct cases. Allow overlaps with more than one layer and mipmap and copies all of them to the new texture. - Fixes textures moving around objects on Xenoblade games
This commit is contained in:
parent
1bb3122c1f
commit
5b37cecd76
|
@ -652,45 +652,54 @@ private:
|
||||||
**/
|
**/
|
||||||
std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
|
std::optional<std::pair<TSurface, TView>> TryReconstructSurface(std::vector<TSurface>& overlaps,
|
||||||
const SurfaceParams& params,
|
const SurfaceParams& params,
|
||||||
const GPUVAddr gpu_addr) {
|
GPUVAddr gpu_addr) {
|
||||||
if (params.target == SurfaceTarget::Texture3D) {
|
if (params.target == SurfaceTarget::Texture3D) {
|
||||||
return {};
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
bool modified = false;
|
|
||||||
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
|
TSurface new_surface = GetUncachedSurface(gpu_addr, params);
|
||||||
u32 passed_tests = 0;
|
std::size_t passed_tests = 0;
|
||||||
|
bool modified = false;
|
||||||
|
|
||||||
for (auto& surface : overlaps) {
|
for (auto& surface : overlaps) {
|
||||||
const SurfaceParams& src_params = surface->GetSurfaceParams();
|
const SurfaceParams& src_params = surface->GetSurfaceParams();
|
||||||
if (src_params.is_layered || src_params.num_levels > 1) {
|
const auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
|
||||||
// We send this cases to recycle as they are more complex to handle
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const std::size_t candidate_size = surface->GetSizeInBytes();
|
|
||||||
auto mipmap_layer{new_surface->GetLayerMipmap(surface->GetGpuAddr())};
|
|
||||||
if (!mipmap_layer) {
|
if (!mipmap_layer) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto [layer, mipmap] = *mipmap_layer;
|
const auto [base_layer, base_mipmap] = *mipmap_layer;
|
||||||
if (new_surface->GetMipmapSize(mipmap) != candidate_size) {
|
if (new_surface->GetMipmapSize(base_mipmap) != surface->GetMipmapSize(0)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Copy all mipmaps and layers
|
||||||
|
const u32 block_width = params.GetDefaultBlockWidth();
|
||||||
|
const u32 block_height = params.GetDefaultBlockHeight();
|
||||||
|
for (u32 mipmap = base_mipmap; mipmap < base_mipmap + src_params.num_levels; ++mipmap) {
|
||||||
|
const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap);
|
||||||
|
const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap);
|
||||||
|
if (width < block_width || height < block_height) {
|
||||||
|
// Current APIs forbid copying small compressed textures, avoid errors
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const CopyParams copy_params(0, 0, 0, 0, 0, base_layer, 0, mipmap, width, height,
|
||||||
|
src_params.depth);
|
||||||
|
ImageCopy(surface, new_surface, copy_params);
|
||||||
|
}
|
||||||
|
++passed_tests;
|
||||||
modified |= surface->IsModified();
|
modified |= surface->IsModified();
|
||||||
// Now we got all the data set up
|
|
||||||
const u32 width = SurfaceParams::IntersectWidth(src_params, params, 0, mipmap);
|
|
||||||
const u32 height = SurfaceParams::IntersectHeight(src_params, params, 0, mipmap);
|
|
||||||
const CopyParams copy_params(0, 0, 0, 0, 0, layer, 0, mipmap, width, height, 1);
|
|
||||||
passed_tests++;
|
|
||||||
ImageCopy(surface, new_surface, copy_params);
|
|
||||||
}
|
}
|
||||||
if (passed_tests == 0) {
|
if (passed_tests == 0) {
|
||||||
return {};
|
return std::nullopt;
|
||||||
// In Accurate GPU all tests should pass, else we recycle
|
|
||||||
} else if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
|
if (Settings::IsGPULevelExtreme() && passed_tests != overlaps.size()) {
|
||||||
|
// In Accurate GPU all tests should pass, else we recycle
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& surface : overlaps) {
|
for (const auto& surface : overlaps) {
|
||||||
Unregister(surface);
|
Unregister(surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_surface->MarkAsModified(modified, Tick());
|
new_surface->MarkAsModified(modified, Tick());
|
||||||
Register(new_surface);
|
Register(new_surface);
|
||||||
return {{new_surface, new_surface->GetMainView()}};
|
return {{new_surface, new_surface->GetMainView()}};
|
||||||
|
@ -868,12 +877,9 @@ private:
|
||||||
// two things either the candidate surface is a supertexture of the overlap
|
// two things either the candidate surface is a supertexture of the overlap
|
||||||
// or they don't match in any known way.
|
// or they don't match in any known way.
|
||||||
if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
|
if (!current_surface->IsInside(gpu_addr, gpu_addr + candidate_size)) {
|
||||||
if (current_surface->GetGpuAddr() == gpu_addr) {
|
const std::optional view = TryReconstructSurface(overlaps, params, gpu_addr);
|
||||||
std::optional<std::pair<TSurface, TView>> view =
|
if (view) {
|
||||||
TryReconstructSurface(overlaps, params, gpu_addr);
|
return *view;
|
||||||
if (view) {
|
|
||||||
return *view;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
|
return RecycleSurface(overlaps, params, gpu_addr, preserve_contents,
|
||||||
MatchTopologyResult::FullMatch);
|
MatchTopologyResult::FullMatch);
|
||||||
|
|
Reference in New Issue