OpenGL Cache: Refactor Surface Cache interface

Changes the public interface of the surface cache to make it easier to
use. Reintroduces the cached page count cached pages that was removed in
an earlier commit.
This commit is contained in:
James Rowe 2017-11-25 13:21:32 -07:00
parent 3e1cbb7d14
commit 81ea32d1e0
2 changed files with 170 additions and 53 deletions

View file

@ -1179,42 +1179,140 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
return match_surface;
}
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface,
bool invalidate) {
if (size == 0) {
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
if (size == 0)
return;
}
// Gather up unique surfaces that touch the region
std::unordered_set<std::shared_ptr<CachedSurface>> touching_surfaces;
const auto flush_interval = SurfaceInterval(addr, addr + size);
for (auto& pair : RangeFromInterval(dirty_regions, flush_interval)) {
const auto interval = pair.first & flush_interval;
auto& surface = pair.second;
auto surface_interval = boost::icl::interval<PAddr>::right_open(addr, addr + size);
auto cache_upper_bound = surface_cache.upper_bound(surface_interval);
for (auto it = surface_cache.lower_bound(surface_interval); it != cache_upper_bound; ++it) {
std::copy_if(it->second.begin(), it->second.end(),
std::inserter(touching_surfaces, touching_surfaces.end()),
[skip_surface](std::shared_ptr<CachedSurface> surface) {
return (surface.get() != skip_surface);
});
}
if (flush_surface != nullptr && surface != flush_surface)
continue;
// Flush and invalidate surfaces
for (auto surface : touching_surfaces) {
FlushSurface(surface.get());
if (invalidate) {
Memory::RasterizerMarkRegionCached(surface->addr, surface->size, -1);
surface_cache.subtract(
std::make_pair(boost::icl::interval<PAddr>::right_open(
surface->addr, surface->addr + surface->size),
std::set<std::shared_ptr<CachedSurface>>({surface})));
// Sanity check, this surface is the last one that marked this region dirty
ASSERT(surface->IsRegionValid(interval));
if (surface->type != SurfaceType::Fill) {
SurfaceParams params = surface->FromInterval(interval);
surface->DownloadGLTexture(surface->GetSubRect(params));
}
surface->FlushGLBuffer(boost::icl::first(interval), boost::icl::last_next(interval));
}
// Reset dirty regions
dirty_regions.erase(flush_interval);
}
void RasterizerCacheOpenGL::FlushAll() {
for (auto& surfaces : surface_cache) {
for (auto& surface : surfaces.second) {
FlushSurface(surface.get());
FlushRegion(0, 0xFFFFFFFF);
}
void RasterizerCacheOpenGL::InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner) {
if (size == 0)
return;
const auto invalid_interval = SurfaceInterval(addr, addr + size);
if (region_owner != nullptr) {
ASSERT(region_owner->type != SurfaceType::Texture);
ASSERT(addr >= region_owner->addr && addr + size <= region_owner->end);
ASSERT(region_owner->width == region_owner->stride); // Surfaces can't have a gap
region_owner->invalid_regions.erase(invalid_interval);
}
for (auto& pair : RangeFromInterval(surface_cache, invalid_interval)) {
for (auto& cached_surface : pair.second) {
if (cached_surface == region_owner)
continue;
const auto interval = cached_surface->GetInterval() & invalid_interval;
cached_surface->invalid_regions.insert(interval);
// Remove only "empty" fill surfaces to avoid destroying and recreating OGL textures
if (cached_surface->type == SurfaceType::Fill &&
cached_surface->IsSurfaceFullyInvalid()) {
remove_surfaces.emplace(cached_surface);
}
}
}
if (region_owner != nullptr)
dirty_regions.set({invalid_interval, region_owner});
else
dirty_regions.erase(invalid_interval);
for (auto& remove_surface : remove_surfaces) {
if (remove_surface == region_owner) {
Surface expanded_surface = FindMatch<MatchFlags::SubRect | MatchFlags::Invalid>(
surface_cache, *region_owner, ScaleMatch::Ignore);
ASSERT(expanded_surface);
if ((region_owner->invalid_regions - expanded_surface->invalid_regions).empty()) {
DuplicateSurface(region_owner, expanded_surface);
} else {
continue;
}
}
UnregisterSurface(remove_surface);
}
remove_surfaces.clear();
}
Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) {
Surface surface = std::make_shared<CachedSurface>();
static_cast<SurfaceParams&>(*surface) = params;
surface->texture.Create();
surface->gl_buffer_size = 0;
surface->invalid_regions.insert(surface->GetInterval());
AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format),
surface->GetScaledWidth(), surface->GetScaledHeight());
return surface;
}
void RasterizerCacheOpenGL::RegisterSurface(const Surface& surface) {
surface_cache.add({surface->GetInterval(), SurfaceSet{surface}});
UpdatePagesCachedCount(surface->addr, surface->size, 1);
}
void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
UpdatePagesCachedCount(surface->addr, surface->size, -1);
surface_cache.subtract({surface->GetInterval(), SurfaceSet{surface}});
}
void RasterizerCacheOpenGL::UpdatePagesCachedCount(PAddr addr, u32 size, int delta) {
const u32 num_pages =
((addr + size - 1) >> Memory::PAGE_BITS) - (addr >> Memory::PAGE_BITS) + 1;
const u32 page_start = addr >> Memory::PAGE_BITS;
const u32 page_end = page_start + num_pages;
// Interval maps will erase segments if count reaches 0, so if delta is negative we have to
// subtract after iterating
const auto pages_interval = PageMap::interval_type::right_open(page_start, page_end);
if (delta > 0)
cached_pages.add({pages_interval, delta});
for (auto& pair : RangeFromInterval(cached_pages, pages_interval)) {
const auto interval = pair.first & pages_interval;
const int count = pair.second;
const PAddr interval_start_addr = boost::icl::first(interval) << Memory::PAGE_BITS;
const PAddr interval_end_addr = boost::icl::last_next(interval) << Memory::PAGE_BITS;
const u32 interval_size = interval_end_addr - interval_start_addr;
if (delta > 0 && count == delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, true);
else if (delta < 0 && count == -delta)
Memory::RasterizerMarkRegionCached(interval_start_addr, interval_size, false);
else
ASSERT(count >= 0);
}
if (delta < 0)
cached_pages.add({pages_interval, delta});
}

