texture_cache: Blacklist BGRA8 copies and views on OpenGL

In order to force the BGRA8 conversion on Nvidia using OpenGL, we need to forbid texture copies and views with other formats.

This commit also adds a boolean relating to this, as this needs to be done only for the OpenGL api, Vulkan must remain unchanged.
This commit is contained in:
ameerj 2021-03-04 14:12:25 -05:00
parent 0639244d85
commit 5213f70230
9 changed files with 80 additions and 28 deletions

View file

@ -48,6 +48,15 @@ constexpr std::array VIEW_CLASS_32_BITS{
PixelFormat::A2B10G10R10_UINT, PixelFormat::A2B10G10R10_UINT,
}; };
constexpr std::array VIEW_CLASS_32_BITS_NO_BGR{
PixelFormat::R16G16_FLOAT, PixelFormat::B10G11R11_FLOAT, PixelFormat::R32_FLOAT,
PixelFormat::A2B10G10R10_UNORM, PixelFormat::R16G16_UINT, PixelFormat::R32_UINT,
PixelFormat::R16G16_SINT, PixelFormat::R32_SINT, PixelFormat::A8B8G8R8_UNORM,
PixelFormat::R16G16_UNORM, PixelFormat::A8B8G8R8_SNORM, PixelFormat::R16G16_SNORM,
PixelFormat::A8B8G8R8_SRGB, PixelFormat::E5B9G9R9_FLOAT, PixelFormat::A8B8G8R8_UINT,
PixelFormat::A8B8G8R8_SINT, PixelFormat::A2B10G10R10_UINT,
};
// TODO: How should we handle 24 bits? // TODO: How should we handle 24 bits?
constexpr std::array VIEW_CLASS_16_BITS{ constexpr std::array VIEW_CLASS_16_BITS{
@ -205,7 +214,6 @@ constexpr Table MakeViewTable() {
EnableRange(view, VIEW_CLASS_128_BITS); EnableRange(view, VIEW_CLASS_128_BITS);
EnableRange(view, VIEW_CLASS_96_BITS); EnableRange(view, VIEW_CLASS_96_BITS);
EnableRange(view, VIEW_CLASS_64_BITS); EnableRange(view, VIEW_CLASS_64_BITS);
EnableRange(view, VIEW_CLASS_32_BITS);
EnableRange(view, VIEW_CLASS_16_BITS); EnableRange(view, VIEW_CLASS_16_BITS);
EnableRange(view, VIEW_CLASS_8_BITS); EnableRange(view, VIEW_CLASS_8_BITS);
EnableRange(view, VIEW_CLASS_RGTC1_RED); EnableRange(view, VIEW_CLASS_RGTC1_RED);
@ -231,20 +239,47 @@ constexpr Table MakeCopyTable() {
EnableRange(copy, COPY_CLASS_64_BITS); EnableRange(copy, COPY_CLASS_64_BITS);
return copy; return copy;
} }
constexpr Table MakeNativeBgrViewTable() {
Table copy = MakeViewTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
constexpr Table MakeNonNativeBgrViewTable() {
Table copy = MakeViewTable();
EnableRange(copy, VIEW_CLASS_32_BITS_NO_BGR);
return copy;
}
constexpr Table MakeNativeBgrCopyTable() {
Table copy = MakeCopyTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
constexpr Table MakeNonNativeBgrCopyTable() {
Table copy = MakeCopyTable();
EnableRange(copy, VIEW_CLASS_32_BITS);
return copy;
}
} // Anonymous namespace } // Anonymous namespace
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views) { bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
bool native_bgr) {
if (broken_views) { if (broken_views) {
// If format views are broken, only accept formats that are identical. // If format views are broken, only accept formats that are identical.
return format_a == format_b; return format_a == format_b;
} }
static constexpr Table TABLE = MakeViewTable(); static constexpr Table BGR_TABLE = MakeNativeBgrViewTable();
return IsSupported(TABLE, format_a, format_b); static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrViewTable();
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
} }
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b) { bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr) {
static constexpr Table TABLE = MakeCopyTable(); static constexpr Table BGR_TABLE = MakeNativeBgrCopyTable();
return IsSupported(TABLE, format_a, format_b); static constexpr Table NO_BGR_TABLE = MakeNonNativeBgrCopyTable();
return IsSupported(native_bgr ? BGR_TABLE : NO_BGR_TABLE, format_a, format_b);
} }
} // namespace VideoCore::Surface } // namespace VideoCore::Surface

