Reintroduce mipmaps for images in the UI like screenshots (D3D11, OpenGL, D3D9)

This commit is contained in:
Henrik Rydgard 2017-03-11 14:43:42 +01:00
parent 21d97a7947
commit 8a8360ef96
9 changed files with 68 additions and 42 deletions

View File

@ -85,7 +85,7 @@ void AsyncImageFileView::SetFilename(std::string filename) {
void AsyncImageFileView::Draw(UIContext &dc) {
using namespace Draw;
if (!texture_ && !textureFailed_ && !filename_.empty()) {
texture_ = CreateTextureFromFile(dc.GetDrawContext(), filename_.c_str(), DETECT);
texture_ = CreateTextureFromFile(dc.GetDrawContext(), filename_.c_str(), DETECT, true);
if (!texture_)
textureFailed_ = true;
}

View File

@ -1,6 +1,9 @@
#include <algorithm>
#include "thin3d/thin3d.h"
#include "image/zim_load.h"
#include "image/png_load.h"
#include "math/math_util.h"
#include "file/vfs.h"
#include "ext/jpge/jpgd.h"
#include "UI/TextureUtil.h"
@ -83,7 +86,7 @@ static bool LoadTextureLevels(const uint8_t *data, size_t size, ImageFileType ty
return *num_levels > 0;
}
bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, ImageFileType type) {
bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, ImageFileType type, bool generateMips) {
using namespace Draw;
int width[16]{}, height[16]{};
@ -110,13 +113,16 @@ bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, Imag
texture_ = nullptr;
}
int potentialLevels = std::min(log2i(width[0]), log2i(height[0]));
TextureDesc desc{};
desc.type = TextureType::LINEAR2D;
desc.format = fmt;
desc.width = width[0];
desc.height = height[0];
desc.depth = 1;
desc.mipLevels = num_levels;
desc.mipLevels = generateMips ? potentialLevels : num_levels;
desc.generateMips = generateMips && potentialLevels > num_levels;
for (int i = 0; i < num_levels; i++) {
desc.initData.push_back(image[i]);
}
@ -125,10 +131,11 @@ bool ManagedTexture::LoadFromFileData(const uint8_t *data, size_t dataSize, Imag
if (image[i])
free(image[i]);
}
return true;
}
bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType type) {
bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType type, bool generateMips) {
filename_ = "";
size_t fileSize;
uint8_t *buffer = VFSReadFile(filename.c_str(), &fileSize);
@ -136,7 +143,7 @@ bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType typ
ELOG("Failed to read file %s", filename.c_str());
return false;
}
bool retval = LoadFromFileData(buffer, fileSize, type);
bool retval = LoadFromFileData(buffer, fileSize, type, generateMips);
if (retval) {
filename_ = filename;
} else {
@ -146,11 +153,11 @@ bool ManagedTexture::LoadFromFile(const std::string &filename, ImageFileType typ
return retval;
}
ManagedTexture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType type) {
ManagedTexture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType type, bool generateMips) {
if (!draw)
return nullptr;
ManagedTexture *mtex = new ManagedTexture(draw);
if (!mtex->LoadFromFile(filename, type)) {
if (!mtex->LoadFromFile(filename, type, generateMips)) {
delete mtex;
return nullptr;
}
@ -158,10 +165,10 @@ ManagedTexture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filen
}
// TODO: Remove the code duplication between this and LoadFromFileData
ManagedTexture *CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t *data, int size, ImageFileType type) {
ManagedTexture *CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t *data, int size, ImageFileType type, bool generateMips) {
if (!draw)
return nullptr;
ManagedTexture *mtex = new ManagedTexture(draw);
mtex->LoadFromFileData(data, size, type);
mtex->LoadFromFileData(data, size, type, generateMips);
return mtex;
}

View File

@ -42,8 +42,8 @@ public:
}
}
bool LoadFromFile(const std::string &filename, ImageFileType type = ImageFileType::DETECT);
bool LoadFromFileData(const uint8_t *data, size_t dataSize, ImageFileType type = ImageFileType::DETECT);
bool LoadFromFile(const std::string &filename, ImageFileType type = ImageFileType::DETECT, bool generateMips = false);
bool LoadFromFileData(const uint8_t *data, size_t dataSize, ImageFileType type = ImageFileType::DETECT, bool generateMips = false);
Draw::Texture *GetTexture() { return texture_; } // For immediate use, don't store.
int Width() const { return texture_->Width(); }
int Height() const { return texture_->Height(); }
@ -54,6 +54,5 @@ private:
std::string filename_; // Textures that are loaded from files can reload themselves automatically.
};
// Common Thin3D function, uses CreateTexture
ManagedTexture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType fileType);
ManagedTexture *CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t *data, int size, ImageFileType fileType);
ManagedTexture *CreateTextureFromFile(Draw::DrawContext *draw, const char *filename, ImageFileType fileType, bool generateMips = false);
ManagedTexture *CreateTextureFromFileData(Draw::DrawContext *draw, const uint8_t *data, int size, ImageFileType fileType, bool generateMips = false);

