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/sampler_params.h
rasterizer_cache/slot_vector.h rasterizer_cache/slot_vector.h
rasterizer_cache/surface_base.h rasterizer_cache/surface_base.h
rasterizer_cache/surface_base.cpp
rasterizer_cache/utils.cpp rasterizer_cache/utils.cpp
rasterizer_cache/utils.h rasterizer_cache/utils.h
rasterizer_cache/surface_params.cpp rasterizer_cache/surface_params.cpp

View File

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

View File

@ -47,7 +47,6 @@ class RasterizerCache : NonCopyable {
using TextureRuntime = typename T::RuntimeType; using TextureRuntime = typename T::RuntimeType;
using Sampler = typename T::Sampler; using Sampler = typename T::Sampler;
using Surface = std::shared_ptr<typename T::SurfaceType>; using Surface = std::shared_ptr<typename T::SurfaceType>;
using Watcher = SurfaceWatcher<typename T::SurfaceType>;
/// Declare rasterizer interval types /// Declare rasterizer interval types
using SurfaceMap = boost::icl::interval_map<PAddr, Surface, boost::icl::partial_absorber, 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 #pragma once
#include <memory> #include <memory>
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/rasterizer_cache/surface_params.h" #include "video_core/rasterizer_cache/surface_params.h"
namespace VideoCore { namespace VideoCore {
using SurfaceRegions = boost::icl::interval_set<PAddr, std::less, SurfaceInterval>; 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 * A watcher that notifies whether a cached surface has been changed. This is useful for caching
* surface collection objects, including texture cube and mipmap. * surface collection objects, including texture cube and mipmap.
*/ */
template <class S> class Watcher {
class SurfaceWatcher {
public: 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. /// Checks whether the surface has been changed.
bool IsValid() const { bool IsValid() const {
@ -34,23 +33,19 @@ public:
} }
/// Gets the referencing surface. Returns null if the surface has been destroyed /// 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(); return surface.lock();
} }
public: public:
std::weak_ptr<S> surface; std::weak_ptr<SurfaceBase> surface;
bool valid = false; bool valid = false;
}; };
template <class S> class SurfaceBase : public SurfaceParams, public std::enable_shared_from_this<SurfaceBase> {
class SurfaceBase : public SurfaceParams, public std::enable_shared_from_this<S> {
using Watcher = SurfaceWatcher<S>;
public: public:
SurfaceBase() = default; SurfaceBase();
SurfaceBase(const SurfaceParams& params) : SurfaceParams{params} {} explicit SurfaceBase(const SurfaceParams& params);
virtual ~SurfaceBase() = default;
[[nodiscard]] bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept { [[nodiscard]] bool Overlaps(PAddr overlap_addr, size_t overlap_size) const noexcept {
const PAddr overlap_end = overlap_addr + static_cast<PAddr>(overlap_size); const PAddr overlap_end = overlap_addr + static_cast<PAddr>(overlap_size);
@ -99,124 +94,4 @@ public:
std::vector<std::weak_ptr<Watcher>> watchers; 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 } // namespace VideoCore

View File

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

View File

@ -99,10 +99,10 @@ private:
OGLFramebuffer read_fbo, draw_fbo; OGLFramebuffer read_fbo, draw_fbo;
}; };
class Surface : public VideoCore::SurfaceBase<Surface> { class Surface : public VideoCore::SurfaceBase {
public: public:
Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime); Surface(VideoCore::SurfaceParams& params, TextureRuntime& runtime);
~Surface() override; ~Surface();
/// Returns the surface image handle /// Returns the surface image handle
GLuint Handle() const noexcept { 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); 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) 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)} { scheduler{runtime.GetScheduler()}, traits{instance.GetTraits(pixel_format)} {
if (pixel_format != VideoCore::PixelFormat::Invalid) { 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, Surface::Surface(const VideoCore::SurfaceParams& params, vk::Format format,
vk::ImageUsageFlags usage, vk::ImageAspectFlags aspect, TextureRuntime& runtime) 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()} { scheduler{runtime.GetScheduler()} {
if (format != vk::Format::eUndefined) { if (format != vk::Format::eUndefined) {
alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, pixel_format, alloc = runtime.Allocate(GetScaledWidth(), GetScaledHeight(), levels, pixel_format,

View File

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