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();
|
int GetMemoryProtectPageSize();
|
||||||
|
|
||||||
|
// A simple buffer that bypasses the libc memory allocator. As a result the buffer is always page-aligned.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class SimpleBuf {
|
class SimpleBuf {
|
||||||
public:
|
public:
|
||||||
SimpleBuf() : buf_(0), size_(0) {
|
SimpleBuf() : buf_(0), size_(0) {}
|
||||||
}
|
|
||||||
|
|
||||||
SimpleBuf(size_t size) : buf_(0) {
|
SimpleBuf(size_t size) : buf_(0) {
|
||||||
resize(size);
|
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() {
|
~SimpleBuf() {
|
||||||
if (buf_ != 0) {
|
if (buf_ != 0) {
|
||||||
FreeMemoryPages(buf_, size_ * sizeof(T));
|
FreeMemoryPages(buf_, size_ * sizeof(T));
|
||||||
|
|
|
@ -33,6 +33,7 @@
|
||||||
#include "Common/StringUtils.h"
|
#include "Common/StringUtils.h"
|
||||||
#include "Common/Thread/ParallelLoop.h"
|
#include "Common/Thread/ParallelLoop.h"
|
||||||
#include "Common/Thread/Waitable.h"
|
#include "Common/Thread/Waitable.h"
|
||||||
|
#include "Common/Thread/ThreadManager.h"
|
||||||
#include "Common/TimeUtil.h"
|
#include "Common/TimeUtil.h"
|
||||||
#include "Core/Config.h"
|
#include "Core/Config.h"
|
||||||
#include "Core/Host.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) {
|
void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h) {
|
||||||
_assert_msg_(enabled_, "Replacement not enabled");
|
_assert_msg_(enabled_, "Replacement not enabled");
|
||||||
if (!g_Config.bSaveNewTextures) {
|
if (!g_Config.bSaveNewTextures) {
|
||||||
|
@ -564,6 +598,10 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
||||||
h = lookupH * replacedInfo.scaleFactor;
|
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) {
|
if (replacedInfo.fmt != ReplacedTextureFormat::F_8888) {
|
||||||
saveBuf.resize((pitch * h) / sizeof(u16));
|
saveBuf.resize((pitch * h) / sizeof(u16));
|
||||||
switch (replacedInfo.fmt) {
|
switch (replacedInfo.fmt) {
|
||||||
|
@ -598,24 +636,27 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
|
||||||
// We doubled our pitch.
|
// We doubled our pitch.
|
||||||
pitch *= 2;
|
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;
|
TextureSaveTask *task = new TextureSaveTask(std::move(saveBuf));
|
||||||
memset(&png, 0, sizeof(png));
|
// Should probably do a proper move constructor but this'll work.
|
||||||
png.version = PNG_IMAGE_VERSION;
|
task->w = w;
|
||||||
png.format = PNG_FORMAT_RGBA;
|
task->h = h;
|
||||||
png.width = w;
|
task->pitch = pitch;
|
||||||
png.height = h;
|
task->path = saveFilename;
|
||||||
bool success = WriteTextureToPNG(&png, saveFilename, 0, data, pitch, nullptr);
|
task->replacedInfoHash = replacedInfo.hash;
|
||||||
png_image_free(&png);
|
g_threadManager.EnqueueTask(task); // We don't care about waiting for the task. It'll be fine.
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remember that we've saved this for next time.
|
// Remember that we've saved this for next time.
|
||||||
|
// Should be OK that the actual disk write may not be finished yet.
|
||||||
ReplacedTextureLevel saved;
|
ReplacedTextureLevel saved;
|
||||||
saved.fmt = ReplacedTextureFormat::F_8888;
|
saved.fmt = ReplacedTextureFormat::F_8888;
|
||||||
saved.file = filename;
|
saved.file = filename;
|
||||||
|
|
|
@ -227,7 +227,6 @@ protected:
|
||||||
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
void PopulateReplacement(ReplacedTexture *result, u64 cachekey, u32 hash, int w, int h);
|
||||||
bool PopulateLevel(ReplacedTextureLevel &level);
|
bool PopulateLevel(ReplacedTextureLevel &level);
|
||||||
|
|
||||||
SimpleBuf<u32> saveBuf;
|
|
||||||
bool enabled_ = false;
|
bool enabled_ = false;
|
||||||
bool allowVideo_ = false;
|
bool allowVideo_ = false;
|
||||||
bool ignoreAddress_ = false;
|
bool ignoreAddress_ = false;
|
||||||
|
|
|
@ -736,6 +736,7 @@ void TextureCacheD3D11::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &
|
||||||
replacedInfo.scaleFactor = scaleFactor;
|
replacedInfo.scaleFactor = scaleFactor;
|
||||||
replacedInfo.fmt = FromD3D11Format(dstFmt);
|
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);
|
replacer_.NotifyTextureDecoded(replacedInfo, pixelData, decPitch, level, w, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue