2019-04-24 21:35:54 +02:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
#include <algorithm>
|
2019-04-24 21:35:54 +02:00
|
|
|
#include <unordered_map>
|
2019-05-07 16:56:45 +02:00
|
|
|
#include <vector>
|
2019-04-24 21:35:54 +02:00
|
|
|
|
|
|
|
#include "common/assert.h"
|
2019-05-09 00:27:29 +02:00
|
|
|
#include "common/common_funcs.h"
|
2019-04-24 21:35:54 +02:00
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "video_core/gpu.h"
|
2019-05-07 16:56:45 +02:00
|
|
|
#include "video_core/morton.h"
|
|
|
|
#include "video_core/texture_cache/copy_params.h"
|
2019-04-24 21:35:54 +02:00
|
|
|
#include "video_core/texture_cache/surface_params.h"
|
|
|
|
#include "video_core/texture_cache/surface_view.h"
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
namespace Tegra {
|
|
|
|
class MemoryManager;
|
|
|
|
}
|
|
|
|
|
2019-04-24 21:35:54 +02:00
|
|
|
namespace VideoCommon {
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
using VideoCore::MortonSwizzleMode;
|
2019-05-07 19:58:37 +02:00
|
|
|
using VideoCore::Surface::SurfaceTarget;
|
|
|
|
|
|
|
|
enum class MatchStructureResult : u32 {
|
|
|
|
FullMatch = 0,
|
|
|
|
SemiMatch = 1,
|
|
|
|
None = 2,
|
|
|
|
};
|
2019-05-07 16:56:45 +02:00
|
|
|
|
2019-04-24 21:35:54 +02:00
|
|
|
class SurfaceBaseImpl {
|
|
|
|
public:
|
2019-05-07 16:56:45 +02:00
|
|
|
void LoadBuffer(Tegra::MemoryManager& memory_manager, std::vector<u8>& staging_buffer);
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 23:30:36 +02:00
|
|
|
void FlushBuffer(Tegra::MemoryManager& memory_manager, std::vector<u8>& staging_buffer);
|
2019-04-24 21:35:54 +02:00
|
|
|
|
|
|
|
GPUVAddr GetGpuAddr() const {
|
|
|
|
return gpu_addr;
|
|
|
|
}
|
|
|
|
|
2019-05-07 23:30:36 +02:00
|
|
|
bool Overlaps(const CacheAddr start, const CacheAddr end) const {
|
|
|
|
return (cache_addr < end) && (cache_addr_end > start);
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
2019-05-08 23:45:59 +02:00
|
|
|
bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) {
|
|
|
|
const GPUVAddr gpu_addr_end = gpu_addr + guest_memory_size;
|
|
|
|
return (gpu_addr <= other_start && other_end <= gpu_addr_end);
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
// Use only when recycling a surface
|
|
|
|
void SetGpuAddr(const GPUVAddr new_addr) {
|
|
|
|
gpu_addr = new_addr;
|
|
|
|
}
|
|
|
|
|
2019-04-24 21:35:54 +02:00
|
|
|
VAddr GetCpuAddr() const {
|
2019-05-07 23:30:36 +02:00
|
|
|
return cpu_addr;
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void SetCpuAddr(const VAddr new_addr) {
|
|
|
|
cpu_addr = new_addr;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 23:30:36 +02:00
|
|
|
CacheAddr GetCacheAddr() const {
|
|
|
|
return cache_addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CacheAddr GetCacheAddrEnd() const {
|
|
|
|
return cache_addr_end;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 23:30:36 +02:00
|
|
|
void SetCacheAddr(const CacheAddr new_addr) {
|
|
|
|
cache_addr = new_addr;
|
2019-05-08 02:48:02 +02:00
|
|
|
cache_addr_end = new_addr + guest_memory_size;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
const SurfaceParams& GetSurfaceParams() const {
|
|
|
|
return params;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
std::size_t GetSizeInBytes() const {
|
2019-05-08 02:48:02 +02:00
|
|
|
return guest_memory_size;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
std::size_t GetHostSizeInBytes() const {
|
|
|
|
return host_memory_size;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
std::size_t GetMipmapSize(const u32 level) const {
|
|
|
|
return mipmap_sizes[level];
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-13 02:33:52 +02:00
|
|
|
bool IsLinear() const {
|
|
|
|
return !params.is_tiled;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const {
|
|
|
|
return params.pixel_format == pixel_format;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool MatchTarget(VideoCore::Surface::SurfaceTarget target) const {
|
|
|
|
return params.target == target;
|
|
|
|
}
|
|
|
|
|
2019-05-08 23:45:59 +02:00
|
|
|
bool MatchesSubTexture(const SurfaceParams& rhs, const GPUVAddr other_gpu_addr) const {
|
|
|
|
return std::tie(gpu_addr, params.target, params.num_levels) ==
|
|
|
|
std::tie(other_gpu_addr, rhs.target, rhs.num_levels) &&
|
|
|
|
params.target == SurfaceTarget::Texture2D && params.num_levels == 1;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool MatchesTopology(const SurfaceParams& rhs) const {
|
2019-05-08 02:55:55 +02:00
|
|
|
const u32 src_bpp{params.GetBytesPerPixel()};
|
|
|
|
const u32 dst_bpp{rhs.GetBytesPerPixel()};
|
2019-05-07 16:56:45 +02:00
|
|
|
return std::tie(src_bpp, params.is_tiled) == std::tie(dst_bpp, rhs.is_tiled);
|
|
|
|
}
|
|
|
|
|
2019-05-07 19:58:37 +02:00
|
|
|
MatchStructureResult MatchesStructure(const SurfaceParams& rhs) const {
|
2019-05-08 02:55:55 +02:00
|
|
|
if (!params.is_tiled) {
|
2019-05-07 19:58:37 +02:00
|
|
|
if (std::tie(params.width, params.height, params.pitch) ==
|
|
|
|
std::tie(rhs.width, rhs.height, rhs.pitch)) {
|
|
|
|
return MatchStructureResult::FullMatch;
|
|
|
|
}
|
|
|
|
return MatchStructureResult::None;
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
2019-05-08 02:55:55 +02:00
|
|
|
// Tiled surface
|
2019-05-18 10:57:49 +02:00
|
|
|
if (std::tie(params.depth, params.block_width, params.block_height, params.block_depth,
|
|
|
|
params.tile_width_spacing, params.num_levels) ==
|
|
|
|
std::tie(rhs.depth, rhs.block_width, rhs.block_height, rhs.block_depth,
|
2019-05-08 23:45:59 +02:00
|
|
|
rhs.tile_width_spacing, rhs.num_levels)) {
|
2019-05-18 10:57:49 +02:00
|
|
|
if (std::tie(params.width, params.height) == std::tie(rhs.width, rhs.height)) {
|
2019-05-08 02:55:55 +02:00
|
|
|
return MatchStructureResult::FullMatch;
|
|
|
|
}
|
2019-05-18 10:57:49 +02:00
|
|
|
const u32 ws = SurfaceParams::ConvertWidth(rhs.GetBlockAlignedWidth(),
|
|
|
|
params.pixel_format, rhs.pixel_format);
|
|
|
|
const u32 hs =
|
|
|
|
SurfaceParams::ConvertHeight(rhs.height, params.pixel_format, rhs.pixel_format);
|
|
|
|
const u32 w1 = params.GetBlockAlignedWidth();
|
|
|
|
if (std::tie(w1, params.height) == std::tie(ws, hs)) {
|
2019-05-08 02:55:55 +02:00
|
|
|
return MatchStructureResult::SemiMatch;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return MatchStructureResult::None;
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::pair<u32, u32>> GetLayerMipmap(const GPUVAddr candidate_gpu_addr) const {
|
2019-05-08 02:48:02 +02:00
|
|
|
if (candidate_gpu_addr < gpu_addr) {
|
2019-05-07 16:56:45 +02:00
|
|
|
return {};
|
2019-05-08 02:48:02 +02:00
|
|
|
}
|
|
|
|
const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
|
|
|
|
const auto layer{static_cast<u32>(relative_address / layer_size)};
|
2019-05-07 16:56:45 +02:00
|
|
|
const GPUVAddr mipmap_address = relative_address - layer_size * layer;
|
2019-05-07 19:58:37 +02:00
|
|
|
const auto mipmap_it =
|
2019-05-09 00:27:29 +02:00
|
|
|
Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
|
2019-05-08 02:48:02 +02:00
|
|
|
if (mipmap_it == mipmap_offsets.end()) {
|
|
|
|
return {};
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
2019-05-08 02:48:02 +02:00
|
|
|
const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))};
|
|
|
|
return std::make_pair(layer, level);
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 19:58:37 +02:00
|
|
|
std::vector<CopyParams> BreakDown(const SurfaceParams& in_params) const {
|
2019-05-08 02:55:55 +02:00
|
|
|
return params.is_layered ? BreakDownLayered(in_params) : BreakDownNonLayered(in_params);
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2019-05-08 02:48:02 +02:00
|
|
|
explicit SurfaceBaseImpl(GPUVAddr gpu_addr, const SurfaceParams& params);
|
2019-05-07 16:56:45 +02:00
|
|
|
~SurfaceBaseImpl() = default;
|
2019-04-24 21:35:54 +02:00
|
|
|
|
|
|
|
virtual void DecorateSurfaceName() = 0;
|
|
|
|
|
|
|
|
const SurfaceParams params;
|
2019-05-09 00:27:29 +02:00
|
|
|
std::size_t layer_size;
|
|
|
|
std::size_t guest_memory_size;
|
2019-05-07 16:56:45 +02:00
|
|
|
const std::size_t host_memory_size;
|
2019-05-08 02:48:02 +02:00
|
|
|
GPUVAddr gpu_addr{};
|
|
|
|
CacheAddr cache_addr{};
|
2019-05-07 23:30:36 +02:00
|
|
|
CacheAddr cache_addr_end{};
|
2019-05-08 02:48:02 +02:00
|
|
|
VAddr cpu_addr{};
|
|
|
|
|
|
|
|
std::vector<std::size_t> mipmap_sizes;
|
|
|
|
std::vector<std::size_t> mipmap_offsets;
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
private:
|
|
|
|
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
|
|
|
|
u32 level);
|
2019-05-08 02:55:55 +02:00
|
|
|
|
|
|
|
std::vector<CopyParams> BreakDownLayered(const SurfaceParams& in_params) const {
|
|
|
|
const u32 layers{params.depth};
|
|
|
|
const u32 mipmaps{params.num_levels};
|
|
|
|
std::vector<CopyParams> result;
|
|
|
|
result.reserve(static_cast<std::size_t>(layers) * static_cast<std::size_t>(mipmaps));
|
|
|
|
|
|
|
|
for (u32 layer = 0; layer < layers; layer++) {
|
|
|
|
for (u32 level = 0; level < mipmaps; level++) {
|
|
|
|
const u32 width{std::min(params.GetMipWidth(level), in_params.GetMipWidth(level))};
|
|
|
|
const u32 height{
|
|
|
|
std::min(params.GetMipHeight(level), in_params.GetMipHeight(level))};
|
|
|
|
result.emplace_back(width, height, layer, level);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<CopyParams> BreakDownNonLayered(const SurfaceParams& in_params) const {
|
|
|
|
const u32 mipmaps{params.num_levels};
|
|
|
|
std::vector<CopyParams> result;
|
|
|
|
result.reserve(mipmaps);
|
|
|
|
|
|
|
|
for (u32 level = 0; level < mipmaps; level++) {
|
|
|
|
const u32 width{std::min(params.GetMipWidth(level), in_params.GetMipWidth(level))};
|
|
|
|
const u32 height{std::min(params.GetMipHeight(level), in_params.GetMipHeight(level))};
|
|
|
|
const u32 depth{std::min(params.GetMipDepth(level), in_params.GetMipDepth(level))};
|
|
|
|
result.emplace_back(width, height, depth, level);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
2019-04-24 21:35:54 +02:00
|
|
|
};
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
template <typename TView>
|
2019-04-24 21:35:54 +02:00
|
|
|
class SurfaceBase : public SurfaceBaseImpl {
|
|
|
|
public:
|
2019-05-07 16:56:45 +02:00
|
|
|
virtual void UploadTexture(std::vector<u8>& staging_buffer) = 0;
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0;
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
void MarkAsModified(const bool is_modified_, const u64 tick) {
|
2019-05-11 05:42:08 +02:00
|
|
|
is_modified = is_modified_ || is_target;
|
2019-05-07 16:56:45 +02:00
|
|
|
modification_tick = tick;
|
|
|
|
}
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-11 05:42:08 +02:00
|
|
|
void MarkAsRenderTarget(const bool is_target) {
|
|
|
|
this->is_target = is_target;
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
void MarkAsPicked(const bool is_picked) {
|
|
|
|
this->is_picked = is_picked;
|
|
|
|
}
|
2019-04-24 21:35:54 +02:00
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool IsModified() const {
|
|
|
|
return is_modified;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool IsProtected() const {
|
2019-05-11 05:42:08 +02:00
|
|
|
// Only 3D Slices are to be protected
|
|
|
|
return is_target && params.block_depth > 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsRenderTarget() const {
|
|
|
|
return is_target;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool IsRegistered() const {
|
|
|
|
return is_registered;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
bool IsPicked() const {
|
|
|
|
return is_picked;
|
|
|
|
}
|
|
|
|
|
|
|
|
void MarkAsRegistered(bool is_reg) {
|
|
|
|
is_registered = is_reg;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
u64 GetModificationTick() const {
|
|
|
|
return modification_tick;
|
|
|
|
}
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
TView EmplaceOverview(const SurfaceParams& overview_params) {
|
2019-05-08 08:51:54 +02:00
|
|
|
const u32 num_layers{(params.is_layered && !overview_params.is_layered) ? 1 : params.depth};
|
|
|
|
return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels));
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
2019-05-09 00:27:29 +02:00
|
|
|
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr,
|
|
|
|
const std::size_t candidate_size) {
|
|
|
|
if (params.target == SurfaceTarget::Texture3D ||
|
|
|
|
(params.num_levels == 1 && !params.is_layered) ||
|
|
|
|
view_params.target == SurfaceTarget::Texture3D) {
|
2019-05-07 16:56:45 +02:00
|
|
|
return {};
|
|
|
|
}
|
2019-05-08 03:03:33 +02:00
|
|
|
const auto layer_mipmap{GetLayerMipmap(view_addr)};
|
2019-05-07 16:56:45 +02:00
|
|
|
if (!layer_mipmap) {
|
|
|
|
return {};
|
|
|
|
}
|
2019-05-08 03:03:33 +02:00
|
|
|
const u32 layer{layer_mipmap->first};
|
|
|
|
const u32 mipmap{layer_mipmap->second};
|
2019-05-09 00:27:29 +02:00
|
|
|
if (GetMipmapSize(mipmap) != candidate_size) {
|
2019-05-08 03:03:33 +02:00
|
|
|
// TODO: The view may cover many mimaps, this case can still go on.
|
|
|
|
// This edge-case can be safely be ignored since it will just result in worse
|
|
|
|
// performance.
|
2019-05-07 16:56:45 +02:00
|
|
|
return {};
|
|
|
|
}
|
2019-05-08 13:09:02 +02:00
|
|
|
return GetView(ViewParams(view_params.target, layer, 1, mipmap, 1));
|
2019-05-07 16:56:45 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
TView GetMainView() const {
|
|
|
|
return main_view;
|
|
|
|
}
|
|
|
|
|
2019-04-24 21:35:54 +02:00
|
|
|
protected:
|
2019-05-07 16:56:45 +02:00
|
|
|
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params)
|
|
|
|
: SurfaceBaseImpl(gpu_addr, params) {}
|
2019-04-24 21:35:54 +02:00
|
|
|
|
|
|
|
~SurfaceBase() = default;
|
|
|
|
|
2019-05-07 16:56:45 +02:00
|
|
|
virtual TView CreateView(const ViewParams& view_key) = 0;
|
|
|
|
|
|
|
|
std::unordered_map<ViewParams, TView> views;
|
|
|
|
TView main_view;
|
2019-04-24 21:35:54 +02:00
|
|
|
|
|
|
|
private:
|
2019-05-07 16:56:45 +02:00
|
|
|
TView GetView(const ViewParams& key) {
|
2019-04-24 21:35:54 +02:00
|
|
|
const auto [entry, is_cache_miss] = views.try_emplace(key);
|
|
|
|
auto& view{entry->second};
|
|
|
|
if (is_cache_miss) {
|
|
|
|
view = CreateView(key);
|
|
|
|
}
|
2019-05-07 16:56:45 +02:00
|
|
|
return view;
|
2019-04-24 21:35:54 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool is_modified{};
|
2019-05-11 05:42:08 +02:00
|
|
|
bool is_target{};
|
2019-05-07 16:56:45 +02:00
|
|
|
bool is_registered{};
|
|
|
|
bool is_picked{};
|
2019-04-24 21:35:54 +02:00
|
|
|
u64 modification_tick{};
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace VideoCommon
|