mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-14 15:00:30 +02:00
Coded Arms: Contagion: Read back the stencil the crosshair checks
The crosshair turns red, and lock-on works, when stencil bit 3 is set at the centre of the frame, which the game tests by reading the pixel from VRAM with the CPU (#17936). Hook that and read back the stencil of that row into the alpha bytes in RAM. The alpha the hardware backends keep doesn't follow stencil everywhere, so reading color isn't enough. Adds PerformReadbackStencilToMemory for this (8888 only for now). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
ecad07b3b1
commit
ae68a7e73f
8 files changed
+103
No files matched your search
@@ -1467,6 +1467,22 @@ static int Hook_everybodysgolf_spinner_background() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Hook_codedarms_stencil_check() {
|
||||
// The crosshair turns red, and lock-on works, when stencil bit 3 is set at the centre of the frame
|
||||
// just rendered, which the game tests by reading the pixel from VRAM (#17936). Hooked at the load,
|
||||
// where v0 is the pixel's address. Read back the stencil of the row it's in (the game's stride is
|
||||
// 512). Only stencil: the alpha the hardware backends keep doesn't follow it everywhere.
|
||||
const u32 addr = currentMIPS->r[MIPS_REG_V0];
|
||||
if (!Memory::IsVRAMAddress(addr)) {
|
||||
return 0;
|
||||
}
|
||||
const u32 row = addr & ~0x7FF;
|
||||
if (Memory::IsValidRange(row, 0x800)) {
|
||||
gpu->PerformReadbackStencilToMemory(row, 0x800);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int Hook_katamari_render_check() {
|
||||
if (!Memory::IsValidRange(currentMIPS->r[MIPS_REG_A0] + 0x3C, 8)) {
|
||||
return 0;
|
||||
@@ -1804,6 +1820,7 @@ static const ReplacementTableEntry entries[] = {
|
||||
{ "everybodystennis_copy_portrait", &Hook_everybodys_copy_portrait, 0, REPFLAG_HOOKENTER, 0x54 },
|
||||
{ "everybodysgolf_copy_portrait", &Hook_everybodysgolf_copy_portrait, 0, REPFLAG_HOOKENTER, 0x50 },
|
||||
{ "everybodysgolf_spinner_background", &Hook_everybodysgolf_spinner_background, 0, REPFLAG_HOOKENTER, 0x138 },
|
||||
{ "codedarms_stencil_check", &Hook_codedarms_stencil_check, 0, REPFLAG_HOOKENTER, 0x8C },
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -482,6 +482,7 @@ static const HardHashTableEntry hardcodedHashes[] = {
|
||||
{ 0xeabb9c1b4f83d2b4, 52, "memset_jak", }, // Crisis Core (US), Jak and Daxter (this is a slow memset and needs to have slow timing)
|
||||
{ 0xeb0f7bf63d52ece9, 88, "strncat", },
|
||||
{ 0xeb8c0834d8bbc28c, 416, "fmodf", },
|
||||
{ 0xec9ad8df944a770b, 180, "codedarms_stencil_check", }, // Coded Arms: Contagion (EUR)
|
||||
{ 0xed8918f378e9a563, 628, "sd_gundam_g_generation_download_frame", }, // SD Gundam G Generation Overworld
|
||||
{ 0xedbbe9bf9fbceca8, 172, "dl_write_viewport2", },
|
||||
{ 0xedc3f476221f96e6, 148, "tanf", },
|
||||
|
||||
@@ -3487,6 +3487,73 @@ bool FramebufferManagerCommon::ReadbackStencilbuffer(Draw::Framebuffer *fbo, int
|
||||
return draw_->CopyFramebufferToMemory(fbo, Draw::Aspect::DEPTH_BIT, x, y, w, h, Draw::DataFormat::S8, pixels, pixelsStride, mode, "ReadbackStencilbufferSync");
|
||||
}
|
||||
|
||||
bool FramebufferManagerCommon::ReadbackStencilToMemory(u32 addr, int size) {
|
||||
if (!useBufferedRendering_) {
|
||||
return false;
|
||||
}
|
||||
addr &= 0x3FFFFFFF;
|
||||
if (Memory::IsVRAMAddress(addr))
|
||||
addr &= 0x041FFFFF;
|
||||
|
||||
VirtualFramebuffer *vfb = nullptr;
|
||||
for (auto v : vfbs_) {
|
||||
if (!v->fbo || v->fb_stride == 0 || v->fb_format != GE_FORMAT_8888) {
|
||||
continue;
|
||||
}
|
||||
if (addr >= v->fb_address && addr + size <= v->fb_address + v->BufferByteSize(RASTER_COLOR)) {
|
||||
if (!vfb || v->colorBindSeq > vfb->colorBindSeq) {
|
||||
vfb = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!vfb) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Whole rows only.
|
||||
const u32 byteStride = vfb->fb_stride * 4;
|
||||
const u32 offset = addr - vfb->fb_address;
|
||||
if (offset % byteStride != 0 || size % byteStride != 0) {
|
||||
return false;
|
||||
}
|
||||
const int y = offset / byteStride;
|
||||
const int h = std::min((int)(size / byteStride), (int)vfb->height - y);
|
||||
const int w = std::min((int)vfb->fb_stride, (int)vfb->width);
|
||||
if (w <= 0 || h <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FlushBeforeCopy();
|
||||
|
||||
const int scale = vfb->renderScaleFactor;
|
||||
const int sw = w * scale;
|
||||
const int sh = h * scale;
|
||||
std::vector<u8> stencil(sw * sh);
|
||||
bool ok = draw_->CopyFramebufferToMemory(vfb->fbo, Draw::Aspect::STENCIL_BIT, 0, y * scale, sw, sh, Draw::DataFormat::S8, stencil.data(), sw, Draw::ReadbackMode::BLOCK, "ReadbackStencilToMemory");
|
||||
if (!ok) {
|
||||
ok = ReadbackStencilbuffer(vfb->fbo, 0, y * scale, sw, sh, stencil.data(), sw, Draw::ReadbackMode::BLOCK);
|
||||
}
|
||||
draw_->Invalidate(InvalidationFlags::CACHED_RENDER_STATE);
|
||||
textureCache_->ForgetLastTexture();
|
||||
RebindFramebuffer("RebindFramebuffer - ReadbackStencilToMemory");
|
||||
gpuStats.perFrame.numBlockingReadbacks++;
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// PSP stencil is the alpha byte, so that's all that changes.
|
||||
u8 *dest = Memory::GetPointerWriteUnchecked(vfb->fb_address + offset);
|
||||
for (int row = 0; row < h; row++) {
|
||||
const u8 *src = &stencil[row * scale * sw];
|
||||
u8 *dst = dest + row * byteStride;
|
||||
for (int x = 0; x < w; x++) {
|
||||
dst[x * 4 + 3] = src[x * scale];
|
||||
}
|
||||
}
|
||||
NotifyMemInfo(MemBlockFlags::WRITE, vfb->fb_address + offset, h * byteStride, "ReadbackStencil");
|
||||
return true;
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::ReadFramebufferToMemory(VirtualFramebuffer *vfb, int x, int y, int w, int h, RasterChannel channel, Draw::ReadbackMode mode) {
|
||||
if (!vfb || !vfb->fbo) {
|
||||
return;
|
||||
|
||||
@@ -351,6 +351,7 @@ public:
|
||||
void UpdateFromMemory(u32 addr, int size);
|
||||
void ApplyClearToMemory(int x1, int y1, int x2, int y2, u32 clearColor);
|
||||
bool PerformWriteStencilFromMemory(u32 addr, int size, WriteStencil flags);
|
||||
bool ReadbackStencilToMemory(u32 addr, int size);
|
||||
void WriteStencilFromFramebufferAlpha(VirtualFramebuffer *dst);
|
||||
|
||||
// We changed our depth mode, gotta start over.
|
||||
|
||||
@@ -2358,6 +2358,13 @@ void GPUCommon::PerformWriteFormattedFromMemory(u32 addr, int size, int frameWid
|
||||
InvalidateCache(addr, size, GPU_INVALIDATE_SAFE);
|
||||
}
|
||||
|
||||
bool GPUCommon::PerformReadbackStencilToMemory(u32 dest, int size) {
|
||||
if (Memory::IsVRAMAddress(dest) && framebufferManager_->MayIntersectFramebufferColor(dest)) {
|
||||
return framebufferManager_->ReadbackStencilToMemory(dest, size);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool GPUCommon::PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags) {
|
||||
if (framebufferManager_->MayIntersectFramebufferColor(dest)) {
|
||||
framebufferManager_->PerformWriteStencilFromMemory(dest, size, flags);
|
||||
|
||||
@@ -168,6 +168,9 @@ public:
|
||||
// a write that starts partway into a framebuffer row be uploaded without clobbering the rest.
|
||||
virtual void PerformWriteFormattedFromMemory(u32 addr, int size, int width, GEBufferFormat format, int rectWidth = 0);
|
||||
virtual bool PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags);
|
||||
// Reads the stencil of whole framebuffer rows into the alpha bytes in RAM, leaving the color alone.
|
||||
// For games that test stencil with the CPU. Only 8888 for now.
|
||||
virtual bool PerformReadbackStencilToMemory(u32 dest, int size);
|
||||
|
||||
// Tells the texture cache that a block copy moved pixels, so it can carry "this is video" from
|
||||
// the source to the destination. Cheap and safe to call for any copy.
|
||||
|
||||
@@ -1493,6 +1493,12 @@ bool SoftGPU::PerformReadbackToMemory(u32 dest, int size)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftGPU::PerformReadbackStencilToMemory(u32 dest, int size) {
|
||||
// Stencil is already in RAM, once any queued drawing is done.
|
||||
drawEngine_->transformUnit.FlushIfOverlap(this, "readback", false, dest, size, size, 1);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SoftGPU::PerformWriteColorFromMemory(u32 dest, int size)
|
||||
{
|
||||
drawEngine_->transformUnit.FlushIfOverlap(this, "upload", true, dest, size, size, 1);
|
||||
|
||||
@@ -176,6 +176,7 @@ public:
|
||||
bool PerformReadbackToMemory(u32 dest, int size) override;
|
||||
bool PerformWriteColorFromMemory(u32 dest, int size) override;
|
||||
bool PerformWriteStencilFromMemory(u32 dest, int size, WriteStencil flags) override;
|
||||
bool PerformReadbackStencilToMemory(u32 dest, int size) override;
|
||||
|
||||
void DeviceLost() override;
|
||||
void DeviceRestore(Draw::DrawContext *draw) override;
|
||||
|
||||
Reference in new issue
Block a user