View file

@ -298,46 +298,65 @@ public:
RasterizerCacheOpenGL();
~RasterizerCacheOpenGL();
/// Blits one texture to another
void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type,
const MathUtil::Rectangle<int>& src_rect,
const MathUtil::Rectangle<int>& dst_rect);
/// Blit one surface's texture to another
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
/// Attempt to blit one surface's texture to another
bool TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle<int>& src_rect,
CachedSurface* dst_surface, const MathUtil::Rectangle<int>& dst_rect);
/// Copy one surface's region to another
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
SurfaceInterval copy_interval);
/// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
CachedSurface* GetSurface(const CachedSurface& params, bool match_res_scale,
bool load_if_create);
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
/// 3DS memory to OpenGL and caches it (if not already cached)
CachedSurface* GetSurfaceRect(const CachedSurface& params, bool match_res_scale,
bool load_if_create, MathUtil::Rectangle<int>& out_rect);
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
bool load_if_create);
/// Gets a surface based on the texture configuration
CachedSurface* GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
/// Get a surface based on the texture configuration
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
/// Gets the color and depth surfaces and rect (resolution scaled) based on the framebuffer
/// configuration
std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> GetFramebufferSurfaces(
const Pica::FramebufferRegs::FramebufferConfig& config);
/// Get the color and depth surfaces based on the framebuffer configuration
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
const MathUtil::Rectangle<s32>& viewport_rect);
/// Attempt to get a surface that exactly matches the fill region and format
CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config);
/// Get a surface that matches the fill config
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);
/// Write the surface back to memory
void FlushSurface(CachedSurface* surface);
/// Get a surface that matches a "texture copy" display transfer config
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
/// Write any cached resources overlapping the region back to memory (if dirty) and optionally
/// invalidate them in the cache
void FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate);
/// Write any cached resources overlapping the region back to memory (if dirty)
void FlushRegion(PAddr addr, u32 size, Surface flush_surface = nullptr);
/// Mark region as being invalidated by region_owner (nullptr if 3DS memory)
void InvalidateRegion(PAddr addr, u32 size, const Surface& region_owner);
/// Flush all cached resources tracked by this cache manager
void FlushAll();
private:
void DuplicateSurface(const Surface& src_surface, const Surface& dest_surface);
/// Update surface's texture for given region when necessary
void ValidateSurface(const Surface& surface, PAddr addr, u32 size);
/// Create a new surface
Surface CreateSurface(const SurfaceParams& params);
/// Register surface into the cache
void RegisterSurface(const Surface& surface);
/// Remove surface from the cache
void UnregisterSurface(const Surface& surface);
/// Increase/decrease the number of surface in pages touching the specified region
void UpdatePagesCachedCount(PAddr addr, u32 size, int delta);
SurfaceCache surface_cache;
OGLFramebuffer transfer_framebuffers[2];
SurfaceMap dirty_regions;
PageMap cached_pages;
SurfaceSet remove_surfaces;
};