Custom textures loading and dumping
This commit is contained in:
parent
031a40ec81
commit
dd280ee24d
|
@ -520,6 +520,8 @@ void LoadSettings()
|
||||||
settings.rend.ExtraDepthScale = atof(extra_depth_scale_str);
|
settings.rend.ExtraDepthScale = atof(extra_depth_scale_str);
|
||||||
if (settings.rend.ExtraDepthScale == 0)
|
if (settings.rend.ExtraDepthScale == 0)
|
||||||
settings.rend.ExtraDepthScale = 1.f;
|
settings.rend.ExtraDepthScale = 1.f;
|
||||||
|
settings.rend.CustomTextures = cfgLoadInt("config", "rend.CustomTextures", 0);
|
||||||
|
settings.rend.DumpTextures = cfgLoadInt("config", "rend.DumpTextures", 0);
|
||||||
|
|
||||||
settings.pvr.subdivide_transp = cfgLoadInt("config","pvr.Subdivide",0);
|
settings.pvr.subdivide_transp = cfgLoadInt("config","pvr.Subdivide",0);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 flyinghead
|
||||||
|
|
||||||
|
This file is part of reicast.
|
||||||
|
|
||||||
|
reicast is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
reicast is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
#include "CustomTexture.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <sstream>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
|
||||||
|
#include "deps/libpng/png.h"
|
||||||
|
#include "reios.h"
|
||||||
|
|
||||||
|
void CustomTexture::LoaderThread()
|
||||||
|
{
|
||||||
|
while (initialized)
|
||||||
|
{
|
||||||
|
TextureCacheData *texture = NULL;
|
||||||
|
|
||||||
|
work_queue_mutex.Lock();
|
||||||
|
if (!work_queue.empty())
|
||||||
|
{
|
||||||
|
texture = work_queue.back();
|
||||||
|
work_queue.pop_back();
|
||||||
|
}
|
||||||
|
work_queue_mutex.Unlock();
|
||||||
|
|
||||||
|
if (texture != NULL)
|
||||||
|
{
|
||||||
|
// FIXME texture may have been deleted. Need to detect this.
|
||||||
|
texture->ComputeHash();
|
||||||
|
int width, height;
|
||||||
|
u8 *image_data = LoadCustomTexture(texture->texture_hash, width, height);
|
||||||
|
if (image_data != NULL)
|
||||||
|
{
|
||||||
|
if (texture->custom_image_data != NULL)
|
||||||
|
delete [] texture->custom_image_data;
|
||||||
|
texture->custom_width = width;
|
||||||
|
texture->custom_height = height;
|
||||||
|
texture->custom_image_data = image_data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wakeup_thread.Wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CustomTexture::Init()
|
||||||
|
{
|
||||||
|
if (!initialized)
|
||||||
|
{
|
||||||
|
initialized = true;
|
||||||
|
std::string game_id_str = reios_product_number;
|
||||||
|
if (game_id_str.length() > 0)
|
||||||
|
{
|
||||||
|
std::replace(game_id_str.begin(), game_id_str.end(), ' ', '_');
|
||||||
|
textures_path = get_readonly_data_path("/data/") + "textures/" + game_id_str + "/";
|
||||||
|
|
||||||
|
DIR *dir = opendir(textures_path.c_str());
|
||||||
|
if (dir != NULL)
|
||||||
|
{
|
||||||
|
printf("Found custom textures directory: %s\n", textures_path.c_str());
|
||||||
|
custom_textures_available = true;
|
||||||
|
closedir(dir);
|
||||||
|
loader_thread.Start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return custom_textures_available;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomTexture::Terminate()
|
||||||
|
{
|
||||||
|
if (initialized)
|
||||||
|
{
|
||||||
|
initialized = false;
|
||||||
|
work_queue_mutex.Lock();
|
||||||
|
work_queue.clear();
|
||||||
|
work_queue_mutex.Unlock();
|
||||||
|
wakeup_thread.Set();
|
||||||
|
loader_thread.WaitToEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8* CustomTexture::LoadCustomTexture(u32 hash, int& width, int& height)
|
||||||
|
{
|
||||||
|
if (unknown_hashes.find(hash) != unknown_hashes.end())
|
||||||
|
return NULL;
|
||||||
|
std::stringstream path;
|
||||||
|
path << textures_path << std::hex << hash << ".png";
|
||||||
|
|
||||||
|
u8 *image_data = loadPNGData(path.str(), width, height, false);
|
||||||
|
if (image_data == NULL)
|
||||||
|
unknown_hashes.insert(hash);
|
||||||
|
|
||||||
|
return image_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomTexture::LoadCustomTextureAsync(TextureCacheData *texture_data)
|
||||||
|
{
|
||||||
|
if (!Init())
|
||||||
|
return;
|
||||||
|
work_queue_mutex.Lock();
|
||||||
|
work_queue.insert(work_queue.begin(), texture_data);
|
||||||
|
work_queue_mutex.Unlock();
|
||||||
|
wakeup_thread.Set();
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
Copyright 2018 flyinghead
|
||||||
|
|
||||||
|
This file is part of reicast.
|
||||||
|
|
||||||
|
reicast is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 2 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
reicast is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CORE_REND_GLES_CUSTOMTEXTURE_H_
|
||||||
|
#define CORE_REND_GLES_CUSTOMTEXTURE_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <set>
|
||||||
|
#include "gles.h"
|
||||||
|
|
||||||
|
class CustomTexture {
|
||||||
|
public:
|
||||||
|
CustomTexture() : loader_thread(loader_thread_func, this), wakeup_thread(false, true) {}
|
||||||
|
~CustomTexture() { Terminate(); }
|
||||||
|
u8* LoadCustomTexture(u32 hash, int& width, int& height);
|
||||||
|
void LoadCustomTextureAsync(TextureCacheData *texture_data);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Init();
|
||||||
|
void Terminate();
|
||||||
|
void LoaderThread();
|
||||||
|
|
||||||
|
static void *loader_thread_func(void *param) { ((CustomTexture *)param)->LoaderThread(); return NULL; }
|
||||||
|
|
||||||
|
bool initialized;
|
||||||
|
bool custom_textures_available;
|
||||||
|
std::string textures_path;
|
||||||
|
std::set<u32> unknown_hashes;
|
||||||
|
cThread loader_thread;
|
||||||
|
cResetEvent wakeup_thread;
|
||||||
|
std::vector<struct TextureCacheData *> work_queue;
|
||||||
|
cMutex work_queue_mutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* CORE_REND_GLES_CUSTOMTEXTURE_H_ */
|
|
@ -2039,7 +2039,7 @@ void png_cstd_read(png_structp png_ptr, png_bytep data, png_size_t length)
|
||||||
fread(data,1, length,pngfile);
|
fread(data,1, length,pngfile);
|
||||||
}
|
}
|
||||||
|
|
||||||
GLuint loadPNG(const string& fname, int &width, int &height)
|
u8* loadPNGData(const string& fname, int &width, int &height, bool bottom_to_top)
|
||||||
{
|
{
|
||||||
const char* filename=fname.c_str();
|
const char* filename=fname.c_str();
|
||||||
FILE* file = fopen(filename, "rb");
|
FILE* file = fopen(filename, "rb");
|
||||||
|
@ -2047,7 +2047,7 @@ GLuint loadPNG(const string& fname, int &width, int &height)
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
{
|
{
|
||||||
printf("Error opening %s\n", filename);
|
EMUERROR("Error opening %s\n", filename);
|
||||||
return TEXTURE_LOAD_ERROR;
|
return TEXTURE_LOAD_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2157,12 +2157,33 @@ GLuint loadPNG(const string& fname, int &width, int &height)
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the individual row_pointers to point at the correct offsets of image_data
|
// set the individual row_pointers to point at the correct offsets of image_data
|
||||||
for (int i = 0; i < height; ++i)
|
if (bottom_to_top)
|
||||||
row_pointers[height - 1 - i] = image_data + i * rowbytes;
|
{
|
||||||
|
for (int i = 0; i < height; ++i)
|
||||||
|
row_pointers[height - 1 - i] = image_data + i * rowbytes;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = 0; i < height; ++i)
|
||||||
|
row_pointers[i] = image_data + i * rowbytes;
|
||||||
|
}
|
||||||
|
|
||||||
//read the png into image_data through row_pointers
|
//read the png into image_data through row_pointers
|
||||||
png_read_image(png_ptr, row_pointers);
|
png_read_image(png_ptr, row_pointers);
|
||||||
|
|
||||||
|
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
||||||
|
delete[] row_pointers;
|
||||||
|
fclose(file);
|
||||||
|
|
||||||
|
return image_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLuint loadPNG(const string& fname, int &width, int &height, bool bottom_to_top)
|
||||||
|
{
|
||||||
|
png_byte *image_data = loadPNGData(fname, width, height);
|
||||||
|
if (image_data == NULL)
|
||||||
|
return TEXTURE_LOAD_ERROR;
|
||||||
|
|
||||||
//Now generate the OpenGL texture object
|
//Now generate the OpenGL texture object
|
||||||
GLuint texture = glcache.GenTexture();
|
GLuint texture = glcache.GenTexture();
|
||||||
glcache.BindTexture(GL_TEXTURE_2D, texture);
|
glcache.BindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
@ -2170,11 +2191,7 @@ GLuint loadPNG(const string& fname, int &width, int &height)
|
||||||
GL_UNSIGNED_BYTE, (GLvoid*) image_data);
|
GL_UNSIGNED_BYTE, (GLvoid*) image_data);
|
||||||
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
//clean up memory and close stuff
|
|
||||||
png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
|
|
||||||
delete[] image_data;
|
delete[] image_data;
|
||||||
delete[] row_pointers;
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,7 +169,8 @@ GLuint gl_CompileShader(const char* shader, GLuint type);
|
||||||
GLuint gl_CompileAndLink(const char* VertexShader, const char* FragmentShader);
|
GLuint gl_CompileAndLink(const char* VertexShader, const char* FragmentShader);
|
||||||
bool CompilePipelineShader(PipelineShader* s);
|
bool CompilePipelineShader(PipelineShader* s);
|
||||||
#define TEXTURE_LOAD_ERROR 0
|
#define TEXTURE_LOAD_ERROR 0
|
||||||
GLuint loadPNG(const string& subpath, int &width, int &height);
|
u8* loadPNGData(const string& subpath, int &width, int &height, bool bottom_to_top = true);
|
||||||
|
GLuint loadPNG(const string& subpath, int &width, int &height, bool bottom_to_top = true);
|
||||||
|
|
||||||
extern struct ShaderUniforms_t
|
extern struct ShaderUniforms_t
|
||||||
{
|
{
|
||||||
|
@ -228,3 +229,61 @@ struct FBT
|
||||||
};
|
};
|
||||||
|
|
||||||
extern FBT fb_rtt;
|
extern FBT fb_rtt;
|
||||||
|
|
||||||
|
struct PvrTexInfo;
|
||||||
|
template <class pixel_type> class PixelBuffer;
|
||||||
|
typedef void TexConvFP(PixelBuffer<u16>* pb,u8* p_in,u32 Width,u32 Height);
|
||||||
|
typedef void TexConvFP32(PixelBuffer<u32>* pb,u8* p_in,u32 Width,u32 Height);
|
||||||
|
|
||||||
|
struct TextureCacheData
|
||||||
|
{
|
||||||
|
TSP tsp; //dreamcast texture parameters
|
||||||
|
TCW tcw;
|
||||||
|
|
||||||
|
GLuint texID; //gl texture
|
||||||
|
u16* pData;
|
||||||
|
int tex_type;
|
||||||
|
|
||||||
|
u32 Lookups;
|
||||||
|
|
||||||
|
//decoded texture info
|
||||||
|
u32 sa; //pixel data start address in vram (might be offset for mipmaps/etc)
|
||||||
|
u32 sa_tex; //texture data start address in vram
|
||||||
|
u32 w,h; //width & height of the texture
|
||||||
|
u32 size; //size, in bytes, in vram
|
||||||
|
|
||||||
|
PvrTexInfo* tex;
|
||||||
|
TexConvFP* texconv;
|
||||||
|
TexConvFP32* texconv32;
|
||||||
|
|
||||||
|
u32 dirty;
|
||||||
|
vram_block* lock_block;
|
||||||
|
|
||||||
|
u32 Updates;
|
||||||
|
|
||||||
|
//used for palette updates
|
||||||
|
u32 palette_hash; // Palette hash at time of last update
|
||||||
|
u32 indirect_color_ptr; //palette color table index for pal. tex
|
||||||
|
//VQ quantizers table for VQ tex
|
||||||
|
//a texture can't be both VQ and PAL at the same time
|
||||||
|
u32 texture_hash; // xxhash of texture data, used for custom textures
|
||||||
|
u8* custom_image_data; // loaded custom image data
|
||||||
|
u32 custom_width;
|
||||||
|
u32 custom_height;
|
||||||
|
|
||||||
|
void PrintTextureName();
|
||||||
|
|
||||||
|
bool IsPaletted()
|
||||||
|
{
|
||||||
|
return tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Create(bool isGL);
|
||||||
|
void ComputeHash();
|
||||||
|
void Update();
|
||||||
|
void UploadToGPU(GLuint textype, int width, int height, u8 *temp_tex_buffer);
|
||||||
|
void CheckCustomTexture();
|
||||||
|
//true if : dirty or paletted texture and hashes don't match
|
||||||
|
bool NeedsUpdate();
|
||||||
|
void Delete();
|
||||||
|
};
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
|
#include <sstream>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include "glcache.h"
|
#include "glcache.h"
|
||||||
#include "rend/TexCache.h"
|
#include "rend/TexCache.h"
|
||||||
#include "hw/pvr/pvr_mem.h"
|
#include "hw/pvr/pvr_mem.h"
|
||||||
#include "hw/mem/_vmem.h"
|
#include "hw/mem/_vmem.h"
|
||||||
#include "deps/libpng/png.h"
|
#include "deps/libpng/png.h"
|
||||||
|
#include "deps/xxhash/xxhash.h"
|
||||||
|
#include "CustomTexture.h"
|
||||||
|
#include "reios.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Textures
|
Textures
|
||||||
|
@ -28,9 +33,6 @@ Compression
|
||||||
|
|
||||||
extern u32 decoded_colors[3][65536];
|
extern u32 decoded_colors[3][65536];
|
||||||
|
|
||||||
typedef void TexConvFP(PixelBuffer<u16>* pb,u8* p_in,u32 Width,u32 Height);
|
|
||||||
typedef void TexConvFP32(PixelBuffer<u32>* pb,u8* p_in,u32 Width,u32 Height);
|
|
||||||
|
|
||||||
struct PvrTexInfo
|
struct PvrTexInfo
|
||||||
{
|
{
|
||||||
const char* name;
|
const char* name;
|
||||||
|
@ -73,6 +75,8 @@ const u32 MipPoint[8] =
|
||||||
const GLuint PAL_TYPE[4]=
|
const GLuint PAL_TYPE[4]=
|
||||||
{GL_UNSIGNED_SHORT_5_5_5_1,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_INT_8_8_8_8};
|
{GL_UNSIGNED_SHORT_5_5_5_1,GL_UNSIGNED_SHORT_5_6_5,GL_UNSIGNED_SHORT_4_4_4_4, GL_UNSIGNED_INT_8_8_8_8};
|
||||||
|
|
||||||
|
CustomTexture custom_texture;
|
||||||
|
|
||||||
static void dumpRtTexture(u32 name, u32 w, u32 h) {
|
static void dumpRtTexture(u32 name, u32 w, u32 h) {
|
||||||
char sname[256];
|
char sname[256];
|
||||||
sprintf(sname, "texdump/%x-%d.png", name, FrameCount);
|
sprintf(sname, "texdump/%x-%d.png", name, FrameCount);
|
||||||
|
@ -114,13 +118,27 @@ static void dumpRtTexture(u32 name, u32 w, u32 h) {
|
||||||
free(rows);
|
free(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumpTexture(int texID, int w, int h, GLuint textype, void *temp_tex_buffer)
|
static void dumpTexture(u32 hash, int w, int h, GLuint textype, void *temp_tex_buffer)
|
||||||
{
|
{
|
||||||
char sname[256];
|
std::string base_dump_dir = get_writable_data_path("/data/texdump/");
|
||||||
sprintf(sname, "texdump/%d.png", texID);
|
if (!file_exists(base_dump_dir))
|
||||||
FILE *fp = fopen(sname, "wb");
|
mkdir(base_dump_dir.c_str(), 0755);
|
||||||
if (fp == NULL)
|
std::string game_id_str = reios_product_number;
|
||||||
|
if (game_id_str.length() == 0)
|
||||||
return;
|
return;
|
||||||
|
std::replace(game_id_str.begin(), game_id_str.end(), ' ', '_');
|
||||||
|
base_dump_dir += game_id_str + "/";
|
||||||
|
if (!file_exists(base_dump_dir))
|
||||||
|
mkdir(base_dump_dir.c_str(), 0755);
|
||||||
|
|
||||||
|
std::stringstream path;
|
||||||
|
path << base_dump_dir << std::hex << hash << ".png";
|
||||||
|
FILE *fp = fopen(path.str().c_str(), "wb");
|
||||||
|
if (fp == NULL)
|
||||||
|
{
|
||||||
|
printf("Failed to open %s for writing\n", path.str().c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
u16 *src = (u16 *)temp_tex_buffer;
|
u16 *src = (u16 *)temp_tex_buffer;
|
||||||
|
|
||||||
|
@ -178,6 +196,7 @@ static void dumpTexture(int texID, int w, int h, GLuint textype, void *temp_tex_
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("dumpTexture: unsupported picture format %x\n", textype);
|
printf("dumpTexture: unsupported picture format %x\n", textype);
|
||||||
|
fclose(fp);
|
||||||
free(rows[0]);
|
free(rows[0]);
|
||||||
free(rows);
|
free(rows);
|
||||||
return;
|
return;
|
||||||
|
@ -206,366 +225,362 @@ static void dumpTexture(int texID, int w, int h, GLuint textype, void *temp_tex_
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
for (int y = 0; y < h; y++)
|
for (int y = 0; y < h; y++)
|
||||||
free(rows[y]);
|
free(rows[y]);
|
||||||
free(rows);
|
free(rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
//Texture Cache :)
|
//Texture Cache :)
|
||||||
struct TextureCacheData
|
void TextureCacheData::PrintTextureName()
|
||||||
{
|
{
|
||||||
TSP tsp; //dreamcast texture parameters
|
printf("Texture: %s ",tex?tex->name:"?format?");
|
||||||
TCW tcw;
|
|
||||||
|
|
||||||
GLuint texID; //gl texture
|
if (tcw.VQ_Comp)
|
||||||
u16* pData;
|
printf(" VQ");
|
||||||
int tex_type;
|
|
||||||
|
|
||||||
u32 Lookups;
|
if (tcw.ScanOrder==0)
|
||||||
|
printf(" TW");
|
||||||
|
|
||||||
//decoded texture info
|
if (tcw.MipMapped)
|
||||||
u32 sa; //pixel data start address in vram (might be offset for mipmaps/etc)
|
printf(" MM");
|
||||||
u32 sa_tex; //texture data start address in vram
|
|
||||||
u32 w,h; //width & height of the texture
|
|
||||||
u32 size; //size, in bytes, in vram
|
|
||||||
|
|
||||||
PvrTexInfo* tex;
|
if (tcw.StrideSel)
|
||||||
TexConvFP* texconv;
|
printf(" Stride");
|
||||||
TexConvFP32* texconv32;
|
|
||||||
|
|
||||||
u32 dirty;
|
printf(" %dx%d @ 0x%X",8<<tsp.TexU,8<<tsp.TexV,tcw.TexAddr<<3);
|
||||||
vram_block* lock_block;
|
printf(" id=%d\n", texID);
|
||||||
|
}
|
||||||
|
|
||||||
u32 Updates;
|
//Create GL texture from tsp/tcw
|
||||||
|
void TextureCacheData::Create(bool isGL)
|
||||||
//used for palette updates
|
{
|
||||||
u32 palette_hash; // Palette hash at time of last update
|
//ask GL for texture ID
|
||||||
u32 indirect_color_ptr; //palette color table index for pal. tex
|
if (isGL) {
|
||||||
//VQ quantizers table for VQ tex
|
texID = glcache.GenTexture();
|
||||||
//a texture can't be both VQ and PAL at the same time
|
}
|
||||||
|
else {
|
||||||
void PrintTextureName()
|
texID = 0;
|
||||||
{
|
|
||||||
printf("Texture: %s ",tex?tex->name:"?format?");
|
|
||||||
|
|
||||||
if (tcw.VQ_Comp)
|
|
||||||
printf(" VQ");
|
|
||||||
|
|
||||||
if (tcw.ScanOrder==0)
|
|
||||||
printf(" TW");
|
|
||||||
|
|
||||||
if (tcw.MipMapped)
|
|
||||||
printf(" MM");
|
|
||||||
|
|
||||||
if (tcw.StrideSel)
|
|
||||||
printf(" Stride");
|
|
||||||
|
|
||||||
printf(" %dx%d @ 0x%X",8<<tsp.TexU,8<<tsp.TexV,tcw.TexAddr<<3);
|
|
||||||
printf(" id=%d\n", texID);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsPaletted()
|
pData = 0;
|
||||||
|
tex_type = 0;
|
||||||
|
|
||||||
|
//Reset state info ..
|
||||||
|
Lookups=0;
|
||||||
|
Updates=0;
|
||||||
|
dirty=FrameCount;
|
||||||
|
lock_block=0;
|
||||||
|
|
||||||
|
//decode info from tsp/tcw into the texture struct
|
||||||
|
tex=&format[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry
|
||||||
|
|
||||||
|
sa_tex = (tcw.TexAddr<<3) & VRAM_MASK; //texture start address
|
||||||
|
sa = sa_tex; //data texture start address (modified for MIPs, as needed)
|
||||||
|
w=8<<tsp.TexU; //tex width
|
||||||
|
h=8<<tsp.TexV; //tex height
|
||||||
|
|
||||||
|
//PAL texture
|
||||||
|
if (tex->bpp==4)
|
||||||
|
indirect_color_ptr=tcw.PalSelect<<4;
|
||||||
|
else if (tex->bpp==8)
|
||||||
|
indirect_color_ptr=(tcw.PalSelect>>4)<<8;
|
||||||
|
|
||||||
|
//VQ table (if VQ tex)
|
||||||
|
if (tcw.VQ_Comp)
|
||||||
|
indirect_color_ptr=sa;
|
||||||
|
|
||||||
|
//Convert a pvr texture into OpenGL
|
||||||
|
switch (tcw.PixelFmt)
|
||||||
{
|
{
|
||||||
return tcw.PixelFmt == PixelPal4 || tcw.PixelFmt == PixelPal8;
|
|
||||||
}
|
|
||||||
|
|
||||||
//Create GL texture from tsp/tcw
|
case Pixel1555: //0 1555 value: 1 bit; RGB values: 5 bits each
|
||||||
void Create(bool isGL)
|
case PixelReserved: //7 Reserved Regarded as 1555
|
||||||
{
|
case Pixel565: //1 565 R value: 5 bits; G value: 6 bits; B value: 5 bits
|
||||||
//ask GL for texture ID
|
case Pixel4444: //2 4444 value: 4 bits; RGB values: 4 bits each
|
||||||
if (isGL) {
|
case PixelYUV: //3 YUV422 32 bits per 2 pixels; YUYV values: 8 bits each
|
||||||
texID = glcache.GenTexture();
|
case PixelBumpMap: //4 Bump Map 16 bits/pixel; S value: 8 bits; R value: 8 bits
|
||||||
}
|
case PixelPal4: //5 4 BPP Palette Palette texture with 4 bits/pixel
|
||||||
else {
|
case PixelPal8: //6 8 BPP Palette Palette texture with 8 bits/pixel
|
||||||
texID = 0;
|
if (tcw.ScanOrder && (tex->PL || tex->PL32))
|
||||||
}
|
|
||||||
|
|
||||||
pData = 0;
|
|
||||||
tex_type = 0;
|
|
||||||
|
|
||||||
//Reset state info ..
|
|
||||||
Lookups=0;
|
|
||||||
Updates=0;
|
|
||||||
dirty=FrameCount;
|
|
||||||
lock_block=0;
|
|
||||||
|
|
||||||
//decode info from tsp/tcw into the texture struct
|
|
||||||
tex=&format[tcw.PixelFmt == PixelReserved ? Pixel1555 : tcw.PixelFmt]; //texture format table entry
|
|
||||||
|
|
||||||
sa_tex = (tcw.TexAddr<<3) & VRAM_MASK; //texture start address
|
|
||||||
sa = sa_tex; //data texture start address (modified for MIPs, as needed)
|
|
||||||
w=8<<tsp.TexU; //tex width
|
|
||||||
h=8<<tsp.TexV; //tex height
|
|
||||||
|
|
||||||
//PAL texture
|
|
||||||
if (tex->bpp==4)
|
|
||||||
indirect_color_ptr=tcw.PalSelect<<4;
|
|
||||||
else if (tex->bpp==8)
|
|
||||||
indirect_color_ptr=(tcw.PalSelect>>4)<<8;
|
|
||||||
|
|
||||||
//VQ table (if VQ tex)
|
|
||||||
if (tcw.VQ_Comp)
|
|
||||||
indirect_color_ptr=sa;
|
|
||||||
|
|
||||||
//Convert a pvr texture into OpenGL
|
|
||||||
switch (tcw.PixelFmt)
|
|
||||||
{
|
{
|
||||||
|
//Texture is stored 'planar' in memory, no deswizzle is needed
|
||||||
|
//verify(tcw.VQ_Comp==0);
|
||||||
|
if (tcw.VQ_Comp != 0)
|
||||||
|
printf("Warning: planar texture with VQ set (invalid)\n");
|
||||||
|
|
||||||
case Pixel1555: //0 1555 value: 1 bit; RGB values: 5 bits each
|
//Planar textures support stride selection, mostly used for non power of 2 textures (videos)
|
||||||
case PixelReserved: //7 Reserved Regarded as 1555
|
int stride=w;
|
||||||
case Pixel565: //1 565 R value: 5 bits; G value: 6 bits; B value: 5 bits
|
if (tcw.StrideSel)
|
||||||
case Pixel4444: //2 4444 value: 4 bits; RGB values: 4 bits each
|
stride=(TEXT_CONTROL&31)*32;
|
||||||
case PixelYUV: //3 YUV422 32 bits per 2 pixels; YUYV values: 8 bits each
|
//Call the format specific conversion code
|
||||||
case PixelBumpMap: //4 Bump Map 16 bits/pixel; S value: 8 bits; R value: 8 bits
|
texconv = tex->PL;
|
||||||
case PixelPal4: //5 4 BPP Palette Palette texture with 4 bits/pixel
|
texconv32 = tex->PL32;
|
||||||
case PixelPal8: //6 8 BPP Palette Palette texture with 8 bits/pixel
|
//calculate the size, in bytes, for the locking
|
||||||
if (tcw.ScanOrder && (tex->PL || tex->PL32))
|
size=stride*h*tex->bpp/8;
|
||||||
{
|
|
||||||
//Texture is stored 'planar' in memory, no deswizzle is needed
|
|
||||||
//verify(tcw.VQ_Comp==0);
|
|
||||||
if (tcw.VQ_Comp != 0)
|
|
||||||
printf("Warning: planar texture with VQ set (invalid)\n");
|
|
||||||
|
|
||||||
//Planar textures support stride selection, mostly used for non power of 2 textures (videos)
|
|
||||||
int stride=w;
|
|
||||||
if (tcw.StrideSel)
|
|
||||||
stride=(TEXT_CONTROL&31)*32;
|
|
||||||
//Call the format specific conversion code
|
|
||||||
texconv = tex->PL;
|
|
||||||
texconv32 = tex->PL32;
|
|
||||||
//calculate the size, in bytes, for the locking
|
|
||||||
size=stride*h*tex->bpp/8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Quake 3 Arena uses one. Not sure if valid but no need to crash
|
|
||||||
//verify(w==h || !tcw.MipMapped); // are non square mipmaps supported ? i can't recall right now *WARN*
|
|
||||||
|
|
||||||
if (tcw.VQ_Comp)
|
|
||||||
{
|
|
||||||
verify(tex->VQ != NULL || tex->VQ32 != NULL);
|
|
||||||
indirect_color_ptr=sa;
|
|
||||||
if (tcw.MipMapped)
|
|
||||||
sa+=MipPoint[tsp.TexU];
|
|
||||||
texconv = tex->VQ;
|
|
||||||
texconv32 = tex->VQ32;
|
|
||||||
size=w*h/8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
verify(tex->TW != NULL || tex->TW32 != NULL)
|
|
||||||
if (tcw.MipMapped)
|
|
||||||
sa+=MipPoint[tsp.TexU]*tex->bpp/2;
|
|
||||||
texconv = tex->TW;
|
|
||||||
texconv32 = tex->TW32;
|
|
||||||
size=w*h*tex->bpp/8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printf("Unhandled texture %d\n",tcw.PixelFmt);
|
|
||||||
size=w*h*2;
|
|
||||||
texconv = NULL;
|
|
||||||
texconv32 = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Update()
|
|
||||||
{
|
|
||||||
//texture state tracking stuff
|
|
||||||
Updates++;
|
|
||||||
dirty=0;
|
|
||||||
|
|
||||||
GLuint textype=tex->type;
|
|
||||||
|
|
||||||
bool has_alpha = false;
|
|
||||||
if (IsPaletted())
|
|
||||||
{
|
|
||||||
textype=PAL_TYPE[PAL_RAM_CTRL&3];
|
|
||||||
if (textype == GL_UNSIGNED_INT_8_8_8_8)
|
|
||||||
has_alpha = true;
|
|
||||||
|
|
||||||
// Get the palette hash to check for future updates
|
|
||||||
if (tcw.PixelFmt == PixelPal4)
|
|
||||||
palette_hash = pal_hash_16[tcw.PalSelect];
|
|
||||||
else
|
|
||||||
palette_hash = pal_hash_256[tcw.PalSelect >> 4];
|
|
||||||
}
|
|
||||||
|
|
||||||
palette_index=indirect_color_ptr; //might be used if pal. tex
|
|
||||||
vq_codebook=(u8*)&vram[indirect_color_ptr]; //might be used if VQ tex
|
|
||||||
|
|
||||||
//texture conversion work
|
|
||||||
u32 stride=w;
|
|
||||||
|
|
||||||
if (tcw.StrideSel && tcw.ScanOrder && (tex->PL || tex->PL32))
|
|
||||||
stride=(TEXT_CONTROL&31)*32; //I think this needs +1 ?
|
|
||||||
|
|
||||||
//PrintTextureName();
|
|
||||||
u32 original_h = h;
|
|
||||||
if (sa_tex > VRAM_SIZE || size == 0 || sa + size > VRAM_SIZE)
|
|
||||||
{
|
|
||||||
if (sa + size > VRAM_SIZE)
|
|
||||||
{
|
|
||||||
// Shenmue Space Harrier mini-arcade loads a texture that goes beyond the end of VRAM
|
|
||||||
// but only uses the top portion of it
|
|
||||||
h = (VRAM_SIZE - sa) * 8 / stride / tex->bpp;
|
|
||||||
size = stride * h * tex->bpp/8;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
printf("Warning: invalid texture. Address %08X %08X size %d\n", sa_tex, sa, size);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void *temp_tex_buffer = NULL;
|
|
||||||
u32 upscaled_w = w;
|
|
||||||
u32 upscaled_h = h;
|
|
||||||
|
|
||||||
PixelBuffer<u16> pb16;
|
|
||||||
PixelBuffer<u32> pb32;
|
|
||||||
|
|
||||||
// Figure out if we really need to use a 32-bit pixel buffer
|
|
||||||
bool need_32bit_buffer = true;
|
|
||||||
if ((settings.rend.TextureUpscale <= 1
|
|
||||||
|| w * h > settings.rend.MaxFilteredTextureSize
|
|
||||||
* settings.rend.MaxFilteredTextureSize // Don't process textures that are too big
|
|
||||||
|| tcw.PixelFmt == PixelYUV) // Don't process YUV textures
|
|
||||||
&& (!IsPaletted() || textype != GL_UNSIGNED_INT_8_8_8_8)
|
|
||||||
&& texconv != NULL)
|
|
||||||
need_32bit_buffer = false;
|
|
||||||
// TODO avoid upscaling/depost. textures that change too often
|
|
||||||
|
|
||||||
if (texconv32 != NULL && need_32bit_buffer)
|
|
||||||
{
|
|
||||||
// Force the texture type since that's the only 32-bit one we know
|
|
||||||
textype = GL_UNSIGNED_INT_8_8_8_8;
|
|
||||||
|
|
||||||
pb32.init(w, h);
|
|
||||||
|
|
||||||
texconv32(&pb32, (u8*)&vram[sa], stride, h);
|
|
||||||
|
|
||||||
#ifdef DEPOSTERIZE
|
|
||||||
{
|
|
||||||
// Deposterization
|
|
||||||
PixelBuffer<u32> tmp_buf;
|
|
||||||
tmp_buf.init(w, h);
|
|
||||||
|
|
||||||
DePosterize(pb32.data(), tmp_buf.data(), w, h);
|
|
||||||
pb32.steal_data(tmp_buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// xBRZ scaling
|
|
||||||
if (settings.rend.TextureUpscale > 1)
|
|
||||||
{
|
|
||||||
PixelBuffer<u32> tmp_buf;
|
|
||||||
tmp_buf.init(w * settings.rend.TextureUpscale, h * settings.rend.TextureUpscale);
|
|
||||||
|
|
||||||
if (tcw.PixelFmt == Pixel1555 || tcw.PixelFmt == Pixel4444)
|
|
||||||
// Alpha channel formats. Palettes with alpha are already handled
|
|
||||||
has_alpha = true;
|
|
||||||
UpscalexBRZ(settings.rend.TextureUpscale, pb32.data(), tmp_buf.data(), w, h, has_alpha);
|
|
||||||
pb32.steal_data(tmp_buf);
|
|
||||||
upscaled_w *= settings.rend.TextureUpscale;
|
|
||||||
upscaled_h *= settings.rend.TextureUpscale;
|
|
||||||
}
|
|
||||||
temp_tex_buffer = pb32.data();
|
|
||||||
}
|
|
||||||
else if (texconv != NULL)
|
|
||||||
{
|
|
||||||
pb16.init(w, h);
|
|
||||||
|
|
||||||
texconv(&pb16,(u8*)&vram[sa],stride,h);
|
|
||||||
temp_tex_buffer = pb16.data();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//fill it in with a temp color
|
// Quake 3 Arena uses one. Not sure if valid but no need to crash
|
||||||
printf("UNHANDLED TEXTURE\n");
|
//verify(w==h || !tcw.MipMapped); // are non square mipmaps supported ? i can't recall right now *WARN*
|
||||||
pb16.init(w, h);
|
|
||||||
memset(pb16.data(), 0x80, w * h * 2);
|
if (tcw.VQ_Comp)
|
||||||
temp_tex_buffer = pb16.data();
|
{
|
||||||
|
verify(tex->VQ != NULL || tex->VQ32 != NULL);
|
||||||
|
indirect_color_ptr=sa;
|
||||||
|
if (tcw.MipMapped)
|
||||||
|
sa+=MipPoint[tsp.TexU];
|
||||||
|
texconv = tex->VQ;
|
||||||
|
texconv32 = tex->VQ32;
|
||||||
|
size=w*h/8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
verify(tex->TW != NULL || tex->TW32 != NULL);
|
||||||
|
if (tcw.MipMapped)
|
||||||
|
sa+=MipPoint[tsp.TexU]*tex->bpp/2;
|
||||||
|
texconv = tex->TW;
|
||||||
|
texconv32 = tex->TW32;
|
||||||
|
size=w*h*tex->bpp/8;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Restore the original texture height if it was constrained to VRAM limits above
|
break;
|
||||||
h = original_h;
|
default:
|
||||||
|
printf("Unhandled texture %d\n",tcw.PixelFmt);
|
||||||
|
size=w*h*2;
|
||||||
|
texconv = NULL;
|
||||||
|
texconv32 = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//lock the texture to detect changes in it
|
void TextureCacheData::ComputeHash()
|
||||||
lock_block = libCore_vramlock_Lock(sa_tex,sa+size-1,this);
|
{
|
||||||
|
texture_hash = XXH32(&vram[sa], size, 7);
|
||||||
|
if (IsPaletted())
|
||||||
|
texture_hash ^= palette_hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureCacheData::Update()
|
||||||
|
{
|
||||||
|
//texture state tracking stuff
|
||||||
|
Updates++;
|
||||||
|
dirty=0;
|
||||||
|
|
||||||
if (texID) {
|
GLuint textype=tex->type;
|
||||||
//upload to OpenGL !
|
|
||||||
glcache.BindTexture(GL_TEXTURE_2D, texID);
|
bool has_alpha = false;
|
||||||
GLuint comps=textype==GL_UNSIGNED_SHORT_5_6_5?GL_RGB:GL_RGBA;
|
if (IsPaletted())
|
||||||
|
{
|
||||||
|
textype=PAL_TYPE[PAL_RAM_CTRL&3];
|
||||||
|
if (textype == GL_UNSIGNED_INT_8_8_8_8)
|
||||||
|
has_alpha = true;
|
||||||
|
|
||||||
|
// Get the palette hash to check for future updates
|
||||||
|
if (tcw.PixelFmt == PixelPal4)
|
||||||
|
palette_hash = pal_hash_16[tcw.PalSelect];
|
||||||
|
else
|
||||||
|
palette_hash = pal_hash_256[tcw.PalSelect >> 4];
|
||||||
|
}
|
||||||
|
|
||||||
|
palette_index=indirect_color_ptr; //might be used if pal. tex
|
||||||
|
vq_codebook=(u8*)&vram[indirect_color_ptr]; //might be used if VQ tex
|
||||||
|
|
||||||
|
//texture conversion work
|
||||||
|
u32 stride=w;
|
||||||
|
|
||||||
|
if (tcw.StrideSel && tcw.ScanOrder && (tex->PL || tex->PL32))
|
||||||
|
stride=(TEXT_CONTROL&31)*32; //I think this needs +1 ?
|
||||||
|
|
||||||
|
//PrintTextureName();
|
||||||
|
u32 original_h = h;
|
||||||
|
if (sa_tex > VRAM_SIZE || size == 0 || sa + size > VRAM_SIZE)
|
||||||
|
{
|
||||||
|
if (sa + size > VRAM_SIZE)
|
||||||
|
{
|
||||||
|
// Shenmue Space Harrier mini-arcade loads a texture that goes beyond the end of VRAM
|
||||||
|
// but only uses the top portion of it
|
||||||
|
h = (VRAM_SIZE - sa) * 8 / stride / tex->bpp;
|
||||||
|
size = stride * h * tex->bpp/8;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
printf("Warning: invalid texture. Address %08X %08X size %d\n", sa_tex, sa, size);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (settings.rend.CustomTextures)
|
||||||
|
{
|
||||||
|
custom_texture.LoadCustomTextureAsync(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *temp_tex_buffer = NULL;
|
||||||
|
u32 upscaled_w = w;
|
||||||
|
u32 upscaled_h = h;
|
||||||
|
|
||||||
|
PixelBuffer<u16> pb16;
|
||||||
|
PixelBuffer<u32> pb32;
|
||||||
|
|
||||||
|
// Figure out if we really need to use a 32-bit pixel buffer
|
||||||
|
bool need_32bit_buffer = true;
|
||||||
|
if ((settings.rend.TextureUpscale <= 1
|
||||||
|
|| w * h > settings.rend.MaxFilteredTextureSize
|
||||||
|
* settings.rend.MaxFilteredTextureSize // Don't process textures that are too big
|
||||||
|
|| tcw.PixelFmt == PixelYUV) // Don't process YUV textures
|
||||||
|
&& (!IsPaletted() || textype != GL_UNSIGNED_INT_8_8_8_8)
|
||||||
|
&& texconv != NULL)
|
||||||
|
need_32bit_buffer = false;
|
||||||
|
// TODO avoid upscaling/depost. textures that change too often
|
||||||
|
|
||||||
|
if (texconv32 != NULL && need_32bit_buffer)
|
||||||
|
{
|
||||||
|
// Force the texture type since that's the only 32-bit one we know
|
||||||
|
textype = GL_UNSIGNED_INT_8_8_8_8;
|
||||||
|
|
||||||
|
pb32.init(w, h);
|
||||||
|
|
||||||
|
texconv32(&pb32, (u8*)&vram[sa], stride, h);
|
||||||
|
|
||||||
|
#ifdef DEPOSTERIZE
|
||||||
|
{
|
||||||
|
// Deposterization
|
||||||
|
PixelBuffer<u32> tmp_buf;
|
||||||
|
tmp_buf.init(w, h);
|
||||||
|
|
||||||
|
DePosterize(pb32.data(), tmp_buf.data(), w, h);
|
||||||
|
pb32.steal_data(tmp_buf);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// xBRZ scaling
|
||||||
|
if (settings.rend.TextureUpscale > 1)
|
||||||
|
{
|
||||||
|
PixelBuffer<u32> tmp_buf;
|
||||||
|
tmp_buf.init(w * settings.rend.TextureUpscale, h * settings.rend.TextureUpscale);
|
||||||
|
|
||||||
|
if (tcw.PixelFmt == Pixel1555 || tcw.PixelFmt == Pixel4444)
|
||||||
|
// Alpha channel formats. Palettes with alpha are already handled
|
||||||
|
has_alpha = true;
|
||||||
|
UpscalexBRZ(settings.rend.TextureUpscale, pb32.data(), tmp_buf.data(), w, h, has_alpha);
|
||||||
|
pb32.steal_data(tmp_buf);
|
||||||
|
upscaled_w *= settings.rend.TextureUpscale;
|
||||||
|
upscaled_h *= settings.rend.TextureUpscale;
|
||||||
|
}
|
||||||
|
temp_tex_buffer = pb32.data();
|
||||||
|
}
|
||||||
|
else if (texconv != NULL)
|
||||||
|
{
|
||||||
|
pb16.init(w, h);
|
||||||
|
|
||||||
|
texconv(&pb16,(u8*)&vram[sa],stride,h);
|
||||||
|
temp_tex_buffer = pb16.data();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//fill it in with a temp color
|
||||||
|
printf("UNHANDLED TEXTURE\n");
|
||||||
|
pb16.init(w, h);
|
||||||
|
memset(pb16.data(), 0x80, w * h * 2);
|
||||||
|
temp_tex_buffer = pb16.data();
|
||||||
|
}
|
||||||
|
// Restore the original texture height if it was constrained to VRAM limits above
|
||||||
|
h = original_h;
|
||||||
|
|
||||||
|
//lock the texture to detect changes in it
|
||||||
|
lock_block = libCore_vramlock_Lock(sa_tex,sa+size-1,this);
|
||||||
|
|
||||||
|
if (texID) {
|
||||||
|
//upload to OpenGL !
|
||||||
|
UploadToGPU(textype, upscaled_w, upscaled_h, (u8*)temp_tex_buffer);
|
||||||
|
if (settings.rend.DumpTextures)
|
||||||
|
{
|
||||||
|
ComputeHash();
|
||||||
|
dumpTexture(texture_hash, upscaled_w, upscaled_h, textype, temp_tex_buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
#if FEAT_HAS_SOFTREND
|
||||||
|
if (textype == GL_UNSIGNED_SHORT_5_6_5)
|
||||||
|
tex_type = 0;
|
||||||
|
else if (textype == GL_UNSIGNED_SHORT_5_5_5_1)
|
||||||
|
tex_type = 1;
|
||||||
|
else if (textype == GL_UNSIGNED_SHORT_4_4_4_4)
|
||||||
|
tex_type = 2;
|
||||||
|
|
||||||
|
u16 *tex_data = (u16 *)temp_tex_buffer;
|
||||||
|
if (pData) {
|
||||||
|
_mm_free(pData);
|
||||||
|
}
|
||||||
|
|
||||||
|
pData = (u16*)_mm_malloc(w * h * 16, 16);
|
||||||
|
for (int y = 0; y < h; y++) {
|
||||||
|
for (int x = 0; x < w; x++) {
|
||||||
|
u32* data = (u32*)&pData[(x + y*w) * 8];
|
||||||
|
|
||||||
|
data[0] = decoded_colors[tex_type][tex_data[(x + 1) % w + (y + 1) % h * w]];
|
||||||
|
data[1] = decoded_colors[tex_type][tex_data[(x + 0) % w + (y + 1) % h * w]];
|
||||||
|
data[2] = decoded_colors[tex_type][tex_data[(x + 1) % w + (y + 0) % h * w]];
|
||||||
|
data[3] = decoded_colors[tex_type][tex_data[(x + 0) % w + (y + 0) % h * w]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
die("Soft rend disabled, invalid code path");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureCacheData::UploadToGPU(GLuint textype, int width, int height, u8 *temp_tex_buffer)
|
||||||
|
{
|
||||||
|
//upload to OpenGL !
|
||||||
|
glcache.BindTexture(GL_TEXTURE_2D, texID);
|
||||||
|
GLuint comps=textype == GL_UNSIGNED_SHORT_5_6_5 ? GL_RGB : GL_RGBA;
|
||||||
#ifdef GLES
|
#ifdef GLES
|
||||||
GLuint actual_textype = textype == GL_UNSIGNED_INT_8_8_8_8 ? GL_UNSIGNED_BYTE : textype;
|
GLuint actual_textype = textype == GL_UNSIGNED_INT_8_8_8_8 ? GL_UNSIGNED_BYTE : textype;
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, comps, upscaled_w, upscaled_h, 0, comps, actual_textype,
|
glTexImage2D(GL_TEXTURE_2D, 0, comps, width, height, 0, comps, actual_textype,
|
||||||
temp_tex_buffer);
|
temp_tex_buffer);
|
||||||
#else
|
#else
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0,comps , upscaled_w, upscaled_h, 0, comps, textype, temp_tex_buffer);
|
glTexImage2D(GL_TEXTURE_2D, 0,comps, width, height, 0, comps, textype, temp_tex_buffer);
|
||||||
#endif
|
#endif
|
||||||
if (tcw.MipMapped && settings.rend.UseMipmaps)
|
if (tcw.MipMapped && settings.rend.UseMipmaps)
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
//dumpTexture(texID, upscaled_w, upscaled_h, textype, temp_tex_buffer);
|
}
|
||||||
}
|
|
||||||
else {
|
|
||||||
#if FEAT_HAS_SOFTREND
|
|
||||||
if (textype == GL_UNSIGNED_SHORT_5_6_5)
|
|
||||||
tex_type = 0;
|
|
||||||
else if (textype == GL_UNSIGNED_SHORT_5_5_5_1)
|
|
||||||
tex_type = 1;
|
|
||||||
else if (textype == GL_UNSIGNED_SHORT_4_4_4_4)
|
|
||||||
tex_type = 2;
|
|
||||||
|
|
||||||
u16 *tex_data = (u16 *)temp_tex_buffer;
|
void TextureCacheData::CheckCustomTexture()
|
||||||
if (pData) {
|
{
|
||||||
_mm_free(pData);
|
if (custom_image_data != NULL)
|
||||||
}
|
|
||||||
|
|
||||||
pData = (u16*)_mm_malloc(w * h * 16, 16);
|
|
||||||
for (int y = 0; y < h; y++) {
|
|
||||||
for (int x = 0; x < w; x++) {
|
|
||||||
u32* data = (u32*)&pData[(x + y*w) * 8];
|
|
||||||
|
|
||||||
data[0] = decoded_colors[tex_type][tex_data[(x + 1) % w + (y + 1) % h * w]];
|
|
||||||
data[1] = decoded_colors[tex_type][tex_data[(x + 0) % w + (y + 1) % h * w]];
|
|
||||||
data[2] = decoded_colors[tex_type][tex_data[(x + 1) % w + (y + 0) % h * w]];
|
|
||||||
data[3] = decoded_colors[tex_type][tex_data[(x + 0) % w + (y + 0) % h * w]];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
die("Soft rend disabled, invalid code path");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//true if : dirty or paletted texture and hashes don't match
|
|
||||||
bool NeedsUpdate() {
|
|
||||||
bool rc = dirty
|
|
||||||
|| (tcw.PixelFmt == PixelPal4 && palette_hash != pal_hash_16[tcw.PalSelect])
|
|
||||||
|| (tcw.PixelFmt == PixelPal8 && palette_hash != pal_hash_256[tcw.PalSelect >> 4]);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Delete()
|
|
||||||
{
|
{
|
||||||
if (pData) {
|
UploadToGPU(GL_UNSIGNED_BYTE, custom_width, custom_height, custom_image_data);
|
||||||
#if FEAT_HAS_SOFTREND
|
delete [] custom_image_data;
|
||||||
_mm_free(pData);
|
custom_image_data = NULL;
|
||||||
pData = 0;
|
|
||||||
#else
|
|
||||||
die("softrend disabled, invalid codepath");
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (texID) {
|
|
||||||
glcache.DeleteTextures(1, &texID);
|
|
||||||
}
|
|
||||||
if (lock_block)
|
|
||||||
libCore_vramlock_Unlock_block(lock_block);
|
|
||||||
lock_block=0;
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
//true if : dirty or paletted texture and hashes don't match
|
||||||
|
bool TextureCacheData::NeedsUpdate() {
|
||||||
|
bool rc = dirty
|
||||||
|
|| (tcw.PixelFmt == PixelPal4 && palette_hash != pal_hash_16[tcw.PalSelect])
|
||||||
|
|| (tcw.PixelFmt == PixelPal8 && palette_hash != pal_hash_256[tcw.PalSelect >> 4]);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextureCacheData::Delete()
|
||||||
|
{
|
||||||
|
if (pData) {
|
||||||
|
#if FEAT_HAS_SOFTREND
|
||||||
|
_mm_free(pData);
|
||||||
|
pData = 0;
|
||||||
|
#else
|
||||||
|
die("softrend disabled, invalid codepath");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (texID) {
|
||||||
|
glcache.DeleteTextures(1, &texID);
|
||||||
|
}
|
||||||
|
if (lock_block)
|
||||||
|
libCore_vramlock_Unlock_block(lock_block);
|
||||||
|
lock_block=0;
|
||||||
|
if (custom_image_data != NULL)
|
||||||
|
delete [] custom_image_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
map<u64,TextureCacheData> TexCache;
|
map<u64,TextureCacheData> TexCache;
|
||||||
|
@ -855,7 +870,10 @@ GLuint gl_GetTexture(TSP tsp, TCW tcw)
|
||||||
if (tf->NeedsUpdate())
|
if (tf->NeedsUpdate())
|
||||||
tf->Update();
|
tf->Update();
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
tf->CheckCustomTexture();
|
||||||
TexCacheHits++;
|
TexCacheHits++;
|
||||||
|
}
|
||||||
|
|
||||||
// if (os_GetSeconds() - LastTexCacheStats >= 2.0)
|
// if (os_GetSeconds() - LastTexCacheStats >= 2.0)
|
||||||
// {
|
// {
|
||||||
|
|
|
@ -724,6 +724,8 @@ struct settings_t
|
||||||
int TextureUpscale;
|
int TextureUpscale;
|
||||||
int MaxFilteredTextureSize;
|
int MaxFilteredTextureSize;
|
||||||
f32 ExtraDepthScale;
|
f32 ExtraDepthScale;
|
||||||
|
bool CustomTextures;
|
||||||
|
bool DumpTextures;
|
||||||
} rend;
|
} rend;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|
|
|
@ -205,6 +205,7 @@
|
||||||
AE2A2D7E21D6851E004B308D /* Lzma2Dec.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7021D6851D004B308D /* Lzma2Dec.c */; };
|
AE2A2D7E21D6851E004B308D /* Lzma2Dec.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7021D6851D004B308D /* Lzma2Dec.c */; };
|
||||||
AE2A2D7F21D6851E004B308D /* 7zFile.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7321D6851D004B308D /* 7zFile.c */; };
|
AE2A2D7F21D6851E004B308D /* 7zFile.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7321D6851D004B308D /* 7zFile.c */; };
|
||||||
AE2A2D8021D6851E004B308D /* 7zDec.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7721D6851E004B308D /* 7zDec.c */; };
|
AE2A2D8021D6851E004B308D /* 7zDec.c in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D7721D6851E004B308D /* 7zDec.c */; };
|
||||||
|
AE2A2D8321D7DB78004B308D /* CustomTexture.cpp in Sources */ = {isa = PBXBuildFile; fileRef = AE2A2D8121D7DB77004B308D /* CustomTexture.cpp */; };
|
||||||
AE4FF4E1211588B5009BF202 /* font.png in Resources */ = {isa = PBXBuildFile; fileRef = AE4FF4E0211588B5009BF202 /* font.png */; };
|
AE4FF4E1211588B5009BF202 /* font.png in Resources */ = {isa = PBXBuildFile; fileRef = AE4FF4E0211588B5009BF202 /* font.png */; };
|
||||||
AE649BB62188689000EF4A81 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BB42188689000EF4A81 /* xxhash.c */; };
|
AE649BB62188689000EF4A81 /* xxhash.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BB42188689000EF4A81 /* xxhash.c */; };
|
||||||
AE649BF3218C552500EF4A81 /* bitmath.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BCD218C552500EF4A81 /* bitmath.c */; };
|
AE649BF3218C552500EF4A81 /* bitmath.c in Sources */ = {isa = PBXBuildFile; fileRef = AE649BCD218C552500EF4A81 /* bitmath.c */; };
|
||||||
|
@ -657,6 +658,8 @@
|
||||||
AE2A2D7521D6851D004B308D /* 7zCrc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7zCrc.h; sourceTree = "<group>"; };
|
AE2A2D7521D6851D004B308D /* 7zCrc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 7zCrc.h; sourceTree = "<group>"; };
|
||||||
AE2A2D7621D6851E004B308D /* Lzma2Dec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lzma2Dec.h; sourceTree = "<group>"; };
|
AE2A2D7621D6851E004B308D /* Lzma2Dec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lzma2Dec.h; sourceTree = "<group>"; };
|
||||||
AE2A2D7721D6851E004B308D /* 7zDec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 7zDec.c; sourceTree = "<group>"; };
|
AE2A2D7721D6851E004B308D /* 7zDec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = 7zDec.c; sourceTree = "<group>"; };
|
||||||
|
AE2A2D8121D7DB77004B308D /* CustomTexture.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CustomTexture.cpp; sourceTree = "<group>"; };
|
||||||
|
AE2A2D8221D7DB78004B308D /* CustomTexture.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CustomTexture.h; sourceTree = "<group>"; };
|
||||||
AE4FF4E0211588B5009BF202 /* font.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = font.png; path = ../../../linux/font.png; sourceTree = "<group>"; };
|
AE4FF4E0211588B5009BF202 /* font.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = font.png; path = ../../../linux/font.png; sourceTree = "<group>"; };
|
||||||
AE649BB42188689000EF4A81 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = "<group>"; };
|
AE649BB42188689000EF4A81 /* xxhash.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = xxhash.c; sourceTree = "<group>"; };
|
||||||
AE649BB52188689000EF4A81 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = "<group>"; };
|
AE649BB52188689000EF4A81 /* xxhash.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xxhash.h; sourceTree = "<group>"; };
|
||||||
|
@ -1586,6 +1589,8 @@
|
||||||
84B7BE9B1B72720200F9733F /* gles */ = {
|
84B7BE9B1B72720200F9733F /* gles */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
AE2A2D8121D7DB77004B308D /* CustomTexture.cpp */,
|
||||||
|
AE2A2D8221D7DB78004B308D /* CustomTexture.h */,
|
||||||
AE2A2D4921D6820D004B308D /* glcache.h */,
|
AE2A2D4921D6820D004B308D /* glcache.h */,
|
||||||
84B7BE9C1B72720200F9733F /* gldraw.cpp */,
|
84B7BE9C1B72720200F9733F /* gldraw.cpp */,
|
||||||
84B7BE9D1B72720200F9733F /* gles.cpp */,
|
84B7BE9D1B72720200F9733F /* gles.cpp */,
|
||||||
|
@ -2119,6 +2124,7 @@
|
||||||
84B7BEB31B72720200F9733F /* coreio.cpp in Sources */,
|
84B7BEB31B72720200F9733F /* coreio.cpp in Sources */,
|
||||||
84B7BF281B72720200F9733F /* dsp.cpp in Sources */,
|
84B7BF281B72720200F9733F /* dsp.cpp in Sources */,
|
||||||
84B7BF3D1B72720200F9733F /* Renderer_if.cpp in Sources */,
|
84B7BF3D1B72720200F9733F /* Renderer_if.cpp in Sources */,
|
||||||
|
AE2A2D8321D7DB78004B308D /* CustomTexture.cpp in Sources */,
|
||||||
84B7BF191B72720200F9733F /* deflate.c in Sources */,
|
84B7BF191B72720200F9733F /* deflate.c in Sources */,
|
||||||
84B7BF7E1B72720200F9733F /* TexCache.cpp in Sources */,
|
84B7BF7E1B72720200F9733F /* TexCache.cpp in Sources */,
|
||||||
AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */,
|
AEFF7F4E214D9D590068CE11 /* pico_dev_ppp.c in Sources */,
|
||||||
|
|
Loading…
Reference in New Issue