1120 lines
46 KiB
C++

//=====================================================================
// Copyright 2020 (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.
//
// Windows Header Files:
#ifdef _WIN32
#include <windows.h>
#endif
#include "ktx2.h"
#include "tc_pluginapi.h"
#include "tc_plugininternal.h"
#include "common.h"
#include "softfloat.h"
#include "textureio.h"
#include <sstream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>
#include "gl_format.h"
#pragma comment(lib, "opengl32.lib") // Open GL
#pragma comment(lib, "Glu32.lib") // Glu
#pragma comment(lib, "glew32.lib") // glew
using namespace std;
CMIPS* KTX2_CMips;
#ifdef BUILD_AS_PLUGIN_DLL
DECLARE_PLUGIN(Plugin_KTX2)
SET_PLUGIN_TYPE("IMAGE")
SET_PLUGIN_NAME("KTX2")
#else
void* make_Plugin_KTX2()
{
return new Plugin_KTX2;
}
#endif
static void writeId2(std::ostream& dst)
{
dst << "glTF Compressonator v2.0";
}
Plugin_KTX2::Plugin_KTX2()
{
}
Plugin_KTX2::~Plugin_KTX2()
{
}
int Plugin_KTX2::TC_PluginSetSharedIO(void* Shared)
{
if (Shared)
{
KTX2_CMips = static_cast<CMIPS*>(Shared);
return 0;
}
return 1;
}
int Plugin_KTX2::TC_PluginGetVersion(TC_PluginVersion* pPluginVersion)
{
#ifdef _WIN32
pPluginVersion->guid = g_GUID;
#endif
pPluginVersion->dwAPIVersionMajor = TC_API_VERSION_MAJOR;
pPluginVersion->dwAPIVersionMinor = TC_API_VERSION_MINOR;
pPluginVersion->dwPluginVersionMajor = TC_PLUGIN_VERSION_MAJOR;
pPluginVersion->dwPluginVersionMinor = TC_PLUGIN_VERSION_MINOR;
return 0;
}
int Plugin_KTX2::TC_PluginFileLoadTexture(const char* pszFilename, CMP_Texture* srcTexture)
{
return -1;
}
int Plugin_KTX2::TC_PluginFileSaveTexture(const char* pszFilename, CMP_Texture* srcTexture)
{
return -1;
}
int Plugin_KTX2::TC_PluginFileLoadTexture(const char* pszFilename, MipSet* pMipSet)
{
ktxTexture2* texture2 = nullptr;
ktxTexture* texture = nullptr;
KTX_error_code loadStatus;
bool isCompressed = false;
ktx_uint32_t glInternalformat;
ktx_uint32_t glType;
ktx_uint32_t glFormat;
loadStatus = ktxTexture2_CreateFromNamedFile(pszFilename, KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &texture2);
if (loadStatus != KTX_SUCCESS)
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError(("Error(%x): KTX2 Plugin ID(%d) opening file = %s \n"), loadStatus, IDS_ERROR_FILE_OPEN, pszFilename);
}
return -1;
}
// CMP_DFD* extended_format = (CMP_DFD *)texture2->pDfd;
glInternalformat = glGetInternalFormatFromVkFormat((VkFormat)texture2->vkFormat);
glType = glGetTypeFromInternalFormat(glInternalformat);
glFormat = glGetFormatFromInternalFormat(glInternalformat);
texture = ktxTexture(texture2);
isCompressed = texture->isCompressed;
int channelByteSize = 0;
try {
if (isCompressed)
{
pMipSet->m_compressed = true;
pMipSet->m_nBlockHeight = 4;
pMipSet->m_nBlockWidth = 4;
pMipSet->m_nBlockDepth = 1;
pMipSet->m_ChannelFormat = CF_Compressed;
pMipSet->m_TextureDataType = TDT_ARGB;
pMipSet->m_format = CMP_FORMAT_Unknown;
channelByteSize = 1;
// Search using VL formats first
switch ((VkFormat)texture2->vkFormat)
{
case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC1;
break;
case VK_FORMAT_BC2_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC2;
break;
case VK_FORMAT_BC3_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC3;
// These are unsupport types used to map into cmp formats
// this is a trick for the CMP compressed DXT5 swizzle types
// switch (glInternalformat)
// {
// case COMPRESSED_FORMAT_DXT5_xGBR_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_xGBR;
// break;
// case COMPRESSED_FORMAT_DXT5_RxBG_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_RxBG;
// break;
// case COMPRESSED_FORMAT_DXT5_RBxG_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_RBxG;
// break;
// case COMPRESSED_FORMAT_DXT5_xRBG_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_xRBG;
// break;
// case COMPRESSED_FORMAT_DXT5_RGxB_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_RGxB;
// break;
// case COMPRESSED_FORMAT_DXT5_xGxR_TMP:
// pMipSet->m_format = CMP_FORMAT_DXT5_xGxR;
// break;
// }
break;
case VK_FORMAT_BC4_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC4;
break;
case VK_FORMAT_BC4_SNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC4_S;
// if (glInternalformat == COMPRESSED_FORMAT_ATI1N_UNorm_TMP)
// {
// pMipSet->m_format = CMP_FORMAT_ATI1N;
// }
break;
case VK_FORMAT_BC5_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC5;
//if (glInternalformat == COMPRESSED_FORMAT_ATI2N_UNorm_TMP)
//{
// pMipSet->m_format = CMP_FORMAT_ATI2N;
//}
//else if (glInternalformat == COMPRESSED_FORMAT_ATI2N_XY_UNorm_TMP)
//{
// pMipSet->m_format = CMP_FORMAT_ATI2N_XY;
//}
break;
case VK_FORMAT_BC5_SNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC5_S;
break;
case VK_FORMAT_BC6H_UFLOAT_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC6H;
break;
case VK_FORMAT_BC6H_SFLOAT_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC6H_SF;
break;
case VK_FORMAT_BC7_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_BC7;
break;
case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ETC2_RGB; // Skip ETC as ETC2 is backward comp
break;
case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
pMipSet->m_format = CMP_FORMAT_ETC2_SRGB;
break;
case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ETC2_RGBA;
break;
case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ETC2_RGBA1;
break;
case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
pMipSet->m_format = CMP_FORMAT_ETC2_SRGBA;
break;
case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 4;
pMipSet->m_nBlockHeight = 4;
break;
case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 5;
pMipSet->m_nBlockHeight = 4;
break;
case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 5;
pMipSet->m_nBlockHeight = 5;
break;
case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 6;
pMipSet->m_nBlockHeight = 5;
break;
case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 6;
pMipSet->m_nBlockHeight = 6;
break;
case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 8;
pMipSet->m_nBlockHeight = 5;
break;
case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 8;
pMipSet->m_nBlockHeight = 6;
break;
case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 8;
pMipSet->m_nBlockHeight = 8;
break;
case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 10;
pMipSet->m_nBlockHeight = 5;
break;
case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 10;
pMipSet->m_nBlockHeight = 6;
break;
case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 10;
pMipSet->m_nBlockHeight = 8;
break;
case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 10;
pMipSet->m_nBlockHeight = 10;
break;
case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 12;
pMipSet->m_nBlockHeight = 10;
break;
case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
pMipSet->m_format = CMP_FORMAT_ASTC;
pMipSet->m_nBlockWidth = 12;
pMipSet->m_nBlockHeight = 12;
break;
}
}
else
{
pMipSet->m_compressed = false;
switch (glType)
{
case GL_UNSIGNED_BYTE:
pMipSet->m_ChannelFormat = CF_8bit;
switch (glFormat)
{
case GL_RED:
pMipSet->m_format = CMP_FORMAT_R_8;
pMipSet->m_TextureDataType = TDT_R;
break;
case GL_RG:
pMipSet->m_format = CMP_FORMAT_RG_8;
pMipSet->m_TextureDataType = TDT_RG;
break;
case GL_RGB:
pMipSet->m_format = CMP_FORMAT_RGB_888;
pMipSet->m_TextureDataType = TDT_XRGB;
break;
case GL_RGBA:
case GL_RGBA8:
pMipSet->m_format = CMP_FORMAT_ARGB_8888;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
case GL_BGR:
pMipSet->m_swizzle = true;
pMipSet->m_format = CMP_FORMAT_RGB_888;
pMipSet->m_TextureDataType = TDT_XRGB;
break;
case GL_BGRA:
pMipSet->m_swizzle = true;
pMipSet->m_format = CMP_FORMAT_ARGB_8888;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
}
break;
case GL_UNSIGNED_SHORT:
pMipSet->m_ChannelFormat = CF_16bit;
switch (glFormat)
{
case GL_RED:
pMipSet->m_format = CMP_FORMAT_R_16;
pMipSet->m_TextureDataType = TDT_R;
break;
case GL_RG:
pMipSet->m_format = CMP_FORMAT_RG_16;
pMipSet->m_TextureDataType = TDT_RG;
break;
case GL_RGBA:
pMipSet->m_format = CMP_FORMAT_ARGB_16;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
case GL_BGRA:
pMipSet->m_swizzle = true;
pMipSet->m_format = CMP_FORMAT_ARGB_16;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
}
break;
case GL_HALF_FLOAT:
pMipSet->m_ChannelFormat = CF_Float16;
switch (glFormat)
{
case GL_RED:
pMipSet->m_format = CMP_FORMAT_R_16F;
pMipSet->m_TextureDataType = TDT_R;
break;
case GL_RG:
pMipSet->m_format = CMP_FORMAT_RG_16F;
pMipSet->m_TextureDataType = TDT_RG;
break;
case GL_RGBA:
pMipSet->m_format = CMP_FORMAT_ARGB_16F;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
case GL_BGRA:
pMipSet->m_swizzle = true;
pMipSet->m_format = CMP_FORMAT_ARGB_16F;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
}
break;
case GL_UNSIGNED_INT_2_10_10_10_REV:
pMipSet->m_format = CMP_FORMAT_ARGB_2101010;
pMipSet->m_TextureDataType = TDT_ARGB;
pMipSet->m_ChannelFormat = CF_2101010;
break;
case GL_FLOAT:
pMipSet->m_ChannelFormat = CF_Float32;
switch (glFormat)
{
case GL_RED:
pMipSet->m_format = CMP_FORMAT_R_32F;
pMipSet->m_TextureDataType = TDT_R;
break;
case GL_RG:
pMipSet->m_format = CMP_FORMAT_RG_32F;
pMipSet->m_TextureDataType = TDT_RG;
break;
case GL_RGBA:
pMipSet->m_format = CMP_FORMAT_ARGB_32F;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
case GL_BGRA:
pMipSet->m_swizzle = true;
pMipSet->m_format = CMP_FORMAT_ARGB_32F;
pMipSet->m_TextureDataType = TDT_ARGB;
break;
}
break;
break;
default:
if (KTX2_CMips)
{
KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) unsupported GL format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat);
}
return -1;
}
}
if (texture->isCubemap)
{
pMipSet->m_TextureType = TT_CubeMap;
}
else if (texture->baseDepth > 1 && texture->numFaces == 1)
{
pMipSet->m_TextureType = TT_VolumeTexture;
}
else if (texture->baseDepth == 1 && texture->numFaces == 1)
{
pMipSet->m_TextureType = TT_2D;
}
else
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) unsupported texture format\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE);
}
return -1;
}
pMipSet->m_nMipLevels = texture->numLevels;
// Allocate MipSet header
KTX2_CMips->AllocateMipSet(
pMipSet, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType, pMipSet->m_TextureType, texture->baseWidth, texture->baseHeight, texture->numFaces);
int w = pMipSet->m_nWidth;
int h = pMipSet->m_nHeight;
unsigned int totalByteRead = 0;
unsigned int faceSize = 0;
unsigned int MipSetdataSize = 0;
unsigned int numArrayElement = texture->numLayers;
unsigned int TexturedataSize = texture->dataSize; // This is all data in cubemap levels and mip levels.
unsigned int TotalMipSetdataSize = 0;
for (uint32_t nMipLevel = 0; nMipLevel < texture->numLevels; nMipLevel++)
{
if ((w <= 1) || (h <= 1))
{
break;
}
else
{
w = max(1, pMipSet->m_nWidth >> nMipLevel);
h = max(1, pMipSet->m_nHeight >> nMipLevel);
}
for (uint32_t face = 0; face < texture->numFaces; ++face)
{
// Determine buffer size and set Mip Set Levels
MipLevel* pMipLevel = KTX2_CMips->GetMipLevel(pMipSet, nMipLevel, face);
int channelCount = 0;
if (pMipSet->m_compressed)
{
// calculate the compressed miplevel size to allocate
CMP_Texture destGPUMipTexture;
destGPUMipTexture.dwSize = sizeof(CMP_Texture);
destGPUMipTexture.dwPitch = 0;
destGPUMipTexture.format = pMipSet->m_format;
destGPUMipTexture.dwWidth = w;
destGPUMipTexture.dwHeight = h;
destGPUMipTexture.nBlockWidth = pMipSet->m_nBlockWidth;
destGPUMipTexture.nBlockHeight = pMipSet->m_nBlockHeight;
MipSetdataSize = CMP_CalculateBufferSize(&destGPUMipTexture);
KTX2_CMips->AllocateCompressedMipLevelData(pMipLevel, w, h, MipSetdataSize);
TotalMipSetdataSize += pMipLevel->m_dwLinearSize;
}
else {
channelByteSize = 0;
switch (glType)
{
case GL_UNSIGNED_BYTE:
channelByteSize = 1;
break;
case GL_UNSIGNED_SHORT:
channelByteSize = 2;
break;
case GL_HALF_FLOAT:
channelByteSize = 2;
break;
case GL_FLOAT:
channelByteSize = 4;
break;
default:
return -1;
}
switch (glFormat)
{
case GL_RED:
channelCount = 1;
break;
case GL_RG:
channelCount = 2;
break;
case GL_RGB:
channelCount = 3;
break;
case GL_BGR:
channelCount = 3;
break;
case GL_RGBA:
channelCount = 4;
break;
case GL_BGRA:
channelCount = 4;
break;
default:
return -1;
}
KTX2_CMips->AllocateMipLevelData(pMipLevel, w, h, pMipSet->m_ChannelFormat, pMipSet->m_TextureDataType);
MipSetdataSize = pMipLevel->m_dwLinearSize;
}
CMP_BYTE* pData = (CMP_BYTE*)(pMipLevel->m_pbData);
if (!pData)
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError(
("Error(%d): KTX2 Plugin ID(%d) Read image data failed, Out of Memory. Format %x\n"), EL_Error, IDS_ERROR_UNSUPPORTED_TYPE, glFormat);
}
return -1;
}
//
// Read image data into temporary buffer
//
ktx_size_t offset = 0;
KTX_error_code dataStatus = ktxTexture_GetImageOffset(ktxTexture(texture), nMipLevel, 0, face, &offset);
if (dataStatus != KTX_SUCCESS)
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError("Error(%d): KTX2 Plugin Read image data offset at %d failed\n", dataStatus,offset);
}
return -1;
}
uint8_t* imageData = ktxTexture_GetData(ktxTexture(texture)) + offset;
if (imageData == nullptr)
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError("Error: KTX2 Plugin Read image data at offset %d is null\n", offset);
}
return -1;
}
if (!pMipSet->m_compressed)
{
size_t readSize = channelByteSize * channelCount * w * h;
std::vector<uint8_t> pixelData(readSize);
memcpy(&pixelData[0], imageData, readSize);
int pixelSize = channelCount * channelByteSize;
int targetPixelSize = channelCount * channelByteSize;
if (channelCount == 3)
{
// XRGB conversion.
targetPixelSize = 4 * channelByteSize;
}
int py = 0;
for (py = 0; py < h; py++)
{
int px = 0;
for (px = 0; px < w; px++)
{
memcpy(&pData[targetPixelSize * px + py * targetPixelSize * w], &pixelData[pixelSize * px + py * pixelSize * w], pixelSize);
}
}
}
else
{
if (TotalMipSetdataSize <= TexturedataSize)
memcpy(pData, imageData, MipSetdataSize);
else
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError("Error: KTX2 Plugin MipSetdataSize error (%d, %d)\n", TexturedataSize, MipSetdataSize);
}
return -1;
}
}
}
}
}
catch (...)
{
if (KTX2_CMips)
{
KTX2_CMips->PrintError("Error KTX2 Plugin Exception: \n");
}
return -1;
}
return 0;
}
int Plugin_KTX2::TC_PluginFileSaveTexture(const char* pszFilename, MipSet* pMipSet)
{
assert(pszFilename);
assert(pMipSet);
assert(pszFilename);
assert(pMipSet);
if (pMipSet->m_pMipLevelTable == NULL)
{
if (KTX2_CMips)
KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename);
return -1;
}
if (KTX2_CMips->GetMipLevel(pMipSet, 0) == NULL)
{
if (KTX2_CMips)
KTX2_CMips->PrintError(("Error(%d): KTX2 Plugin ID(%d) saving file = %s "), EL_Error, IDS_ERROR_ALLOCATEMIPSET, pszFilename);
return -1;
}
ktxTextureCreateInfo textureCreateInfo;
/*!< Internal format for the texture, e.g., GL_RGB8. Ignored when creating a ktxTexture2. */
textureCreateInfo.baseWidth = pMipSet->m_nWidth; /*!< Width of the base level of the texture. */
textureCreateInfo.baseHeight = pMipSet->m_nHeight; /*!< Height of the base level of the texture. */
textureCreateInfo.baseDepth = 1; /*!< Depth of the base level of the texture. */
textureCreateInfo.numDimensions = 2; /*!< Number of dimensions in the texture, 1, 2 or 3. */
textureCreateInfo.numLevels = pMipSet->m_nMipLevels; /*!< Number of mip levels in the texture. Should be 1 if @c generateMipmaps is KTX_TRUE; */
textureCreateInfo.numLayers = 1; /*!< Number of array layers in the texture. */
textureCreateInfo.numFaces = (pMipSet->m_TextureType == TT_CubeMap) ? 6 : 1; /*!< Number of faces: 6 for cube maps, 1 otherwise. */
textureCreateInfo.isArray = KTX_FALSE; /*!< Set to KTX_TRUE if the texture is to be an array texture. Means OpenGL will use a GL_TEXTURE_*_ARRAY target. */
textureCreateInfo.generateMipmaps = KTX_FALSE; /*!< Set to KTX_TRUE if mipmaps should be generated for the texture when loading into a 3D API. */
textureCreateInfo.pDfd = nullptr;
textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED;
bool isCompressed = CMP_IsCompressedFormat(pMipSet->m_format);
switch (pMipSet->m_TextureDataType)
{
case TDT_R:
{ //single component-- can be Luminance and Alpha case, here only cover R
if (!isCompressed)
{
// GL_R8;
textureCreateInfo.vkFormat = VK_FORMAT_R8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_R16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_R32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32_SFLOAT;
}
}
else
{
// GL_RED;
textureCreateInfo.vkFormat = VK_FORMAT_R8_UNORM;
}
}
break;
case TDT_RG:
{ //two component
if (!isCompressed)
{
// GL_RG8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RG16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RG32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32_SFLOAT;
}
}
else
{
// GL_COMPRESSED_RG;
// TODO: KTX2/Vulkan
}
}
break;
case TDT_XRGB:
{ //normally 3 component
if (!isCompressed)
{
// GL_RGB8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RGB16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RGB32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT;
}
}
else
{
if (pMipSet->m_format == CMP_FORMAT_BC1 || pMipSet->m_format == CMP_FORMAT_DXT1)
{
// GL_COMPRESSED_RGB_S3TC_DXT1_EXT;
// TODO: KTX2/Vulkan
}
else
{
// GL_RGB8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RGB16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RGB32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT;
}
}
}
}
break;
case TDT_RGB:
{ //3 component uncompressed formats
// GL_RGB8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RGB16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RGB32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32_SFLOAT;
}
}
break;
case TDT_ARGB:
{ //4 component
if (!isCompressed)
{
// GL_RGBA8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RGBA16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RGBA32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
}
}
else
{
switch (pMipSet->m_format)
{
case CMP_FORMAT_BC1:
case CMP_FORMAT_DXT1:
// GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
textureCreateInfo.vkFormat = VK_FORMAT_BC1_RGB_UNORM_BLOCK;
break;
case CMP_FORMAT_BC2:
case CMP_FORMAT_DXT3:
// GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
textureCreateInfo.vkFormat = VK_FORMAT_BC2_UNORM_BLOCK;
break;
case CMP_FORMAT_BC3:
case CMP_FORMAT_DXT5:
// GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
break;
case CMP_FORMAT_BC4:
// GL_COMPRESSED_RED_RGTC1;
textureCreateInfo.vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
break;
case CMP_FORMAT_BC4_S:
// GL_COMPRESSED_SIGNED_RED_RGTC1;
textureCreateInfo.vkFormat = VK_FORMAT_BC4_SNORM_BLOCK;
break;
case CMP_FORMAT_BC5:
// GL_COMPRESSED_RG_RGTC2;
textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
break;
case CMP_FORMAT_BC5_S:
// GL_COMPRESSED_SIGNED_RG_RGTC2;
textureCreateInfo.vkFormat = VK_FORMAT_BC5_SNORM_BLOCK;
break;
case CMP_FORMAT_BC6H:
// GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;
textureCreateInfo.vkFormat = VK_FORMAT_BC6H_UFLOAT_BLOCK;
break;
case CMP_FORMAT_BC6H_SF:
// GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;
textureCreateInfo.vkFormat = VK_FORMAT_BC6H_SFLOAT_BLOCK;
break;
case CMP_FORMAT_BC7:
// RGB_BP_UNorm;
textureCreateInfo.vkFormat = VK_FORMAT_BC7_UNORM_BLOCK;
break;
//case CMP_FORMAT_ATI1N:
// // COMPRESSED_FORMAT_ATI1N_UNorm_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
// break;
//case CMP_FORMAT_ATI2N:
// // COMPRESSED_FORMAT_ATI2N_UNorm_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
// break;
//case CMP_FORMAT_ATI2N_XY:
// // COMPRESSED_FORMAT_ATI2N_XY_UNorm_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
// // break;
// case CMP_FORMAT_ATC_RGB:
// // ATC_RGB_AMD;
// textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED;
// break;
// case CMP_FORMAT_ATC_RGBA_Explicit:
// // ATC_RGBA_EXPLICIT_ALPHA_AMD;
// textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED;
// break;
// case CMP_FORMAT_ATC_RGBA_Interpolated:
// // ATC_RGBA_INTERPOLATED_ALPHA_AMD;
// textureCreateInfo.vkFormat = VK_FORMAT_UNDEFINED;
// break;
case CMP_FORMAT_ETC_RGB:
// GL_ETC1_RGB8_OES;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case CMP_FORMAT_ETC2_RGB:
// GL_COMPRESSED_RGB8_ETC2;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case CMP_FORMAT_ETC2_SRGB:
// GL_COMPRESSED_SRGB8_ETC2;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
break;
case CMP_FORMAT_ETC2_RGBA:
// GL_COMPRESSED_RGBA8_ETC2_EAC;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
break;
case CMP_FORMAT_ETC2_RGBA1:
// GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
break;
case CMP_FORMAT_ETC2_SRGBA:
// GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
break;
case CMP_FORMAT_ETC2_SRGBA1:
// GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;
textureCreateInfo.vkFormat = VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
break;
// Not supported by GL_COMPRESSION_ formats
// case CMP_FORMAT_DXT5_xGBR:
// // COMPRESSED_FORMAT_DXT5_xGBR_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
// case CMP_FORMAT_DXT5_RxBG:
// // COMPRESSED_FORMAT_DXT5_RxBG_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
// case CMP_FORMAT_DXT5_RBxG:
// // COMPRESSED_FORMAT_DXT5_RBxG_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
// case CMP_FORMAT_DXT5_xRBG:
// // COMPRESSED_FORMAT_DXT5_xRBG_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
// case CMP_FORMAT_DXT5_RGxB:
// // COMPRESSED_FORMAT_DXT5_RGxB_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
// case CMP_FORMAT_DXT5_xGxR:
// // COMPRESSED_FORMAT_DXT5_xGxR_TMP;
// textureCreateInfo.vkFormat = VK_FORMAT_BC3_UNORM_BLOCK;
// break;
case CMP_FORMAT_ASTC:
if ((pMipSet->m_nBlockWidth == 4) && (pMipSet->m_nBlockHeight == 4))
{
// GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 4))
{
// GL_COMPRESSED_RGBA_ASTC_5x4_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 5) && (pMipSet->m_nBlockHeight == 5))
{
// GL_COMPRESSED_RGBA_ASTC_5x5_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 5))
{
// GL_COMPRESSED_RGBA_ASTC_6x5_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 6) && (pMipSet->m_nBlockHeight == 6))
{
// GL_COMPRESSED_RGBA_ASTC_6x6_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 5))
{
// GL_COMPRESSED_RGBA_ASTC_8x5_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 6))
{
// GL_COMPRESSED_RGBA_ASTC_8x6_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 8) && (pMipSet->m_nBlockHeight == 8))
{
// GL_COMPRESSED_RGBA_ASTC_8x8_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 5))
{
// GL_COMPRESSED_RGBA_ASTC_10x5_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 6))
{
// GL_COMPRESSED_RGBA_ASTC_10x6_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 8))
{
// GL_COMPRESSED_RGBA_ASTC_10x8_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 10) && (pMipSet->m_nBlockHeight == 10))
{
// GL_COMPRESSED_RGBA_ASTC_10x10_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 10))
{
// GL_COMPRESSED_RGBA_ASTC_12x10_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
}
else if ((pMipSet->m_nBlockWidth == 12) && (pMipSet->m_nBlockHeight == 12))
{
// GL_COMPRESSED_RGBA_ASTC_12x12_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
}
else
{
// GL_COMPRESSED_RGBA_ASTC_4x4_KHR;
textureCreateInfo.vkFormat = VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
}
break;
case CMP_FORMAT_BASIS:
// GL_RGBA8;
textureCreateInfo.vkFormat = VK_FORMAT_R8G8B8A8_UNORM;
if (pMipSet->m_ChannelFormat == CF_Float16)
{
// GL_RGBA16F;
textureCreateInfo.vkFormat = VK_FORMAT_R16G16B16A16_SFLOAT;
}
else if (pMipSet->m_ChannelFormat == CF_Float32)
{
// GL_RGBA32F;
textureCreateInfo.vkFormat = VK_FORMAT_R32G32B32A32_SFLOAT;
}
break;
}
}
}
break;
}
if (textureCreateInfo.vkFormat == VK_FORMAT_UNDEFINED)
{
if (KTX2_CMips)
KTX2_CMips->PrintError("Error: KTX2 plugin. Destination format is not supported.\n");
return -1;
}
ktxTexture2* texture2 = nullptr;
ktxTexture* texture = nullptr;
KTX_error_code createStatus;
createStatus = ktxTexture2_Create(&textureCreateInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE, &texture2);
texture = ktxTexture(texture2);
if (createStatus != KTX_SUCCESS)
{
if (KTX2_CMips)
{
switch (createStatus)
{
case KTX_UNSUPPORTED_TEXTURE_TYPE:
KTX2_CMips->PrintError("Error(KTX2 UNSUPPORTED TEXTURE TYPE) saving file = %s \n", pszFilename);
break;
default:
KTX2_CMips->PrintError("Error(%d): Create status KTX2 Plugin on saving file = %s \n", createStatus, pszFilename);
}
}
return -1;
}
int nSlices = (pMipSet->m_TextureType == TT_2D) ? 1 : CMP_MaxFacesOrSlices(pMipSet, 0);
for (int nSlice = 0; nSlice < nSlices; nSlice++)
{
for (int nMipLevel = 0; nMipLevel < pMipSet->m_nMipLevels; nMipLevel++)
{
MipLevel* pMipLevel = KTX2_CMips->GetMipLevel(pMipSet, nMipLevel, nSlice);
if (pMipLevel)
{
KTX_error_code setMemory = ktxTexture_SetImageFromMemory(texture,
nMipLevel,
0,
nSlice,
pMipLevel->m_pbData,
pMipLevel->m_dwLinearSize);
if (setMemory != KTX_SUCCESS)
{
KTX2_CMips->PrintError("Error(%d):SetImageFromMemory KTX2 Plugin on saving file = %s \n", setMemory, pszFilename);
return -1;
}
}
else
{
KTX2_CMips->PrintError("Error:GetMipLevel (%d,%d) KTX2 Plugin on saving file = %s \n", nMipLevel, nSlice, pszFilename);
return -1;
}
}
}
if (pMipSet->m_format == CMP_FORMAT_BASIS)
{
ktx_uint32_t* basisQuality = reinterpret_cast<ktx_uint32_t*>(pMipSet->pData); // m_userData;
KTX_error_code basisStatus = ktxTexture2_CompressBasis(texture2, *basisQuality);
if (basisStatus != KTX_SUCCESS)
{
KTX2_CMips->PrintError("Error(%d): Basis status KTX2 Plugin on saving file = %s \n", basisStatus, pszFilename);
return -1;
}
}
std::stringstream writer;
writeId2(writer);
ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_KEY, (ktx_uint32_t)writer.str().length() + 1, writer.str().c_str());
KTX_error_code save = ktxTexture_WriteToNamedFile(texture, pszFilename);
if (save != KTX_SUCCESS)
{
KTX2_CMips->PrintError("Error(%d): WriteToNamedFile KTX2 Plugin on saving file = %s \n", save, pszFilename);
return -1;
}
return 0;
}