diff --git a/common/com_image.h b/common/com_image.h index d9f5ef6b..9366d361 100644 --- a/common/com_image.h +++ b/common/com_image.h @@ -9,7 +9,7 @@ NOTE: number at end of pixelformat name it's a total bitscount e.g. PF_RGB_24 == ======================================================================== */ #define ImageRAW( type ) (type == PF_RGBA_32 || type == PF_BGRA_32 || type == PF_RGB_24 || type == PF_BGR_24 || type == PF_LUMINANCE) -#define ImageDXT( type ) (type == PF_DXT1 || type == PF_DXT3 || type == PF_DXT5 || type == PF_ATI2 || type == PF_BC6H_SIGNED || type == PF_BC6H_UNSIGNED || type == PF_BC7) +#define ImageDXT( type ) (type == PF_DXT1 || type == PF_DXT3 || type == PF_DXT5 || type == PF_ATI2 || type == PF_BC6H_SIGNED || type == PF_BC6H_UNSIGNED || type == PF_BC7 || type == PF_KTX2_RAW) typedef enum { @@ -28,6 +28,7 @@ typedef enum PF_BC6H_SIGNED, // bptc BC6H signed FP16 format PF_BC6H_UNSIGNED, // bptc BC6H unsigned FP16 format PF_BC7, // bptc BC7 format + PF_KTX2_RAW, // Raw KTX2 data, used for yet unsupported KTX2 subformats PF_TOTALCOUNT, // must be last } pixformat_t; diff --git a/engine/common/imagelib/imagelib.h b/engine/common/imagelib/imagelib.h index 26905ba2..623676c8 100644 --- a/engine/common/imagelib/imagelib.h +++ b/engine/common/imagelib/imagelib.h @@ -156,6 +156,7 @@ qboolean Image_LoadDDS( const char *name, const byte *buffer, fs_offset_t filesi qboolean Image_LoadFNT( const char *name, const byte *buffer, fs_offset_t filesize ); qboolean Image_LoadLMP( const char *name, const byte *buffer, fs_offset_t filesize ); qboolean Image_LoadPAL( const char *name, const byte *buffer, fs_offset_t filesize ); +qboolean Image_LoadKTX2( const char *name, const byte *buffer, fs_offset_t filesize ); // // formats save diff --git a/engine/common/imagelib/img_dds.c b/engine/common/imagelib/img_dds.c index 361ddae2..d98df847 100644 --- a/engine/common/imagelib/img_dds.c +++ b/engine/common/imagelib/img_dds.c @@ -255,7 +255,7 @@ uint Image_DXTCalcSize( const char *name, dds_t *hdr, size_t filesize ) if( filesize != buffsize ) // main check { - Con_DPrintf( S_WARN "Image_LoadDDS: (%s) probably corrupted (%i should be %lu)\n", name, buffsize, filesize ); + Con_DPrintf( S_WARN "Image_LoadDDS: (%s) probably corrupted (%zu should be %lu)\n", name, buffsize, filesize ); if( buffsize > filesize ) return false; } diff --git a/engine/common/imagelib/img_ktx2.c b/engine/common/imagelib/img_ktx2.c new file mode 100644 index 00000000..f7bc4903 --- /dev/null +++ b/engine/common/imagelib/img_ktx2.c @@ -0,0 +1,55 @@ +/* +img_dds.c - dds format load +Copyright (C) 2015 Uncle Mike + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. +*/ + +#include "imagelib.h" +#include "xash3d_mathlib.h" +#include "ktx2.h" + +qboolean Image_LoadKTX2( const char *name, const byte *buffer, fs_offset_t filesize ) { + if( filesize < KTX_MINIMAL_HEADER_SIZE ) + return false; + + if ( memcmp(buffer, KTX_IDENTIFIER, KTX_IDENTIFIER_SIZE) != 0) { + Con_DPrintf( S_ERROR "%s: (%s) has invalid identifier\n", __FUNCTION__, name ); + return false; + } + + ktx_header_t header; + memcpy(&header, buffer + KTX_IDENTIFIER_SIZE, sizeof header); + + /* ktx_index_t index; */ + /* memcpy(&header, buffer + KTX_IDENTIFIER_SIZE + sizeof header, sizeof index); */ + + image.width = header.pixelWidth; + image.height = header.pixelHeight; + image.depth = Q_max(1, header.pixelDepth); + + // Just pass file contents as rgba data directly + + // TODO support various formats individually, for other renders to be able to consume them too + // This is a catch-all for ref_vk, which can do this format directly and natively + image.type = PF_KTX2_RAW; + + image.size = filesize; + //image.encode = TODO custom encode type? + + // FIXME format-dependent + image.flags = IMAGE_HAS_COLOR; // | IMAGE_HAS_ALPHA + + image.rgba = Mem_Malloc( host.imagepool, image.size); + memcpy(image.rgba, buffer, image.size); + + return true; +} diff --git a/engine/common/imagelib/img_utils.c b/engine/common/imagelib/img_utils.c index 094f6345..5af3fba3 100644 --- a/engine/common/imagelib/img_utils.c +++ b/engine/common/imagelib/img_utils.c @@ -105,6 +105,7 @@ static const loadpixformat_t load_game[] = { "%s%s.%s", "lmp", Image_LoadLMP, IL_HINT_NO }, // hl menu images (cached.wad etc) { "%s%s.%s", "fnt", Image_LoadFNT, IL_HINT_HL }, // hl console font (fonts.wad etc) { "%s%s.%s", "pal", Image_LoadPAL, IL_HINT_NO }, // install studio\sprite palette +{ "%s%s.%s", "ktx2", Image_LoadKTX2, IL_HINT_NO }, // dds for world and studio models { NULL, NULL, NULL, IL_HINT_NO } }; diff --git a/public/ktx2.h b/public/ktx2.h new file mode 100644 index 00000000..eb4dce31 --- /dev/null +++ b/public/ktx2.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +#define KTX_IDENTIFIER_SIZE 12 +#define KTX_IDENTIFIER "\xABKTX 20\xBB\r\n\x1A\n" + +/* +static const char k_ktx2_identifier[KTX_IDENTIFIER_SIZE] = { + '\xAB', 'K', 'T', 'X', ' ', '2', '0', '\xBB', '\r', '\n', '\x1A', '\n' +}; +*/ + +typedef struct { + uint32_t vkFormat; + uint32_t typeSize; + uint32_t pixelWidth; + uint32_t pixelHeight; + uint32_t pixelDepth; + uint32_t layerCount; + uint32_t faceCount; + uint32_t levelCount; + uint32_t supercompressionScheme; +} ktx_header_t; + +typedef struct { + uint32_t dfdByteOffset; + uint32_t dfdByteLength; + uint32_t kvdByteOffset; + uint32_t kvdByteLength; + uint64_t sgdByteOffset; + uint64_t sgdByteLength; +} ktx_index_t; + +typedef struct { + uint64_t byteOffset; + uint64_t byteLength; + uint64_t uncompressedByteLength; +} ktx_level_t; + +#define KTX_MINIMAL_HEADER_SIZE (KTX_IDENTIFIER_SIZE + sizeof(ktx_header_t) + sizeof(ktx_index_t) + sizeof(ktx_level_t)) diff --git a/ref/vk/vk_textures.c b/ref/vk/vk_textures.c index bb7de608..748c5d73 100644 --- a/ref/vk/vk_textures.c +++ b/ref/vk/vk_textures.c @@ -15,6 +15,7 @@ #include "crclib.h" #include "com_strings.h" #include "eiface.h" +#include "ktx2.h" #define PCG_IMPLEMENT #include "pcg.h" @@ -638,7 +639,13 @@ static VkSampler pickSamplerForFlags( texFlags_t flags ) { return tglob.default_sampler_fixme; } +static qboolean loadKtx2Raw( vk_texture_t *tex, const rgbdata_t* pic ); + static qboolean uploadTexture(vk_texture_t *tex, rgbdata_t *const *const layers, int num_layers, qboolean cubemap, colorspace_hint_e colorspace_hint) { + + if (num_layers == 1 && layers[0]->type == PF_KTX2_RAW) + return loadKtx2Raw(tex, layers[0]); + const VkFormat format = VK_GetFormat(layers[0]->type, colorspace_hint); int mipCount = 0; @@ -896,68 +903,18 @@ const byte* VK_TextureData( unsigned int texnum ) return NULL; } - -#define KTX_IDENTIFIER_SIZE 12 -static const char k_ktx2_identifier[KTX_IDENTIFIER_SIZE] = { - '\xAB', 'K', 'T', 'X', ' ', '2', '0', '\xBB', '\r', '\n', '\x1A', '\n' -}; - -typedef struct { - uint32_t vkFormat; - uint32_t typeSize; - uint32_t pixelWidth; - uint32_t pixelHeight; - uint32_t pixelDepth; - uint32_t layerCount; - uint32_t faceCount; - uint32_t levelCount; - uint32_t supercompressionScheme; -} ktx_header_t; - -typedef struct { - uint32_t dfdByteOffset; - uint32_t dfdByteLength; - uint32_t kvdByteOffset; - uint32_t kvdByteLength; - uint64_t sgdByteOffset; - uint64_t sgdByteLength; -} ktx_index_t; - -typedef struct { - uint64_t byteOffset; - uint64_t byteLength; - uint64_t uncompressedByteLength; -} ktx_level_t; - -static int loadKtx2( const char *name ) { - fs_offset_t size = 0; - byte *data = gEngine.fsapi->LoadFile( name, &size, false ); - - DEBUG("Loading KTX2 file \"%s\", exists=%d", name, data != 0); - - if ( !data ) - return 0; +static qboolean loadKtx2Raw( vk_texture_t *tex, const rgbdata_t* pic ) { + const byte *const data = pic->buffer; + const int size = pic->size; const ktx_header_t* header; const ktx_index_t* index; const ktx_level_t* levels; - vk_texture_t* tex = NULL; - if (size < (sizeof k_ktx2_identifier + sizeof(ktx_header_t) + sizeof(ktx_index_t) + sizeof(ktx_level_t))) { - ERR("KTX2 file \"%s\" seems truncated", name); - goto fail; - } + header = (const ktx_header_t*)(data + KTX_IDENTIFIER_SIZE); + index = (const ktx_index_t*)(data + KTX_IDENTIFIER_SIZE + sizeof(ktx_header_t)); + levels = (const ktx_level_t*)(data + KTX_IDENTIFIER_SIZE + sizeof(ktx_header_t) + sizeof(ktx_index_t)); - if (memcmp(data, k_ktx2_identifier, sizeof k_ktx2_identifier) != 0) { - ERR("KTX2 file \"%s\" identifier is invalid", name); - goto fail; - } - - header = (const ktx_header_t*)(data + sizeof k_ktx2_identifier); - index = (const ktx_index_t*)(data + sizeof k_ktx2_identifier + sizeof(ktx_header_t)); - levels = (const ktx_level_t*)(data + sizeof k_ktx2_identifier + sizeof(ktx_header_t) + sizeof(ktx_index_t)); - - DEBUG("KTX2 file \"%s\"", name); DEBUG(" header:"); #define X(field) DEBUG(" " # field "=%d", header->field); DEBUG(" vkFormat = %s(%d)", R_VkFormatName(header->vkFormat), header->vkFormat); @@ -988,13 +945,6 @@ static int loadKtx2( const char *name ) { DEBUG(" uncompressedByteLength=%llu", (unsigned long long)level->uncompressedByteLength); } - { - const uint32_t flags = 0; - tex = Common_AllocTexture( name, flags ); - if (!tex) - goto fail; - } - // FIXME check that format is supported // FIXME layers == 0 // FIXME has_alpha @@ -1147,18 +1097,20 @@ static int loadKtx2( const char *name ) { tex->width = header->pixelWidth; tex->height = header->pixelHeight; - goto finalize; - -fail: - if (tex) - memset( tex, 0, sizeof( vk_texture_t )); - -finalize: - Mem_Free( data ); - return (tex - vk_textures); + return true; } -static int loadTextureUsingEngine( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint ) { +static int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint ) { + if( !Common_CheckTexName( name )) + return 0; + + // see if already loaded + { + const vk_texture_t *const tex = Common_TextureForName( name ); + if( tex ) + return (tex - vk_textures); + } + uint picFlags = 0; if( FBitSet( flags, TF_NOFLIP_TGA )) @@ -1195,25 +1147,6 @@ static int loadTextureUsingEngine( const char *name, const byte *buf, size_t siz return tex - vk_textures; } -static int loadTextureInternal( const char *name, const byte *buf, size_t size, int flags, colorspace_hint_e colorspace_hint ) { - if( !Common_CheckTexName( name )) - return 0; - - // see if already loaded - vk_texture_t *tex = Common_TextureForName( name ); - if( tex ) - return (tex - vk_textures); - - { - const char *ext = Q_strrchr(name, '.'); - if (Q_strcmp(ext, ".ktx2") == 0) { - return loadKtx2(name); - } - } - - return loadTextureUsingEngine(name, buf, size, flags, colorspace_hint); -} - int VK_LoadTextureExternal( const char *name, const byte *buf, size_t size, int flags ) { return loadTextureInternal(name, buf, size, flags, kColorspaceGamma); }