rasterizer_cache: Simplify SurfaceBase
* The casts are bit ugly but will be refactored soon
This commit is contained in:
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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,
|
||||
|
127
src/video_core/rasterizer_cache/surface_base.cpp
Normal file
127
src/video_core/rasterizer_cache/surface_base.cpp
Normal 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
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
Reference in New Issue
Block a user