View file

@ -8,8 +8,9 @@
namespace VideoCore::Surface { namespace VideoCore::Surface {
bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views); bool IsViewCompatible(PixelFormat format_a, PixelFormat format_b, bool broken_views,
bool native_bgr);
bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b); bool IsCopyCompatible(PixelFormat format_a, PixelFormat format_b, bool native_bgr);
} // namespace VideoCore::Surface } // namespace VideoCore::Surface

View file

@ -86,6 +86,11 @@ public:
FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const; FormatProperties FormatInfo(VideoCommon::ImageType type, GLenum internal_format) const;
bool HasNativeBgr() const noexcept {
// OpenGL does not have native support for the BGR internal format
return false;
}
bool HasBrokenTextureViewFormats() const noexcept { bool HasBrokenTextureViewFormats() const noexcept {
return has_broken_texture_view_formats; return has_broken_texture_view_formats;
} }

View file

@ -93,6 +93,11 @@ struct TextureCacheRuntime {
// No known Vulkan driver has broken image views // No known Vulkan driver has broken image views
return false; return false;
} }
bool HasNativeBgr() const noexcept {
// All known Vulkan drivers can natively handle BGR textures
return true;
}
}; };
class Image : public VideoCommon::ImageBase { class Image : public VideoCommon::ImageBase {

View file

@ -120,9 +120,10 @@ void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_i
if (lhs.info.type == ImageType::Linear) { if (lhs.info.type == ImageType::Linear) {
base = SubresourceBase{.level = 0, .layer = 0}; base = SubresourceBase{.level = 0, .layer = 0};
} else { } else {
// We are passing relaxed formats as an option, having broken views or not won't matter // We are passing relaxed formats as an option, having broken views/bgr or not won't matter
static constexpr bool broken_views = false; static constexpr bool broken_views = false;
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views); static constexpr bool native_bgr = true;
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
} }
if (!base) { if (!base) {
LOG_ERROR(HW_GPU, "Image alias should have been flipped"); LOG_ERROR(HW_GPU, "Image alias should have been flipped");

View file

@ -24,7 +24,7 @@ ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_i
.height = std::max(image_info.size.height >> range.base.level, 1u), .height = std::max(image_info.size.height >> range.base.level, 1u),
.depth = std::max(image_info.size.depth >> range.base.level, 1u), .depth = std::max(image_info.size.depth >> range.base.level, 1u),
} { } {
ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false), ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
"Image view format {} is incompatible with image format {}", info.format, "Image view format {} is incompatible with image format {}", info.format,
image_info.format); image_info.format);
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue(); const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();

View file

@ -876,6 +876,7 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
return ImageId{}; return ImageId{};
} }
const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr();
ImageId image_id; ImageId image_id;
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) { const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) { if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear) {
@ -885,11 +886,12 @@ ImageId TextureCache<P>::FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
if (existing_image.gpu_addr == gpu_addr && existing.type == info.type && if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
existing.pitch == info.pitch && existing.pitch == info.pitch &&
IsPitchLinearSameSize(existing, info, strict_size) && IsPitchLinearSameSize(existing, info, strict_size) &&
IsViewCompatible(existing.format, info.format, broken_views)) { IsViewCompatible(existing.format, info.format, broken_views, native_bgr)) {
image_id = existing_image_id; image_id = existing_image_id;
return true; return true;
} }
} else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views)) { } else if (IsSubresource(info, existing_image, gpu_addr, options, broken_views,
native_bgr)) {
image_id = existing_image_id; image_id = existing_image_id;
return true; return true;
} }
@ -920,6 +922,7 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
ImageInfo new_info = info; ImageInfo new_info = info;
const size_t size_bytes = CalculateGuestSizeInBytes(new_info); const size_t size_bytes = CalculateGuestSizeInBytes(new_info);
const bool broken_views = runtime.HasBrokenTextureViewFormats(); const bool broken_views = runtime.HasBrokenTextureViewFormats();
const bool native_bgr = runtime.HasNativeBgr();
std::vector<ImageId> overlap_ids; std::vector<ImageId> overlap_ids;
std::vector<ImageId> left_aliased_ids; std::vector<ImageId> left_aliased_ids;
std::vector<ImageId> right_aliased_ids; std::vector<ImageId> right_aliased_ids;
@ -935,8 +938,8 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
return; return;
} }
static constexpr bool strict_size = true; static constexpr bool strict_size = true;
const std::optional<OverlapResult> solution = const std::optional<OverlapResult> solution = ResolveOverlap(
ResolveOverlap(new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views); new_info, gpu_addr, cpu_addr, overlap, strict_size, broken_views, native_bgr);
if (solution) { if (solution) {
gpu_addr = solution->gpu_addr; gpu_addr = solution->gpu_addr;
cpu_addr = solution->cpu_addr; cpu_addr = solution->cpu_addr;
@ -946,10 +949,10 @@ ImageId TextureCache<P>::JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VA
} }
static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format; static constexpr auto options = RelaxedOptions::Size | RelaxedOptions::Format;
const ImageBase new_image_base(new_info, gpu_addr, cpu_addr); const ImageBase new_image_base(new_info, gpu_addr, cpu_addr);
if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views)) { if (IsSubresource(new_info, overlap, gpu_addr, options, broken_views, native_bgr)) {
left_aliased_ids.push_back(overlap_id); left_aliased_ids.push_back(overlap_id);
} else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options, } else if (IsSubresource(overlap.info, new_image_base, overlap.gpu_addr, options,
broken_views)) { broken_views, native_bgr)) {
right_aliased_ids.push_back(overlap_id); right_aliased_ids.push_back(overlap_id);
} }
}); });

View file

@ -1035,13 +1035,13 @@ bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool stri
std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr, std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, GPUVAddr gpu_addr,
VAddr cpu_addr, const ImageBase& overlap, VAddr cpu_addr, const ImageBase& overlap,
bool strict_size, bool broken_views) { bool strict_size, bool broken_views, bool native_bgr) {
ASSERT(new_info.type != ImageType::Linear); ASSERT(new_info.type != ImageType::Linear);
ASSERT(overlap.info.type != ImageType::Linear); ASSERT(overlap.info.type != ImageType::Linear);
if (!IsLayerStrideCompatible(new_info, overlap.info)) { if (!IsLayerStrideCompatible(new_info, overlap.info)) {
return std::nullopt; return std::nullopt;
} }
if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views)) { if (!IsViewCompatible(overlap.info.format, new_info.format, broken_views, native_bgr)) {
return std::nullopt; return std::nullopt;
} }
if (gpu_addr == overlap.gpu_addr) { if (gpu_addr == overlap.gpu_addr) {
@ -1085,14 +1085,14 @@ bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs) {
std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image, std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const ImageBase& image,
GPUVAddr candidate_addr, RelaxedOptions options, GPUVAddr candidate_addr, RelaxedOptions options,
bool broken_views) { bool broken_views, bool native_bgr) {
const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr); const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
if (!base) { if (!base) {
return std::nullopt; return std::nullopt;
} }
const ImageInfo& existing = image.info; const ImageInfo& existing = image.info;
if (False(options & RelaxedOptions::Format)) { if (False(options & RelaxedOptions::Format)) {
if (!IsViewCompatible(existing.format, candidate.format, broken_views)) { if (!IsViewCompatible(existing.format, candidate.format, broken_views, native_bgr)) {
return std::nullopt; return std::nullopt;
} }
} }
@ -1129,8 +1129,9 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
} }
bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr, bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr,
RelaxedOptions options, bool broken_views) { RelaxedOptions options, bool broken_views, bool native_bgr) {
return FindSubresource(candidate, image, candidate_addr, options, broken_views).has_value(); return FindSubresource(candidate, image, candidate_addr, options, broken_views, native_bgr)
.has_value();
} }
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,

View file

@ -87,7 +87,8 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info, [[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
GPUVAddr gpu_addr, VAddr cpu_addr, GPUVAddr gpu_addr, VAddr cpu_addr,
const ImageBase& overlap, const ImageBase& overlap,
bool strict_size, bool broken_views); bool strict_size, bool broken_views,
bool native_bgr);
[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs); [[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
@ -95,11 +96,11 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
const ImageBase& image, const ImageBase& image,
GPUVAddr candidate_addr, GPUVAddr candidate_addr,
RelaxedOptions options, RelaxedOptions options,
bool broken_views); bool broken_views, bool native_bgr);
[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, [[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
GPUVAddr candidate_addr, RelaxedOptions options, GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
bool broken_views); bool native_bgr);
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst, void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
const ImageBase* src); const ImageBase* src);