thunderbrew/src/gx/gll/GLTexture.cpp

660 lines
20 KiB
C++
Raw Normal View History

2023-01-02 13:17:18 -06:00
#include "gx/gll/GLTexture.h"
#include "gx/gll/GLCommand.h"
#include "gx/gll/GLDevice.h"
#include "gx/gll/GLPool.h"
#include "gx/gll/GLUtil.h"
#include "gx/texture/CGxTex.hpp"
#include <deque>
Blizzard::Thread::TLSSlot GLTexture::m_Bindings[4];
void* GLTexture::CreateBindings(void* ptr) {
return new std::deque<std::vector<Binding>>;
}
void GLTexture::DestroyBindings(void* ptr) {
delete static_cast<std::deque<std::vector<Binding>>*>(ptr);
}
void GLTexture::Bind(GLDevice* device, bool force) {
BLIZZARD_ASSERT(!this->IsSystemBuffer());
BLIZZARD_ASSERT(this->m_Depth != 0);
if (!device) {
device = GLDevice::Get();
}
BLIZZARD_ASSERT(device != nullptr);
auto& bindings = this->GetBindings();
uint32_t deviceID = device->GetID();
if (deviceID >= bindings.size()) {
bindings.resize(deviceID + 1);
}
bindings[deviceID].device = device;
uint32_t currentActiveTexture = device->m_States.binding.currentActiveTexture;
if (bindings[deviceID].boundStages[currentActiveTexture]) {
return;
}
if (force) {
bindings[deviceID].boundStages[currentActiveTexture] = 1;
device->BindTexture(this->m_TextureType, this);
return;
}
for (int32_t i = 0; i < 16; i++) {
if (bindings[deviceID].boundStages[i]) {
device->SetActiveTexture(i);
return;
}
}
bindings[deviceID].boundStages[currentActiveTexture] = 1;
device->BindTexture(this->m_TextureType, this);
}
void GLTexture::FreeTexture() {
auto device = GLDevice::Get();
if (device->m_TexWorker) {
// TODO
this->m_LastFrameUsed = 0;
int32_t numFace = this->m_TextureType == GL_TEXTURE_CUBE_MAP ? 6 : 1;
for (int32_t face = 0; face < numFace; face++) {
for (int32_t level = 0; level < this->m_NumMipmap; level++) {
this->m_Mipmaps[face][level].ReleaseObject();
}
}
// TODO this->Sub690D0();
// TODO device->m_TexWorker->Sub72340(this);
device->m_TexWorker->Lock();
auto command = new GLTexDestroy(this);
device->m_TexWorker->Send(command);
device->m_TexWorker->Signal();
device->m_TexWorker->Unlock();
return;
}
int32_t numFace = this->m_TextureType == GL_TEXTURE_CUBE_MAP ? 6 : 1;
for (int32_t face = 0; face < numFace; face++) {
if (this->m_Mipmaps[face]) {
delete[] this->m_Mipmaps[face];
}
}
if (this->m_Mipmaps) {
delete[] this->m_Mipmaps;
}
this->m_Mipmaps = nullptr;
this->m_Depth = 0;
// TODO this->Sub690D0();
glDeleteTextures(1, &this->m_TextureID);
this->m_GenerateMipmaps = 0;
this->m_MaxMipmapLevel = 1000;
this->m_BaseMipmapLevel = 0;
this->m_CompareMode = 0;
this->m_Sampler.mipmapBias = 0.0f;
this->m_Sampler.borderColor = { 0.0f, 0.0f, 0.0f, 0.0f };
this->m_Sampler.addressModeS = GL_REPEAT;
this->m_Sampler.addressModeT = GL_REPEAT;
this->m_Sampler.addressModeR = GL_REPEAT;
this->m_Sampler.magFilterMode = GL_LINEAR;
this->m_Sampler.minFilterMode = GL_NEAREST_MIPMAP_LINEAR;
this->m_Sampler.maxAnisotropy = 1.0f;
Blizzard::Memory::Free(this->m_Data);
this->m_Data = nullptr;
switch (this->m_TextureType) {
case GL_TEXTURE_3D:
// TODO GLPool<GLTexture3D>::GLObjectPool::Push(GLPool<GLTexture3D>::m_pool + 264, this);
break;
case GL_TEXTURE_CUBE_MAP:
// TODO GLPool<GLTextureCubeMap>::GLObjectPool::Push(GLPool<GLTextureCubeMap>::m_pool + 520, this);
break;
case GL_TEXTURE_2D:
// TODO GLPool<GLTexture2D>::GLObjectPool::Push(GLPool<GLTexture2D>::m_pool + 131080, this);
break;
}
}
std::vector<GLTexture::Binding>& GLTexture::GetBindings() {
uint32_t index = GLLTextureTypeToIndex(this->m_TextureType);
uint32_t id;
if (index == 0) {
id = this->m_TextureID - 33;
} else if (index == 1) {
id = this->m_TextureID - 32801;
} else if (index == 2) {
id = this->m_TextureID - 32865;
} else if (index == 3) {
id = this->m_TextureID - 1;
}
auto target = static_cast<std::deque<std::vector<Binding>>*>(
Blizzard::Thread::RegisterLocalStorage(
&GLTexture::m_Bindings[index],
GLTexture::CreateBindings,
nullptr,
GLTexture::DestroyBindings
)
);
if (id >= target->size()) {
target->resize(id + 1);
}
return (*target)[id];
}
GLTextureFormat GLTexture::GetFormat() {
return this->m_Format;
}
TextureFormatInfo& GLTexture::GetFormatInfo() {
return k_TextureFormatInfo[this->m_Format];
}
GLMipmap* GLTexture::GetMipmap(uint32_t level, GLEnum face) {
BLIZZARD_ASSERT(face >= GL_TEXTURE_CUBE_MAP_POSITIVE_X && face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z);
BLIZZARD_ASSERT(level < this->m_NumMipmap);
BLIZZARD_ASSERT(this->m_Mipmaps != nullptr);
BLIZZARD_ASSERT(this->m_Mipmaps[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X] != nullptr);
return &this->m_Mipmaps[face - GL_TEXTURE_CUBE_MAP_POSITIVE_X][level];
}
bool GLTexture::IsRenderTarget() {
return this->m_Flags & GLTFLAG_RENDERTARGET;
}
bool GLTexture::IsSystemBuffer() {
return this->m_Flags & GLTFLAG_SYSTEM_BUFFER;
}
bool GLTexture::IsValid() {
return this->m_Depth;
}
void* GLTexture::Map(uint32_t level, const GLRect* a3, uint32_t& a4, GLEnum a5) {
BLIZZARD_ASSERT(this->m_TextureType != GL_TEXTURE_3D);
auto mipmap = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X);
a4 = mipmap->GetPitch();
return mipmap->Map(a5, a3);
}
void GLTexture::RecreateGLTexture() {
BLIZZARD_ASSERT(GLDevice::Get()->m_TexWorker != nullptr);
if (this->m_TextureType == GL_TEXTURE_RECTANGLE_EXT) {
return;
}
bool isCubeMap = this->m_TextureType == GL_TEXTURE_CUBE_MAP;
int32_t numFace = isCubeMap ? 6 : 1;
for (int32_t face = 0; face < numFace; face++) {
for (int32_t level = 0; level < this->m_NumMipmap; level++) {
this->m_Mipmaps[face][level].Map(GL_WRITE_ONLY, static_cast<GLBox*>(nullptr));
this->m_Mipmaps[face][level].Unmap();
}
}
glTexParameterf(this->m_TextureType, GL_TEXTURE_LOD_BIAS, this->m_Sampler.mipmapBias);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, this->m_Sampler.addressModeS);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, this->m_Sampler.addressModeT);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_R, this->m_Sampler.addressModeR);
glTexParameteri(this->m_TextureType, GL_TEXTURE_MIN_FILTER, this->m_Sampler.minFilterMode);
glTexParameteri(this->m_TextureType, GL_TEXTURE_MAG_FILTER, this->m_Sampler.magFilterMode);
glTexParameterf(this->m_TextureType, GL_TEXTURE_MAX_ANISOTROPY_EXT, this->m_Sampler.maxAnisotropy);
glTexParameterfv(this->m_TextureType, GL_TEXTURE_BORDER_COLOR, reinterpret_cast<GLfloat*>(&this->m_Sampler.borderColor));
glTexParameteri(this->m_TextureType, GL_TEXTURE_MAX_LEVEL, this->m_MaxMipmapLevel);
glTexParameteri(this->m_TextureType, GL_TEXTURE_BASE_LEVEL, this->m_BaseMipmapLevel);
glTexParameteri(this->m_TextureType, GL_GENERATE_MIPMAP, this->m_GenerateMipmaps);
}
void GLTexture::ResizeMipmaps() {
BLIZZARD_ASSERT(this->m_Mipmaps == nullptr);
int32_t numFace = this->m_TextureType == GL_TEXTURE_CUBE_MAP ? 6 : 1;
this->m_Mipmaps = new GLMipmap*[numFace];
for (int32_t face = 0; face < numFace; face++) {
this->m_Mipmaps[face] = new GLMipmap[this->m_NumMipmap];
}
}
void GLTexture::SetAddressModeR(GLEnum mode) {
// TODO
}
void GLTexture::SetAddressModeS(GLEnum mode) {
if (this->m_Sampler.addressModeS == mode) {
return;
}
if (mode == GL_CLAMP_TO_EDGE) {
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, mode);
this->m_Sampler.addressModeS = mode;
} else {
// Workaround for buggy GPU (possibly ATI Radeon X1900)
if (GLDevice::GetRendererInfo().renderer_id == 0x21900) {
if (this->m_Width & (this->m_Width - 1)) {
return;
}
if (this->m_Height & (this->m_Height - 1)) {
return;
}
}
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_S, mode);
this->m_Sampler.addressModeS = mode;
}
}
void GLTexture::SetAddressModeT(GLEnum mode) {
if (this->m_Sampler.addressModeT == mode) {
return;
}
if (mode == GL_CLAMP_TO_EDGE) {
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, mode);
this->m_Sampler.addressModeT = mode;
} else {
// Workaround for buggy GPU (possibly ATI Radeon X1900)
if (GLDevice::GetRendererInfo().renderer_id == 0x21900) {
if (this->m_Width & (this->m_Width - 1)) {
return;
}
if (this->m_Height & (this->m_Height - 1)) {
return;
}
}
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_WRAP_T, mode);
this->m_Sampler.addressModeT = mode;
}
}
void GLTexture::SetBorderColor(const GLColor4f& color) {
// TODO
}
void GLTexture::SetCompareMode(GLEnum compareMode) {
BLIZZARD_ASSERT(
this->GetFormatInfo().m_DataFormat == GL_DEPTH_COMPONENT
|| this->GetFormatInfo().m_DataFormat == GL_DEPTH_STENCIL_EXT
|| compareMode == GL_NONE
);
if (this->m_CompareMode != compareMode) {
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_COMPARE_MODE, compareMode);
this->m_CompareMode = compareMode;
}
}
void GLTexture::SetMagFilterMode(GLEnum mode) {
if (this->m_Sampler.magFilterMode == mode) {
return;
}
if (GLDevice::GetRendererInfo().vendor_id == 2 && this->IsRenderTarget() && mode != GL_LINEAR) {
return;
}
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_MAG_FILTER, mode);
this->m_Sampler.magFilterMode = mode;
}
void GLTexture::SetMaxAnisotropy(int32_t maxAnisotropy) {
if (this->m_Sampler.maxAnisotropy == maxAnisotropy) {
return;
}
this->Bind(nullptr, 0);
glTexParameterf(this->m_TextureType, GL_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy);
this->m_Sampler.maxAnisotropy = maxAnisotropy;
}
void GLTexture::SetMinFilterMode(GLEnum mode) {
if (this->m_Sampler.minFilterMode == mode) {
return;
}
if (GLDevice::GetRendererInfo().vendor_id == 2 && this->IsRenderTarget() && mode != GL_LINEAR) {
return;
}
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_MIN_FILTER, mode);
this->m_Sampler.minFilterMode = mode;
}
void GLTexture::SetupTexture() {
BLIZZARD_ASSERT(this->m_NumMipmap == 1 || (this->m_Flags & GLTFLAG_AUTOGEN_MIPMAP) == 0);
BLIZZARD_ASSERT(!this->IsRenderTarget() || this->m_NumMipmap == 1);
BLIZZARD_ASSERT(!this->IsRenderTarget() || (this->m_Flags & GLTFLAG_READ_ACCESS) == 0);
GLDevice* device = GLDevice::Get();
if (this->GetFormatInfo().m_IsCompressed) {
int32_t smallestDim = std::min(this->m_Width, this->m_Height);
BLIZZARD_ASSERT(smallestDim >= 4);
if (smallestDim == 4) {
this->m_NumMipmap = 1;
} else if (smallestDim == 8) {
this->m_NumMipmap = 2;
} else if (smallestDim == 16) {
this->m_NumMipmap = 3;
} else if (smallestDim == 32) {
this->m_NumMipmap = 4;
} else if (smallestDim == 64) {
this->m_NumMipmap = 5;
} else if (smallestDim == 128) {
this->m_NumMipmap = 6;
} else if (smallestDim == 256) {
this->m_NumMipmap = 7;
} else if (smallestDim == 512) {
this->m_NumMipmap = 8;
} else if (smallestDim == 1024) {
this->m_NumMipmap = 9;
} else if (smallestDim == 2048) {
this->m_NumMipmap = 10;
} else if (smallestDim == 4096) {
this->m_NumMipmap = 11;
} else {
int32_t i = smallestDim >> 1;
int32_t n = 0;
while (i) {
i >>= 1;
n++;
}
this->m_NumMipmap = n - 1;
}
} else {
int32_t largestDim = std::max(this->m_Width, this->m_Height);
if (largestDim == 1) {
this->m_NumMipmap = 1;
} else if (largestDim == 2) {
this->m_NumMipmap = 2;
} else if (largestDim == 4) {
this->m_NumMipmap = 3;
} else if (largestDim == 8) {
this->m_NumMipmap = 4;
} else if (largestDim == 16) {
this->m_NumMipmap = 5;
} else if (largestDim == 32) {
this->m_NumMipmap = 6;
} else if (largestDim == 64) {
this->m_NumMipmap = 7;
} else if (largestDim == 128) {
this->m_NumMipmap = 8;
} else if (largestDim == 256) {
this->m_NumMipmap = 9;
} else if (largestDim == 512) {
this->m_NumMipmap = 10;
} else if (largestDim == 1024) {
this->m_NumMipmap = 11;
} else if (largestDim == 2048) {
this->m_NumMipmap = 12;
} else if (largestDim == 4096) {
this->m_NumMipmap = 13;
} else {
int32_t i = largestDim >> 1;
int32_t n = 0;
while (i) {
i >>= 1;
n++;
}
this->m_NumMipmap = n + 1;
}
}
if (!(this->m_Flags & GLTFLAG_SYSTEM_BUFFER)) {
BLIZZARD_ASSERT(this->m_RequestedNumMipmaps != 0);
this->m_NumMipmap = std::min(this->m_NumMipmap, this->m_RequestedNumMipmaps);
}
this->var12 = this->GetFormatInfo().m_BytePerPixel * this->m_Width;
if (this->GetFormatInfo().m_IsCompressed) {
this->var12 >>= 2;
}
this->ResizeMipmaps();
bool isCubeMap = this->m_TextureType == GL_TEXTURE_CUBE_MAP;
this->m_Size = 0;
int32_t numFace = isCubeMap ? 6 : 1;
for (int32_t face = 0; face < numFace; face++) {
for (int32_t level = 0; level < this->m_NumMipmap; level++) {
GLMipmap* mip = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face);
mip->m_Level = level;
mip->m_Texture = this;
mip->ResetSize(this->m_Width >> level, this->m_Height >> level, this->m_Depth >> level);
this->m_Size += mip->m_Size;
}
}
this->var7 = GLDevice::m_Devices[0]->m_TextureList.begin();
if (this->m_Flags & GLTFLAG_SYSTEM_BUFFER) {
return;
}
BLIZZARD_ASSERT(this->m_Data == nullptr);
if (!this->IsRenderTarget()) {
this->m_Data = static_cast<char*>(Blizzard::Memory::Allocate(this->m_Size));
}
this->Bind(nullptr, 0);
if (this->IsRenderTarget()) {
this->SetAddressModeR(GL_CLAMP_TO_EDGE);
this->SetAddressModeS(GL_CLAMP_TO_EDGE);
this->SetAddressModeT(GL_CLAMP_TO_EDGE);
this->SetMinFilterMode(GL_LINEAR);
this->SetMagFilterMode(GL_LINEAR);
glTexParameteri(this->m_TextureType, GL_TEXTURE_STORAGE_HINT_APPLE, GL_STORAGE_CACHED_APPLE);
} else {
this->SetAddressModeR(GL_REPEAT);
this->SetAddressModeS(GL_REPEAT);
this->SetAddressModeT(GL_REPEAT);
this->SetMinFilterMode(GL_NEAREST_MIPMAP_NEAREST);
this->SetMagFilterMode(GL_NEAREST);
}
if (this->GetFormatInfo().m_DataFormat == GL_DEPTH_COMPONENT || this->GetFormatInfo().m_DataFormat == GL_DEPTH_STENCIL) {
this->SetCompareMode(GLDevice::m_ExtARBShadow >= 1 ? GL_COMPARE_R_TO_TEXTURE : 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_COMPARE_FUNC, GL_LEQUAL);
glTexParameteri(this->m_TextureType, GL_DEPTH_TEXTURE_MODE, GL_LUMINANCE);
} else {
this->SetCompareMode(0);
}
this->SetMaxAnisotropy(1);
this->SetBorderColor(GLColor4f::ZERO);
int32_t autogenMipmap = this->m_Flags & GLTFLAG_AUTOGEN_MIPMAP;
if (autogenMipmap != this->m_GenerateMipmaps) {
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_GENERATE_MIPMAP, autogenMipmap);
this->m_GenerateMipmaps = autogenMipmap;
}
int32_t maxMipmapLevel = this->m_NumMipmap - 1;
if (maxMipmapLevel != this->m_MaxMipmapLevel) {
this->Bind(nullptr, 0);
glTexParameteri(this->m_TextureType, GL_TEXTURE_MAX_LEVEL, maxMipmapLevel);
this->m_MaxMipmapLevel = maxMipmapLevel;
}
if (this->m_TextureType == GL_TEXTURE_RECTANGLE_EXT) {
return;
}
if (this->IsRenderTarget()) {
device->SetUnpackClientStorage(0);
}
unsigned char* data = reinterpret_cast<unsigned char*>(this->m_Data);
for (int32_t face = 0; face < numFace; face++) {
for (int32_t level = 0; level < this->m_NumMipmap; level++) {
GLMipmap* mip = this->GetMipmap(level, GL_TEXTURE_CUBE_MAP_POSITIVE_X + face);
if (this->m_TextureType == GL_TEXTURE_CUBE_MAP) {
mip->ResetData(face, level, data);
} else {
mip->ResetData(this->m_TextureType, level, data);
}
if (data) {
data += mip->m_Size;
}
}
}
// TODO
// this->m_TimeStamp = Blizzard::Time::GetTimestamp();
if (this->IsRenderTarget()) {
device->SetUnpackClientStorage(1);
}
}
void GLTexture::Unbind(GLDevice* device, uint32_t stage) {
auto& bindings = this->GetBindings();
BLIZZARD_ASSERT(device->GetID() < bindings.size());
BLIZZARD_ASSERT(bindings[device->GetID()].device == device);
BLIZZARD_ASSERT(bindings[device->GetID()].boundStages[stage]);
bindings[device->GetID()].boundStages[stage] = 0;
}
void GLTexture::Unmap(uint32_t level, GLEnum face) {
auto mipmap = this->GetMipmap(level, face);
mipmap->Unmap();
}
GLTexture2D* GLTexture2D::Create(uint32_t width, uint32_t height, uint32_t numMipMap, GLTextureFormat format, uint32_t flags) {
GLTexture2D* tex;
// TODO
// tex = GLPool<GLTexture2D>::GLObjectPool::Pop(GLPool<GLTexture2D>::m_pool + 131080);
tex = nullptr;
if (!tex) {
tex = new GLTexture2D();
}
// TODO
// Blizzard::Debug::Assert(tex->m_refCount == 0);
tex->m_RefCount = 1;
// TODO
// Blizzard::Debug::Assert(tex->m_TextureID >= PoolStats<GLTexture2D>::NAME_POOL_FIRST_NAME);
tex->m_TextureType = GL_TEXTURE_2D;
tex->m_Width = width;
tex->m_Depth = 1;
tex->m_Height = height;
tex->m_Format = format;
tex->m_NumMipmap = numMipMap;
tex->m_RequestedNumMipmaps = numMipMap;
tex->m_Flags = flags;
tex->SetupTexture();
return tex;
}
GLTexture2D::GLTexture2D() : GLTexture() {
this->m_TextureType = GL_TEXTURE_2D;
this->m_TextureID = GLPool<GLTexture2D>::Get()->GetNextName();
// TODO
// Blizzard::Debug::Assert(this->m_TextureID >= PoolStats<GLTexture2D>::NAME_POOL_FIRST_NAME);
}
void GLTexture2D::ReleaseObject() {
BLIZZARD_ASSERT(this->m_TextureType == GL_TEXTURE_2D);
this->FreeTexture();
}
void GLLTexSetFlags(CGxTex* texId, GLTexture* a2) {
static GLEnum convertMagFilterToOgl[] = {
GL_NEAREST,
GL_LINEAR,
GL_NEAREST,
GL_LINEAR,
GL_LINEAR,
GL_LINEAR
};
static GLEnum convertMinFilterToOgl[] = {
GL_NEAREST,
GL_LINEAR,
GL_NEAREST_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR_MIPMAP_LINEAR
};
a2->SetMagFilterMode(convertMagFilterToOgl[texId->m_flags.m_filter]);
a2->SetMinFilterMode(convertMinFilterToOgl[texId->m_flags.m_filter]);
a2->SetAddressModeS(texId->m_flags.m_wrapU ? GL_REPEAT : GL_CLAMP_TO_EDGE);
a2->SetAddressModeT(texId->m_flags.m_wrapV ? GL_REPEAT : GL_CLAMP_TO_EDGE);
a2->SetMaxAnisotropy(texId->m_flags.m_maxAnisotropy);
}