VSH prep: Free up kernel memory by not storing the PPGe atlas in it

This commit is contained in:
Henrik Rydgård committed 2026-08-19 18:45:52 +02:00
1 parent 14338534e7
commit 46d5fae342
9 files changed
+161 -111

No files matched your search

+20
View File
@@ -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
View File
@@ -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();
}
}
+1 -3
View File
@@ -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:
+3 -1
View File
@@ -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) {
+1
View File
@@ -844,6 +844,7 @@ static void WebServerThread() {
delete http;
UpdateStatus(ServerStatus::FINISHED);
INFO_LOG(Log::HTTP, "Left web server loop.");
}
// Only adds flags.
+30 -10
View File
@@ -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;
}
+1 -1
View File
@@ -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),
};
+3 -1
View File
@@ -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) {
+10 -3
View File
@@ -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();