Skip to content

Commit

Permalink
Merge pull request #13514 from hrydgard/replacement-tex-mipmap-gen
Browse files Browse the repository at this point in the history
Vulkan: Automatically generate mipmaps for replaced/scaled textures
  • Loading branch information
hrydgard authored Oct 6, 2020
2 parents ed3b99d + d798e23 commit 2f79257
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 55 deletions.
53 changes: 2 additions & 51 deletions Core/TextureReplacer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,13 @@
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.

#ifdef USING_QT_UI
#include <QtGui/QImage>
#else
#include <png.h>
#endif

#include <algorithm>

#include "Common/Data/Text/I18n.h"
#include "ext/xxhash.h"

#include "Common/Data/Text/I18n.h"
#include "Common/Data/Format/IniFile.h"
#include "Common/Data/Text/Parsers.h"
#include "Common/ColorConv.h"
Expand Down Expand Up @@ -333,16 +330,6 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey,
level.fmt = ReplacedTextureFormat::F_8888;
level.file = filename;

#ifdef USING_QT_UI
QImage image(filename.c_str(), "PNG");
if (image.isNull()) {
ERROR_LOG(G3D, "Could not load texture replacement info: %s", filename.c_str());
} else {
level.w = (image.width() * w) / newW;
level.h = (image.height() * h) / newH;
good = true;
}
#else
png_image png = {};
png.version = PNG_IMAGE_VERSION;
FILE *fp = File::OpenCFile(filename, "rb");
Expand All @@ -357,7 +344,6 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey,
fclose(fp);

png_image_free(&png);
#endif

if (good && i != 0) {
// Check that the mipmap size is correct. Can't load mips of the wrong size.
Expand All @@ -377,7 +363,6 @@ void TextureReplacer::PopulateReplacement(ReplacedTexture *result, u64 cachekey,
result->alphaStatus_ = ReplacedTextureAlpha::UNKNOWN;
}

#ifndef USING_QT_UI
static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int convert_to_8bit, const void *buffer, png_int_32 row_stride, const void *colormap) {
FILE *fp = File::OpenCFile(filename, "wb");
if (!fp) {
Expand All @@ -395,7 +380,6 @@ static bool WriteTextureToPNG(png_imagep image, const std::string &filename, int
return false;
}
}
#endif

void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &replacedInfo, const void *data, int pitch, int level, int w, int h) {
_assert_msg_(enabled_, "Replacement not enabled");
Expand Down Expand Up @@ -459,9 +443,6 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
h = lookupH * replacedInfo.scaleFactor;
}

#ifdef USING_QT_UI
ERROR_LOG(G3D, "Replacement texture saving not implemented for Qt");
#else
if (replacedInfo.fmt != ReplacedTextureFormat::F_8888) {
saveBuf.resize((pitch * h) / sizeof(u16));
switch (replacedInfo.fmt) {
Expand Down Expand Up @@ -512,7 +493,6 @@ void TextureReplacer::NotifyTextureDecoded(const ReplacedTextureDecodeInfo &repl
} else if (success) {
NOTICE_LOG(G3D, "Saving texture for replacement: %08x / %dx%d", replacedInfo.hash, w, h);
}
#endif

// Remember that we've saved this for next time.
ReplacedTextureLevel saved;
Expand Down Expand Up @@ -600,34 +580,6 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) {

const ReplacedTextureLevel &info = levels_[level];

#ifdef USING_QT_UI
QImage image(info.file.c_str(), "PNG");
if (image.isNull()) {
ERROR_LOG(G3D, "Could not load texture replacement info: %s", info.file.c_str());
return;
}

image = image.convertToFormat(QImage::Format_ARGB32);
bool alphaFull = true;
for (int y = 0; y < image.height(); ++y) {
const QRgb *src = (const QRgb *)image.constScanLine(y);
uint8_t *outLine = (uint8_t *)out + y * rowPitch;
for (int x = 0; x < image.width(); ++x) {
outLine[x * 4 + 0] = qRed(src[x]);
outLine[x * 4 + 1] = qGreen(src[x]);
outLine[x * 4 + 2] = qBlue(src[x]);
outLine[x * 4 + 3] = qAlpha(src[x]);
// We're already scanning each pixel...
if (qAlpha(src[x]) != 255) {
alphaFull = false;
}
}
}

if (level == 0 || !alphaFull) {
alphaStatus_ = alphaFull ? ReplacedTextureAlpha::FULL : ReplacedTextureAlpha::UNKNOWN;
}
#else
png_image png = {};
png.version = PNG_IMAGE_VERSION;

Expand Down Expand Up @@ -662,7 +614,6 @@ void ReplacedTexture::Load(int level, void *out, int rowPitch) {

fclose(fp);
png_image_free(&png);
#endif
}

bool TextureReplacer::GenerateIni(const std::string &gameID, std::string *generatedFilename) {
Expand Down
19 changes: 15 additions & 4 deletions GPU/Vulkan/TextureCacheVulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -710,7 +710,10 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {

// Adjust maxLevel to actually present levels..
bool badMipSizes = false;

// maxLevel here is the max level to upload. Not the count.
int maxLevel = entry->maxLevel;

for (int i = 0; i <= maxLevel; i++) {
// If encountering levels pointing to nothing, adjust max level.
u32 levelTexaddr = gstate.getTextureAddress(i);
Expand Down Expand Up @@ -740,6 +743,10 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
maxLevel = 0;
}

// We generate missing mipmaps from maxLevel+1 up to this level. maxLevel can get overwritten below
// such as when using replacement textures - but let's keep the same amount of levels.
int maxLevelToGenerate = maxLevel;

// If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient.
VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat());

Expand Down Expand Up @@ -824,8 +831,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
}

VkImageLayout imageLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
// If we want to use the GE debugger, we should add VK_IMAGE_USAGE_TRANSFER_SRC_BIT too...
VkImageUsageFlags usage = VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;

// Compute experiment
if (actualFmt == VULKAN_8888_FORMAT && scaleFactor > 1 && hardwareScaling) {
Expand All @@ -845,7 +851,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
snprintf(texName, sizeof(texName), "texture_%08x_%s", entry->addr, GeTextureFormatToString((GETextureFormat)entry->format));
image->SetTag(texName);

bool allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, imageLayout, usage, mapping);
bool allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, imageLayout, usage, mapping);
if (!allocSuccess && !lowMemoryMode_) {
WARN_LOG_REPORT(G3D, "Texture cache ran out of GPU memory; switching to low memory mode");
lowMemoryMode_ = true;
Expand All @@ -864,7 +870,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
scaleFactor = 1;
actualFmt = dstFmt;

allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevel + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
allocSuccess = image->CreateDirect(cmdInit, allocator_, w * scaleFactor, h * scaleFactor, maxLevelToGenerate + 1, actualFmt, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, mapping);
}

if (!allocSuccess) {
Expand Down Expand Up @@ -987,6 +993,11 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) {
}
}

// Generate any additional mipmap levels.
for (int level = maxLevel + 1; level <= maxLevelToGenerate; level++) {
entry->vkTex->GenerateMip(cmdInit, level);
}

if (maxLevel == 0) {
entry->status |= TexCacheEntry::STATUS_BAD_MIPS;
} else {
Expand Down

0 comments on commit 2f79257

Please sign in to comment.