mirror of
https://github.com/open-goal/jak-project
synced 2026-05-23 06:54:31 -04:00
7292cb5765
* tfrag improvements * cleanup * one last fix * clang
189 lines
5.8 KiB
C++
189 lines
5.8 KiB
C++
#pragma once
|
|
|
|
#include <array>
|
|
#include <memory>
|
|
#include <string>
|
|
#include "common/common_types.h"
|
|
#include "game/graphics/texture/TextureConverter.h"
|
|
#include "common/util/Serializer.h"
|
|
|
|
// Converting textures happens when textures are uploaded by the game.
|
|
// Uploading textures to the gpu and creating samplers is done lazily, as needed.
|
|
|
|
// Each sampler
|
|
struct TextureSampler {
|
|
u64 sampler_object = -1; // the opengl sampler
|
|
u64 handle = -1; // the handle used for bindless textures.
|
|
bool created = false; // lazily created as needed, by default we don't make them
|
|
};
|
|
|
|
// Each texture in our pool has a record:
|
|
struct TextureRecord {
|
|
std::string page_name; // the page we belong to (from game info)
|
|
std::string name; // our name (from game info)
|
|
u8 mip_level; // which mip we are
|
|
u8 psm = -1; // format in the game
|
|
u8 cpsm = -1; // clut format in the game
|
|
u16 w, h; // our dimensions
|
|
u8 data_segment; // which segment we came from in the texture page
|
|
bool on_gpu = false; // if we are uploaded to the GPU
|
|
|
|
// garbage collection settings.
|
|
// by default, do_gc is set, and the pool will take care of freeing textures.
|
|
// when a texture is uploaded on top of a texture (in PS2 VRAM), the texture will be unloaded from
|
|
// the GPU. Unless somebody has another instance of the shared_ptr to this texture, this structure
|
|
// (including converted texture data) will be discarded.
|
|
|
|
// In some cases, this is not desirable because the game may toggle between two different textures
|
|
// in VRAM, and we don't want to reconvert every time. The TextureUploadHandler will implement its
|
|
// own caching to help with this. To manage textures yourself, you should:
|
|
// - keep around a shared_ptr to the TextureRecord (so it doesn't get deleted when it's out of PS2
|
|
// VRAM).
|
|
// - set do_gc to false (to keep texture in GPU memory when replaced).
|
|
// In this case, you must use the discard function to remove the texture from the GPU, if you
|
|
// really want to get rid of it.
|
|
bool do_gc = true;
|
|
|
|
// The texture data. In some cases, we keep textures only on the GPU (for example, the result of a
|
|
// render to texture). In these, data is not populated, but you must set only_on_gpu = true. When
|
|
// saving graphics state, the texture will be dumped from the GPU and saved to the file so it is
|
|
// possible to restore.
|
|
bool only_on_gpu = false;
|
|
std::vector<u8> data;
|
|
|
|
// if we're on the gpu, our OpenGL texture
|
|
u64 gpu_texture = 0;
|
|
|
|
// our VRAM address.
|
|
u32 dest = -1;
|
|
|
|
void unload_from_gpu();
|
|
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
struct TextureData {
|
|
std::shared_ptr<TextureRecord> normal_texture;
|
|
std::shared_ptr<TextureRecord> mt4hh_texture;
|
|
|
|
void serialize(Serializer& ser);
|
|
};
|
|
|
|
struct GoalTexture {
|
|
s16 w;
|
|
s16 h;
|
|
u8 num_mips;
|
|
u8 tex1_control;
|
|
u8 psm;
|
|
u8 mip_shift;
|
|
u16 clutpsm;
|
|
u16 dest[7];
|
|
u16 clut_dest;
|
|
u8 width[7];
|
|
u32 name_ptr;
|
|
u32 size;
|
|
float uv_dist;
|
|
u32 masks[3];
|
|
|
|
s32 segment_of_mip(s32 mip) const {
|
|
if (2 >= num_mips) {
|
|
return num_mips - mip - 1;
|
|
} else {
|
|
return std::max(0, 2 - mip);
|
|
}
|
|
}
|
|
};
|
|
|
|
static_assert(sizeof(GoalTexture) == 60, "GoalTexture size");
|
|
static_assert(offsetof(GoalTexture, clutpsm) == 8);
|
|
static_assert(offsetof(GoalTexture, clut_dest) == 24);
|
|
|
|
struct GoalTexturePage {
|
|
struct Seg {
|
|
u32 block_data_ptr;
|
|
u32 size;
|
|
u32 dest;
|
|
};
|
|
u32 file_info_ptr;
|
|
u32 name_ptr;
|
|
u32 id;
|
|
s32 length; // texture count
|
|
u32 mip0_size;
|
|
u32 size;
|
|
Seg segment[3];
|
|
u32 pad[16];
|
|
// start of array.
|
|
|
|
std::string print() const;
|
|
|
|
bool try_copy_texture_description(GoalTexture* dest,
|
|
int idx,
|
|
const u8* memory_base,
|
|
const u8* tpage,
|
|
u32 s7_ptr) {
|
|
u32 ptr;
|
|
memcpy(&ptr, tpage + sizeof(GoalTexturePage) + 4 * idx, 4);
|
|
if (ptr == s7_ptr) {
|
|
return false;
|
|
}
|
|
memcpy(dest, memory_base + ptr, sizeof(GoalTexture));
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class TexturePool {
|
|
public:
|
|
void handle_upload_now(const u8* tpage, int mode, const u8* memory_base, u32 s7_ptr);
|
|
|
|
std::vector<std::shared_ptr<TextureRecord>> convert_textures(const u8* tpage,
|
|
int mode,
|
|
const u8* memory_base,
|
|
u32 s7_ptr);
|
|
|
|
void set_texture(u32 location, std::shared_ptr<TextureRecord> record);
|
|
void draw_debug_window();
|
|
TextureRecord* lookup(u32 location) {
|
|
if (m_textures.at(location).normal_texture) {
|
|
return m_textures[location].normal_texture.get();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
TextureRecord* lookup_mt4hh(u32 location) {
|
|
if (m_textures.at(location).mt4hh_texture) {
|
|
return m_textures[location].mt4hh_texture.get();
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
TextureRecord* get_random_texture();
|
|
|
|
void upload_to_gpu(TextureRecord* rec);
|
|
|
|
void relocate(u32 destination, u32 source, u32 format);
|
|
|
|
void remove_garbage_textures();
|
|
void discard(std::shared_ptr<TextureRecord> tex);
|
|
|
|
void serialize(Serializer& ser);
|
|
|
|
private:
|
|
void unload_all_textures();
|
|
void draw_debug_for_tex(const std::string& name, TextureRecord& tex);
|
|
TextureConverter m_tex_converter;
|
|
|
|
// uses tex.dest[mip] indexing. (bytes / 256). Currently only sets the base of a texture.
|
|
std::array<TextureData, 1024 * 1024 * 4 / 256> m_textures;
|
|
|
|
// textures that the game overwrote, but may be still allocated on the GPU.
|
|
// TODO: free these periodically.
|
|
std::vector<std::shared_ptr<TextureRecord>> m_garbage_textures;
|
|
|
|
char m_regex_input[256] = "";
|
|
|
|
int m_most_recent_gc_count = 0;
|
|
int m_most_recent_gc_count_gpu = 0;
|
|
};
|