2020-07-31 11:31:32 +08:00

580 lines
18 KiB
C++

//=====================================================================
// Copyright 2008 (c), ATI Technologies Inc. All rights reserved.
// Copyright 2016 (c), Advanced Micro Devices, Inc. All rights reserved.
//=====================================================================
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files(the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions :
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
#include "Compressonator.h"
#include "CMP_MIPS.h"
#include<stdarg.h>
#include<stdio.h>
#include <assert.h>
void(*PrintStatusLine)(char *) = NULL;
void PrintInfo(const char* Format, ... )
{
if (PrintStatusLine)
{
// define a pointer to save argument list
va_list args;
char buff[1024];
// process the arguments into our debug buffer
va_start(args, Format);
vsnprintf(buff, 1024, Format, args);
va_end(args);
PrintStatusLine(buff);
}
}
void CMP_CMIPS::PrintError(const char* Format, ... )
{
char buff[1024];
// define a pointer to save argument list
va_list args;
// process the arguments into our debug buffer
va_start(args, Format);
vsnprintf(buff,1024,Format,args);
va_end(args);
PrintInfo(buff);
}
void CMP_CMIPS::Print(const char* Format, ...)
{
if (!PrintLine) return;
if (m_infolevel & 0x01)
{
char buff[1024];
// define a pointer to save argument list
va_list args;
// process the arguments into our debug buffer
va_start(args, Format);
vsnprintf(buff, 1024, Format, args);
va_end(args);
PrintLine(buff);
}
}
CMP_INT CMP_API CMP_CalcMinMipSize(CMP_INT nHeight, CMP_INT nWidth, CMP_INT MipsLevel)
{
while (MipsLevel > 0)
{
nWidth = CMP_MAX(nWidth >> 1, 1);
nHeight = CMP_MAX(nHeight >> 1, 1);
MipsLevel--;
}
if (nWidth > nHeight)
return (nHeight);
return (nWidth);
}
CMP_ERROR CMP_API CMP_CreateCompressMipSet(CMP_MipSet* pMipSetCMP,CMP_MipSet* pMipSetSRC)
{
CMP_CMIPS CMips;
pMipSetCMP->m_Flags = MS_FLAG_Default;
pMipSetCMP->m_nHeight = pMipSetSRC->m_nHeight;
pMipSetCMP->m_nWidth = pMipSetSRC->m_nWidth;
pMipSetCMP->dwWidth = pMipSetSRC->dwWidth;
pMipSetCMP->dwHeight = pMipSetSRC->dwHeight;
pMipSetCMP->m_ChannelFormat = CF_Compressed;
pMipSetCMP->m_dwFourCC = CMP_MAKEFOURCC('D', 'X', '1', '0');
pMipSetCMP->m_nBlockHeight = 4;
pMipSetCMP->m_nBlockWidth = 4;
pMipSetCMP->m_nMaxMipLevels = pMipSetSRC->m_nMaxMipLevels;
pMipSetCMP->m_nMipLevels = 1;
pMipSetCMP->m_TextureType = pMipSetSRC->m_TextureType;
pMipSetCMP->m_nDepth = pMipSetSRC->m_nDepth;
if (!CMips.AllocateMipSet(pMipSetCMP, CF_Compressed, TDT_ARGB, pMipSetCMP->m_TextureType,pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight,pMipSetCMP->m_nDepth))
return CMP_ERR_MEM_ALLOC_FOR_MIPSET;
//----------------------------------------------------------------
// Access the data table for mip level 0 (full texture width and height)
//----------------------------------------------------------------
MipLevel* pOutMipLevel = CMips.GetMipLevel(pMipSetCMP, 0);
//-----------------------------------------------------------------------
// Calculate the target compressed block buffer size (4 bytes x 4 bytes)
//-----------------------------------------------------------------------
unsigned int Width = ((pMipSetSRC->m_nWidth + 3) / 4) * 4;
unsigned int Height = ((pMipSetSRC->m_nHeight + 3) / 4) * 4;
pMipSetCMP->dwDataSize = Width * Height;
//----------------------------------------------------------------
// Allocate memory for the mip level 0
//----------------------------------------------------------------
if (!CMips.AllocateCompressedMipLevelData(pOutMipLevel, pMipSetSRC->m_nWidth, pMipSetSRC->m_nHeight, pMipSetCMP->dwDataSize))
{
std::printf("Memory Error(1): allocating MIPSet compression level data buffer\n");
return CMP_ERR_MEM_ALLOC_FOR_MIPSET;
}
pMipSetCMP->pData = pOutMipLevel->m_pbData;
return CMP_OK;
}
CMP_INT CMP_MaxFacesOrSlices(const CMP_MipSet* pMipSet, CMP_INT nMipLevel)
{
if (!pMipSet)
return 0;
if (pMipSet->m_nDepth < 1)
return 0;
if (pMipSet->m_TextureType == TT_2D || pMipSet->m_TextureType == TT_CubeMap)
return pMipSet->m_nDepth;
int nMaxSlices = pMipSet->m_nDepth;
for (int i = 0; i<pMipSet->m_nMipLevels; i++)
{
if (i == nMipLevel)
return nMaxSlices;
nMaxSlices = nMaxSlices>1 ? nMaxSlices >> 1 : 1; //div by 2, min of 1
}
return 0; //nMipLevel was too high
}
CMP_MipLevel* CMP_CMIPS::GetMipLevel(const CMP_MipSet* pMipSet, int nMipLevel, int nFaceOrSlice)
{
if(!pMipSet)
{
assert(pMipSet);
return NULL;
}
if (!pMipSet->m_pMipLevelTable)
{
return NULL;
}
if (nMipLevel > MAX_MIPLEVEL_SUPPORTED)
{
return NULL;
}
if(nMipLevel > pMipSet->m_nMaxMipLevels)
{
assert(nMipLevel <= pMipSet->m_nMaxMipLevels);
return NULL;
}
if(nFaceOrSlice < 0)
{
return NULL; //not an error, indicates requested face doesn't exist
}
int nDepth = pMipSet->m_nDepth, index = 0, whichMipLevel = 0;
switch(pMipSet->m_TextureType)
{
case TT_2D:
if(nFaceOrSlice != 0)
{
return NULL;
}
return (pMipSet->m_pMipLevelTable)[nMipLevel];
case TT_CubeMap:
if(nFaceOrSlice > 6) //cubemap have at most 6 faces
{
assert(nFaceOrSlice > 6);
return NULL;
}
return (pMipSet->m_pMipLevelTable)[nMipLevel * nDepth + nFaceOrSlice];
case TT_VolumeTexture:
while(whichMipLevel <= nMipLevel)
{
if(whichMipLevel == nMipLevel)
{
return (pMipSet->m_pMipLevelTable)[index + nFaceOrSlice];
}
else
{
index += nDepth;
whichMipLevel++;
nDepth = nDepth>1 ? nDepth>>1 : 1;
}
}
return NULL;
default:
assert(0);
return NULL;
}
}
// Mip Levels are coded as follows
// 1 is original size 2 = 1/2 size 3 = 1/4 size etc...
// this value is based on arrays been MipMap[maxMipLevels]
int CMP_CMIPS::GetMaxMipLevels(int nWidth, int nHeight, int nDepth)
{
int maxMipLevels = 0;
assert(nWidth > 0 && nHeight > 0 && nDepth > 0);
while (nWidth >= 1 || nHeight >= 1 || nDepth > 1)
{
maxMipLevels++;
if (nWidth == 1 || nHeight == 1)
break;
//div by 2
nWidth = nWidth >1 ? nWidth >>1 : 1;
nHeight = nHeight>1 ? nHeight>>1 : 1;
nDepth = nDepth >1 ? nDepth >>1 : 1;
}
return maxMipLevels;
}
bool CMP_CMIPS::AllocateMipLevelTable(CMP_MipLevelTable** ppMipLevelTable, int nMaxMipLevels, CMP_TextureType textureType, int nDepth, int& nLevelsToAllocate)
{
//TODO test
assert(nDepth > 0);
nLevelsToAllocate = 0;
//determine # miplevels to allocate based on texture type
switch(textureType)
{
case TT_2D:
nLevelsToAllocate = nMaxMipLevels;
if(nDepth != 1)
{
return false;
}
break;
case TT_CubeMap:
if (nDepth > 6)
{
return false;
}
nLevelsToAllocate = nMaxMipLevels * nDepth;
break;
case TT_VolumeTexture:
for(int i=0; i < nMaxMipLevels; i++)
{
nLevelsToAllocate += nDepth;
if(nDepth > 1)
{
nDepth >>= 1;
}
}
break;
default:
return false;
}
//allocate the mipLevelTable (buncha pointers to miplevels)
*ppMipLevelTable = reinterpret_cast<CMP_MipLevelTable*>(calloc(nLevelsToAllocate, sizeof(CMP_MipLevel*)));
assert(*ppMipLevelTable);
return (*ppMipLevelTable != NULL);
}
bool CMP_CMIPS::AllocateAllMipLevels(CMP_MipLevelTable* pMipLevelTable, CMP_TextureType /*textureType*/, int nLevelsToAllocate)
{
//TODO test
//allocate each MipLevel that the table points to
for(int i=0; i<nLevelsToAllocate; i++)
{
pMipLevelTable[i] = reinterpret_cast<CMP_MipLevel*>(calloc(sizeof(CMP_MipLevel), 1));
//make sure it was allocated ok
assert(pMipLevelTable[i]);
if(!pMipLevelTable[i])
{
//free previous mipLevels
for(i -= 1; i>=0; i--)
{
if (pMipLevelTable[i])
{
free(pMipLevelTable[i]);
pMipLevelTable[i] = NULL;
}
}
return false;
}
}
return true;
}
bool CMP_CMIPS::AllocateMipSet(CMP_MipSet* pMipSet, CMP_ChannelFormat channelFormat, TextureDataType textureDataType, CMP_TextureType textureType, int nWidth, int nHeight, int nDepth)
{
//TODO test
assert(pMipSet);
if (!(nWidth > 0 && nHeight > 0 && nDepth > 0)) return false;
if(pMipSet->m_pMipLevelTable)
{
assert(!pMipSet->m_pMipLevelTable);
return false;
}
//depth only matters for this when its volume texture
pMipSet->m_nMaxMipLevels = GetMaxMipLevels(nWidth, nHeight, textureType== TT_VolumeTexture ? nDepth : 1);
if(pMipSet->m_nMipLevels > pMipSet->m_nMaxMipLevels || pMipSet->m_nMipLevels < 0)
pMipSet->m_nMipLevels = 0;
pMipSet->m_ChannelFormat = channelFormat;
pMipSet->m_TextureDataType = textureDataType;
pMipSet->m_TextureType = textureType;
//Probably shouldn't wipe this out either pMipSet->m_Flags = MS_Default;
//On second thought, DONT wipe this out pMipSet->m_CubeFaceMask = 0;
pMipSet->m_nWidth = nWidth;
pMipSet->m_nHeight = nHeight;
pMipSet->m_nDepth = nDepth;
int numLevelsToAllocate;
if(!AllocateMipLevelTable(&pMipSet->m_pMipLevelTable, pMipSet->m_nMaxMipLevels, textureType, nDepth, numLevelsToAllocate))
{
//mipleveltable allocation failed
return false;
}
if(!AllocateAllMipLevels(pMipSet->m_pMipLevelTable, textureType, numLevelsToAllocate))
{
//allocation of mip levels failed
if (pMipSet->m_pMipLevelTable)
{
free(pMipSet->m_pMipLevelTable);
pMipSet->m_pMipLevelTable = NULL;
}
return false;
}
return true;
}
bool CMP_CMIPS::AllocateMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nHeight, CMP_ChannelFormat channelFormat, TextureDataType textureDataType)
{
//TODO test
assert(pMipLevel);
assert(nWidth > 0 && nHeight > 0);
CMP_DWORD dwBitsPerPixel;
switch(channelFormat)
{
case CF_8bit:
case CF_2101010:
case CF_Float9995E:
dwBitsPerPixel = 8;
break;
case CF_16bit:
case CF_Float16:
dwBitsPerPixel = 16;
break;
case CF_32bit:
case CF_Float32:
dwBitsPerPixel = 32;
break;
default:
assert(0);
return false;
}
switch(textureDataType)
{
case TDT_XRGB:
case TDT_ARGB:
case TDT_NORMAL_MAP:
dwBitsPerPixel *= 4;
break;
case TDT_R:
break;
case TDT_RG:
dwBitsPerPixel *= 2;
break;
default:
assert(0);
return false;
}
CMP_DWORD dwPitch = CMP_PAD_BYTE(nWidth, dwBitsPerPixel);
pMipLevel->m_dwLinearSize = dwPitch * nHeight;
pMipLevel->m_nWidth = nWidth;
pMipLevel->m_nHeight = nHeight;
pMipLevel->m_pbData = reinterpret_cast<CMP_BYTE*>(malloc(pMipLevel->m_dwLinearSize));
return (pMipLevel->m_pbData != NULL);
}
bool CMP_CMIPS::AllocateCompressedMipLevelData(CMP_MipLevel* pMipLevel, int nWidth, int nHeight, CMP_DWORD dwSize)
{
//TODO test
assert(pMipLevel);
assert(nWidth > 0 && nHeight > 0);
pMipLevel->m_dwLinearSize = dwSize;
pMipLevel->m_nWidth = nWidth;
pMipLevel->m_nHeight = nHeight;
pMipLevel->m_pbData = reinterpret_cast<CMP_BYTE*>(malloc(pMipLevel->m_dwLinearSize));
return (pMipLevel->m_pbData != NULL);
}
void CMP_CMIPS::FreeMipSet(CMP_MipSet* pMipSet)
{
//TODO test
int nTotalOldMipLevels = 0;
assert(pMipSet);
if(pMipSet)
{
if(pMipSet->m_pMipLevelTable)
{
//determine number of miplevels in the old mipleveltable
switch(pMipSet->m_TextureType)
{
case TT_2D:
nTotalOldMipLevels = pMipSet->m_nMaxMipLevels;
break;
case TT_CubeMap:
nTotalOldMipLevels = pMipSet->m_nMaxMipLevels * pMipSet->m_nDepth;
break;
case TT_VolumeTexture:
for(int depth=pMipSet->m_nDepth, mipLevels=0;
mipLevels < pMipSet->m_nMaxMipLevels;
mipLevels++)
{
nTotalOldMipLevels += depth;
if(depth > 1)
{
depth >>= 1;
}
}
break;
default:
assert(0);
}
//free all miplevels and their data except the one use in gui view
for(int i=0; i<nTotalOldMipLevels-2 ; i++)
{
if (pMipSet->m_pMipLevelTable[i]->m_pbData)
{
#ifdef USE_BASIS
if (pMipSet->m_format == CMP_FORMAT_BASIS)
{
CMP_VEC8 &basis_data = *(pMipSet->m_pMipLevelTable[i]->m_pvec8Data);
if (basis_data.size() > 0)
delete &basis_data;
}
else
#endif
free(pMipSet->m_pMipLevelTable[i]->m_pbData);
pMipSet->m_pMipLevelTable[i]->m_pbData = NULL;
}
if (pMipSet->m_pMipLevelTable[i])
{
free(pMipSet->m_pMipLevelTable[i]);
pMipSet->m_pMipLevelTable[i] = NULL;
}
}
free(pMipSet->m_pMipLevelTable);
pMipSet->m_pMipLevelTable = NULL;
pMipSet->m_nMaxMipLevels = 0;
pMipSet->m_nMipLevels = 0;
}
}
}
bool CMP_CMIPS::AllocateCompressedDestBuffer(CMP_MipSet *SourceTexture, CMP_FORMAT format, CMP_MipSet *DestTexture)
{
CMP_MipLevel* pInMipLevel = GetMipLevel(SourceTexture, 0);
SourceTexture->dwWidth = pInMipLevel->m_nWidth;
SourceTexture->dwHeight = pInMipLevel->m_nHeight;
SourceTexture->dwDataSize = pInMipLevel->m_dwLinearSize;
SourceTexture->pData = pInMipLevel->m_pbData;
SourceTexture->m_swizzle = false;
memset(DestTexture, 0, sizeof(CMP_MipSet));
DestTexture->m_Flags = MS_FLAG_Default;
//----------------------------------------------------------------
// Allocate the compression buffer and miplevel tables
//----------------------------------------------------------------
DestTexture->m_nHeight = SourceTexture->m_nHeight;
DestTexture->m_nWidth = SourceTexture->m_nWidth;
DestTexture->dwWidth = pInMipLevel->m_nWidth;
DestTexture->dwHeight = pInMipLevel->m_nHeight;
DestTexture->m_ChannelFormat = CF_Compressed;
DestTexture->m_format = format;
DestTexture->m_dwFourCC = CMP_MIPS_FOURCC_DX10;
DestTexture->m_nBlockHeight = 4;
DestTexture->m_nBlockWidth = 4;
DestTexture->m_nMaxMipLevels = SourceTexture->m_nMaxMipLevels;
DestTexture->m_nMipLevels = 1;
DestTexture->m_nDepth = SourceTexture->m_nDepth;
if (DestTexture->m_nDepth < 1) DestTexture->m_nDepth = 1; // depthsupport on compressed data ?
if (!AllocateMipSet(DestTexture, CF_8bit, TDT_ARGB, TT_2D, SourceTexture->m_nWidth, SourceTexture->m_nHeight, 1))
{
// std::printf("Memory Error(1): allocating MIPSet Compression buffer\n");
return false;
}
//----------------------------------------------------------------
// Access the data table for mip level 0 (full texture width and height)
//----------------------------------------------------------------
CMP_MipLevel* pOutMipLevel = GetMipLevel(DestTexture, 0);
//----------------------------------------------------------------
// Calculate the target compressed block buffer size (4 bytes x 4 bytes)
//----------------------------------------------------------------
unsigned int Width = ((SourceTexture->m_nWidth + 3) / 4) * 4;
unsigned int Height = ((SourceTexture->m_nHeight + 3) / 4) * 4;
DestTexture->dwDataSize = Width * Height;
//----------------------------------------------------------------
// Allocate memory for the mip level 0
//----------------------------------------------------------------
if (!AllocateCompressedMipLevelData(pOutMipLevel, DestTexture->dwWidth, DestTexture->dwHeight, DestTexture->dwDataSize))
{
//std::printf("Memory Error(1): allocating MIPSet compression level data buffer\n");
return false;
}
DestTexture->pData = pOutMipLevel->m_pbData;
return true;
}
void CMP_CMIPS::SetProgress(unsigned int value)
{
if (SetProgressValue)
{
SetProgressValue(value, &m_canceled);
}
}