#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 #include #include Blizzard::Thread::TLSSlot GLTexture::m_Bindings[4]; void* GLTexture::CreateBindings(void* ptr) { return new std::deque>; } void GLTexture::DestroyBindings(void* ptr) { delete static_cast>*>(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::GLObjectPool::Push(GLPool::m_pool + 264, this); break; case GL_TEXTURE_CUBE_MAP: // TODO GLPool::GLObjectPool::Push(GLPool::m_pool + 520, this); break; case GL_TEXTURE_2D: // TODO GLPool::GLObjectPool::Push(GLPool::m_pool + 131080, this); break; } } std::vector& 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>*>( 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(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(&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(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(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::GLObjectPool::Pop(GLPool::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::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::Get()->GetNextName(); // TODO // Blizzard::Debug::Assert(this->m_TextureID >= PoolStats::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); }