|
@@ -615,6 +615,85 @@ private:
|
|
|
return {{new_surface, new_surface->GetMainView()}};
|
|
return {{new_surface, new_surface->GetMainView()}};
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Takes care of managing 3D textures and its slices. Does some HLE methods when possible.
|
|
|
|
|
+ * Fallsback to LLE when it isn't possible.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param overlaps The overlapping surfaces registered in the cache.
|
|
|
|
|
+ * @param params The parameters on the new surface.
|
|
|
|
|
+ * @param gpu_addr The starting address of the new surface.
|
|
|
|
|
+ * @param cache_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,
|
|
|
|
|
+ const SurfaceParams& params,
|
|
|
|
|
+ const GPUVAddr gpu_addr,
|
|
|
|
|
+ const CacheAddr cache_addr,
|
|
|
|
|
+ bool preserve_contents) {
|
|
|
|
|
+ if (params.target == SurfaceTarget::Texture3D) {
|
|
|
|
|
+ bool failed = false;
|
|
|
|
|
+ if (params.num_levels > 1) {
|
|
|
|
|
+ // We can't handle mipmaps in 3D textures yet, better fallback to LLE approach
|
|
|
|
|
+ return std::nullopt;
|
|
|
|
|
+ }
|
|
|
|
|
+ TSurface new_surface = GetUncachedSurface(gpu_addr, params);
|
|
|
|
|
+ bool modified = false;
|
|
|
|
|
+ for (auto& surface : overlaps) {
|
|
|
|
|
+ const SurfaceParams& src_params = surface->GetSurfaceParams();
|
|
|
|
|
+ if (src_params.target != SurfaceTarget::Texture2D) {
|
|
|
|
|
+ failed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (src_params.height != params.height) {
|
|
|
|
|
+ failed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (src_params.block_depth != params.block_depth ||
|
|
|
|
|
+ src_params.block_height != params.block_height) {
|
|
|
|
|
+ failed = true;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ const u32 offset = static_cast<u32>(surface->GetCacheAddr() - cache_addr);
|
|
|
|
|
+ const auto [x, y, z] = params.GetBlockOffsetXYZ(offset);
|
|
|
|
|
+ modified |= surface->IsModified();
|
|
|
|
|
+ const CopyParams copy_params(0, 0, 0, 0, 0, z, 0, 0, params.width, params.height,
|
|
|
|
|
+ 1);
|
|
|
|
|
+ ImageCopy(surface, new_surface, copy_params);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (failed) {
|
|
|
|
|
+ return std::nullopt;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (const auto& surface : overlaps) {
|
|
|
|
|
+ Unregister(surface);
|
|
|
|
|
+ }
|
|
|
|
|
+ new_surface->MarkAsModified(modified, Tick());
|
|
|
|
|
+ Register(new_surface);
|
|
|
|
|
+ return {{new_surface, new_surface->GetMainView()}};
|
|
|
|
|
+ } else {
|
|
|
|
|
+ for (const auto& surface : overlaps) {
|
|
|
|
|
+ if (!surface->MatchTarget(params.target)) {
|
|
|
|
|
+ if (overlaps.size() == 1 && surface->GetCacheAddr() == cache_addr) {
|
|
|
|
|
+ if (Settings::values.use_accurate_gpu_emulation) {
|
|
|
|
|
+ return std::nullopt;
|
|
|
|
|
+ }
|
|
|
|
|
+ Unregister(surface);
|
|
|
|
|
+ return InitializeSurface(gpu_addr, params, preserve_contents);
|
|
|
|
|
+ }
|
|
|
|
|
+ return std::nullopt;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (surface->GetCacheAddr() != cache_addr) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ const auto struct_result = surface->MatchesStructure(params);
|
|
|
|
|
+ if (struct_result == MatchStructureResult::FullMatch) {
|
|
|
|
|
+ return {{surface, surface->GetMainView()}};
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return InitializeSurface(gpu_addr, params, preserve_contents);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/**
|
|
/**
|
|
|
* Gets the starting address and parameters of a candidate surface and tries
|
|
* Gets the starting address and parameters of a candidate surface and tries
|
|
|
* to find a matching surface within the cache. This is done in 3 big steps:
|
|
* to find a matching surface within the cache. This is done in 3 big steps:
|
|
@@ -687,6 +766,15 @@ private:
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // Look if it's a 3D texture
|
|
|
|
|
+ if (params.block_depth > 0) {
|
|
|
|
|
+ std::optional<std::pair<TSurface, TView>> surface =
|
|
|
|
|
+ Manage3DSurfaces(overlaps, params, gpu_addr, cache_addr, preserve_contents);
|
|
|
|
|
+ if (surface) {
|
|
|
|
|
+ return *surface;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// Split cases between 1 overlap or many.
|
|
// Split cases between 1 overlap or many.
|
|
|
if (overlaps.size() == 1) {
|
|
if (overlaps.size() == 1) {
|
|
|
TSurface current_surface = overlaps[0];
|
|
TSurface current_surface = overlaps[0];
|