texture_cache: Report incompatible textures as black

Some games bind incompatible texture types to certain types.
For example Astral Chain binds a 2D texture with 1 layer (non-array) to
a cubemap slot (that's how it's used in the shader). After testing this
in hardware, the expected "undefined behavior" is to report all pixels
as black.

We already have a path for reporting black textures in the texture
cache. When textures types are incompatible, this commit binds these
kind of textures. This is done on the API agnostic texture cache so no
extra code has to be inserted on OpenGL or Vulkan.

As a side effect, this fixes invalidations of ASTC textures on Astral
Chain. This happened because yuzu detected a cube texture and forced
6 faces, generating a texture larger than what the TIC reported.
This commit is contained in:
ReinUsesLisp 2020-03-12 17:28:49 -03:00
parent fce33adcf1
commit e22816a5bb

View File

@ -104,6 +104,11 @@ public:
if (!cache_addr) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
if (!IsTypeCompatible(tic.texture_type, entry)) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
const auto params{SurfaceParams::CreateForTexture(format_lookup_table, tic, entry)};
const auto [surface, view] = GetSurface(gpu_addr, cache_addr, params, true, false);
if (guard_samplers) {
@ -914,13 +919,15 @@ private:
params.width = 1;
params.height = 1;
params.depth = 1;
if (target == SurfaceTarget::TextureCubemap || target == SurfaceTarget::TextureCubeArray) {
params.depth = 6;
}
params.pitch = 4;
params.num_levels = 1;
params.emulated_levels = 1;
params.pixel_format = VideoCore::Surface::PixelFormat::RGBA16F;
params.pixel_format = VideoCore::Surface::PixelFormat::R8U;
params.type = VideoCore::Surface::SurfaceType::ColorTexture;
auto surface = CreateSurface(0ULL, params);
invalid_memory.clear();
invalid_memory.resize(surface->GetHostSizeInBytes(), 0U);
surface->UploadTexture(invalid_memory);
surface->MarkAsModified(false, Tick());
@ -1082,6 +1089,36 @@ private:
return siblings_table[static_cast<std::size_t>(format)];
}
/// Returns true the shader sampler entry is compatible with the TIC texture type.
static bool IsTypeCompatible(Tegra::Texture::TextureType tic_type,
const VideoCommon::Shader::Sampler& entry) {
const auto shader_type = entry.GetType();
switch (tic_type) {
case Tegra::Texture::TextureType::Texture1D:
case Tegra::Texture::TextureType::Texture1DArray:
return shader_type == Tegra::Shader::TextureType::Texture1D;
case Tegra::Texture::TextureType::Texture1DBuffer:
// TODO(Rodrigo): Assume as valid for now
return true;
case Tegra::Texture::TextureType::Texture2D:
case Tegra::Texture::TextureType::Texture2DNoMipmap:
return shader_type == Tegra::Shader::TextureType::Texture2D;
case Tegra::Texture::TextureType::Texture2DArray:
return shader_type == Tegra::Shader::TextureType::Texture2D ||
shader_type == Tegra::Shader::TextureType::TextureCube;
case Tegra::Texture::TextureType::Texture3D:
return shader_type == Tegra::Shader::TextureType::Texture3D;
case Tegra::Texture::TextureType::TextureCubeArray:
case Tegra::Texture::TextureType::TextureCubemap:
if (shader_type == Tegra::Shader::TextureType::TextureCube) {
return true;
}
return shader_type == Tegra::Shader::TextureType::Texture2D && entry.IsArray();
}
UNREACHABLE();
return true;
}
struct FramebufferTargetInfo {
TSurface target;
TView view;