mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
VSH prep: Free up kernel memory by not storing the PPGe atlas in it
This commit is contained in:
1 parent
14338534e7
commit
46d5fae342
9 files changed
+161
-111
No files matched your search
@@ -42,6 +42,24 @@
|
||||
// Global declarations
|
||||
class PointerWrap;
|
||||
|
||||
// The PPGe font atlas doesn't live in emulated RAM, but the GE still needs an address to texture
|
||||
// from, so we hand it this fake one and translate it back to a host pointer where it's used.
|
||||
// NOTE: The top 4 bits must be zero due to how the address is stored in the gstate.
|
||||
constexpr u32 PPGE_ATLAS_FAKE_ADDRESS = 0x03000000;
|
||||
// The size of the atlas, or 0 when PPGe isn't initialized. Set up by PPGeDraw.cpp.
|
||||
extern u32 g_ppgeAtlasFakeSize;
|
||||
|
||||
inline bool IsPPGEAtlasFakeAddress(u32 addr, u32 *offset) {
|
||||
if (addr >= PPGE_ATLAS_FAKE_ADDRESS && addr < PPGE_ATLAS_FAKE_ADDRESS + g_ppgeAtlasFakeSize) {
|
||||
if (offset) {
|
||||
*offset = addr - PPGE_ATLAS_FAKE_ADDRESS;
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef void (*writeFn8 )(const u8, const u32);
|
||||
typedef void (*writeFn16)(const u16,const u32);
|
||||
typedef void (*writeFn32)(const u32,const u32);
|
||||
@@ -377,6 +395,8 @@ inline bool IsValidTextureAddress(const u32 address) {
|
||||
return true;
|
||||
} else if ((address & 0x3E00000F) == 0x08000000 && (address & 0x3F000000) >= 0x08000000 && ((address & 0x3F000000) < 0x08000000 + g_MemorySize)) {
|
||||
return true; // Extended RAM.
|
||||
} else if (IsPPGEAtlasFakeAddress(address, nullptr)) {
|
||||
return true; // PPGe atlas texture
|
||||
} else {
|
||||
// Can't texture from scratchpad or other kinds of memory.
|
||||
return false;
|
||||
|
||||
+92
-92
@@ -48,11 +48,11 @@
|
||||
static Atlas g_ppge_atlas;
|
||||
static Draw::DrawContext *g_draw = nullptr;
|
||||
|
||||
static u32 atlasPtr;
|
||||
static int atlasWidth;
|
||||
static int atlasHeight;
|
||||
static uint64_t atlasHash;
|
||||
static bool atlasRequiresReset;
|
||||
|
||||
// The size of the atlas as seen through PPGE_ATLAS_FAKE_ADDRESS, see MemMap.h. 0 until initialized.
|
||||
u32 g_ppgeAtlasFakeSize;
|
||||
|
||||
struct PPGeVertex {
|
||||
u16_le u, v;
|
||||
@@ -67,43 +67,43 @@ struct PPGeRemasterVertex {
|
||||
};
|
||||
|
||||
static PSPPointer<PspGeListArgs> listArgs;
|
||||
static u32 listArgsSize = sizeof(PspGeListArgs);
|
||||
constexpr u32 listArgsSize = sizeof(PspGeListArgs);
|
||||
static u32 savedContextPtr;
|
||||
static u32 savedContextSize = 512 * 4;
|
||||
constexpr u32 savedContextSize = 512 * 4;
|
||||
|
||||
// Display list writer
|
||||
static u32 dlPtr;
|
||||
static u32 dlWritePtr;
|
||||
static u32 dlSize = 0x10000; // should be enough for a frame of gui...
|
||||
constexpr u32 dlSize = 0x10000; // should be enough for a frame of gui...
|
||||
|
||||
static u32 dataPtr;
|
||||
static u32 dataWritePtr;
|
||||
static u32 dataSize = 0x10000; // should be enough for a frame of gui...
|
||||
constexpr u32 dataSize = 0x10000; // should be enough for a frame of gui...
|
||||
|
||||
static u8 *g_atlasImageData = nullptr;
|
||||
|
||||
static PSPPointer<u16_le> palette;
|
||||
static u32 paletteSize = sizeof(u16) * 16;
|
||||
constexpr u32 paletteSize = sizeof(u16) * 16;
|
||||
static constexpr u16_le paletteData[16] = { 0x0FFF, 0x1FFF, 0x2FFF, 0x3FFF, 0x4FFF, 0x5FFF, 0x6FFF, 0x7FFF, 0x8FFF, 0x9FFF, 0xAFFF, 0xBFFF, 0xCFFF, 0xDFFF, 0xEFFF, 0xFFFF };
|
||||
|
||||
// Vertex collector
|
||||
static u32 vertexStart;
|
||||
static u32 vertexCount;
|
||||
|
||||
// Used for formatting text
|
||||
struct AtlasCharVertex
|
||||
{
|
||||
struct AtlasCharVertex {
|
||||
float x;
|
||||
float y;
|
||||
const AtlasChar *c;
|
||||
};
|
||||
|
||||
struct AtlasTextMetrics
|
||||
{
|
||||
struct AtlasTextMetrics {
|
||||
float x;
|
||||
float y;
|
||||
float maxWidth;
|
||||
float lineHeight;
|
||||
float scale;
|
||||
int numLines;
|
||||
|
||||
};
|
||||
|
||||
typedef std::vector<AtlasCharVertex> AtlasCharLine;
|
||||
@@ -129,9 +129,8 @@ struct PPGeTextDrawerCacheKey {
|
||||
};
|
||||
|
||||
struct PPGeTextDrawerImage {
|
||||
PPGeTextDrawerImage() : entry(0) {}
|
||||
TextStringEntry entry;
|
||||
u32 ptr;
|
||||
TextStringEntry entry = 0;
|
||||
u32 ptr = 0;
|
||||
};
|
||||
|
||||
static std::map<PPGeTextDrawerCacheKey, PPGeTextDrawerImage> textDrawerImages;
|
||||
@@ -154,7 +153,7 @@ static void PPGeDecimateTextImages(int age = 97);
|
||||
|
||||
void PPGeSetTexture(u32 dataAddr, int width, int height);
|
||||
|
||||
//only 0xFFFFFF of data is used
|
||||
// only 0xFFFFFF of data is used
|
||||
static void WriteCmd(u8 cmd, u32 data) {
|
||||
Memory::WriteUnchecked_U32((cmd << 24) | (data & 0xFFFFFF), dlWritePtr);
|
||||
dlWritePtr += 4;
|
||||
@@ -209,18 +208,15 @@ static void EndVertexDataAndDraw(int prim) {
|
||||
vertexStart = 0;
|
||||
}
|
||||
|
||||
bool PPGeIsFontTextureAddress(u32 addr) {
|
||||
return addr == atlasPtr;
|
||||
}
|
||||
static u32 __PPGeDoAlloc(u32 size, bool fromTop, const char *name) {
|
||||
u32 sizeRounded = size;
|
||||
|
||||
static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) {
|
||||
u32 ptr = kernelMemory.Alloc(size, fromTop, name);
|
||||
u32 ptr = kernelMemory.Alloc(sizeRounded, fromTop, name);
|
||||
// Didn't get it, try again after decimating images.
|
||||
if (ptr == (u32)-1) {
|
||||
PPGeDecimateTextImages(4);
|
||||
PPGeImage::Decimate(4);
|
||||
|
||||
ptr = kernelMemory.Alloc(size, fromTop, name);
|
||||
ptr = kernelMemory.Alloc(sizeRounded, fromTop, name);
|
||||
if (ptr == (u32)-1) {
|
||||
return 0;
|
||||
}
|
||||
@@ -228,8 +224,7 @@ static u32 __PPGeDoAlloc(u32 &size, bool fromTop, const char *name) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void __PPGeSetupListArgs()
|
||||
{
|
||||
void __PPGeSetupListArgs() {
|
||||
if (listArgs.IsValid())
|
||||
return;
|
||||
|
||||
@@ -243,9 +238,9 @@ void __PPGeSetupListArgs()
|
||||
}
|
||||
|
||||
void __PPGeInit() {
|
||||
// PPGe isn't really important for headless, and LoadZIM takes a long time.
|
||||
// TODO: Load it lazily, and not into kernel memory.
|
||||
bool skipZIM = System_GetPropertyBool(SYSPROP_IS_HEADLESS);
|
||||
// PPGe isn't really important for headless, and LoadZIM takes a long time (well not that long, but unnecessary).
|
||||
// TODO: Load it lazily.
|
||||
const bool skipZIM = System_GetPropertyBool(SYSPROP_IS_HEADLESS);
|
||||
|
||||
u8 *imageData[12]{};
|
||||
int width[12]{};
|
||||
@@ -275,35 +270,28 @@ void __PPGeInit() {
|
||||
dlPtr = __PPGeDoAlloc(dlSize, false, "PPGe Display List");
|
||||
dataPtr = __PPGeDoAlloc(dataSize, false, "PPGe Vertex Data");
|
||||
__PPGeSetupListArgs();
|
||||
atlasPtr = atlasSize == 0 ? 0 : __PPGeDoAlloc(atlasSize, false, "PPGe Atlas Texture");
|
||||
palette = __PPGeDoAlloc(paletteSize, false, "PPGe Texture Palette");
|
||||
Memory::Memcpy(palette.ptr, (const void *)paletteData, paletteSize, "PPGe Palette");
|
||||
|
||||
// Generate 16-greyscale palette. All PPGe graphics are greyscale so we can use a tiny paletted texture.
|
||||
for (int i = 0; i < 16; i++) {
|
||||
int val = i;
|
||||
palette[i] = (val << 12) | 0xFFF;
|
||||
if (loadedZIM) {
|
||||
const u32_le *imagePtr = (u32_le *)imageData[0];
|
||||
_dbg_assert_(!g_atlasImageData);
|
||||
g_atlasImageData = new u8[atlasSize];
|
||||
g_ppgeAtlasFakeSize = atlasSize;
|
||||
|
||||
// Palettize to 4-bit, the easy way.
|
||||
for (int i = 0; i < width[0] * height[0] / 2; i++) {
|
||||
// Each pixel is 16 bits, so this loads two pixels.
|
||||
const u32 c = imagePtr[i];
|
||||
// It's white anyway, so we only look at one channel of each pixel.
|
||||
const int a1 = (c & 0x0000000F) >> 0;
|
||||
const int a2 = (c & 0x000F0000) >> 16;
|
||||
const u8 cval = (a2 << 4) | a1;
|
||||
g_atlasImageData[i] = cval;
|
||||
}
|
||||
|
||||
free(imageData[0]);
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, palette.ptr, 16 * sizeof(u16_le), "PPGe Palette");
|
||||
|
||||
const u32_le *imagePtr = (u32_le *)imageData[0];
|
||||
u8 *ramPtr = atlasPtr == 0 ? nullptr : (u8 *)Memory::GetPointerRangeOrException(atlasPtr, atlasSize);
|
||||
|
||||
// Palettize to 4-bit, the easy way.
|
||||
for (int i = 0; i < width[0] * height[0] / 2; i++) {
|
||||
// Each pixel is 16 bits, so this loads two pixels.
|
||||
u32 c = imagePtr[i];
|
||||
// It's white anyway, so we only look at one channel of each pixel.
|
||||
int a1 = (c & 0x0000000F) >> 0;
|
||||
int a2 = (c & 0x000F0000) >> 16;
|
||||
u8 cval = (a2 << 4) | a1;
|
||||
ramPtr[i] = cval;
|
||||
}
|
||||
if (atlasPtr != 0) {
|
||||
atlasHash = XXH3_64bits(ramPtr, atlasSize);
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, atlasPtr, atlasSize, "PPGe Atlas");
|
||||
}
|
||||
|
||||
free(imageData[0]);
|
||||
|
||||
// We can't create it here, because Android needs it on the right thread.
|
||||
// Avoid creating ever on headless just to be safe.
|
||||
@@ -311,10 +299,7 @@ void __PPGeInit() {
|
||||
textDrawer = nullptr;
|
||||
textDrawerImages.clear();
|
||||
|
||||
atlasRequiresReset = false;
|
||||
|
||||
INFO_LOG(Log::sceGe, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas: %08x (%i) Args: %08x",
|
||||
dlPtr, dataPtr, atlasPtr, atlasSize, listArgs.ptr);
|
||||
INFO_LOG(Log::sceGe, "PPGe drawing library initialized. DL: %08x Data: %08x Atlas (%i) Args: %08x", dlPtr, dataPtr, atlasSize, listArgs.ptr);
|
||||
}
|
||||
|
||||
void __PPGeDoState(PointerWrap &p)
|
||||
@@ -323,27 +308,35 @@ void __PPGeDoState(PointerWrap &p)
|
||||
if (!s)
|
||||
return;
|
||||
|
||||
// Old format: the atlas lived in emulated kernel RAM. We just dummy those fields out, and free
|
||||
// the memory if the state we're loading has some. Note that these must stay initialized - they're
|
||||
// read (not just written) when saving, and uninitialized memory in a savestate is not acceptable.
|
||||
u32 atlasPtr = 0;
|
||||
int dummyAtlasWidth = 0;
|
||||
int dummyAtlasHeight = 0;
|
||||
Do(p, atlasPtr);
|
||||
Do(p, atlasWidth);
|
||||
Do(p, atlasHeight);
|
||||
Do(p, dummyAtlasWidth);
|
||||
Do(p, dummyAtlasHeight);
|
||||
Do(p, palette);
|
||||
|
||||
// If the atlas the save state was created with differs from the current one, reload.
|
||||
uint64_t savedHash = atlasHash;
|
||||
if (s >= 4) {
|
||||
Do(p, savedHash);
|
||||
} else {
|
||||
// Memory was already updated by this point, so check directly.
|
||||
if (atlasPtr != 0) {
|
||||
savedHash = XXH3_64bits(Memory::GetPointerRangeOrException(atlasPtr, atlasWidth * atlasHeight / 2), atlasWidth * atlasHeight / 2);
|
||||
} else {
|
||||
savedHash ^= 1;
|
||||
}
|
||||
if (p.mode == PointerWrap::MODE_READ && atlasPtr) {
|
||||
// We don't store the atlas in emulated RAM anymore, so free the old allocation. This is safe
|
||||
// here - __KernelMemoryDoState() runs before us, so the block allocator is already restored.
|
||||
kernelMemory.Free(atlasPtr);
|
||||
}
|
||||
atlasRequiresReset = savedHash != atlasHash;
|
||||
|
||||
// Used to be the hash of the atlas the state was created with, to detect a changed atlas.
|
||||
// The atlas is host-side now and gets rebuilt on load anyway, so it's meaningless.
|
||||
uint64_t dummySavedHash = 0;
|
||||
if (s >= 4) {
|
||||
Do(p, dummySavedHash);
|
||||
}
|
||||
|
||||
// These are compile-time constants now, so a savestate can't be allowed to change them.
|
||||
// Note that old states hold the size *rounded up* to the allocator grain, which we no longer use.
|
||||
u32 dummySavedContextSize = savedContextSize;
|
||||
Do(p, savedContextPtr);
|
||||
Do(p, savedContextSize);
|
||||
Do(p, dummySavedContextSize);
|
||||
|
||||
if (s == 1) {
|
||||
listArgs = 0;
|
||||
@@ -375,13 +368,15 @@ void __PPGeDoState(PointerWrap &p)
|
||||
textDrawerImages.clear();
|
||||
}
|
||||
|
||||
u32 dummyDlSize = dlSize;
|
||||
Do(p, dlPtr);
|
||||
Do(p, dlWritePtr);
|
||||
Do(p, dlSize);
|
||||
Do(p, dummyDlSize);
|
||||
|
||||
u32 dummyDataSize = dataSize;
|
||||
Do(p, dataPtr);
|
||||
Do(p, dataWritePtr);
|
||||
Do(p, dataSize);
|
||||
Do(p, dummyDataSize);
|
||||
|
||||
Do(p, vertexStart);
|
||||
Do(p, vertexCount);
|
||||
@@ -390,10 +385,13 @@ void __PPGeDoState(PointerWrap &p)
|
||||
Do(p, char_lines_metrics);
|
||||
}
|
||||
|
||||
void __PPGeShutdown()
|
||||
{
|
||||
if (atlasPtr)
|
||||
kernelMemory.Free(atlasPtr);
|
||||
void __PPGeShutdown() {
|
||||
if (g_atlasImageData) {
|
||||
delete[] g_atlasImageData;
|
||||
g_atlasImageData = nullptr;
|
||||
}
|
||||
g_ppgeAtlasFakeSize = 0;
|
||||
|
||||
if (dataPtr)
|
||||
kernelMemory.Free(dataPtr);
|
||||
if (dlPtr)
|
||||
@@ -405,22 +403,25 @@ void __PPGeShutdown()
|
||||
if (palette)
|
||||
kernelMemory.Free(palette.ptr);
|
||||
|
||||
atlasPtr = 0;
|
||||
dataPtr = 0;
|
||||
dlPtr = 0;
|
||||
savedContextPtr = 0;
|
||||
listArgs = 0;
|
||||
palette = 0;
|
||||
|
||||
delete textDrawer;
|
||||
textDrawer = nullptr;
|
||||
|
||||
for (auto im : textDrawerImages)
|
||||
for (auto &im : textDrawerImages)
|
||||
kernelMemory.Free(im.second.ptr);
|
||||
textDrawerImages.clear();
|
||||
}
|
||||
|
||||
void PPGeBegin()
|
||||
{
|
||||
const u8 *PPGeAtlasGetData() {
|
||||
return g_atlasImageData;
|
||||
}
|
||||
|
||||
void PPGeBegin() {
|
||||
if (!dlPtr)
|
||||
return;
|
||||
|
||||
@@ -852,6 +853,8 @@ void PPGeDrawCurrentText(u32 color) {
|
||||
WriteCmd(GE_CMD_TEXSIZE0, 9 | (9 << 8));
|
||||
}
|
||||
|
||||
WriteCmd(GE_CMD_TEXBUFWIDTH0, atlasWidth | ((PPGE_ATLAS_FAKE_ADDRESS & 0x0F000000) >> 8));
|
||||
|
||||
BeginVertexData();
|
||||
for (auto i = char_lines.begin(); i != char_lines.end(); ++i) {
|
||||
for (auto j = i->begin(); j != i->end(); ++j) {
|
||||
@@ -863,7 +866,9 @@ void PPGeDrawCurrentText(u32 color) {
|
||||
EndVertexDataAndDraw(GE_PRIM_RECTANGLES);
|
||||
|
||||
uint32_t offset = atlasWidth * wantedPosY * 256 + wantedPosX * 256;
|
||||
WriteCmd(GE_CMD_TEXADDR0, (atlasPtr & 0xFFFFF0) + offset / 2);
|
||||
_dbg_assert_(offset / 2 < g_ppgeAtlasFakeSize);
|
||||
// The base ptr is already set.
|
||||
WriteCmd(GE_CMD_TEXADDR0, (PPGE_ATLAS_FAKE_ADDRESS & 0xFFFFF0) + offset / 2);
|
||||
texturePosX = wantedPosX;
|
||||
texturePosY = wantedPosY;
|
||||
|
||||
@@ -1316,8 +1321,8 @@ void PPGeSetDefaultTexture()
|
||||
WriteCmd(GE_CMD_TEXFILTER, (1 << 8) | 1); // mag = LINEAR min = LINEAR
|
||||
WriteCmd(GE_CMD_TEXWRAP, (1 << 8) | 1); // clamp texture wrapping
|
||||
WriteCmd(GE_CMD_TEXFUNC, (0 << 16) | (1 << 8) | 0); // RGBA texture reads, modulate, no color doubling
|
||||
WriteCmd(GE_CMD_TEXADDR0, atlasPtr & 0xFFFFF0);
|
||||
WriteCmd(GE_CMD_TEXBUFWIDTH0, atlasWidth | ((atlasPtr & 0xFF000000) >> 8));
|
||||
WriteCmd(GE_CMD_TEXADDR0, PPGE_ATLAS_FAKE_ADDRESS & 0xFFFFF0);
|
||||
WriteCmd(GE_CMD_TEXBUFWIDTH0, atlasWidth | ((PPGE_ATLAS_FAKE_ADDRESS & 0x0F000000) >> 8));
|
||||
WriteCmd(GE_CMD_TEXFLUSH, 0);
|
||||
}
|
||||
|
||||
@@ -1499,9 +1504,4 @@ void PPGeNotifyFrame() {
|
||||
|
||||
PPGeDecimateTextImages();
|
||||
PPGeImage::Decimate();
|
||||
|
||||
if (atlasRequiresReset) {
|
||||
__PPGeShutdown();
|
||||
__PPGeInit();
|
||||
}
|
||||
}
|
||||
@@ -108,9 +108,7 @@ void PPGeScissor(int x1, int y1, int x2, int y2);
|
||||
void PPGeScissorReset();
|
||||
|
||||
void PPGeNotifyFrame();
|
||||
|
||||
// Could have returned the address directly I guess, but nothing out side of PPGe should actually use it so..
|
||||
bool PPGeIsFontTextureAddress(u32 addr);
|
||||
const u8 *PPGeAtlasGetData();
|
||||
|
||||
class PPGeImage {
|
||||
public:
|
||||
|
||||
@@ -526,7 +526,7 @@ int upnpService(const unsigned int timeout) {
|
||||
g_PortManager.Shutdown();
|
||||
}
|
||||
|
||||
// Should we ingore any leftover UPnP requests? instead of processing it on the next game start
|
||||
// Should we ignore any leftover UPnP requests? instead of processing it on the next game start
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(g_upnpLock);
|
||||
g_upnpReqs.clear();
|
||||
@@ -550,9 +550,11 @@ void __UPnPShutdown() {
|
||||
g_upnpCond.notify_one();
|
||||
}
|
||||
|
||||
INFO_LOG(Log::HTTP, "Waiting for upnp thread to shut down...");
|
||||
if (g_upnpServiceThread.joinable()) {
|
||||
g_upnpServiceThread.join();
|
||||
}
|
||||
INFO_LOG(Log::HTTP, "upnp thread shut down.");
|
||||
}
|
||||
|
||||
void UPnP_Add(const char* protocol, unsigned short port, unsigned short intport) {
|
||||
|
||||
@@ -844,6 +844,7 @@ static void WebServerThread() {
|
||||
delete http;
|
||||
|
||||
UpdateStatus(ServerStatus::FINISHED);
|
||||
INFO_LOG(Log::HTTP, "Left web server loop.");
|
||||
}
|
||||
|
||||
// Only adds flags.
|
||||
|
||||
@@ -409,6 +409,7 @@ static u32 ComputeTextureHash(TextureReplacer &replacer, u32 addr, int bufw, int
|
||||
}
|
||||
const u32 *checkp = (const u32 *)Memory::GetPointerOrException(addr);
|
||||
|
||||
// NOTE: I'm not sure we want to align-check the end, so can't use IsValidTextureAddress here.
|
||||
if (Memory::IsValidAddress(addr + sizeInRAM)) {
|
||||
gpuStats.perFrame.numTextureDataBytesHashed += sizeInRAM;
|
||||
|
||||
@@ -517,6 +518,7 @@ TextureApplyResult TextureCacheCommon::ApplyTexture(bool doBind) {
|
||||
level = std::max(0, gstate.getTexLevelOffset16() / 16);
|
||||
}
|
||||
const u32 texaddr = gstate.getTextureAddress(level);
|
||||
_dbg_assert_(texaddr != 0);
|
||||
if (!Memory::IsValidTextureAddress(texaddr)) {
|
||||
// Bind a null texture and return.
|
||||
Unbind();
|
||||
@@ -570,7 +572,6 @@ TextureApplyResult TextureCacheCommon::ApplyTexture(bool doBind) {
|
||||
cluthash = 0;
|
||||
}
|
||||
const u64 cachekey = TexCacheEntry::CacheKey(texaddr, texFormat, dim, cluthash);
|
||||
|
||||
int bufw = GetTextureBufw(0, texaddr, texFormat);
|
||||
u8 maxLevel = gstate.getTextureMaxLevel();
|
||||
const bool swizzled = gstate.isTextureSwizzled();
|
||||
@@ -820,8 +821,10 @@ TextureApplyResult TextureCacheCommon::ApplyTexture(bool doBind) {
|
||||
def.format = texFormat;
|
||||
def.bufw = bufw;
|
||||
|
||||
const bool isPPGE = IsPPGEAtlasFakeAddress(texaddr, nullptr);
|
||||
|
||||
AttachCandidate bestCandidate;
|
||||
if (GetBestFramebufferCandidate(framebufferManager_, def, 0, &bestCandidate, "texture")) {
|
||||
if (!isPPGE && GetBestFramebufferCandidate(framebufferManager_, def, 0, &bestCandidate, "texture")) {
|
||||
RasterChannel channel;
|
||||
VirtualFramebuffer *framebuffer = SetTextureFramebuffer(bestCandidate, &channel); // sets curTexture3D
|
||||
TextureApplyResult result;
|
||||
@@ -852,9 +855,9 @@ TextureApplyResult TextureCacheCommon::ApplyTexture(bool doBind) {
|
||||
TexCacheEntry *entry = new TexCacheEntry{};
|
||||
cache_[cachekey].reset(entry);
|
||||
entry->status = {};
|
||||
if (PPGeIsFontTextureAddress(texaddr)) {
|
||||
if (isPPGE) {
|
||||
// It's the builtin font texture.
|
||||
entry->status |= TexStatus::RELIABLE;
|
||||
entry->status |= TexStatus::RELIABLE | TexStatus::IS_PPGE_ATLAS;
|
||||
}
|
||||
|
||||
if (hasClutGPU) {
|
||||
@@ -897,7 +900,11 @@ TextureApplyResult TextureCacheCommon::ApplyTexture(bool doBind) {
|
||||
gstate_c.curTextureWidth = w;
|
||||
gstate_c.curTextureHeight = h;
|
||||
UpdateMaxSeenV(entry, gstate.isModeThrough()); // Critical to update this before hashing! As it's used to decide the hash range.
|
||||
entry->fullhash = ComputeTextureHash(replacer_, entry->addr, entry->bufw, w, h, swizzled, entry);
|
||||
|
||||
// TODO: Avoid hashing known video textures.
|
||||
if (!(entry->status & TexStatus::IS_PPGE_ATLAS)) {
|
||||
entry->fullhash = ComputeTextureHash(replacer_, entry->addr, entry->bufw, w, h, swizzled, entry);
|
||||
}
|
||||
|
||||
VERBOSE_LOG(Log::TexCache, "%08x: Creating new texture, hash %08x (maxSeenV=%d), w: %d h: %d, creating", texaddr, entry->fullhash, entry->maxSeenV, w, h);
|
||||
|
||||
@@ -1958,7 +1965,11 @@ TextureAlpha TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETex
|
||||
|
||||
int w = gstate.getTextureWidth(level);
|
||||
int h = gstate.getTextureHeight(level);
|
||||
const u8 *texptr = Memory::GetPointerOrException(texaddr);
|
||||
|
||||
u32 ppgeOffset;
|
||||
const bool isPPGE = IsPPGEAtlasFakeAddress(texaddr, &ppgeOffset);
|
||||
|
||||
const u8 *texptr = isPPGE ? (PPGeAtlasGetData() + ppgeOffset) : Memory::GetPointerOrException(texaddr);
|
||||
const uint32_t byteSize = (textureBitsPerPixel[format] * bufw * h) / 8;
|
||||
|
||||
// Validate the texture data fits in mapped RAM, like the DXT path does.
|
||||
@@ -1968,7 +1979,7 @@ TextureAlpha TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETex
|
||||
// Swizzled textures are read in 8-row blocks, rounding the height up.
|
||||
const uint32_t rows = swizzled ? ((h + 7) & ~7) : h;
|
||||
const uint32_t neededBytes = bytesPerRow * rows;
|
||||
if (bytesPerRow > 0 && !Memory::IsValidRange(texaddr, neededBytes)) {
|
||||
if (bytesPerRow > 0 && !isPPGE && !Memory::IsValidRange(texaddr, neededBytes)) {
|
||||
ERROR_LOG_REPORT(Log::G3D, "Texture extends beyond valid RAM: %08x + %d x %d", texaddr, bufw, h);
|
||||
uint32_t limited = Memory::ClampValidSizeAt(texaddr, neededBytes);
|
||||
h = limited / bytesPerRow;
|
||||
@@ -1976,9 +1987,11 @@ TextureAlpha TextureCacheCommon::DecodeTextureLevel(u8 *out, int outPitch, GETex
|
||||
h &= ~7;
|
||||
}
|
||||
|
||||
char buf[128];
|
||||
size_t len = snprintf(buf, sizeof(buf), "Tex_%08x_%dx%d_%s", texaddr, w, h, GeTextureFormatToString(format, clutformat));
|
||||
NotifyMemInfo(MemBlockFlags::TEXTURE, texaddr, byteSize, buf, len);
|
||||
if (!isPPGE) {
|
||||
char buf[128];
|
||||
size_t len = snprintf(buf, sizeof(buf), "Tex_%08x_%dx%d_%s", texaddr, w, h, GeTextureFormatToString(format, clutformat));
|
||||
NotifyMemInfo(MemBlockFlags::TEXTURE, texaddr, byteSize, buf, len);
|
||||
}
|
||||
|
||||
switch (format) {
|
||||
case GE_TFMT_CLUT4:
|
||||
@@ -3092,5 +3105,12 @@ std::string TexStatusToString(TexStatus status) {
|
||||
if (status & TexStatus::VIDEO) {
|
||||
result += "VIDEO ";
|
||||
}
|
||||
if (status & TexStatus::RELIABLE) {
|
||||
result += "RELIABLE ";
|
||||
}
|
||||
if (status & TexStatus::IS_PPGE_ATLAS) {
|
||||
result += "IS_PPGE_ATLAS ";
|
||||
}
|
||||
|
||||
return result.empty() ? "None" : result;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ enum class TexStatus : u16 {
|
||||
IS_3D = (1 << 10),
|
||||
NO_MIPS = (1 << 11), // Has bad or unusable mipmap levels.
|
||||
FRAMEBUFFER_OVERLAP = (1 << 12),
|
||||
// Free bit 13
|
||||
IS_PPGE_ATLAS = (1 << 13),
|
||||
CLUT8_INDEXED = (1 << 14), // Decoded as plain CLUT8 indices instead of all the way to colors.
|
||||
CLUT_GPU = (1 << 15),
|
||||
};
|
||||
|
||||
@@ -244,8 +244,10 @@ static const u32 textureAlignMask16[16] = {
|
||||
|
||||
u32 GetTextureBufw(int level, u32 texaddr, GETextureFormat format) {
|
||||
// This is a hack to allow for us to draw the huge PPGe texture, which is always in kernel ram.
|
||||
if (texaddr >= PSP_GetKernelMemoryBase() && texaddr < PSP_GetKernelMemoryEnd())
|
||||
// Without this, the bufwidth gets masked away improperly.
|
||||
if (IsPPGEAtlasFakeAddress(texaddr, nullptr)) {
|
||||
return gstate.texbufwidth[level] & 0x1FFF;
|
||||
}
|
||||
|
||||
u32 bufw = gstate.texbufwidth[level] & textureAlignMask16[format];
|
||||
if (bufw == 0 && format <= GE_TFMT_DXT5) {
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Debugger/MemBlockInfo.h"
|
||||
#include "Core/MemMap.h"
|
||||
#include "Core/Util/PPGeDraw.h"
|
||||
#include "GPU/GPUState.h"
|
||||
|
||||
#include "GPU/Common/TextureDecoder.h"
|
||||
@@ -133,10 +134,16 @@ void ComputeRasterizerState(RasterizerState *state, BinManager *binner) {
|
||||
u32 texaddr = gstate.getTextureAddress(i);
|
||||
state->texaddr[i] = texaddr;
|
||||
state->texbufw[i] = (uint16_t)GetTextureBufw(i, texaddr, texfmt);
|
||||
if (Memory::IsValidAddress(texaddr))
|
||||
state->texptr[i] = Memory::GetPointerUnchecked(texaddr);
|
||||
else
|
||||
if (Memory::IsValidTextureAddress(texaddr)) {
|
||||
u32 offset;
|
||||
if (IsPPGEAtlasFakeAddress(texaddr, &offset)) {
|
||||
state->texptr[i] = PPGeAtlasGetData() + offset;
|
||||
} else {
|
||||
state->texptr[i] = Memory::GetPointerUnchecked(texaddr);
|
||||
}
|
||||
} else {
|
||||
state->texptr[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
state->textureLodSlope = gstate.getTextureLodSlope();
|
||||
|
||||
Reference in new issue
Block a user