mirror of
https://github.com/hrydgard/ppsspp.git
synced 2025-04-02 11:01:50 -04:00
Save textures on background tasks when texture dumping is enabled.
Should help #15478, at least a bit.
This commit is contained in:
parent
3a09c85fb1
commit
b1af940d8e
4 changed files with 63 additions and 17 deletions
|
@ -45,16 +45,21 @@ void FreeAlignedMemory(void* ptr);
|
|||
|
||||
int GetMemoryProtectPageSize();
|
||||
|
||||
// A simple buffer that bypasses the libc memory allocator. As a result the buffer is always page-aligned.
|
||||
template <typename T>
|
||||
class SimpleBuf {
|
||||
public:
|
||||
SimpleBuf() : buf_(0), size_(0) {
|
||||
}
|
||||
SimpleBuf() : buf_(0), size_(0) {}
|
||||
|
||||
SimpleBuf(size_t size) : buf_(0) {
|
||||
resize(size);
|
||||
}
|
||||
|
||||
SimpleBuf(const SimpleBuf &o) : buf_(o.buf_), size_(o.size_) {}
|
||||
|
||||
// Move constructor
|
||||
SimpleBuf(SimpleBuf &&o) noexcept : buf_(o.buf_), size_(o.size_) { o.buf_ = nullptr; o.size_ = 0; }
|
||||
|
||||
~SimpleBuf() {
|
||||
if (buf_ != 0) {
|
||||
FreeMemoryPages(buf_, size_ * sizeof(T));
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
#include "Common/StringUtils.h"
|
||||
#include "Common/Thread/ParallelLoop.h"
|
||||
#include "Common/Thread/Waitable.h"
|
||||
#include "Common/Thread/ThreadManager.h"
|
||||
#include "Common/TimeUtil.h"
|
||||
#include "Core/Config.h"
|
||||
#include "Core/Host.h"
|
||||
|
@ -502,6 +503,39 @@ static bool WriteTextureToPNG(png_imagep image, const Path &filename, int conver
|
|||
}
|
||||
}
|
||||
|
||||
class TextureSaveTask : public Task {
|
||||
public:
|
||||
// Could probably just use a vector.
|
||||
SimpleBuf<u32> data;
|
||||
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
int pitch = 0; // bytes
|
||||
|
||||
Path path;
|
||||
u32 replacedInfoHash;
|
||||
|
||||
TextureSaveTask(SimpleBuf<u32> _data) : data(std::move(_data)) {}
|
||||
|
||||
TaskType Type() const override { return TaskType::CPU_COMPUTE; } // Also I/O blocking but dominated by compute
|
||||
void Run() override {
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
png.width = w;
|
||||
png.height = h;
|
||||
bool success = WriteTextureToPNG(&png, path, 0, data.data(), pitch, nullptr);
|
||||
png_image_free(&png);
|
||||
if (png.warning_or_error >= 2) {
|
||||
ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors.");
|
||||
} else if (success) {
|
||||
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfoHash, w, h);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h) {
|
||||
_assert_msg_(enabled_, "Replacement not enabled");
|
||||
if (!g_Config.bSaveNewTextures) {
|
||||
|
@ -564,6 +598,10 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
|||
h = lookupH * replacedInfo.scaleFactor;
|
||||
}
|
||||
|
||||
SimpleBuf<u32> saveBuf;
|
||||
|
||||
// TODO: Move the color conversion to the thread as well.
|
||||
// Actually may be better to re-decode using expand32?
|
||||
if (replacedInfo.fmt != ReplacedTextureFormat::F_8888) {
|
||||
saveBuf.resize((pitch * h) / sizeof(u16));
|
||||
switch (replacedInfo.fmt) {
|
||||
|
@ -598,24 +636,27 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
|||
// We doubled our pitch.
|
||||
pitch *= 2;
|
||||
}
|
||||
} else {
|
||||
// Copy data to a buffer so we can send it to the thread. Might as well compact-away the pitch
|
||||
// while we're at it.
|
||||
saveBuf.resize(w * h);
|
||||
for (int y = 0; y < h; y++) {
|
||||
memcpy((u8 *)saveBuf.data() + y * w * 4, (const u8 *)data + y * pitch, w * sizeof(u32));
|
||||
}
|
||||
pitch = w * 4;
|
||||
}
|
||||
|
||||
png_image png;
|
||||
memset(&png, 0, sizeof(png));
|
||||
png.version = PNG_IMAGE_VERSION;
|
||||
png.format = PNG_FORMAT_RGBA;
|
||||
png.width = w;
|
||||
png.height = h;
|
||||
bool success = WriteTextureToPNG(&png, saveFilename, 0, data, pitch, nullptr);
|
||||
png_image_free(&png);
|
||||
|
||||
if (png.warning_or_error >= 2) {
|
||||
ERROR_LOG(COMMON, "Saving screenshot to PNG produced errors.");
|
||||
} else if (success) {
|
||||
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfo.hash, w, h);
|
||||
}
|
||||
TextureSaveTask *task = new TextureSaveTask(std::move(saveBuf));
|
||||
// Should probably do a proper move constructor but this'll work.
|
||||
task->w = w;
|
||||
task->h = h;
|
||||
task->pitch = pitch;
|
||||
task->path = saveFilename;
|
||||
task->replacedInfoHash = replacedInfo.hash;
|
||||
g_threadManager.EnqueueTask(task); // We don't care about waiting for the task. It'll be fine.
|
||||
|
||||
// Remember that we've saved this for next time.
|
||||
// Should be OK that the actual disk write may not be finished yet.
|
||||
ReplacedTextureLevel saved;
|
||||
saved.fmt = ReplacedTextureFormat::F_8888;
|
||||
saved.file = filename;
|
||||
|
|
|
@ -227,7 +227,6 @@ protected:
|
|||
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
||||
bool PopulateLevel(ReplacedTextureLevel &level);
|
||||
|
||||
SimpleBuf<u32> saveBuf;
|
||||
bool enabled_ = false;
|
||||
bool allowVideo_ = false;
|
||||
bool ignoreAddress_ = false;
|
||||
|
|
|
@ -736,6 +736,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &
|
|||
replacedInfo.scaleFactor = scaleFactor;
|
||||
replacedInfo.fmt = FromD3D11Format(dstFmt);
|
||||
|
||||
// NOTE: Reading the decoded texture here may be very slow, if we just wrote it to write-combined memory.
|
||||
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue