rasterizer_cache: Simplify SurfaceBase

* The casts are bit ugly but will be refactored soon
This commit is contained in:
GPUCode
2023-02-13 22:47:07 +02:00
parent e4502a1a6c
commit a14be97238
9 changed files with 147 additions and 148 deletions

View File

@ -37,6 +37,7 @@ add_library(video_core STATIC
rasterizer_cache/sampler_params.h
rasterizer_cache/slot_vector.h
rasterizer_cache/surface_base.h
rasterizer_cache/surface_base.cpp
rasterizer_cache/utils.cpp
rasterizer_cache/utils.h
rasterizer_cache/surface_params.cpp

View File

@ -611,7 +611,8 @@ auto RasterizerCache<T>::GetTextureSurface(const Pica::Texture::TextureInfo& inf
}
if (watcher && !watcher->IsValid()) {
auto level_surface = watcher->Get();
auto level_surface =
std::static_pointer_cast<typename T::SurfaceType>(watcher->Get());
if (!level_surface->invalid_regions.empty()) {
ValidateSurface(level_surface, level_surface->addr, level_surface->size);
}
@ -684,7 +685,7 @@ auto RasterizerCache<T>::GetTextureCube(const TextureCubeConfig& config) -> cons
for (std::size_t i = 0; i < addresses.size(); i++) {
const auto& watcher = watchers[i];
if (watcher && !watcher->IsValid()) {
auto face = watcher->Get();
auto face = std::static_pointer_cast<typename T::SurfaceType>(watcher->Get());
if (!face->invalid_regions.empty()) {
ValidateSurface(face, face->addr, face->size);
}

View File

@ -47,7 +47,6 @@ class RasterizerCache : NonCopyable {
using TextureRuntime = typename T::RuntimeType;
using Sampler = typename T::Sampler;
using Surface = std::shared_ptr<typename T::SurfaceType>;
using Watcher = SurfaceWatcher<typename T::SurfaceType>;
/// Declare rasterizer interval types
using SurfaceMap = boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber,

View File

@ -0,0 +1,127 @@
// Copyright 2023 Citra Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/rasterizer_cache/surface_base.h"
namespace VideoCore {
SurfaceBase::SurfaceBase() = default;
SurfaceBase::SurfaceBase(const SurfaceParams& params) : SurfaceParams{params} {}
bool SurfaceBase::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const {
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
boost::icl::first(fill_interval) >= addr &&
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
dest_surface.FromInterval(fill_interval).GetInterval() ==
fill_interval) { // make sure interval is a rectangle in dest surface
if (fill_size * 8 != dest_surface.GetFormatBpp()) {
// Check if bits repeat for our fill_size
const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
for (u32 i = 0; i < fill_size; ++i)
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
dest_bytes_per_pixel) != 0)
return false;
if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4))
return false;
}
return true;
}
return false;
}
bool SurfaceBase::CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const {
SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
ASSERT(subrect_params.GetInterval() == copy_interval);
if (CanSubRect(subrect_params))
return true;
if (CanFill(dest_surface, copy_interval))
return true;
return false;
}
SurfaceInterval SurfaceBase::GetCopyableInterval(const SurfaceParams& params) const {
SurfaceInterval result{};
const u32 tile_align = params.BytesInPixels(params.is_tiled ? 8 * 8 : 1);
const auto valid_regions =
SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
for (auto& valid_interval : valid_regions) {
const SurfaceInterval aligned_interval{
params.addr +
Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
params.addr +
Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)};
if (tile_align > boost::icl::length(valid_interval) ||
boost::icl::length(aligned_interval) == 0) {
continue;
}
// Get the rectangle within aligned_interval
const u32 stride_bytes = params.BytesInPixels(params.stride) * (params.is_tiled ? 8 : 1);
SurfaceInterval rect_interval{
params.addr +
Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr,
stride_bytes),
};
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
// 1 row
rect_interval = aligned_interval;
} else if (boost::icl::length(rect_interval) == 0) {
// 2 rows that do not make a rectangle, return the larger one
const SurfaceInterval row1{boost::icl::first(aligned_interval),
boost::icl::first(rect_interval)};
const SurfaceInterval row2{boost::icl::first(rect_interval),
boost::icl::last_next(aligned_interval)};
rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
}
if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
result = rect_interval;
}
}
return result;
}
std::shared_ptr<Watcher> SurfaceBase::CreateWatcher() {
auto weak_ptr = weak_from_this();
auto watcher = std::make_shared<Watcher>(std::move(weak_ptr));
watchers.push_back(watcher);
return watcher;
}
void SurfaceBase::InvalidateAllWatcher() {
for (const auto& watcher : watchers) {
if (auto locked = watcher.lock()) {
locked->valid = false;
}
}
}
void SurfaceBase::UnlinkAllWatcher() {
for (const auto& watcher : watchers) {
if (auto locked = watcher.lock()) {
locked->valid = false;
locked->surface.reset();
}
}
watchers.clear();
}
} // namespace VideoCore

