texture_cache: Split texture cache into different files
This commit is contained in:
parent
5f3aacdc37
commit
1b4503c571
|
@ -109,6 +109,13 @@ add_library(video_core STATIC
|
||||||
shader/track.cpp
|
shader/track.cpp
|
||||||
surface.cpp
|
surface.cpp
|
||||||
surface.h
|
surface.h
|
||||||
|
texture_cache/surface_base.cpp
|
||||||
|
texture_cache/surface_base.h
|
||||||
|
texture_cache/surface_params.cpp
|
||||||
|
texture_cache/surface_params.h
|
||||||
|
texture_cache/surface_view.cpp
|
||||||
|
texture_cache/surface_view.h
|
||||||
|
texture_cache/texture_cache.h
|
||||||
textures/astc.cpp
|
textures/astc.cpp
|
||||||
textures/astc.h
|
textures/astc.h
|
||||||
textures/convert.cpp
|
textures/convert.cpp
|
||||||
|
@ -116,8 +123,6 @@ add_library(video_core STATIC
|
||||||
textures/decoders.cpp
|
textures/decoders.cpp
|
||||||
textures/decoders.h
|
textures/decoders.h
|
||||||
textures/texture.h
|
textures/texture.h
|
||||||
texture_cache.cpp
|
|
||||||
texture_cache.h
|
|
||||||
video_core.cpp
|
video_core.cpp
|
||||||
video_core.h
|
video_core.h
|
||||||
)
|
)
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
#include "video_core/renderer_opengl/gl_texture_cache.h"
|
||||||
#include "video_core/renderer_opengl/utils.h"
|
#include "video_core/renderer_opengl/utils.h"
|
||||||
#include "video_core/texture_cache.h"
|
#include "video_core/texture_cache/texture_cache_contextless.h"
|
||||||
#include "video_core/textures/convert.h"
|
#include "video_core/textures/convert.h"
|
||||||
#include "video_core/textures/texture.h"
|
#include "video_core/textures/texture.h"
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
|
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/engines/shader_bytecode.h"
|
#include "video_core/engines/shader_bytecode.h"
|
||||||
#include "video_core/texture_cache.h"
|
#include "video_core/texture_cache/texture_cache_contextless.h"
|
||||||
|
|
||||||
namespace OpenGL {
|
namespace OpenGL {
|
||||||
|
|
||||||
|
|
|
@ -1,750 +0,0 @@
|
||||||
// Copyright 2019 yuzu Emulator Project
|
|
||||||
// Licensed under GPLv2 or any later version
|
|
||||||
// Refer to the license.txt file included.
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <set>
|
|
||||||
#include <tuple>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <unordered_map>
|
|
||||||
|
|
||||||
#include <boost/icl/interval_map.hpp>
|
|
||||||
#include <boost/range/iterator_range.hpp>
|
|
||||||
|
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/common_types.h"
|
|
||||||
#include "core/memory.h"
|
|
||||||
#include "video_core/engines/fermi_2d.h"
|
|
||||||
#include "video_core/engines/maxwell_3d.h"
|
|
||||||
#include "video_core/gpu.h"
|
|
||||||
#include "video_core/memory_manager.h"
|
|
||||||
#include "video_core/rasterizer_interface.h"
|
|
||||||
#include "video_core/surface.h"
|
|
||||||
|
|
||||||
namespace Core {
|
|
||||||
class System;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Tegra::Texture {
|
|
||||||
struct FullTextureInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCore {
|
|
||||||
class RasterizerInterface;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
class HasheableSurfaceParams {
|
|
||||||
public:
|
|
||||||
std::size_t Hash() const;
|
|
||||||
|
|
||||||
bool operator==(const HasheableSurfaceParams& rhs) const;
|
|
||||||
|
|
||||||
bool operator!=(const HasheableSurfaceParams& rhs) const {
|
|
||||||
return !operator==(rhs);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
// Avoid creation outside of a managed environment.
|
|
||||||
HasheableSurfaceParams() = default;
|
|
||||||
|
|
||||||
bool is_tiled;
|
|
||||||
bool srgb_conversion;
|
|
||||||
u32 block_width;
|
|
||||||
u32 block_height;
|
|
||||||
u32 block_depth;
|
|
||||||
u32 tile_width_spacing;
|
|
||||||
u32 width;
|
|
||||||
u32 height;
|
|
||||||
u32 depth;
|
|
||||||
u32 pitch;
|
|
||||||
u32 unaligned_height;
|
|
||||||
u32 num_levels;
|
|
||||||
VideoCore::Surface::PixelFormat pixel_format;
|
|
||||||
VideoCore::Surface::ComponentType component_type;
|
|
||||||
VideoCore::Surface::SurfaceType type;
|
|
||||||
VideoCore::Surface::SurfaceTarget target;
|
|
||||||
};
|
|
||||||
|
|
||||||
class SurfaceParams final : public HasheableSurfaceParams {
|
|
||||||
public:
|
|
||||||
/// Creates SurfaceCachedParams from a texture configuration.
|
|
||||||
static SurfaceParams CreateForTexture(Core::System& system,
|
|
||||||
const Tegra::Texture::FullTextureInfo& config);
|
|
||||||
|
|
||||||
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
|
||||||
static SurfaceParams CreateForDepthBuffer(
|
|
||||||
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
|
||||||
u32 block_width, u32 block_height, u32 block_depth,
|
|
||||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
|
|
||||||
|
|
||||||
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
|
||||||
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
|
|
||||||
|
|
||||||
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
|
|
||||||
static SurfaceParams CreateForFermiCopySurface(
|
|
||||||
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
|
||||||
|
|
||||||
bool IsTiled() const {
|
|
||||||
return is_tiled;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetSrgbConversion() const {
|
|
||||||
return srgb_conversion;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetBlockWidth() const {
|
|
||||||
return block_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetTileWidthSpacing() const {
|
|
||||||
return tile_width_spacing;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetWidth() const {
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetHeight() const {
|
|
||||||
return height;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetDepth() const {
|
|
||||||
return depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetPitch() const {
|
|
||||||
return pitch;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetNumLevels() const {
|
|
||||||
return num_levels;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCore::Surface::PixelFormat GetPixelFormat() const {
|
|
||||||
return pixel_format;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCore::Surface::ComponentType GetComponentType() const {
|
|
||||||
return component_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCore::Surface::SurfaceTarget GetTarget() const {
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCore::Surface::SurfaceType GetType() const {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t GetGuestSizeInBytes() const {
|
|
||||||
return guest_size_in_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t GetHostSizeInBytes() const {
|
|
||||||
return host_size_in_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
u32 GetNumLayers() const {
|
|
||||||
return num_layers;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the width of a given mipmap level.
|
|
||||||
u32 GetMipWidth(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the height of a given mipmap level.
|
|
||||||
u32 GetMipHeight(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the depth of a given mipmap level.
|
|
||||||
u32 GetMipDepth(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns true if these parameters are from a layered surface.
|
|
||||||
bool IsLayered() const;
|
|
||||||
|
|
||||||
/// Returns the block height of a given mipmap level.
|
|
||||||
u32 GetMipBlockHeight(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the block depth of a given mipmap level.
|
|
||||||
u32 GetMipBlockDepth(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the offset in bytes in guest memory of a given mipmap level.
|
|
||||||
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
|
|
||||||
std::size_t GetHostMipmapLevelOffset(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the size in bytes in host memory (linear) of a given mipmap level.
|
|
||||||
std::size_t GetHostMipmapSize(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the size of a layer in bytes in guest memory.
|
|
||||||
std::size_t GetGuestLayerSize() const;
|
|
||||||
|
|
||||||
/// Returns the size of a layer in bytes in host memory for a given mipmap level.
|
|
||||||
std::size_t GetHostLayerSize(u32 level) const;
|
|
||||||
|
|
||||||
/// Returns the default block width.
|
|
||||||
u32 GetDefaultBlockWidth() const;
|
|
||||||
|
|
||||||
/// Returns the default block height.
|
|
||||||
u32 GetDefaultBlockHeight() const;
|
|
||||||
|
|
||||||
/// Returns the bits per pixel.
|
|
||||||
u32 GetBitsPerPixel() const;
|
|
||||||
|
|
||||||
/// Returns the bytes per pixel.
|
|
||||||
u32 GetBytesPerPixel() const;
|
|
||||||
|
|
||||||
/// Returns true if another surface can be familiar with this. This is a loosely defined term
|
|
||||||
/// that reflects the possibility of these two surface parameters potentially being part of a
|
|
||||||
/// bigger superset.
|
|
||||||
bool IsFamiliar(const SurfaceParams& view_params) const;
|
|
||||||
|
|
||||||
/// Returns true if the pixel format is a depth and/or stencil format.
|
|
||||||
bool IsPixelFormatZeta() const;
|
|
||||||
|
|
||||||
/// Creates a map that redirects an address difference to a layer and mipmap level.
|
|
||||||
std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
|
|
||||||
|
|
||||||
/// Returns true if the passed surface view parameters is equal or a valid subset of this.
|
|
||||||
bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
/// Calculates values that can be deduced from HasheableSurfaceParams.
|
|
||||||
void CalculateCachedValues();
|
|
||||||
|
|
||||||
/// Returns the size of a given mipmap level inside a layer.
|
|
||||||
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const;
|
|
||||||
|
|
||||||
/// Returns the size of all mipmap levels and aligns as needed.
|
|
||||||
std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
|
|
||||||
|
|
||||||
/// Returns the size of a layer
|
|
||||||
std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const;
|
|
||||||
|
|
||||||
/// Returns true if the passed view width and height match the size of this params in a given
|
|
||||||
/// mipmap level.
|
|
||||||
bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
|
|
||||||
|
|
||||||
/// Returns true if the passed view depth match the size of this params in a given mipmap level.
|
|
||||||
bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
|
|
||||||
|
|
||||||
/// Returns true if the passed view layers and mipmap levels are in bounds.
|
|
||||||
bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
|
||||||
|
|
||||||
std::size_t guest_size_in_bytes;
|
|
||||||
std::size_t host_size_in_bytes;
|
|
||||||
u32 num_layers;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ViewKey {
|
|
||||||
std::size_t Hash() const;
|
|
||||||
|
|
||||||
bool operator==(const ViewKey& rhs) const;
|
|
||||||
|
|
||||||
u32 base_layer{};
|
|
||||||
u32 num_layers{};
|
|
||||||
u32 base_level{};
|
|
||||||
u32 num_levels{};
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
||||||
|
|
||||||
namespace std {
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<VideoCommon::SurfaceParams> {
|
|
||||||
std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
|
|
||||||
return k.Hash();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <>
|
|
||||||
struct hash<VideoCommon::ViewKey> {
|
|
||||||
std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
|
|
||||||
return k.Hash();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace std
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
|
||||||
|
|
||||||
class SurfaceBaseImpl {
|
|
||||||
public:
|
|
||||||
void LoadBuffer();
|
|
||||||
|
|
||||||
void FlushBuffer();
|
|
||||||
|
|
||||||
GPUVAddr GetGpuAddr() const {
|
|
||||||
ASSERT(is_registered);
|
|
||||||
return gpu_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
VAddr GetCpuAddr() const {
|
|
||||||
ASSERT(is_registered);
|
|
||||||
return cpu_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* GetHostPtr() const {
|
|
||||||
ASSERT(is_registered);
|
|
||||||
return host_ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
CacheAddr GetCacheAddr() const {
|
|
||||||
ASSERT(is_registered);
|
|
||||||
return cache_addr;
|
|
||||||
}
|
|
||||||
|
|
||||||
const SurfaceParams& GetSurfaceParams() const {
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) {
|
|
||||||
ASSERT(!is_registered);
|
|
||||||
is_registered = true;
|
|
||||||
gpu_addr = gpu_addr_;
|
|
||||||
cpu_addr = cpu_addr_;
|
|
||||||
host_ptr = host_ptr_;
|
|
||||||
cache_addr = ToCacheAddr(host_ptr_);
|
|
||||||
DecorateSurfaceName();
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unregister() {
|
|
||||||
ASSERT(is_registered);
|
|
||||||
is_registered = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsRegistered() const {
|
|
||||||
return is_registered;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t GetSizeInBytes() const {
|
|
||||||
return params.GetGuestSizeInBytes();
|
|
||||||
}
|
|
||||||
|
|
||||||
u8* GetStagingBufferLevelData(u32 level) {
|
|
||||||
return staging_buffer.data() + params.GetHostMipmapLevelOffset(level);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit SurfaceBaseImpl(const SurfaceParams& params);
|
|
||||||
~SurfaceBaseImpl(); // non-virtual is intended
|
|
||||||
|
|
||||||
virtual void DecorateSurfaceName() = 0;
|
|
||||||
|
|
||||||
const SurfaceParams params;
|
|
||||||
|
|
||||||
private:
|
|
||||||
GPUVAddr gpu_addr{};
|
|
||||||
VAddr cpu_addr{};
|
|
||||||
u8* host_ptr{};
|
|
||||||
CacheAddr cache_addr{};
|
|
||||||
bool is_registered{};
|
|
||||||
|
|
||||||
std::vector<u8> staging_buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TTextureCache, typename TView, typename TExecutionContext>
|
|
||||||
class SurfaceBase : public SurfaceBaseImpl {
|
|
||||||
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
|
|
||||||
|
|
||||||
virtual TExecutionContext DownloadTexture(TExecutionContext exctx) = 0;
|
|
||||||
|
|
||||||
TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
|
|
||||||
if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) {
|
|
||||||
// It can't be a view if it's in a prior address.
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())};
|
|
||||||
const auto it{view_offset_map.find(relative_offset)};
|
|
||||||
if (it == view_offset_map.end()) {
|
|
||||||
// Couldn't find an aligned view.
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const auto [layer, level] = it->second;
|
|
||||||
|
|
||||||
if (!params.IsViewValid(view_params, layer, level)) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
|
|
||||||
}
|
|
||||||
|
|
||||||
void MarkAsModified(bool is_modified_) {
|
|
||||||
is_modified = is_modified_;
|
|
||||||
if (is_modified_) {
|
|
||||||
modification_tick = texture_cache.Tick();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
|
|
||||||
TView* view{TryGetView(view_addr, view_params)};
|
|
||||||
ASSERT(view != nullptr);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsModified() const {
|
|
||||||
return is_modified;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 GetModificationTick() const {
|
|
||||||
return modification_tick;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params)
|
|
||||||
: SurfaceBaseImpl{params}, texture_cache{texture_cache},
|
|
||||||
view_offset_map{params.CreateViewOffsetMap()} {}
|
|
||||||
|
|
||||||
~SurfaceBase() = default;
|
|
||||||
|
|
||||||
virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
|
|
||||||
const ViewKey key{base_layer, num_layers, base_level, num_levels};
|
|
||||||
const auto [entry, is_cache_miss] = views.try_emplace(key);
|
|
||||||
auto& view{entry->second};
|
|
||||||
if (is_cache_miss) {
|
|
||||||
view = CreateView(key);
|
|
||||||
}
|
|
||||||
return view.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
TTextureCache& texture_cache;
|
|
||||||
const std::map<u64, std::pair<u32, u32>> view_offset_map;
|
|
||||||
|
|
||||||
bool is_modified{};
|
|
||||||
u64 modification_tick{};
|
|
||||||
std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TSurface, typename TView, typename TExecutionContext>
|
|
||||||
class TextureCache {
|
|
||||||
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
|
||||||
|
|
||||||
using ResultType = std::tuple<TView*, TExecutionContext>;
|
|
||||||
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>;
|
|
||||||
using IntervalType = typename IntervalMap::interval_type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
|
||||||
for (const auto& surface : GetSurfacesInRegion(addr, size)) {
|
|
||||||
if (!surface->IsRegistered()) {
|
|
||||||
// Skip duplicates
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Unregister(surface);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultType GetTextureSurface(TExecutionContext exctx,
|
|
||||||
const Tegra::Texture::FullTextureInfo& config) {
|
|
||||||
const auto gpu_addr{config.tic.Address()};
|
|
||||||
if (!gpu_addr) {
|
|
||||||
return {{}, exctx};
|
|
||||||
}
|
|
||||||
const auto params{SurfaceParams::CreateForTexture(system, config)};
|
|
||||||
return GetSurfaceView(exctx, gpu_addr, params, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
|
|
||||||
const auto& regs{system.GPU().Maxwell3D().regs};
|
|
||||||
const auto gpu_addr{regs.zeta.Address()};
|
|
||||||
if (!gpu_addr || !regs.zeta_enable) {
|
|
||||||
return {{}, exctx};
|
|
||||||
}
|
|
||||||
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
|
||||||
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
|
||||||
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
|
||||||
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
|
||||||
return GetSurfaceView(exctx, gpu_addr, depth_params, preserve_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
|
|
||||||
bool preserve_contents) {
|
|
||||||
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
|
|
||||||
|
|
||||||
const auto& regs{system.GPU().Maxwell3D().regs};
|
|
||||||
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
|
|
||||||
regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
|
|
||||||
return {{}, exctx};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& memory_manager{system.GPU().MemoryManager()};
|
|
||||||
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
|
|
||||||
const auto gpu_addr{config.Address() +
|
|
||||||
config.base_layer * config.layer_stride * sizeof(u32)};
|
|
||||||
if (!gpu_addr) {
|
|
||||||
return {{}, exctx};
|
|
||||||
}
|
|
||||||
|
|
||||||
return GetSurfaceView(exctx, gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
|
||||||
preserve_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultType GetFermiSurface(TExecutionContext exctx,
|
|
||||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
|
||||||
return GetSurfaceView(exctx, config.Address(),
|
|
||||||
SurfaceParams::CreateForFermiCopySurface(config), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
|
|
||||||
const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
|
|
||||||
return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 Tick() {
|
|
||||||
return ++ticks;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
|
||||||
: system{system}, rasterizer{rasterizer} {}
|
|
||||||
|
|
||||||
~TextureCache() = default;
|
|
||||||
|
|
||||||
virtual ResultType TryFastGetSurfaceView(
|
|
||||||
TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
|
|
||||||
const SurfaceParams& params, bool preserve_contents,
|
|
||||||
const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
|
|
||||||
|
|
||||||
virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
|
|
||||||
|
|
||||||
void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr,
|
|
||||||
u8* host_ptr) {
|
|
||||||
surface->Register(gpu_addr, cpu_addr, host_ptr);
|
|
||||||
registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
|
|
||||||
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Unregister(std::shared_ptr<TSurface> surface) {
|
|
||||||
registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
|
|
||||||
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
|
|
||||||
surface->Unregister();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) {
|
|
||||||
if (const auto surface = TryGetReservedSurface(params); surface)
|
|
||||||
return surface;
|
|
||||||
// No reserved surface available, create a new one and reserve it
|
|
||||||
auto new_surface{CreateSurface(params)};
|
|
||||||
ReserveSurface(params, new_surface);
|
|
||||||
return new_surface;
|
|
||||||
}
|
|
||||||
|
|
||||||
Core::System& system;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ResultType GetSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr,
|
|
||||||
const SurfaceParams& params, bool preserve_contents) {
|
|
||||||
auto& memory_manager{system.GPU().MemoryManager()};
|
|
||||||
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
|
||||||
DEBUG_ASSERT(cpu_addr);
|
|
||||||
|
|
||||||
const auto host_ptr{memory_manager.GetPointer(gpu_addr)};
|
|
||||||
const auto cache_addr{ToCacheAddr(host_ptr)};
|
|
||||||
auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
|
|
||||||
if (overlaps.empty()) {
|
|
||||||
return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (overlaps.size() == 1) {
|
|
||||||
if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) {
|
|
||||||
return {view, exctx};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* fast_view;
|
|
||||||
std::tie(fast_view, exctx) = TryFastGetSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr,
|
|
||||||
params, preserve_contents, overlaps);
|
|
||||||
|
|
||||||
if (!fast_view) {
|
|
||||||
std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) {
|
|
||||||
return lhs->GetModificationTick() < rhs->GetModificationTick();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& surface : overlaps) {
|
|
||||||
if (!fast_view) {
|
|
||||||
// Flush even when we don't care about the contents, to preserve memory not
|
|
||||||
// written by the new surface.
|
|
||||||
exctx = FlushSurface(exctx, surface);
|
|
||||||
}
|
|
||||||
Unregister(surface);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fast_view) {
|
|
||||||
return {fast_view, exctx};
|
|
||||||
}
|
|
||||||
|
|
||||||
return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
|
|
||||||
}
|
|
||||||
|
|
||||||
ResultType LoadSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr,
|
|
||||||
u8* host_ptr, const SurfaceParams& params, bool preserve_contents) {
|
|
||||||
const auto new_surface{GetUncachedSurface(params)};
|
|
||||||
Register(new_surface, gpu_addr, cpu_addr, host_ptr);
|
|
||||||
if (preserve_contents) {
|
|
||||||
exctx = LoadSurface(exctx, new_surface);
|
|
||||||
}
|
|
||||||
return {new_surface->GetView(gpu_addr, params), exctx};
|
|
||||||
}
|
|
||||||
|
|
||||||
TExecutionContext LoadSurface(TExecutionContext exctx,
|
|
||||||
const std::shared_ptr<TSurface>& surface) {
|
|
||||||
surface->LoadBuffer();
|
|
||||||
exctx = surface->UploadTexture(exctx);
|
|
||||||
surface->MarkAsModified(false);
|
|
||||||
return exctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
TExecutionContext FlushSurface(TExecutionContext exctx,
|
|
||||||
const std::shared_ptr<TSurface>& surface) {
|
|
||||||
if (!surface->IsModified()) {
|
|
||||||
return exctx;
|
|
||||||
}
|
|
||||||
exctx = surface->DownloadTexture(exctx);
|
|
||||||
surface->FlushBuffer();
|
|
||||||
return exctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr,
|
|
||||||
std::size_t size) const {
|
|
||||||
if (size == 0) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
const IntervalType interval{cache_addr, cache_addr + size};
|
|
||||||
|
|
||||||
std::vector<std::shared_ptr<TSurface>> surfaces;
|
|
||||||
for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
|
|
||||||
surfaces.push_back(*pair.second.begin());
|
|
||||||
}
|
|
||||||
return surfaces;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) {
|
|
||||||
surface_reserve[params].push_back(std::move(surface));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) {
|
|
||||||
auto search{surface_reserve.find(params)};
|
|
||||||
if (search == surface_reserve.end()) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
for (auto& surface : search->second) {
|
|
||||||
if (!surface->IsRegistered()) {
|
|
||||||
return surface;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const {
|
|
||||||
return IntervalType::right_open(surface->GetCacheAddr(),
|
|
||||||
surface->GetCacheAddr() + surface->GetSizeInBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
VideoCore::RasterizerInterface& rasterizer;
|
|
||||||
|
|
||||||
u64 ticks{};
|
|
||||||
|
|
||||||
IntervalMap registered_surfaces;
|
|
||||||
|
|
||||||
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
|
|
||||||
/// previously been used. This is to prevent surfaces from being constantly created and
|
|
||||||
/// destroyed when used with different surface parameters.
|
|
||||||
std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DummyExecutionContext {};
|
|
||||||
|
|
||||||
template <typename TSurface, typename TView>
|
|
||||||
class TextureCacheContextless : protected TextureCache<TSurface, TView, DummyExecutionContext> {
|
|
||||||
using Base = TextureCache<TSurface, TView, DummyExecutionContext>;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
|
||||||
Base::InvalidateRegion(addr, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
|
|
||||||
return RemoveContext(Base::GetTextureSurface({}, config));
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* GetDepthBufferSurface(bool preserve_contents) {
|
|
||||||
return RemoveContext(Base::GetDepthBufferSurface({}, preserve_contents));
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) {
|
|
||||||
return RemoveContext(Base::GetColorBufferSurface({}, index, preserve_contents));
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
|
||||||
return RemoveContext(Base::GetFermiSurface({}, config));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
|
|
||||||
return Base::TryFindFramebufferSurface(host_ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 Tick() {
|
|
||||||
return Base::Tick();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit TextureCacheContextless(Core::System& system,
|
|
||||||
VideoCore::RasterizerInterface& rasterizer)
|
|
||||||
: TextureCache<TSurface, TView, DummyExecutionContext>{system, rasterizer} {}
|
|
||||||
|
|
||||||
virtual TView* TryFastGetSurfaceView(
|
|
||||||
GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params,
|
|
||||||
bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::tuple<TView*, DummyExecutionContext> TryFastGetSurfaceView(
|
|
||||||
DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
|
|
||||||
const SurfaceParams& params, bool preserve_contents,
|
|
||||||
const std::vector<std::shared_ptr<TSurface>>& overlaps) {
|
|
||||||
return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents,
|
|
||||||
overlaps),
|
|
||||||
{}};
|
|
||||||
}
|
|
||||||
|
|
||||||
TView* RemoveContext(std::tuple<TView*, DummyExecutionContext> return_value) {
|
|
||||||
const auto [view, exctx] = return_value;
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename TTextureCache, typename TView>
|
|
||||||
class SurfaceBaseContextless : public SurfaceBase<TTextureCache, TView, DummyExecutionContext> {
|
|
||||||
public:
|
|
||||||
DummyExecutionContext DownloadTexture(DummyExecutionContext) {
|
|
||||||
DownloadTextureImpl();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
DummyExecutionContext UploadTexture(DummyExecutionContext) {
|
|
||||||
UploadTextureImpl();
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params)
|
|
||||||
: SurfaceBase<TTextureCache, TView, DummyExecutionContext>{texture_cache, params} {}
|
|
||||||
|
|
||||||
virtual void DownloadTextureImpl() = 0;
|
|
||||||
|
|
||||||
virtual void UploadTextureImpl() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/morton.h"
|
||||||
|
#include "video_core/texture_cache/surface_base.h"
|
||||||
|
#include "video_core/texture_cache/surface_params.h"
|
||||||
|
#include "video_core/textures/convert.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
using Tegra::Texture::ConvertFromGuestToHost;
|
||||||
|
using VideoCore::MortonSwizzleMode;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
|
||||||
|
u32 level) {
|
||||||
|
const u32 width{params.GetMipWidth(level)};
|
||||||
|
const u32 height{params.GetMipHeight(level)};
|
||||||
|
const u32 block_height{params.GetMipBlockHeight(level)};
|
||||||
|
const u32 block_depth{params.GetMipBlockDepth(level)};
|
||||||
|
|
||||||
|
std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)};
|
||||||
|
if (params.IsLayered()) {
|
||||||
|
std::size_t host_offset{0};
|
||||||
|
const std::size_t guest_stride = params.GetGuestLayerSize();
|
||||||
|
const std::size_t host_stride = params.GetHostLayerSize(level);
|
||||||
|
for (u32 layer = 0; layer < params.GetNumLayers(); layer++) {
|
||||||
|
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
|
||||||
|
1, params.GetTileWidthSpacing(), buffer + host_offset,
|
||||||
|
memory + guest_offset);
|
||||||
|
guest_offset += guest_stride;
|
||||||
|
host_offset += host_stride;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
|
||||||
|
params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer,
|
||||||
|
memory + guest_offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // Anonymous namespace
|
||||||
|
|
||||||
|
SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} {
|
||||||
|
staging_buffer.resize(params.GetHostSizeInBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
SurfaceBaseImpl::~SurfaceBaseImpl() = default;
|
||||||
|
|
||||||
|
void SurfaceBaseImpl::LoadBuffer() {
|
||||||
|
if (params.IsTiled()) {
|
||||||
|
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}",
|
||||||
|
params.GetBlockWidth(), static_cast<u32>(params.GetTarget()));
|
||||||
|
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
||||||
|
u8* const buffer{GetStagingBufferLevelData(level)};
|
||||||
|
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, buffer, level);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented");
|
||||||
|
const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT};
|
||||||
|
const u32 block_width{params.GetDefaultBlockWidth()};
|
||||||
|
const u32 block_height{params.GetDefaultBlockHeight()};
|
||||||
|
const u32 width{(params.GetWidth() + block_width - 1) / block_width};
|
||||||
|
const u32 height{(params.GetHeight() + block_height - 1) / block_height};
|
||||||
|
const u32 copy_size{width * bpp};
|
||||||
|
if (params.GetPitch() == copy_size) {
|
||||||
|
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
|
||||||
|
} else {
|
||||||
|
const u8* start{host_ptr};
|
||||||
|
u8* write_to{staging_buffer.data()};
|
||||||
|
for (u32 h = height; h > 0; --h) {
|
||||||
|
std::memcpy(write_to, start, copy_size);
|
||||||
|
start += params.GetPitch();
|
||||||
|
write_to += copy_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
||||||
|
ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(),
|
||||||
|
params.GetMipWidth(level), params.GetMipHeight(level),
|
||||||
|
params.GetMipDepth(level), true, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SurfaceBaseImpl::FlushBuffer() {
|
||||||
|
if (params.IsTiled()) {
|
||||||
|
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}",
|
||||||
|
params.GetBlockWidth());
|
||||||
|
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
||||||
|
u8* const buffer = GetStagingBufferLevelData(level);
|
||||||
|
SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params, buffer, level);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
/*
|
||||||
|
ASSERT(params.GetTarget() == SurfaceTarget::Texture2D);
|
||||||
|
ASSERT(params.GetNumLevels() == 1);
|
||||||
|
|
||||||
|
const u32 bpp{params.GetFormatBpp() / 8};
|
||||||
|
const u32 copy_size{params.GetWidth() * bpp};
|
||||||
|
if (params.GetPitch() == copy_size) {
|
||||||
|
std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes());
|
||||||
|
} else {
|
||||||
|
u8* start{host_ptr};
|
||||||
|
const u8* read_to{staging_buffer.data()};
|
||||||
|
for (u32 h = params.GetHeight(); h > 0; --h) {
|
||||||
|
std::memcpy(start, read_to, copy_size);
|
||||||
|
start += params.GetPitch();
|
||||||
|
read_to += copy_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
|
@ -0,0 +1,172 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/texture_cache/surface_params.h"
|
||||||
|
#include "video_core/texture_cache/surface_view.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
class SurfaceBaseImpl {
|
||||||
|
public:
|
||||||
|
void LoadBuffer();
|
||||||
|
|
||||||
|
void FlushBuffer();
|
||||||
|
|
||||||
|
GPUVAddr GetGpuAddr() const {
|
||||||
|
ASSERT(is_registered);
|
||||||
|
return gpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VAddr GetCpuAddr() const {
|
||||||
|
ASSERT(is_registered);
|
||||||
|
return cpu_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* GetHostPtr() const {
|
||||||
|
ASSERT(is_registered);
|
||||||
|
return host_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
CacheAddr GetCacheAddr() const {
|
||||||
|
ASSERT(is_registered);
|
||||||
|
return cache_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SurfaceParams& GetSurfaceParams() const {
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) {
|
||||||
|
ASSERT(!is_registered);
|
||||||
|
is_registered = true;
|
||||||
|
gpu_addr = gpu_addr_;
|
||||||
|
cpu_addr = cpu_addr_;
|
||||||
|
host_ptr = host_ptr_;
|
||||||
|
cache_addr = ToCacheAddr(host_ptr_);
|
||||||
|
DecorateSurfaceName();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unregister() {
|
||||||
|
ASSERT(is_registered);
|
||||||
|
is_registered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRegistered() const {
|
||||||
|
return is_registered;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetSizeInBytes() const {
|
||||||
|
return params.GetGuestSizeInBytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* GetStagingBufferLevelData(u32 level) {
|
||||||
|
return staging_buffer.data() + params.GetHostMipmapLevelOffset(level);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SurfaceBaseImpl(const SurfaceParams& params);
|
||||||
|
~SurfaceBaseImpl(); // non-virtual is intended
|
||||||
|
|
||||||
|
virtual void DecorateSurfaceName() = 0;
|
||||||
|
|
||||||
|
const SurfaceParams params;
|
||||||
|
|
||||||
|
private:
|
||||||
|
GPUVAddr gpu_addr{};
|
||||||
|
VAddr cpu_addr{};
|
||||||
|
u8* host_ptr{};
|
||||||
|
CacheAddr cache_addr{};
|
||||||
|
bool is_registered{};
|
||||||
|
|
||||||
|
std::vector<u8> staging_buffer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TTextureCache, typename TView, typename TExecutionContext>
|
||||||
|
class SurfaceBase : public SurfaceBaseImpl {
|
||||||
|
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
||||||
|
|
||||||
|
public:
|
||||||
|
virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
|
||||||
|
|
||||||
|
virtual TExecutionContext DownloadTexture(TExecutionContext exctx) = 0;
|
||||||
|
|
||||||
|
TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
|
||||||
|
if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) {
|
||||||
|
// It can't be a view if it's in a prior address.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())};
|
||||||
|
const auto it{view_offset_map.find(relative_offset)};
|
||||||
|
if (it == view_offset_map.end()) {
|
||||||
|
// Couldn't find an aligned view.
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const auto [layer, level] = it->second;
|
||||||
|
|
||||||
|
if (!params.IsViewValid(view_params, layer, level)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MarkAsModified(bool is_modified_) {
|
||||||
|
is_modified = is_modified_;
|
||||||
|
if (is_modified_) {
|
||||||
|
modification_tick = texture_cache.Tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
|
||||||
|
TView* view{TryGetView(view_addr, view_params)};
|
||||||
|
ASSERT(view != nullptr);
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsModified() const {
|
||||||
|
return is_modified;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 GetModificationTick() const {
|
||||||
|
return modification_tick;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params)
|
||||||
|
: SurfaceBaseImpl{params}, texture_cache{texture_cache},
|
||||||
|
view_offset_map{params.CreateViewOffsetMap()} {}
|
||||||
|
|
||||||
|
~SurfaceBase() = default;
|
||||||
|
|
||||||
|
virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
|
||||||
|
const ViewKey key{base_layer, num_layers, base_level, num_levels};
|
||||||
|
const auto [entry, is_cache_miss] = views.try_emplace(key);
|
||||||
|
auto& view{entry->second};
|
||||||
|
if (is_cache_miss) {
|
||||||
|
view = CreateView(key);
|
||||||
|
}
|
||||||
|
return view.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
TTextureCache& texture_cache;
|
||||||
|
const std::map<u64, std::pair<u32, u32>> view_offset_map;
|
||||||
|
|
||||||
|
std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
|
||||||
|
|
||||||
|
bool is_modified{};
|
||||||
|
u64 modification_tick{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
|
@ -2,22 +2,17 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include "common/alignment.h"
|
#include <map>
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/cityhash.h"
|
#include "common/cityhash.h"
|
||||||
#include "common/common_types.h"
|
#include "common/alignment.h"
|
||||||
#include "core/core.h"
|
#include "core/core.h"
|
||||||
#include "video_core/morton.h"
|
|
||||||
#include "video_core/surface.h"
|
#include "video_core/surface.h"
|
||||||
#include "video_core/texture_cache.h"
|
#include "video_core/texture_cache/surface_params.h"
|
||||||
#include "video_core/textures/convert.h"
|
|
||||||
#include "video_core/textures/decoders.h"
|
#include "video_core/textures/decoders.h"
|
||||||
#include "video_core/textures/texture.h"
|
|
||||||
|
|
||||||
namespace VideoCommon {
|
namespace VideoCommon {
|
||||||
|
|
||||||
using VideoCore::MortonSwizzleMode;
|
|
||||||
|
|
||||||
using VideoCore::Surface::ComponentTypeFromDepthFormat;
|
using VideoCore::Surface::ComponentTypeFromDepthFormat;
|
||||||
using VideoCore::Surface::ComponentTypeFromRenderTarget;
|
using VideoCore::Surface::ComponentTypeFromRenderTarget;
|
||||||
using VideoCore::Surface::ComponentTypeFromTexture;
|
using VideoCore::Surface::ComponentTypeFromTexture;
|
||||||
|
@ -27,115 +22,12 @@ using VideoCore::Surface::PixelFormatFromTextureFormat;
|
||||||
using VideoCore::Surface::SurfaceTarget;
|
using VideoCore::Surface::SurfaceTarget;
|
||||||
using VideoCore::Surface::SurfaceTargetFromTextureType;
|
using VideoCore::Surface::SurfaceTargetFromTextureType;
|
||||||
|
|
||||||
using Tegra::Texture::ConvertFromGuestToHost;
|
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
|
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
|
||||||
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
|
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
|
|
||||||
u32 level) {
|
|
||||||
const u32 width{params.GetMipWidth(level)};
|
|
||||||
const u32 height{params.GetMipHeight(level)};
|
|
||||||
const u32 block_height{params.GetMipBlockHeight(level)};
|
|
||||||
const u32 block_depth{params.GetMipBlockDepth(level)};
|
|
||||||
|
|
||||||
std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)};
|
|
||||||
if (params.IsLayered()) {
|
|
||||||
std::size_t host_offset{0};
|
|
||||||
const std::size_t guest_stride = params.GetGuestLayerSize();
|
|
||||||
const std::size_t host_stride = params.GetHostLayerSize(level);
|
|
||||||
for (u32 layer = 0; layer < params.GetNumLayers(); layer++) {
|
|
||||||
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
|
|
||||||
1, params.GetTileWidthSpacing(), buffer + host_offset,
|
|
||||||
memory + guest_offset);
|
|
||||||
guest_offset += guest_stride;
|
|
||||||
host_offset += host_stride;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
|
|
||||||
params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer,
|
|
||||||
memory + guest_offset);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} {
|
|
||||||
staging_buffer.resize(params.GetHostSizeInBytes());
|
|
||||||
}
|
|
||||||
|
|
||||||
SurfaceBaseImpl::~SurfaceBaseImpl() = default;
|
|
||||||
|
|
||||||
void SurfaceBaseImpl::LoadBuffer() {
|
|
||||||
if (params.IsTiled()) {
|
|
||||||
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}",
|
|
||||||
params.GetBlockWidth(), static_cast<u32>(params.GetTarget()));
|
|
||||||
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
|
||||||
u8* const buffer{GetStagingBufferLevelData(level)};
|
|
||||||
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, buffer, level);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented");
|
|
||||||
const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT};
|
|
||||||
const u32 block_width{params.GetDefaultBlockWidth()};
|
|
||||||
const u32 block_height{params.GetDefaultBlockHeight()};
|
|
||||||
const u32 width{(params.GetWidth() + block_width - 1) / block_width};
|
|
||||||
const u32 height{(params.GetHeight() + block_height - 1) / block_height};
|
|
||||||
const u32 copy_size{width * bpp};
|
|
||||||
if (params.GetPitch() == copy_size) {
|
|
||||||
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
|
|
||||||
} else {
|
|
||||||
const u8* start{host_ptr};
|
|
||||||
u8* write_to{staging_buffer.data()};
|
|
||||||
for (u32 h = height; h > 0; --h) {
|
|
||||||
std::memcpy(write_to, start, copy_size);
|
|
||||||
start += params.GetPitch();
|
|
||||||
write_to += copy_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
|
||||||
ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(),
|
|
||||||
params.GetMipWidth(level), params.GetMipHeight(level),
|
|
||||||
params.GetMipDepth(level), true, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SurfaceBaseImpl::FlushBuffer() {
|
|
||||||
if (params.IsTiled()) {
|
|
||||||
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}",
|
|
||||||
params.GetBlockWidth());
|
|
||||||
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
|
|
||||||
u8* const buffer = GetStagingBufferLevelData(level);
|
|
||||||
SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params, buffer, level);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
/*
|
|
||||||
ASSERT(params.GetTarget() == SurfaceTarget::Texture2D);
|
|
||||||
ASSERT(params.GetNumLevels() == 1);
|
|
||||||
|
|
||||||
const u32 bpp{params.GetFormatBpp() / 8};
|
|
||||||
const u32 copy_size{params.GetWidth() * bpp};
|
|
||||||
if (params.GetPitch() == copy_size) {
|
|
||||||
std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes());
|
|
||||||
} else {
|
|
||||||
u8* start{host_ptr};
|
|
||||||
const u8* read_to{staging_buffer.data()};
|
|
||||||
for (u32 h = params.GetHeight(); h > 0; --h) {
|
|
||||||
std::memcpy(start, read_to, copy_size);
|
|
||||||
start += params.GetPitch();
|
|
||||||
read_to += copy_size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
|
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
|
||||||
const Tegra::Texture::FullTextureInfo& config) {
|
const Tegra::Texture::FullTextureInfo& config) {
|
||||||
SurfaceParams params;
|
SurfaceParams params;
|
||||||
|
@ -517,14 +409,4 @@ bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const
|
||||||
rhs.type, rhs.target);
|
rhs.type, rhs.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t ViewKey::Hash() const {
|
|
||||||
return static_cast<std::size_t>(
|
|
||||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ViewKey::operator==(const ViewKey& rhs) const {
|
|
||||||
return std::tie(base_layer, num_layers, base_level, num_levels) ==
|
|
||||||
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace VideoCommon
|
} // namespace VideoCommon
|
|
@ -0,0 +1,229 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/engines/fermi_2d.h"
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
#include "video_core/surface.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
class HasheableSurfaceParams {
|
||||||
|
public:
|
||||||
|
std::size_t Hash() const;
|
||||||
|
|
||||||
|
bool operator==(const HasheableSurfaceParams& rhs) const;
|
||||||
|
|
||||||
|
bool operator!=(const HasheableSurfaceParams& rhs) const {
|
||||||
|
return !operator==(rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// Avoid creation outside of a managed environment.
|
||||||
|
HasheableSurfaceParams() = default;
|
||||||
|
|
||||||
|
bool is_tiled;
|
||||||
|
bool srgb_conversion;
|
||||||
|
u32 block_width;
|
||||||
|
u32 block_height;
|
||||||
|
u32 block_depth;
|
||||||
|
u32 tile_width_spacing;
|
||||||
|
u32 width;
|
||||||
|
u32 height;
|
||||||
|
u32 depth;
|
||||||
|
u32 pitch;
|
||||||
|
u32 unaligned_height;
|
||||||
|
u32 num_levels;
|
||||||
|
VideoCore::Surface::PixelFormat pixel_format;
|
||||||
|
VideoCore::Surface::ComponentType component_type;
|
||||||
|
VideoCore::Surface::SurfaceType type;
|
||||||
|
VideoCore::Surface::SurfaceTarget target;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SurfaceParams final : public HasheableSurfaceParams {
|
||||||
|
public:
|
||||||
|
/// Creates SurfaceCachedParams from a texture configuration.
|
||||||
|
static SurfaceParams CreateForTexture(Core::System& system,
|
||||||
|
const Tegra::Texture::FullTextureInfo& config);
|
||||||
|
|
||||||
|
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
||||||
|
static SurfaceParams CreateForDepthBuffer(
|
||||||
|
Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
|
||||||
|
u32 block_width, u32 block_height, u32 block_depth,
|
||||||
|
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
|
||||||
|
|
||||||
|
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
||||||
|
static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
|
||||||
|
|
||||||
|
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
|
||||||
|
static SurfaceParams CreateForFermiCopySurface(
|
||||||
|
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
||||||
|
|
||||||
|
bool IsTiled() const {
|
||||||
|
return is_tiled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetSrgbConversion() const {
|
||||||
|
return srgb_conversion;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetBlockWidth() const {
|
||||||
|
return block_width;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetTileWidthSpacing() const {
|
||||||
|
return tile_width_spacing;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetWidth() const {
|
||||||
|
return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetHeight() const {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetDepth() const {
|
||||||
|
return depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetPitch() const {
|
||||||
|
return pitch;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNumLevels() const {
|
||||||
|
return num_levels;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCore::Surface::PixelFormat GetPixelFormat() const {
|
||||||
|
return pixel_format;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCore::Surface::ComponentType GetComponentType() const {
|
||||||
|
return component_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCore::Surface::SurfaceTarget GetTarget() const {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCore::Surface::SurfaceType GetType() const {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetGuestSizeInBytes() const {
|
||||||
|
return guest_size_in_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t GetHostSizeInBytes() const {
|
||||||
|
return host_size_in_bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GetNumLayers() const {
|
||||||
|
return num_layers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the width of a given mipmap level.
|
||||||
|
u32 GetMipWidth(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the height of a given mipmap level.
|
||||||
|
u32 GetMipHeight(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the depth of a given mipmap level.
|
||||||
|
u32 GetMipDepth(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns true if these parameters are from a layered surface.
|
||||||
|
bool IsLayered() const;
|
||||||
|
|
||||||
|
/// Returns the block height of a given mipmap level.
|
||||||
|
u32 GetMipBlockHeight(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the block depth of a given mipmap level.
|
||||||
|
u32 GetMipBlockDepth(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the offset in bytes in guest memory of a given mipmap level.
|
||||||
|
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
|
||||||
|
std::size_t GetHostMipmapLevelOffset(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the size in bytes in host memory (linear) of a given mipmap level.
|
||||||
|
std::size_t GetHostMipmapSize(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the size of a layer in bytes in guest memory.
|
||||||
|
std::size_t GetGuestLayerSize() const;
|
||||||
|
|
||||||
|
/// Returns the size of a layer in bytes in host memory for a given mipmap level.
|
||||||
|
std::size_t GetHostLayerSize(u32 level) const;
|
||||||
|
|
||||||
|
/// Returns the default block width.
|
||||||
|
u32 GetDefaultBlockWidth() const;
|
||||||
|
|
||||||
|
/// Returns the default block height.
|
||||||
|
u32 GetDefaultBlockHeight() const;
|
||||||
|
|
||||||
|
/// Returns the bits per pixel.
|
||||||
|
u32 GetBitsPerPixel() const;
|
||||||
|
|
||||||
|
/// Returns the bytes per pixel.
|
||||||
|
u32 GetBytesPerPixel() const;
|
||||||
|
|
||||||
|
/// Returns true if another surface can be familiar with this. This is a loosely defined term
|
||||||
|
/// that reflects the possibility of these two surface parameters potentially being part of a
|
||||||
|
/// bigger superset.
|
||||||
|
bool IsFamiliar(const SurfaceParams& view_params) const;
|
||||||
|
|
||||||
|
/// Returns true if the pixel format is a depth and/or stencil format.
|
||||||
|
bool IsPixelFormatZeta() const;
|
||||||
|
|
||||||
|
/// Creates a map that redirects an address difference to a layer and mipmap level.
|
||||||
|
std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
|
||||||
|
|
||||||
|
/// Returns true if the passed surface view parameters is equal or a valid subset of this.
|
||||||
|
bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// Calculates values that can be deduced from HasheableSurfaceParams.
|
||||||
|
void CalculateCachedValues();
|
||||||
|
|
||||||
|
/// Returns the size of a given mipmap level inside a layer.
|
||||||
|
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const;
|
||||||
|
|
||||||
|
/// Returns the size of all mipmap levels and aligns as needed.
|
||||||
|
std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
|
||||||
|
|
||||||
|
/// Returns the size of a layer
|
||||||
|
std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const;
|
||||||
|
|
||||||
|
/// Returns true if the passed view width and height match the size of this params in a given
|
||||||
|
/// mipmap level.
|
||||||
|
bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
|
||||||
|
|
||||||
|
/// Returns true if the passed view depth match the size of this params in a given mipmap level.
|
||||||
|
bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
|
||||||
|
|
||||||
|
/// Returns true if the passed view layers and mipmap levels are in bounds.
|
||||||
|
bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
|
||||||
|
|
||||||
|
std::size_t guest_size_in_bytes;
|
||||||
|
std::size_t host_size_in_bytes;
|
||||||
|
u32 num_layers;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<VideoCommon::SurfaceParams> {
|
||||||
|
std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#include <tuple>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "video_core/texture_cache/surface_view.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
std::size_t ViewKey::Hash() const {
|
||||||
|
return static_cast<std::size_t>(base_layer) ^ static_cast<std::size_t>(num_layers << 16) ^
|
||||||
|
(static_cast<std::size_t>(base_level) << 32) ^
|
||||||
|
(static_cast<std::size_t>(num_levels) << 48);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ViewKey::operator==(const ViewKey& rhs) const {
|
||||||
|
return std::tie(base_layer, num_layers, base_level, num_levels) ==
|
||||||
|
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
|
@ -0,0 +1,35 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "common/common_types.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
struct ViewKey {
|
||||||
|
std::size_t Hash() const;
|
||||||
|
|
||||||
|
bool operator==(const ViewKey& rhs) const;
|
||||||
|
|
||||||
|
u32 base_layer{};
|
||||||
|
u32 num_layers{};
|
||||||
|
u32 base_level{};
|
||||||
|
u32 num_levels{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct hash<VideoCommon::ViewKey> {
|
||||||
|
std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
|
||||||
|
return k.Hash();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace std
|
|
@ -0,0 +1,282 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <memory>
|
||||||
|
#include <set>
|
||||||
|
#include <tuple>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
#include <boost/icl/interval_map.hpp>
|
||||||
|
#include <boost/range/iterator_range.hpp>
|
||||||
|
|
||||||
|
#include "common/assert.h"
|
||||||
|
#include "common/common_types.h"
|
||||||
|
#include "core/memory.h"
|
||||||
|
#include "video_core/engines/fermi_2d.h"
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
#include "video_core/gpu.h"
|
||||||
|
#include "video_core/memory_manager.h"
|
||||||
|
#include "video_core/rasterizer_interface.h"
|
||||||
|
#include "video_core/surface.h"
|
||||||
|
#include "video_core/texture_cache/surface_base.h"
|
||||||
|
#include "video_core/texture_cache/surface_params.h"
|
||||||
|
#include "video_core/texture_cache/surface_view.h"
|
||||||
|
|
||||||
|
namespace Core {
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Tegra::Texture {
|
||||||
|
struct FullTextureInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VideoCore {
|
||||||
|
class RasterizerInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
template <typename TSurface, typename TView, typename TExecutionContext>
|
||||||
|
class TextureCache {
|
||||||
|
static_assert(std::is_trivially_copyable_v<TExecutionContext>);
|
||||||
|
|
||||||
|
using ResultType = std::tuple<TView*, TExecutionContext>;
|
||||||
|
using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>;
|
||||||
|
using IntervalType = typename IntervalMap::interval_type;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
||||||
|
for (const auto& surface : GetSurfacesInRegion(addr, size)) {
|
||||||
|
if (!surface->IsRegistered()) {
|
||||||
|
// Skip duplicates
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Unregister(surface);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType GetTextureSurface(TExecutionContext exctx,
|
||||||
|
const Tegra::Texture::FullTextureInfo& config) {
|
||||||
|
const auto gpu_addr{config.tic.Address()};
|
||||||
|
if (!gpu_addr) {
|
||||||
|
return {{}, exctx};
|
||||||
|
}
|
||||||
|
const auto params{SurfaceParams::CreateForTexture(system, config)};
|
||||||
|
return GetSurfaceView(exctx, gpu_addr, params, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
|
||||||
|
const auto& regs{system.GPU().Maxwell3D().regs};
|
||||||
|
const auto gpu_addr{regs.zeta.Address()};
|
||||||
|
if (!gpu_addr || !regs.zeta_enable) {
|
||||||
|
return {{}, exctx};
|
||||||
|
}
|
||||||
|
const auto depth_params{SurfaceParams::CreateForDepthBuffer(
|
||||||
|
system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
|
||||||
|
regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
|
||||||
|
regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
|
||||||
|
return GetSurfaceView(exctx, gpu_addr, depth_params, preserve_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
|
||||||
|
bool preserve_contents) {
|
||||||
|
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
|
||||||
|
|
||||||
|
const auto& regs{system.GPU().Maxwell3D().regs};
|
||||||
|
if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
|
||||||
|
regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
|
||||||
|
return {{}, exctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& memory_manager{system.GPU().MemoryManager()};
|
||||||
|
const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
|
||||||
|
const auto gpu_addr{config.Address() +
|
||||||
|
config.base_layer * config.layer_stride * sizeof(u32)};
|
||||||
|
if (!gpu_addr) {
|
||||||
|
return {{}, exctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetSurfaceView(exctx, gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
|
||||||
|
preserve_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType GetFermiSurface(TExecutionContext exctx,
|
||||||
|
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||||
|
return GetSurfaceView(exctx, config.Address(),
|
||||||
|
SurfaceParams::CreateForFermiCopySurface(config), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
|
||||||
|
const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
|
||||||
|
return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Tick() {
|
||||||
|
return ++ticks;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
|
||||||
|
: system{system}, rasterizer{rasterizer} {}
|
||||||
|
|
||||||
|
~TextureCache() = default;
|
||||||
|
|
||||||
|
virtual ResultType TryFastGetSurfaceView(
|
||||||
|
TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
|
||||||
|
const SurfaceParams& params, bool preserve_contents,
|
||||||
|
const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
|
||||||
|
|
||||||
|
virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
|
||||||
|
|
||||||
|
void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||||
|
u8* host_ptr) {
|
||||||
|
surface->Register(gpu_addr, cpu_addr, host_ptr);
|
||||||
|
registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
|
||||||
|
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Unregister(std::shared_ptr<TSurface> surface) {
|
||||||
|
registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
|
||||||
|
rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
|
||||||
|
surface->Unregister();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) {
|
||||||
|
if (const auto surface = TryGetReservedSurface(params); surface)
|
||||||
|
return surface;
|
||||||
|
// No reserved surface available, create a new one and reserve it
|
||||||
|
auto new_surface{CreateSurface(params)};
|
||||||
|
ReserveSurface(params, new_surface);
|
||||||
|
return new_surface;
|
||||||
|
}
|
||||||
|
|
||||||
|
Core::System& system;
|
||||||
|
|
||||||
|
private:
|
||||||
|
ResultType GetSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr,
|
||||||
|
const SurfaceParams& params, bool preserve_contents) {
|
||||||
|
auto& memory_manager{system.GPU().MemoryManager()};
|
||||||
|
const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
|
||||||
|
DEBUG_ASSERT(cpu_addr);
|
||||||
|
|
||||||
|
const auto host_ptr{memory_manager.GetPointer(gpu_addr)};
|
||||||
|
const auto cache_addr{ToCacheAddr(host_ptr)};
|
||||||
|
auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
|
||||||
|
if (overlaps.empty()) {
|
||||||
|
return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (overlaps.size() == 1) {
|
||||||
|
if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) {
|
||||||
|
return {view, exctx};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* fast_view;
|
||||||
|
std::tie(fast_view, exctx) = TryFastGetSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr,
|
||||||
|
params, preserve_contents, overlaps);
|
||||||
|
|
||||||
|
if (!fast_view) {
|
||||||
|
std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) {
|
||||||
|
return lhs->GetModificationTick() < rhs->GetModificationTick();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& surface : overlaps) {
|
||||||
|
if (!fast_view) {
|
||||||
|
// Flush even when we don't care about the contents, to preserve memory not
|
||||||
|
// written by the new surface.
|
||||||
|
exctx = FlushSurface(exctx, surface);
|
||||||
|
}
|
||||||
|
Unregister(surface);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fast_view) {
|
||||||
|
return {fast_view, exctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultType LoadSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||||
|
u8* host_ptr, const SurfaceParams& params, bool preserve_contents) {
|
||||||
|
const auto new_surface{GetUncachedSurface(params)};
|
||||||
|
Register(new_surface, gpu_addr, cpu_addr, host_ptr);
|
||||||
|
if (preserve_contents) {
|
||||||
|
exctx = LoadSurface(exctx, new_surface);
|
||||||
|
}
|
||||||
|
return {new_surface->GetView(gpu_addr, params), exctx};
|
||||||
|
}
|
||||||
|
|
||||||
|
TExecutionContext LoadSurface(TExecutionContext exctx,
|
||||||
|
const std::shared_ptr<TSurface>& surface) {
|
||||||
|
surface->LoadBuffer();
|
||||||
|
exctx = surface->UploadTexture(exctx);
|
||||||
|
surface->MarkAsModified(false);
|
||||||
|
return exctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
TExecutionContext FlushSurface(TExecutionContext exctx,
|
||||||
|
const std::shared_ptr<TSurface>& surface) {
|
||||||
|
if (!surface->IsModified()) {
|
||||||
|
return exctx;
|
||||||
|
}
|
||||||
|
exctx = surface->DownloadTexture(exctx);
|
||||||
|
surface->FlushBuffer();
|
||||||
|
return exctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr,
|
||||||
|
std::size_t size) const {
|
||||||
|
if (size == 0) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const IntervalType interval{cache_addr, cache_addr + size};
|
||||||
|
|
||||||
|
std::vector<std::shared_ptr<TSurface>> surfaces;
|
||||||
|
for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
|
||||||
|
surfaces.push_back(*pair.second.begin());
|
||||||
|
}
|
||||||
|
return surfaces;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) {
|
||||||
|
surface_reserve[params].push_back(std::move(surface));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) {
|
||||||
|
auto search{surface_reserve.find(params)};
|
||||||
|
if (search == surface_reserve.end()) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
for (auto& surface : search->second) {
|
||||||
|
if (!surface->IsRegistered()) {
|
||||||
|
return surface;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const {
|
||||||
|
return IntervalType::right_open(surface->GetCacheAddr(),
|
||||||
|
surface->GetCacheAddr() + surface->GetSizeInBytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoCore::RasterizerInterface& rasterizer;
|
||||||
|
|
||||||
|
u64 ticks{};
|
||||||
|
|
||||||
|
IntervalMap registered_surfaces;
|
||||||
|
|
||||||
|
/// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
|
||||||
|
/// previously been used. This is to prevent surfaces from being constantly created and
|
||||||
|
/// destroyed when used with different surface parameters.
|
||||||
|
std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
|
@ -0,0 +1,93 @@
|
||||||
|
// Copyright 2019 yuzu Emulator Project
|
||||||
|
// Licensed under GPLv2 or any later version
|
||||||
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "video_core/texture_cache/texture_cache.h"
|
||||||
|
|
||||||
|
namespace VideoCommon {
|
||||||
|
|
||||||
|
struct DummyExecutionContext {};
|
||||||
|
|
||||||
|
template <typename TSurface, typename TView>
|
||||||
|
class TextureCacheContextless : protected TextureCache<TSurface, TView, DummyExecutionContext> {
|
||||||
|
using Base = TextureCache<TSurface, TView, DummyExecutionContext>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void InvalidateRegion(CacheAddr addr, std::size_t size) {
|
||||||
|
Base::InvalidateRegion(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
|
||||||
|
return RemoveContext(Base::GetTextureSurface({}, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* GetDepthBufferSurface(bool preserve_contents) {
|
||||||
|
return RemoveContext(Base::GetDepthBufferSurface({}, preserve_contents));
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) {
|
||||||
|
return RemoveContext(Base::GetColorBufferSurface({}, index, preserve_contents));
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||||
|
return RemoveContext(Base::GetFermiSurface({}, config));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
|
||||||
|
return Base::TryFindFramebufferSurface(host_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
u64 Tick() {
|
||||||
|
return Base::Tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit TextureCacheContextless(Core::System& system,
|
||||||
|
VideoCore::RasterizerInterface& rasterizer)
|
||||||
|
: TextureCache<TSurface, TView, DummyExecutionContext>{system, rasterizer} {}
|
||||||
|
|
||||||
|
virtual TView* TryFastGetSurfaceView(
|
||||||
|
GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params,
|
||||||
|
bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::tuple<TView*, DummyExecutionContext> TryFastGetSurfaceView(
|
||||||
|
DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
|
||||||
|
const SurfaceParams& params, bool preserve_contents,
|
||||||
|
const std::vector<std::shared_ptr<TSurface>>& overlaps) {
|
||||||
|
return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents,
|
||||||
|
overlaps),
|
||||||
|
{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
TView* RemoveContext(std::tuple<TView*, DummyExecutionContext> return_value) {
|
||||||
|
const auto [view, exctx] = return_value;
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename TTextureCache, typename TView>
|
||||||
|
class SurfaceBaseContextless : public SurfaceBase<TTextureCache, TView, DummyExecutionContext> {
|
||||||
|
public:
|
||||||
|
DummyExecutionContext DownloadTexture(DummyExecutionContext) {
|
||||||
|
DownloadTextureImpl();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
DummyExecutionContext UploadTexture(DummyExecutionContext) {
|
||||||
|
UploadTextureImpl();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params)
|
||||||
|
: SurfaceBase<TTextureCache, TView, DummyExecutionContext>{texture_cache, params} {}
|
||||||
|
|
||||||
|
virtual void DownloadTextureImpl() = 0;
|
||||||
|
|
||||||
|
virtual void UploadTextureImpl() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace VideoCommon
|
Loading…
Reference in New Issue