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:
parent
3e1cbb7d14
commit
81ea32d1e0
|
@ -1179,42 +1179,140 @@ Surface FindMatch(const SurfaceCache& surface_cache, const SurfaceParams& params
|
||||||
return match_surface;
|
return match_surface;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface,
|
void RasterizerCacheOpenGL::FlushRegion(PAddr addr, u32 size, Surface flush_surface) {
|
||||||
bool invalidate) {
|
if (size == 0)
|
||||||
if (size == 0) {
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (flush_surface != nullptr && surface != flush_surface)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// 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));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gather up unique surfaces that touch the region
|
// Reset dirty regions
|
||||||
std::unordered_set<std::shared_ptr<CachedSurface>> touching_surfaces;
|
dirty_regions.erase(flush_interval);
|
||||||
|
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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})));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RasterizerCacheOpenGL::FlushAll() {
|
void RasterizerCacheOpenGL::FlushAll() {
|
||||||
for (auto& surfaces : surface_cache) {
|
FlushRegion(0, 0xFFFFFFFF);
|
||||||
for (auto& surface : surfaces.second) {
|
}
|
||||||
FlushSurface(surface.get());
|
|
||||||
|
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});
|
||||||
|
}
|
||||||
|
|
|
@ -298,46 +298,65 @@ public:
|
||||||
RasterizerCacheOpenGL();
|
RasterizerCacheOpenGL();
|
||||||
~RasterizerCacheOpenGL();
|
~RasterizerCacheOpenGL();
|
||||||
|
|
||||||
/// Blits one texture to another
|
/// Blit one surface's texture to another
|
||||||
void BlitTextures(GLuint src_tex, GLuint dst_tex, CachedSurface::SurfaceType type,
|
bool BlitSurfaces(const Surface& src_surface, const MathUtil::Rectangle<u32>& src_rect,
|
||||||
const MathUtil::Rectangle<int>& src_rect,
|
const Surface& dst_surface, const MathUtil::Rectangle<u32>& dst_rect);
|
||||||
const MathUtil::Rectangle<int>& dst_rect);
|
|
||||||
|
|
||||||
/// Attempt to blit one surface's texture to another
|
/// Copy one surface's region to another
|
||||||
bool TryBlitSurfaces(CachedSurface* src_surface, const MathUtil::Rectangle<int>& src_rect,
|
void CopySurface(const Surface& src_surface, const Surface& dst_surface,
|
||||||
CachedSurface* dst_surface, const MathUtil::Rectangle<int>& dst_rect);
|
SurfaceInterval copy_interval);
|
||||||
|
|
||||||
/// Loads a texture from 3DS memory to OpenGL and caches it (if not already cached)
|
/// Load a texture from 3DS memory to OpenGL and cache it (if not already cached)
|
||||||
CachedSurface* GetSurface(const CachedSurface& params, bool match_res_scale,
|
Surface GetSurface(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||||
bool load_if_create);
|
bool load_if_create);
|
||||||
|
|
||||||
/// Attempt to find a subrect (resolution scaled) of a surface, otherwise loads a texture from
|
/// 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)
|
/// 3DS memory to OpenGL and caches it (if not already cached)
|
||||||
CachedSurface* GetSurfaceRect(const CachedSurface& params, bool match_res_scale,
|
SurfaceRect_Tuple GetSurfaceSubRect(const SurfaceParams& params, ScaleMatch match_res_scale,
|
||||||
bool load_if_create, MathUtil::Rectangle<int>& out_rect);
|
bool load_if_create);
|
||||||
|
|
||||||
/// Gets a surface based on the texture configuration
|
/// Get a surface based on the texture configuration
|
||||||
CachedSurface* GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
Surface GetTextureSurface(const Pica::TexturingRegs::FullTextureConfig& config);
|
||||||
|
|
||||||
/// Gets the color and depth surfaces and rect (resolution scaled) based on the framebuffer
|
/// Get the color and depth surfaces based on the framebuffer configuration
|
||||||
/// configuration
|
SurfaceSurfaceRect_Tuple GetFramebufferSurfaces(bool using_color_fb, bool using_depth_fb,
|
||||||
std::tuple<CachedSurface*, CachedSurface*, MathUtil::Rectangle<int>> GetFramebufferSurfaces(
|
const MathUtil::Rectangle<s32>& viewport_rect);
|
||||||
const Pica::FramebufferRegs::FramebufferConfig& config);
|
|
||||||
|
|
||||||
/// Attempt to get a surface that exactly matches the fill region and format
|
/// Get a surface that matches the fill config
|
||||||
CachedSurface* TryGetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
Surface GetFillSurface(const GPU::Regs::MemoryFillConfig& config);
|
||||||
|
|
||||||
/// Write the surface back to memory
|
/// Get a surface that matches a "texture copy" display transfer config
|
||||||
void FlushSurface(CachedSurface* surface);
|
SurfaceRect_Tuple GetTexCopySurface(const SurfaceParams& params);
|
||||||
|
|
||||||
/// Write any cached resources overlapping the region back to memory (if dirty) and optionally
|
/// Write any cached resources overlapping the region back to memory (if dirty)
|
||||||
/// invalidate them in the cache
|
void FlushRegion(PAddr addr, u32 size, Surface flush_surface = nullptr);
|
||||||
void FlushRegion(PAddr addr, u32 size, const CachedSurface* skip_surface, bool invalidate);
|
|
||||||
|
/// 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
|
/// Flush all cached resources tracked by this cache manager
|
||||||
void FlushAll();
|
void FlushAll();
|
||||||
|
|
||||||
private:
|
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;
|
SurfaceCache surface_cache;
|
||||||
OGLFramebuffer transfer_framebuffers[2];
|
SurfaceMap dirty_regions;
|
||||||
|
PageMap cached_pages;
|
||||||
|
SurfaceSet remove_surfaces;
|
||||||
};
|
};
|
||||||
|
|
Reference in New Issue