View File

@ -5,22 +5,21 @@
#pragma once
#include <memory>
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/rasterizer_cache/surface_params.h"
namespace VideoCore {
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>;
class SurfaceBase;
/**
* A watcher that notifies whether a cached surface has been changed. This is useful for caching
* surface collection objects, including texture cube and mipmap.
*/
template <class S>
class SurfaceWatcher {
class Watcher {
public:
explicit SurfaceWatcher(std::weak_ptr<S>&& surface) : surface(std::move(surface)) {}
explicit Watcher(std::weak_ptr<SurfaceBase>&& surface) : surface(std::move(surface)) {}
/// Checks whether the surface has been changed.
bool IsValid() const {
@ -34,23 +33,19 @@ public:
}
/// Gets the referencing surface. Returns null if the surface has been destroyed
std::shared_ptr<S> Get() const {
std::shared_ptr<SurfaceBase> Get() const {
return surface.lock();
}
public:
std::weak_ptr<S> surface;
std::weak_ptr<SurfaceBase> surface;
bool valid = false;
};
template <class S>
class SurfaceBase : public SurfaceParams, public std::enable_shared_from_this<S> {
using Watcher = SurfaceWatcher<S>;
class SurfaceBase : public SurfaceParams, public std::enable_shared_from_this<SurfaceBase> {
public:
SurfaceBase() = default;
SurfaceBase(const SurfaceParams& params) : SurfaceParams{params} {}
virtual ~SurfaceBase() = default;
SurfaceBase();
explicit SurfaceBase(const SurfaceParams& params);
[[nodiscard]] bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept {
const PAddr overlap_end = overlap_addr + static_cast<PAddr>(overlap_size);
@ -99,124 +94,4 @@ public:
std::vector<std::weak_ptr<Watcher>> watchers;
};
template <class S>
bool SurfaceBase<S>::CanFill(const SurfaceParams& dest_surface,
SurfaceInterval fill_interval) const {
if (type == SurfaceType::Fill && IsRegionValid(fill_interval) &&
boost::icl::first(fill_interval) >= addr &&
boost::icl::last_next(fill_interval) <= end && // dest_surface is within our fill range
dest_surface.FromInterval(fill_interval).GetInterval() ==
fill_interval) { // make sure interval is a rectangle in dest surface
if (fill_size * 8 != dest_surface.GetFormatBpp()) {
// Check if bits repeat for our fill_size
const u32 dest_bytes_per_pixel = std::max(dest_surface.GetFormatBpp() / 8, 1u);
std::vector<u8> fill_test(fill_size * dest_bytes_per_pixel);
for (u32 i = 0; i < dest_bytes_per_pixel; ++i)
std::memcpy(&fill_test[i * fill_size], &fill_data[0], fill_size);
for (u32 i = 0; i < fill_size; ++i)
if (std::memcmp(&fill_test[dest_bytes_per_pixel * i], &fill_test[0],
dest_bytes_per_pixel) != 0)
return false;
if (dest_surface.GetFormatBpp() == 4 && (fill_test[0] & 0xF) != (fill_test[0] >> 4))
return false;
}
return true;
}
return false;
}
template <class S>
bool SurfaceBase<S>::CanCopy(const SurfaceParams& dest_surface,
SurfaceInterval copy_interval) const {
SurfaceParams subrect_params = dest_surface.FromInterval(copy_interval);
ASSERT(subrect_params.GetInterval() == copy_interval);
if (CanSubRect(subrect_params))
return true;
if (CanFill(dest_surface, copy_interval))
return true;
return false;
}
template <class S>
SurfaceInterval SurfaceBase<S>::GetCopyableInterval(const SurfaceParams& params) const {
SurfaceInterval result{};
const u32 tile_align = params.BytesInPixels(params.is_tiled ? 8 * 8 : 1);
const auto valid_regions =
SurfaceRegions{params.GetInterval() & GetInterval()} - invalid_regions;
for (auto& valid_interval : valid_regions) {
const SurfaceInterval aligned_interval{
params.addr +
Common::AlignUp(boost::icl::first(valid_interval) - params.addr, tile_align),
params.addr +
Common::AlignDown(boost::icl::last_next(valid_interval) - params.addr, tile_align)};
if (tile_align > boost::icl::length(valid_interval) ||
boost::icl::length(aligned_interval) == 0) {
continue;
}
// Get the rectangle within aligned_interval
const u32 stride_bytes = params.BytesInPixels(params.stride) * (params.is_tiled ? 8 : 1);
SurfaceInterval rect_interval{
params.addr +
Common::AlignUp(boost::icl::first(aligned_interval) - params.addr, stride_bytes),
params.addr + Common::AlignDown(boost::icl::last_next(aligned_interval) - params.addr,
stride_bytes),
};
if (boost::icl::first(rect_interval) > boost::icl::last_next(rect_interval)) {
// 1 row
rect_interval = aligned_interval;
} else if (boost::icl::length(rect_interval) == 0) {
// 2 rows that do not make a rectangle, return the larger one
const SurfaceInterval row1{boost::icl::first(aligned_interval),
boost::icl::first(rect_interval)};
const SurfaceInterval row2{boost::icl::first(rect_interval),
boost::icl::last_next(aligned_interval)};
rect_interval = (boost::icl::length(row1) > boost::icl::length(row2)) ? row1 : row2;
}
if (boost::icl::length(rect_interval) > boost::icl::length(result)) {
result = rect_interval;
}
}
return result;
}
template <class S>
auto SurfaceBase<S>::CreateWatcher() -> std::shared_ptr<Watcher> {
auto weak_ptr = reinterpret_cast<S*>(this)->weak_from_this();
auto watcher = std::make_shared<Watcher>(std::move(weak_ptr));
watchers.push_back(watcher);
return watcher;
}
template <class S>
void SurfaceBase<S>::InvalidateAllWatcher() {
for (const auto& watcher : watchers) {
if (auto locked = watcher.lock()) {
locked->valid = false;
}
}
}
template <class S>
void SurfaceBase<S>::UnlinkAllWatcher() {
for (const auto& watcher : watchers) {
if (auto locked = watcher.lock()) {
locked->valid = false;
locked->surface.reset();
}
}
watchers.clear();
}
} // namespace VideoCore

View File

@ -313,7 +313,7 @@ void TextureRuntime::BindFramebuffer(GLenum target, GLint level, GLenum textarge
}
Surface::Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, driver{runtime.GetDriver()} {
: VideoCore::SurfaceBase{params}, runtime{runtime}, driver{runtime.GetDriver()} {
if (pixel_format != VideoCore::PixelFormat::Invalid) {
texture = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, params.pixel_format,
texture_type);

View File

@ -99,10 +99,10 @@ private:
OGLFramebuffer read_fbo, draw_fbo;
};
class Surface : public VideoCore::SurfaceBase<Surface> {
class Surface : public VideoCore::SurfaceBase {
public:
Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
~Surface() override;
~Surface();
/// Returns the surface image handle
GLuint Handle() const noexcept {

View File

@ -747,11 +747,8 @@ bool TextureRuntime::NeedsConvertion(VideoCore::PixelFormat format) const {
traits.aspect != (vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil);
}
Surface::Surface(TextureRuntime& runtime)
: runtime{runtime}, instance{runtime.GetInstance()}, scheduler{runtime.GetScheduler()} {}
Surface::Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime)
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, instance{runtime.GetInstance()},
: VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()},
scheduler{runtime.GetScheduler()}, traits{instance.GetTraits(pixel_format)} {
if (pixel_format != VideoCore::PixelFormat::Invalid) {
@ -762,7 +759,7 @@ Surface::Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime
Surface::Surface(const VideoCore::SurfaceParams& params, vk::Format format,
vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect, TextureRuntime& runtime)
: VideoCore::SurfaceBase<Surface>{params}, runtime{runtime}, instance{runtime.GetInstance()},
: VideoCore::SurfaceBase{params}, runtime{runtime}, instance{runtime.GetInstance()},
scheduler{runtime.GetScheduler()} {
if (format != vk::Format::eUndefined) {
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, pixel_format,

View File

@ -163,15 +163,14 @@ private:
std::unordered_multimap<HostTextureTag, ImageAlloc> texture_recycler;
};
class Surface : public VideoCore::SurfaceBase<Surface> {
class Surface : public VideoCore::SurfaceBase {
friend class TextureRuntime;
public:
Surface(TextureRuntime& runtime);
Surface(const VideoCore::SurfaceParams& params, TextureRuntime& runtime);
Surface(const VideoCore::SurfaceParams& params, vk::Format format, vk::ImageUsageFlags usage,
vk::ImageAspectFlags aspect, TextureRuntime& runtime);
~Surface() override;
~Surface();
/// Returns the surface aspect
vk::ImageAspectFlags Aspect() const noexcept {