Play-/Source/gs/GsCachedArea.cpp
2018-04-30 21:01:23 +01:00

175 lines
4.5 KiB
C++

#include <cassert>
#include "GsCachedArea.h"
#include "GsPixelFormats.h"
static bool DoMemoryRangesOverlap(uint32 start1, uint32 size1, uint32 start2, uint32 size2)
{
uint32 min1 = start1;
uint32 max1 = start1 + size1;
uint32 min2 = start2;
uint32 max2 = start2 + size2;
if(max1 <= min2) return false;
if(min1 >= max2) return false;
return true;
}
CGsCachedArea::CGsCachedArea()
{
ClearDirtyPages();
}
void CGsCachedArea::SetArea(uint32 psm, uint32 bufPtr, uint32 bufWidth, uint32 height)
{
m_psm = psm;
m_bufPtr = bufPtr;
m_bufWidth = bufWidth;
m_height = height;
}
CGsCachedArea::PageRect CGsCachedArea::GetAreaPageRect() const
{
auto texturePageSize = CGsPixelFormats::GetPsmPageSize(m_psm);
uint32 pageCountX = (m_bufWidth + texturePageSize.first - 1) / texturePageSize.first;
uint32 pageCountY = (m_height + texturePageSize.second - 1) / texturePageSize.second;
return PageRect{0, 0, pageCountX, pageCountY};
}
CGsCachedArea::PageRect CGsCachedArea::GetDirtyPageRect() const
{
auto areaRect = GetAreaPageRect();
//Find starting point
uint32 startX = 0, startY = 0;
for(startY = 0; startY < areaRect.height; startY++)
{
bool done = false;
for(startX = 0; startX < areaRect.width; startX++)
{
uint32 pageIndex = startX + (startY * areaRect.width);
if(IsPageDirty(pageIndex))
{
done = true;
break;
}
}
if(done) break;
}
if((startX == areaRect.width) || (startY == areaRect.height))
{
return PageRect{0, 0, 0, 0};
}
const auto getHorzSpan =
[&](uint32 bx, uint32 by) {
uint32 span = 0;
for(uint32 x = bx; x < areaRect.width; x++)
{
uint32 pageIndex = x + (by * areaRect.width);
if(!IsPageDirty(pageIndex)) break;
span++;
}
return span;
};
//Check how high is the dirty rect
uint32 spanX = getHorzSpan(startX, startY);
uint32 spanY = 1;
for(uint32 y = startY + 1; y < areaRect.height; y++)
{
uint32 lineSpanX = getHorzSpan(startX, y);
if(lineSpanX < spanX) break;
spanY++;
}
return PageRect{startX, startY, spanX, spanY};
}
uint32 CGsCachedArea::GetPageCount() const
{
auto areaRect = GetAreaPageRect();
return areaRect.width * areaRect.height;
}
uint32 CGsCachedArea::GetSize() const
{
return GetPageCount() * CGsPixelFormats::PAGESIZE;
}
void CGsCachedArea::Invalidate(uint32 memoryStart, uint32 memorySize)
{
uint32 areaSize = GetSize();
if(DoMemoryRangesOverlap(memoryStart, memorySize, m_bufPtr, areaSize))
{
//Find the pages that are touched by this transfer
uint32 pageStart = (memoryStart < m_bufPtr) ? 0 : ((memoryStart - m_bufPtr) / CGsPixelFormats::PAGESIZE);
uint32 pageCount = (memorySize + CGsPixelFormats::PAGESIZE - 1) / CGsPixelFormats::PAGESIZE;
uint32 areaPageCount = GetPageCount();
for(unsigned int i = 0; i < pageCount; i++)
{
uint32 pageIndex = pageStart + i;
if(pageIndex >= areaPageCount) break;
SetPageDirty(pageIndex);
}
//Wouldn't make sense to go through here and not have at least a dirty page
assert(HasDirtyPages());
}
}
bool CGsCachedArea::IsPageDirty(uint32 pageIndex) const
{
assert(pageIndex < sizeof(m_dirtyPages) * 8);
unsigned int dirtyPageSection = pageIndex / (sizeof(m_dirtyPages[0]) * 8);
unsigned int dirtyPageIndex = pageIndex % (sizeof(m_dirtyPages[0]) * 8);
return (m_dirtyPages[dirtyPageSection] & (1ULL << dirtyPageIndex)) != 0;
}
void CGsCachedArea::SetPageDirty(uint32 pageIndex)
{
assert(pageIndex < sizeof(m_dirtyPages) * 8);
unsigned int dirtyPageSection = pageIndex / (sizeof(m_dirtyPages[0]) * 8);
unsigned int dirtyPageIndex = pageIndex % (sizeof(m_dirtyPages[0]) * 8);
m_dirtyPages[dirtyPageSection] |= (1ULL << dirtyPageIndex);
}
bool CGsCachedArea::HasDirtyPages() const
{
DirtyPageHolder dirtyStatus = 0;
for(unsigned int i = 0; i < MAX_DIRTYPAGES_SECTIONS; i++)
{
dirtyStatus |= m_dirtyPages[i];
}
return (dirtyStatus != 0);
}
void CGsCachedArea::ClearDirtyPages()
{
memset(m_dirtyPages, 0, sizeof(m_dirtyPages));
}
void CGsCachedArea::ClearDirtyPages(const PageRect& rect)
{
auto areaRect = GetAreaPageRect();
uint32 endX = rect.x + rect.width;
uint32 endY = rect.y + rect.height;
for(uint32 y = rect.y; y < endY; y++)
{
for(uint32 x = rect.x; x < endX; x++)
{
uint32 pageIndex = x + (y * areaRect.width);
assert(pageIndex < sizeof(m_dirtyPages) * 8);
assert(pageIndex < GetPageCount());
unsigned int dirtyPageSection = pageIndex / (sizeof(m_dirtyPages[0]) * 8);
unsigned int dirtyPageIndex = pageIndex % (sizeof(m_dirtyPages[0]) * 8);
m_dirtyPages[dirtyPageSection] &= ~(1ULL << dirtyPageIndex);
}
}
}