View File

@ -5,6 +5,7 @@
#include "base/logging.h"
#include "zlib.h"
#include "image/zim_load.h"
#include "math/math_util.h"
#include "file/vfs.h"
int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen) {
@ -46,14 +47,6 @@ int ezuncompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSr
return nExtraChunks ? Z_BUF_ERROR : Z_OK;
}
static unsigned int log2i(unsigned int val) {
unsigned int ret = -1;
while (val != 0) {
val >>= 1; ret++;
}
return ret;
}
int LoadZIMPtr(const uint8_t *zim, size_t datasize, int *width, int *height, int *flags, uint8_t **image) {
if (zim[0] != 'Z' || zim[1] != 'I' || zim[2] != 'M' || zim[3] != 'G') {
ELOG("Not a ZIM file");

View File

@ -1,5 +1,8 @@
#pragma once
// Some of the stuff in this file are snippets from all over the web, esp. dspmusic.org. I think it's all public domain.
// In any case, very little of it is used anywhere at the moment.
#include <cmath>
#include <cstring>
@ -36,14 +39,19 @@ inline uint32_t RoundUpToPowerOf2(uint32_t v) {
return v;
}
inline uint32_t log2i(uint32_t val) {
unsigned int ret = -1;
while (val != 0) {
val >>= 1; ret++;
}
return ret;
}
#define PI 3.141592653589793f
#ifndef M_PI
#define M_PI 3.141592653589793f
#endif
// The stuff in this file is from all over the web, esp. dspmusic.org. I think it's all public domain.
// In any case, very little of it is used anywhere at the moment.
// Calculate pseudo-random 32 bit number based on linear congruential method.
void SetSeed(unsigned int seed);
unsigned int GenerateRandomNumber();

View File

@ -299,6 +299,7 @@ enum FormatSupport {
FMT_TEXTURE = 2,
FMT_INPUTLAYOUT = 4,
FMT_DEPTHSTENCIL = 8,
FMT_AUTOGEN_MIPS = 16,
};
enum InfoField {
@ -552,6 +553,7 @@ struct TextureDesc {
int height;
int depth;
int mipLevels;
bool generateMips;
std::vector<uint8_t *> initData;
};

View File

@ -669,6 +669,12 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
return false;
}
bool generateMips = desc.generateMips;
if (desc.generateMips && !(GetDataFormatSupport(desc.format) & FMT_AUTOGEN_MIPS)) {
// D3D11 does not support autogenerating mipmaps for this format.
generateMips = false;
}
D3D11_TEXTURE2D_DESC descColor{};
descColor.Width = desc.width;
descColor.Height = desc.height;
@ -678,12 +684,12 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
descColor.SampleDesc.Count = 1;
descColor.SampleDesc.Quality = 0;
descColor.Usage = D3D11_USAGE_DEFAULT;
descColor.BindFlags = D3D11_BIND_SHADER_RESOURCE;
descColor.BindFlags = generateMips ? (D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET) : D3D11_BIND_SHADER_RESOURCE;
descColor.MiscFlags = generateMips ? D3D11_RESOURCE_MISC_GENERATE_MIPS : 0;
descColor.CPUAccessFlags = 0;
descColor.MiscFlags = 0;
D3D11_SUBRESOURCE_DATA initData[12]{};
if (desc.initData.size()) {
if (desc.initData.size() && !generateMips) {
int w = desc.width;
int h = desc.height;
for (int i = 0; i < (int)desc.initData.size(); i++) {
@ -695,7 +701,7 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
}
}
HRESULT hr = device_->CreateTexture2D(&descColor, desc.initData.size() ? initData : nullptr, &tex->tex);
HRESULT hr = device_->CreateTexture2D(&descColor, (desc.initData.size() && !generateMips) ? initData : nullptr, &tex->tex);
if (!SUCCEEDED(hr)) {
delete tex;
return nullptr;
@ -705,6 +711,10 @@ Texture *D3D11DrawContext::CreateTexture(const TextureDesc &desc) {
delete tex;
return nullptr;
}
if (generateMips && desc.initData.size() >= 1) {
context_->UpdateSubresource(tex->tex, 0, nullptr, desc.initData[0], desc.width * (int)DataFormatSizeInBytes(desc.format), 0);
context_->GenerateMips(tex->view);
}
return tex;
}
@ -1062,6 +1072,8 @@ uint32_t D3D11DrawContext::GetDataFormatSupport(DataFormat fmt) const {
support |= FMT_INPUTLAYOUT;
if (giSupport & D3D11_FORMAT_SUPPORT_DEPTH_STENCIL)
support |= FMT_DEPTHSTENCIL;
if (giSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN)
support |= FMT_AUTOGEN_MIPS;
return support;
}

View File

@ -345,10 +345,12 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
pool = D3DPOOL_DEFAULT;
usage = D3DUSAGE_DYNAMIC;
}
if (desc.generateMips)
usage |= D3DUSAGE_AUTOGENMIPMAP;
switch (type_) {
case TextureType::LINEAR1D:
case TextureType::LINEAR2D:
hr = device_->CreateTexture(desc.width, desc.height, desc.mipLevels, usage, d3dfmt_, pool, &tex_, NULL);
hr = device_->CreateTexture(desc.width, desc.height, desc.generateMips ? 0 : desc.mipLevels, usage, d3dfmt_, pool, &tex_, NULL);
break;
case TextureType::LINEAR3D:
hr = device_->CreateVolumeTexture(desc.width, desc.height, desc.depth, desc.mipLevels, usage, d3dfmt_, pool, &volTex_, NULL);
@ -363,7 +365,10 @@ bool D3D9Texture::Create(const TextureDesc &desc) {
}
if (desc.initData.size()) {
for (int i = 0; i < (int)desc.initData.size(); i++) {
// In D3D9, after setting D3DUSAGE_AUTOGENMIPS, we can only access the top layer. The rest will be
// automatically generated.
int maxLevel = desc.generateMips ? 1 : (int)desc.initData.size();
for (int i = 0; i < maxLevel; i++) {
SetImageData(0, 0, 0, width_, height_, depth_, i, 0, desc.initData[i]);
}
}
@ -1154,7 +1159,7 @@ DrawContext *T3DCreateDX9Context(IDirect3D9 *d3d, IDirect3D9Ex *d3dEx, int adapt
uint32_t D3D9Context::GetDataFormatSupport(DataFormat fmt) const {
switch (fmt) {
case DataFormat::B8G8R8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE;
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS;
case DataFormat::R4G4B4A4_UNORM_PACK16:
return 0;
@ -1163,10 +1168,10 @@ uint32_t D3D9Context::GetDataFormatSupport(DataFormat fmt) const {
case DataFormat::R5G6B5_UNORM_PACK16:
case DataFormat::A1R5G5B5_UNORM_PACK16:
case DataFormat::A4R4G4B4_UNORM_PACK16:
return FMT_RENDERTARGET | FMT_TEXTURE; // native support
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS; // native support
case DataFormat::R8G8B8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT;
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT | FMT_AUTOGEN_MIPS;
case DataFormat::R32_FLOAT:
case DataFormat::R32G32_FLOAT:

View File

@ -683,19 +683,19 @@ OpenGLTexture::OpenGLTexture(const TextureDesc &desc) {
height_ = (height_ + 1) / 2;
level++;
}
mipLevels_ = level;
mipLevels_ = desc.generateMips ? desc.mipLevels : level;
#ifdef USING_GLES2
if (gl_extensions.GLES3) {
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, level - 1);
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1);
}
#else
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, level - 1);
glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, mipLevels_ - 1);
#endif
glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, level > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTexParameteri(target_, GL_TEXTURE_MIN_FILTER, mipLevels_ > 1 ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR);
glTexParameteri(target_, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if ((int)desc.initData.size() < desc.mipLevels) {
if ((int)desc.initData.size() < desc.mipLevels && desc.generateMips) {
ILOG("Generating mipmaps");
AutoGenMipmaps();
}
@ -1667,9 +1667,9 @@ void OpenGLContext::GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) {
uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
switch (fmt) {
case DataFormat::B8G8R8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE;
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS;
case DataFormat::B4G4R4A4_UNORM_PACK16:
return FMT_RENDERTARGET | FMT_TEXTURE; // native support
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_AUTOGEN_MIPS; // native support
case DataFormat::A4R4G4B4_UNORM_PACK16:
#ifndef USING_GLES2
// Can support this if _REV formats are supported.
@ -1678,7 +1678,7 @@ uint32_t OpenGLContext::GetDataFormatSupport(DataFormat fmt) const {
return 0;
case DataFormat::R8G8B8A8_UNORM:
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT;
return FMT_RENDERTARGET | FMT_TEXTURE | FMT_INPUTLAYOUT | FMT_AUTOGEN_MIPS;
case DataFormat::R32_FLOAT:
case DataFormat::R32G32_FLOAT: