Engines: Implement Accelerate DMA Texture.

This commit is contained in:
Fernando Sahmkow 2022-03-27 05:05:57 +02:00
parent ce8f4da638
commit 8a3411b417
15 changed files with 656 additions and 95 deletions

View file

@ -216,10 +216,51 @@ ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
.height = config.height,
.depth = 1,
};
rescaleable = block.depth == 0;
rescaleable &= size.height > 256;
rescaleable = block.depth == 0 && size.height > 256;
downscaleable = size.height > 512;
}
}
static PixelFormat ByteSizeToFormat(u32 bytes_per_pixel) {
switch (bytes_per_pixel) {
case 1:
return PixelFormat::R8_UINT;
case 2:
return PixelFormat::R8G8_UINT;
case 4:
return PixelFormat::A8B8G8R8_UINT;
case 8:
return PixelFormat::R16G16B16A16_UINT;
case 16:
return PixelFormat::R32G32B32A32_UINT;
default:
UNIMPLEMENTED();
return PixelFormat::Invalid;
}
}
ImageInfo::ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept {
const u32 bytes_per_pixel = config.bytes_per_pixel;
format = ByteSizeToFormat(bytes_per_pixel);
type = config.params.block_size.depth > 0 ? ImageType::e3D : ImageType::e2D;
num_samples = 1;
block = Extent3D{
.width = config.params.block_size.width,
.height = config.params.block_size.height,
.depth = config.params.block_size.depth,
};
size = Extent3D{
.width = config.params.width,
.height = config.params.height,
.depth = config.params.depth,
};
tile_width_spacing = 0;
resources.levels = 1;
resources.layers = 1;
layer_stride = CalculateLayerStride(*this);
maybe_unaligned_layer_stride = CalculateLayerSize(*this);
rescaleable = block.depth == 0 && size.height > 256;
downscaleable = size.height > 512;
}
} // namespace VideoCommon

View file

@ -5,6 +5,7 @@
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"
@ -19,6 +20,7 @@ struct ImageInfo {
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept;
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept;
explicit ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept;
explicit ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept;
PixelFormat format = PixelFormat::Invalid;
ImageType type = ImageType::e1D;

View file

@ -1358,6 +1358,75 @@ std::optional<typename TextureCache<P>::BlitImages> TextureCache<P>::GetBlitImag
}};
}
template <class P>
ImageId TextureCache<P>::FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr) {
std::optional<VAddr> cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
cpu_addr = gpu_memory->GpuToCpuAddress(gpu_addr, CalculateGuestSizeInBytes(info));
if (!cpu_addr) {
return ImageId{};
}
}
ImageId image_id{};
boost::container::small_vector<ImageId, 1> image_ids;
const auto lambda = [&](ImageId existing_image_id, ImageBase& existing_image) {
if (True(existing_image.flags & ImageFlagBits::Remapped)) {
return false;
}
if (info.type == ImageType::Linear || existing_image.info.type == ImageType::Linear)
[[unlikely]] {
const bool strict_size = True(existing_image.flags & ImageFlagBits::Strong);
const ImageInfo& existing = existing_image.info;
if (existing_image.gpu_addr == gpu_addr && existing.type == info.type &&
existing.pitch == info.pitch &&
IsPitchLinearSameSize(existing, info, strict_size) &&
IsViewCompatible(existing.format, info.format, false, true)) {
image_id = existing_image_id;
image_ids.push_back(existing_image_id);
return true;
}
} else if (IsSubCopy(info, existing_image, gpu_addr)) {
image_id = existing_image_id;
image_ids.push_back(existing_image_id);
return true;
}
return false;
};
ForEachImageInRegion(*cpu_addr, CalculateGuestSizeInBytes(info), lambda);
if (image_ids.size() <= 1) [[likely]] {
return image_id;
}
auto image_ids_compare = [this](ImageId a, ImageId b) {
auto& image_a = slot_images[a];
auto& image_b = slot_images[b];
return image_a.modification_tick < image_b.modification_tick;
};
return *std::ranges::max_element(image_ids, image_ids_compare);
}
template <class P>
std::optional<std::pair<typename TextureCache<P>::Image*, std::pair<u32, u32>>>
TextureCache<P>::ObtainImage(const Tegra::DMA::ImageOperand& operand, bool mark_as_modified) {
ImageInfo dst_info(operand);
ImageId dst_id = FindDMAImage(dst_info, operand.address);
if (!dst_id) {
return std::nullopt;
}
auto& image = slot_images[dst_id];
auto base = image.TryFindBase(operand.address);
if (!base) {
return std::nullopt;
}
if (False(image.flags & ImageFlagBits::GpuModified)) {
// No need to waste time on an image that's synced with guest
return std::nullopt;
}
PrepareImage(dst_id, mark_as_modified, false);
auto& new_image = slot_images[dst_id];
lru_cache.Touch(new_image.lru_index, frame_tick);
return std::make_pair(&new_image, std::make_pair(base->level, base->layer));
}
template <class P>
SamplerId TextureCache<P>::FindSampler(const TSCEntry& config) {
if (std::ranges::all_of(config.raw, [](u64 value) { return value == 0; })) {

View file

@ -209,6 +209,9 @@ public:
/// Pop asynchronous downloads
void PopAsyncFlushes();
[[nodiscard]] std::optional<std::pair<Image*, std::pair<u32, u32>>> ObtainImage(
const Tegra::DMA::ImageOperand& operand, bool mark_as_modified);
/// Return true when a CPU region is modified from the GPU
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
@ -300,6 +303,8 @@ private:
/// Remove joined images from the cache
[[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
[[nodiscard]] ImageId FindDMAImage(const ImageInfo& info, GPUVAddr gpu_addr);
/// Return a blit image pair from the given guest blit parameters
[[nodiscard]] std::optional<BlitImages> GetBlitImages(
const Tegra::Engines::Fermi2D::Surface& dst, const Tegra::Engines::Fermi2D::Surface& src,

View file

@ -54,6 +54,7 @@ enum class RelaxedOptions : u32 {
Format = 1 << 1,
Samples = 1 << 2,
ForceBrokenViews = 1 << 3,
FormatBpp = 1 << 4,
};
DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions)

View file

@ -743,6 +743,44 @@ std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst, const ImageIn
return copies;
}
std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src, u32 up_scale,
u32 down_shift) {
std::vector<ImageCopy> copies;
copies.reserve(src.resources.levels);
const bool is_3d = src.type == ImageType::e3D;
for (s32 level = 0; level < src.resources.levels; ++level) {
ImageCopy& copy = copies.emplace_back();
copy.src_subresource = SubresourceLayers{
.base_level = level,
.base_layer = 0,
.num_layers = src.resources.layers,
};
copy.dst_subresource = SubresourceLayers{
.base_level = level,
.base_layer = 0,
.num_layers = src.resources.layers,
};
copy.src_offset = Offset3D{
.x = 0,
.y = 0,
.z = 0,
};
copy.dst_offset = Offset3D{
.x = 0,
.y = 0,
.z = 0,
};
const Extent3D mip_size = AdjustMipSize(src.size, level);
copy.extent = AdjustSamplesSize(mip_size, src.num_samples);
if (is_3d) {
copy.extent.depth = src.size.depth;
}
copy.extent.width = std::max<u32>((copy.extent.width * up_scale) >> down_shift, 1);
copy.extent.height = std::max<u32>((copy.extent.height * up_scale) >> down_shift, 1);
}
return copies;
}
bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config) {
const GPUVAddr address = config.Address();
if (address == 0) {
@ -999,6 +1037,20 @@ bool IsBlockLinearSizeCompatible(const ImageInfo& lhs, const ImageInfo& rhs, u32
}
}
bool IsBlockLinearSizeCompatibleBPPRelaxed(const ImageInfo& lhs, const ImageInfo& rhs,
u32 lhs_level, u32 rhs_level) noexcept {
ASSERT(lhs.type != ImageType::Linear);
ASSERT(rhs.type != ImageType::Linear);
const auto lhs_bpp = BytesPerBlock(lhs.format);
const auto rhs_bpp = BytesPerBlock(rhs.format);
const Extent3D lhs_size = AdjustMipSize(lhs.size, lhs_level);
const Extent3D rhs_size = AdjustMipSize(rhs.size, rhs_level);
return Common::AlignUpLog2(lhs_size.width * lhs_bpp, GOB_SIZE_X_SHIFT) ==
Common::AlignUpLog2(rhs_size.width * rhs_bpp, GOB_SIZE_X_SHIFT) &&
Common::AlignUpLog2(lhs_size.height, GOB_SIZE_Y_SHIFT) ==
Common::AlignUpLog2(rhs_size.height, GOB_SIZE_Y_SHIFT);
}
bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs, bool strict_size) noexcept {
ASSERT(lhs.type == ImageType::Linear);
ASSERT(rhs.type == ImageType::Linear);
@ -1073,7 +1125,8 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
// Format checking is relaxed, but we still have to check for matching bytes per block.
// This avoids creating a view for blits on UE4 titles where formats with different bytes
// per block are aliased.
if (BytesPerBlock(existing.format) != BytesPerBlock(candidate.format)) {
if (BytesPerBlock(existing.format) != BytesPerBlock(candidate.format) &&
False(options & RelaxedOptions::FormatBpp)) {
return std::nullopt;
}
} else {
@ -1088,10 +1141,8 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
if (existing.type != candidate.type) {
return std::nullopt;
}
if (False(options & RelaxedOptions::Samples)) {
if (existing.num_samples != candidate.num_samples) {
return std::nullopt;
}
if (False(options & RelaxedOptions::Samples) && existing.num_samples != candidate.num_samples) {
return std::nullopt;
}
if (existing.resources.levels < candidate.resources.levels + base->level) {
return std::nullopt;
@ -1101,14 +1152,16 @@ std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate, const
if (mip_depth < candidate.size.depth + base->layer) {
return std::nullopt;
}
} else {
if (existing.resources.layers < candidate.resources.layers + base->layer) {
return std::nullopt;
}
} else if (existing.resources.layers < candidate.resources.layers + base->layer) {
return std::nullopt;
}
const bool strict_size = False(options & RelaxedOptions::Size);
if (!IsBlockLinearSizeCompatible(existing, candidate, base->level, 0, strict_size)) {
return std::nullopt;
if (False(options & RelaxedOptions::FormatBpp)) {
return std::nullopt;
} else if (!IsBlockLinearSizeCompatibleBPPRelaxed(existing, candidate, base->level, 0)) {
return std::nullopt;
}
}
// TODO: compare block sizes
return base;
@ -1120,6 +1173,31 @@ bool IsSubresource(const ImageInfo& candidate, const ImageBase& image, GPUVAddr
.has_value();
}
bool IsSubCopy(const ImageInfo& candidate, const ImageBase& image, GPUVAddr candidate_addr) {
const std::optional<SubresourceBase> base = image.TryFindBase(candidate_addr);
if (!base) {
return false;
}
const ImageInfo& existing = image.info;
if (existing.resources.levels < candidate.resources.levels + base->level) {
return false;
}
if (existing.type == ImageType::e3D) {
const u32 mip_depth = std::max(1U, existing.size.depth << base->level);
if (mip_depth < candidate.size.depth + base->layer) {
return false;
}
} else {
if (existing.resources.layers < candidate.resources.layers + base->layer) {
return false;
}
}
if (!IsBlockLinearSizeCompatibleBPPRelaxed(existing, candidate, base->level, 0)) {
return false;
}
return true;
}
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
const ImageBase* src) {
const auto original_dst_format = dst_info.format;

View file

@ -56,6 +56,10 @@ struct OverlapResult {
SubresourceBase base, u32 up_scale = 1,
u32 down_shift = 0);
[[nodiscard]] std::vector<ImageCopy> MakeReinterpretImageCopies(const ImageInfo& src,
u32 up_scale = 1,
u32 down_shift = 0);
[[nodiscard]] bool IsValidEntry(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
@ -88,6 +92,9 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
[[nodiscard]] bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs,
bool strict_size) noexcept;
[[nodiscard]] bool IsBlockLinearSizeCompatibleBPPRelaxed(const ImageInfo& lhs, const ImageInfo& rhs,
u32 lhs_level, u32 rhs_level) noexcept;
[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
GPUVAddr gpu_addr, VAddr cpu_addr,
const ImageBase& overlap,
@ -106,6 +113,9 @@ void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const Ima
GPUVAddr candidate_addr, RelaxedOptions options, bool broken_views,
bool native_bgr);
[[nodiscard]] bool IsSubCopy(const ImageInfo& candidate, const ImageBase& image,
GPUVAddr candidate_addr);
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
const ImageBase* src);