diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index f884c690fa4..aa98d69ca4d 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -256,6 +256,12 @@ Comment: jpeg-compressor
 Copyright: 2012, Rich Geldreich
 License: public-domain or Apache-2.0
 
+Files: ./thirdparty/libktx/
+Comment: KTX
+Copyright: 2013-2020, Mark Callow
+ 2010-2020 The Khronos Group, Inc.
+License: Apache-2.0
+
 Files: ./thirdparty/libogg/
 Comment: OggVorbis
 Copyright: 2002, Xiph.org Foundation
diff --git a/core/io/image.cpp b/core/io/image.cpp
index a5fea09113f..3ca39f98c05 100644
--- a/core/io/image.cpp
+++ b/core/io/image.cpp
@@ -3018,6 +3018,7 @@ ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr;
 ImageMemLoadFunc Image::_bmp_mem_loader_func = nullptr;
 ScalableImageMemLoadFunc Image::_svg_scalable_mem_loader_func = nullptr;
 ImageMemLoadFunc Image::_dds_mem_loader_func = nullptr;
+ImageMemLoadFunc Image::_ktx_mem_loader_func = nullptr;
 
 void (*Image::_image_compress_bc_func)(Image *, Image::UsedChannels) = nullptr;
 void (*Image::_image_compress_bptc_func)(Image *, Image::UsedChannels) = nullptr;
@@ -3490,6 +3491,7 @@ void Image::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("load_tga_from_buffer", "buffer"), &Image::load_tga_from_buffer);
 	ClassDB::bind_method(D_METHOD("load_bmp_from_buffer", "buffer"), &Image::load_bmp_from_buffer);
 	ClassDB::bind_method(D_METHOD("load_dds_from_buffer", "buffer"), &Image::load_dds_from_buffer);
+	ClassDB::bind_method(D_METHOD("load_ktx_from_buffer", "buffer"), &Image::load_ktx_from_buffer);
 
 	ClassDB::bind_method(D_METHOD("load_svg_from_buffer", "buffer", "scale"), &Image::load_svg_from_buffer, DEFVAL(1.0));
 	ClassDB::bind_method(D_METHOD("load_svg_from_string", "svg_str", "scale"), &Image::load_svg_from_string, DEFVAL(1.0));
@@ -3873,6 +3875,14 @@ Error Image::load_dds_from_buffer(const Vector<uint8_t> &p_array) {
 	return _load_from_buffer(p_array, _dds_mem_loader_func);
 }
 
+Error Image::load_ktx_from_buffer(const Vector<uint8_t> &p_array) {
+	ERR_FAIL_NULL_V_MSG(
+			_ktx_mem_loader_func,
+			ERR_UNAVAILABLE,
+			"The KTX module isn't enabled. Recompile the Godot editor or export template binary with the `module_ktx_enabled=yes` SCons option.");
+	return _load_from_buffer(p_array, _ktx_mem_loader_func);
+}
+
 void Image::convert_rg_to_ra_rgba8() {
 	ERR_FAIL_COND(format != FORMAT_RGBA8);
 	ERR_FAIL_COND(!data.size());
diff --git a/core/io/image.h b/core/io/image.h
index f68543ba246..cb7c6bff529 100644
--- a/core/io/image.h
+++ b/core/io/image.h
@@ -151,6 +151,7 @@ public:
 	static ImageMemLoadFunc _bmp_mem_loader_func;
 	static ScalableImageMemLoadFunc _svg_scalable_mem_loader_func;
 	static ImageMemLoadFunc _dds_mem_loader_func;
+	static ImageMemLoadFunc _ktx_mem_loader_func;
 
 	static void (*_image_compress_bc_func)(Image *, UsedChannels p_channels);
 	static void (*_image_compress_bptc_func)(Image *, UsedChannels p_channels);
@@ -404,6 +405,7 @@ public:
 	Error load_tga_from_buffer(const Vector<uint8_t> &p_array);
 	Error load_bmp_from_buffer(const Vector<uint8_t> &p_array);
 	Error load_dds_from_buffer(const Vector<uint8_t> &p_array);
+	Error load_ktx_from_buffer(const Vector<uint8_t> &p_array);
 
 	Error load_svg_from_buffer(const Vector<uint8_t> &p_array, float scale = 1.0);
 	Error load_svg_from_string(const String &p_svg_str, float scale = 1.0);
diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml
index 14869909959..6451062fc55 100644
--- a/doc/classes/Image.xml
+++ b/doc/classes/Image.xml
@@ -333,6 +333,13 @@
 				Loads an image from the binary contents of a JPEG file.
 			</description>
 		</method>
+		<method name="load_ktx_from_buffer">
+			<return type="int" enum="Error" />
+			<param index="0" name="buffer" type="PackedByteArray" />
+			<description>
+				Loads an image from the binary contents of a KTX file.
+			</description>
+		</method>
 		<method name="load_png_from_buffer">
 			<return type="int" enum="Error" />
 			<param index="0" name="buffer" type="PackedByteArray" />
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp b/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp
new file mode 100644
index 00000000000..ca61a24201a
--- /dev/null
+++ b/modules/gltf/extensions/gltf_document_extension_texture_ktx.cpp
@@ -0,0 +1,66 @@
+/**************************************************************************/
+/*  gltf_document_extension_texture_ktx.cpp                               */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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 "gltf_document_extension_texture_ktx.h"
+
+// Import process.
+Error GLTFDocumentExtensionTextureKTX::import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) {
+	if (!p_extensions.has("KHR_texture_basisu")) {
+		return ERR_SKIP;
+	}
+	return OK;
+}
+
+Vector<String> GLTFDocumentExtensionTextureKTX::get_supported_extensions() {
+	Vector<String> ret;
+	ret.push_back("KHR_texture_basisu");
+	return ret;
+}
+
+Error GLTFDocumentExtensionTextureKTX::parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) {
+	if (p_mime_type == "image/ktx2") {
+		return r_image->load_ktx_from_buffer(p_image_data);
+	}
+	return OK;
+}
+
+Error GLTFDocumentExtensionTextureKTX::parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) {
+	if (!p_texture_json.has("extensions")) {
+		return OK;
+	}
+	const Dictionary &extensions = p_texture_json["extensions"];
+	if (!extensions.has("KHR_texture_basisu")) {
+		return OK;
+	}
+	const Dictionary &texture_ktx = extensions["KHR_texture_basisu"];
+	ERR_FAIL_COND_V(!texture_ktx.has("source"), ERR_PARSE_ERROR);
+	r_gltf_texture->set_src_image(texture_ktx["source"]);
+	return OK;
+}
diff --git a/modules/gltf/extensions/gltf_document_extension_texture_ktx.h b/modules/gltf/extensions/gltf_document_extension_texture_ktx.h
new file mode 100644
index 00000000000..e4cb38a0441
--- /dev/null
+++ b/modules/gltf/extensions/gltf_document_extension_texture_ktx.h
@@ -0,0 +1,47 @@
+/**************************************************************************/
+/*  gltf_document_extension_texture_ktx.h                                 */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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.                 */
+/**************************************************************************/
+
+#ifndef GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H
+#define GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H
+
+#include "gltf_document_extension.h"
+
+class GLTFDocumentExtensionTextureKTX : public GLTFDocumentExtension {
+	GDCLASS(GLTFDocumentExtensionTextureKTX, GLTFDocumentExtension);
+
+public:
+	// Import process.
+	Error import_preflight(Ref<GLTFState> p_state, Vector<String> p_extensions) override;
+	Vector<String> get_supported_extensions() override;
+	Error parse_image_data(Ref<GLTFState> p_state, const PackedByteArray &p_image_data, const String &p_mime_type, Ref<Image> r_image) override;
+	Error parse_texture_json(Ref<GLTFState> p_state, const Dictionary &p_texture_json, Ref<GLTFTexture> r_gltf_texture) override;
+};
+
+#endif // GLTF_DOCUMENT_EXTENSION_TEXTURE_KTX_H
diff --git a/modules/gltf/register_types.cpp b/modules/gltf/register_types.cpp
index 1788ffac3ae..c56ad6aeddc 100644
--- a/modules/gltf/register_types.cpp
+++ b/modules/gltf/register_types.cpp
@@ -31,6 +31,7 @@
 #include "register_types.h"
 
 #include "extensions/gltf_document_extension_convert_importer_mesh.h"
+#include "extensions/gltf_document_extension_texture_ktx.h"
 #include "extensions/gltf_document_extension_texture_webp.h"
 #include "extensions/gltf_spec_gloss.h"
 #include "extensions/physics/gltf_document_extension_physics.h"
@@ -133,6 +134,7 @@ void initialize_gltf_module(ModuleInitializationLevel p_level) {
 		GDREGISTER_CLASS(GLTFTextureSampler);
 		// Register GLTFDocumentExtension classes with GLTFDocument.
 		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionPhysics);
+		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureKTX);
 		GLTF_REGISTER_DOCUMENT_EXTENSION(GLTFDocumentExtensionTextureWebP);
 		bool is_editor = ::Engine::get_singleton()->is_editor_hint();
 		if (!is_editor) {
diff --git a/modules/ktx/SCsub b/modules/ktx/SCsub
new file mode 100644
index 00000000000..9e453137017
--- /dev/null
+++ b/modules/ktx/SCsub
@@ -0,0 +1,60 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_ktx = env_modules.Clone()
+
+# libktx thirdparty source files
+
+thirdparty_obj = []
+
+thirdparty_dir = "#thirdparty/libktx/"
+thirdparty_sources = [
+    "lib/checkheader.c",
+    "lib/filestream.c",
+    "lib/hashlist.c",
+    "lib/memstream.c",
+    "lib/swap.c",
+    "lib/texture.c",
+    "lib/texture1.c",
+    "lib/texture2.c",
+    "lib/dfdutils/createdfd.c",
+    "lib/dfdutils/colourspaces.c",
+    "lib/dfdutils/interpretdfd.c",
+    "lib/dfdutils/printdfd.c",
+    "lib/dfdutils/queries.c",
+    "lib/dfdutils/vk2dfd.c",
+]
+thirdparty_sources = [thirdparty_dir + file for file in thirdparty_sources]
+
+env_ktx.Prepend(CPPPATH=[thirdparty_dir + "include"])
+env_ktx.Prepend(CPPPATH=[thirdparty_dir + "utils"])
+env_ktx.Prepend(CPPPATH=[thirdparty_dir + "lib"])
+env_ktx.Prepend(CPPPATH=[thirdparty_dir + "other_include"])
+
+if env["module_basis_universal_enabled"]:
+    thirdparty_sources += [thirdparty_dir + "lib/basis_transcode.cpp"]
+    env_ktx.Prepend(CPPPATH=["#thirdparty/basis_universal"])
+
+if env["vulkan"]:
+    env_ktx.Prepend(CPPPATH=["#thirdparty/vulkan/include"])
+else:
+    # Falls back on bundled `vkformat_enum.h`.
+    env_ktx.Append(CPPDEFINES=["LIBKTX"])
+
+env_ktx.Append(CPPDEFINES=[("KHRONOS_STATIC", 1)])
+
+env_thirdparty = env_ktx.Clone()
+env_thirdparty.disable_warnings()
+env_thirdparty.add_source_files(thirdparty_obj, thirdparty_sources)
+env.modules_sources += thirdparty_obj
+
+# Godot source files
+module_obj = []
+
+env_ktx.add_source_files(module_obj, "*.cpp")
+env.modules_sources += module_obj
+
+# Needed to force rebuilding the module files when the thirdparty library is updated.
+env.Depends(module_obj, thirdparty_obj)
diff --git a/modules/ktx/config.py b/modules/ktx/config.py
new file mode 100644
index 00000000000..d22f9454ed2
--- /dev/null
+++ b/modules/ktx/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+    return True
+
+
+def configure(env):
+    pass
diff --git a/modules/ktx/register_types.cpp b/modules/ktx/register_types.cpp
new file mode 100644
index 00000000000..1d48e05a909
--- /dev/null
+++ b/modules/ktx/register_types.cpp
@@ -0,0 +1,53 @@
+/**************************************************************************/
+/*  register_types.cpp                                                    */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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 "register_types.h"
+
+#include "texture_loader_ktx.h"
+
+static Ref<ResourceFormatKTX> resource_loader_ktx;
+
+void initialize_ktx_module(ModuleInitializationLevel p_level) {
+	if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+		return;
+	}
+
+	resource_loader_ktx.instantiate();
+	ResourceLoader::add_resource_format_loader(resource_loader_ktx);
+}
+
+void uninitialize_ktx_module(ModuleInitializationLevel p_level) {
+	if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+		return;
+	}
+
+	ResourceLoader::remove_resource_format_loader(resource_loader_ktx);
+	resource_loader_ktx.unref();
+}
diff --git a/modules/ktx/register_types.h b/modules/ktx/register_types.h
new file mode 100644
index 00000000000..a50dc48b409
--- /dev/null
+++ b/modules/ktx/register_types.h
@@ -0,0 +1,39 @@
+/**************************************************************************/
+/*  register_types.h                                                      */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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.                 */
+/**************************************************************************/
+
+#ifndef KTX_REGISTER_TYPES_H
+#define KTX_REGISTER_TYPES_H
+
+#include "modules/register_module_types.h"
+
+void initialize_ktx_module(ModuleInitializationLevel p_level);
+void uninitialize_ktx_module(ModuleInitializationLevel p_level);
+
+#endif // KTX_REGISTER_TYPES_H
diff --git a/modules/ktx/texture_loader_ktx.cpp b/modules/ktx/texture_loader_ktx.cpp
new file mode 100644
index 00000000000..155ed56bd08
--- /dev/null
+++ b/modules/ktx/texture_loader_ktx.cpp
@@ -0,0 +1,562 @@
+/**************************************************************************/
+/*  texture_loader_ktx.cpp                                                */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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 "texture_loader_ktx.h"
+
+#include "core/io/file_access.h"
+#include "core/io/file_access_memory.h"
+#include "scene/resources/image_texture.h"
+
+#include <ktx.h>
+#include <vk_format.h>
+
+KTX_error_code ktx_read(ktxStream *stream, void *dst, const ktx_size_t count) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	(*f)->get_buffer(reinterpret_cast<uint8_t *>(dst), count);
+	return KTX_SUCCESS;
+}
+
+KTX_error_code ktx_skip(ktxStream *stream, const ktx_size_t count) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	for (ktx_size_t i = 0; i < count; ++i) {
+		(*f)->get_8();
+	}
+	return KTX_SUCCESS;
+}
+
+KTX_error_code ktx_write(ktxStream *stream, const void *src, const ktx_size_t size, const ktx_size_t count) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	(*f)->store_buffer(reinterpret_cast<const uint8_t *>(src), size * count);
+	return KTX_SUCCESS;
+}
+
+KTX_error_code ktx_getpos(ktxStream *stream, ktx_off_t *const offset) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	*offset = (*f)->get_position();
+	return KTX_SUCCESS;
+}
+
+KTX_error_code ktx_setpos(ktxStream *stream, const ktx_off_t offset) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	(*f)->seek(offset);
+	return KTX_SUCCESS;
+}
+
+KTX_error_code ktx_getsize(ktxStream *stream, ktx_size_t *const size) {
+	Ref<FileAccess> *f = reinterpret_cast<Ref<FileAccess> *>(stream->data.custom_ptr.address);
+	*size = (*f)->get_length();
+	return KTX_SUCCESS;
+}
+
+void ktx_destruct(ktxStream *stream) {
+	(void)stream;
+}
+
+static Ref<Image> load_from_file_access(Ref<FileAccess> f, Error *r_error) {
+	ktxStream ktx_stream;
+	ktx_stream.read = ktx_read;
+	ktx_stream.skip = ktx_skip;
+	ktx_stream.write = ktx_write;
+	ktx_stream.getpos = ktx_getpos;
+	ktx_stream.setpos = ktx_setpos;
+	ktx_stream.getsize = ktx_getsize;
+	ktx_stream.destruct = ktx_destruct;
+	ktx_stream.type = eStreamTypeCustom;
+	ktx_stream.data.custom_ptr.address = &f;
+	ktx_stream.data.custom_ptr.allocatorAddress = NULL;
+	ktx_stream.data.custom_ptr.size = 0;
+	ktx_stream.readpos = 0;
+	ktx_stream.closeOnDestruct = false;
+	ktxTexture *ktx_texture;
+	KTX_error_code result = ktxTexture_CreateFromStream(&ktx_stream,
+			KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT,
+			&ktx_texture);
+	if (result != KTX_SUCCESS) {
+		ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid or unsupported KTX texture file.");
+	}
+
+	if (ktx_texture->numDimensions != 2) {
+		ktxTexture_Destroy(ktx_texture);
+		ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported non-2D KTX texture file.");
+	}
+
+	if (ktx_texture->isCubemap || ktx_texture->numFaces != 1) {
+		ktxTexture_Destroy(ktx_texture);
+		ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported cube map KTX texture file.");
+	}
+
+	if (ktx_texture->isArray) {
+		ktxTexture_Destroy(ktx_texture);
+		ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported array KTX texture file.");
+	}
+
+	uint32_t width = ktx_texture->baseWidth;
+	uint32_t height = ktx_texture->baseHeight;
+	uint32_t mipmaps = ktx_texture->numLevels;
+	Image::Format format;
+	bool srgb = false;
+
+	switch (ktx_texture->classId) {
+		case ktxTexture1_c:
+			switch (((ktxTexture1 *)ktx_texture)->glInternalformat) {
+				case GL_LUMINANCE:
+					format = Image::FORMAT_L8;
+					break;
+				case GL_LUMINANCE_ALPHA:
+					format = Image::FORMAT_LA8;
+					break;
+				case GL_SRGB8:
+					format = Image::FORMAT_RGB8;
+					srgb = true;
+					break;
+				case GL_SRGB8_ALPHA8:
+					format = Image::FORMAT_RGBA8;
+					srgb = true;
+					break;
+				case GL_R8:
+				case GL_R8UI:
+					format = Image::FORMAT_R8;
+					break;
+				case GL_RG8:
+					format = Image::FORMAT_RG8;
+					break;
+				case GL_RGB8:
+					format = Image::FORMAT_RGB8;
+					break;
+				case GL_RGBA8:
+					format = Image::FORMAT_RGBA8;
+					break;
+				case GL_RGBA4:
+					format = Image::FORMAT_RGBA4444;
+					break;
+				case GL_RGB565:
+					format = Image::FORMAT_RGB565;
+					break;
+				case GL_R32F:
+					format = Image::FORMAT_RF;
+					break;
+				case GL_RG32F:
+					format = Image::FORMAT_RGF;
+					break;
+				case GL_RGB32F:
+					format = Image::FORMAT_RGBF;
+					break;
+				case GL_RGBA32F:
+					format = Image::FORMAT_RGBAF;
+					break;
+				case GL_R16F:
+					format = Image::FORMAT_RH;
+					break;
+				case GL_RG16F:
+					format = Image::FORMAT_RGH;
+					break;
+				case GL_RGB16F:
+					format = Image::FORMAT_RGBH;
+					break;
+				case GL_RGBA16F:
+					format = Image::FORMAT_RGBAH;
+					break;
+				case GL_RGB9_E5:
+					format = Image::FORMAT_RGBE9995;
+					break;
+				case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
+				case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
+					format = Image::FORMAT_DXT1;
+					break;
+				case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
+					format = Image::FORMAT_DXT3;
+					break;
+				case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
+					format = Image::FORMAT_DXT5;
+					break;
+				case GL_COMPRESSED_RED_RGTC1:
+					format = Image::FORMAT_RGTC_R;
+					break;
+				case GL_COMPRESSED_RG_RGTC2:
+					format = Image::FORMAT_RGTC_RG;
+					break;
+				case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
+					format = Image::FORMAT_BPTC_RGBFU;
+					break;
+				case GL_COMPRESSED_RGBA_BPTC_UNORM:
+					format = Image::FORMAT_BPTC_RGBA;
+					break;
+#if 0 // TODO: ETC compression is bogus.
+				case GL_ETC1_RGB8_OES:
+					format = Image::FORMAT_ETC;
+					break;
+				case GL_COMPRESSED_R11_EAC:
+					format = Image::FORMAT_ETC2_R11;
+					break;
+				case GL_COMPRESSED_SIGNED_R11_EAC:
+					format = Image::FORMAT_ETC2_R11S;
+					break;
+				case GL_COMPRESSED_RG11_EAC:
+					format = Image::FORMAT_ETC2_RG11;
+					break;
+				case GL_COMPRESSED_SIGNED_RG11_EAC:
+					format = Image::FORMAT_ETC2_RG11S;
+					break;
+				case GL_COMPRESSED_RGB8_ETC2:
+					format = Image::FORMAT_ETC2_RGB8;
+					break;
+				case GL_COMPRESSED_RGBA8_ETC2_EAC:
+					format = Image::FORMAT_ETC2_RGBA8;
+					break;
+				case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
+					format = Image::FORMAT_ETC2_RGB8A1;
+					break;
+#endif
+				case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:
+					format = Image::FORMAT_ASTC_4x4;
+					break;
+				case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:
+					format = Image::FORMAT_ASTC_4x4_HDR;
+					break;
+				case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:
+					format = Image::FORMAT_ASTC_8x8;
+					break;
+				case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:
+					format = Image::FORMAT_ASTC_8x8_HDR;
+					break;
+				default:
+					ktxTexture_Destroy(ktx_texture);
+					ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported format " + itos(((ktxTexture1 *)ktx_texture)->glInternalformat) + " of KTX1 texture file.");
+			}
+			break;
+		case ktxTexture2_c: {
+			ktxTexture2 *ktx_texture2 = reinterpret_cast<ktxTexture2 *>(ktx_texture);
+			if (ktx_texture2->vkFormat == VK_FORMAT_UNDEFINED) {
+				if (!ktxTexture2_NeedsTranscoding(ktx_texture2)) {
+					ktxTexture_Destroy(ktx_texture);
+					ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid VK_FORMAT_UNDEFINED of KTX2 texture file.");
+				}
+				ktx_transcode_fmt_e ktxfmt;
+				switch (ktxTexture2_GetNumComponents(ktx_texture2)) {
+					case 1: {
+						if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support
+							ktxfmt = KTX_TTF_RGBA32;
+						} else if (RS::get_singleton()->has_os_feature("rgtc")) {
+							ktxfmt = KTX_TTF_BC4_R;
+						} else {
+							ktxfmt = KTX_TTF_RGBA32;
+						}
+						break;
+					}
+					case 2: {
+						if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support
+							ktxfmt = KTX_TTF_RGBA32;
+						} else if (RS::get_singleton()->has_os_feature("rgtc")) {
+							ktxfmt = KTX_TTF_BC5_RG;
+						} else {
+							ktxfmt = KTX_TTF_RGBA32;
+						}
+						break;
+					}
+					case 3: {
+						if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO: srgb native support
+							ktxfmt = KTX_TTF_RGBA32;
+						} else if (RS::get_singleton()->has_os_feature("bptc")) {
+							ktxfmt = KTX_TTF_BC7_RGBA;
+						} else if (RS::get_singleton()->has_os_feature("s3tc")) {
+							ktxfmt = KTX_TTF_BC1_RGB;
+						} else if (RS::get_singleton()->has_os_feature("etc")) {
+							ktxfmt = KTX_TTF_ETC1_RGB;
+						} else {
+							ktxfmt = KTX_TTF_RGBA32;
+						}
+						break;
+					}
+					case 4: {
+						if (ktxTexture2_GetOETF_e(ktx_texture2) == KHR_DF_TRANSFER_SRGB) { // TODO srgb native support
+							ktxfmt = KTX_TTF_RGBA32;
+						} else if (RS::get_singleton()->has_os_feature("astc")) {
+							ktxfmt = KTX_TTF_ASTC_4x4_RGBA;
+						} else if (RS::get_singleton()->has_os_feature("bptc")) {
+							ktxfmt = KTX_TTF_BC7_RGBA;
+						} else if (RS::get_singleton()->has_os_feature("s3tc")) {
+							ktxfmt = KTX_TTF_BC3_RGBA;
+						} else if (RS::get_singleton()->has_os_feature("etc2")) {
+							ktxfmt = KTX_TTF_ETC2_RGBA;
+						} else {
+							ktxfmt = KTX_TTF_RGBA32;
+						}
+						break;
+					}
+					default: {
+						ktxTexture_Destroy(ktx_texture);
+						ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid components of KTX2 texture file.");
+					}
+				}
+				result = ktxTexture2_TranscodeBasis(ktx_texture2, ktxfmt, 0);
+				if (result != KTX_SUCCESS) {
+					ktxTexture_Destroy(ktx_texture);
+					ERR_FAIL_V_MSG(Ref<Resource>(), "Failed to transcode KTX2 texture file.");
+				}
+			}
+			switch (ktx_texture2->vkFormat) {
+				case VK_FORMAT_R8_UNORM:
+					format = Image::FORMAT_L8;
+					break;
+				case VK_FORMAT_R8G8_UNORM:
+					format = Image::FORMAT_LA8;
+					break;
+				case VK_FORMAT_R8G8B8_SRGB:
+					format = Image::FORMAT_RGB8;
+					srgb = true;
+					break;
+				case VK_FORMAT_R8G8B8A8_SRGB:
+					format = Image::FORMAT_RGBA8;
+					srgb = true;
+					break;
+				case VK_FORMAT_R8_UINT:
+					format = Image::FORMAT_R8;
+					break;
+				case VK_FORMAT_R8G8_UINT:
+					format = Image::FORMAT_RG8;
+					break;
+				case VK_FORMAT_R8G8B8_UINT:
+					format = Image::FORMAT_RGB8;
+					break;
+				case VK_FORMAT_R8G8B8A8_UINT:
+					format = Image::FORMAT_RGBA8;
+					break;
+				case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
+					format = Image::FORMAT_RGBA4444;
+					break;
+				case VK_FORMAT_R5G6B5_UNORM_PACK16:
+					format = Image::FORMAT_RGB565;
+					break;
+				case VK_FORMAT_R32_SFLOAT:
+					format = Image::FORMAT_RF;
+					break;
+				case VK_FORMAT_R32G32_SFLOAT:
+					format = Image::FORMAT_RGF;
+					break;
+				case VK_FORMAT_R32G32B32_SFLOAT:
+					format = Image::FORMAT_RGBF;
+					break;
+				case VK_FORMAT_R32G32B32A32_SFLOAT:
+					format = Image::FORMAT_RGBAF;
+					break;
+				case VK_FORMAT_R16_SFLOAT:
+					format = Image::FORMAT_RH;
+					break;
+				case VK_FORMAT_R16G16_SFLOAT:
+					format = Image::FORMAT_RGH;
+					break;
+				case VK_FORMAT_R16G16B16_SFLOAT:
+					format = Image::FORMAT_RGBH;
+					break;
+				case VK_FORMAT_R16G16B16A16_SFLOAT:
+					format = Image::FORMAT_RGBAH;
+					break;
+				case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
+					format = Image::FORMAT_RGBE9995;
+					break;
+				case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+				case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+					format = Image::FORMAT_DXT1;
+					break;
+				case VK_FORMAT_BC2_UNORM_BLOCK:
+					format = Image::FORMAT_DXT3;
+					break;
+				case VK_FORMAT_BC3_UNORM_BLOCK:
+					format = Image::FORMAT_DXT5;
+					break;
+				case VK_FORMAT_BC4_UNORM_BLOCK:
+					format = Image::FORMAT_RGTC_R;
+					break;
+				case VK_FORMAT_BC5_UNORM_BLOCK:
+					format = Image::FORMAT_RGTC_RG;
+					break;
+				case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+					format = Image::FORMAT_BPTC_RGBFU;
+					break;
+				case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+					format = Image::FORMAT_BPTC_RGBF;
+					break;
+				case VK_FORMAT_BC7_UNORM_BLOCK:
+					format = Image::FORMAT_BPTC_RGBA;
+					break;
+#if 0 // TODO: ETC compression is bogus.
+				case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+					format = Image::FORMAT_ETC2_R11;
+					break;
+				case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+					format = Image::FORMAT_ETC2_R11S;
+					break;
+				case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+					format = Image::FORMAT_ETC2_RG11;
+					break;
+				case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+					format = Image::FORMAT_ETC2_RG11S;
+					break;
+				case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+					format = Image::FORMAT_ETC2_RGB8;
+					break;
+				case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+					format = Image::FORMAT_ETC2_RGBA8;
+					break;
+				case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+					format = Image::FORMAT_ETC2_RGB8A1;
+					break;
+#endif
+				case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+					format = Image::FORMAT_ASTC_4x4;
+					break;
+				case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+					format = Image::FORMAT_ASTC_4x4_HDR;
+					break;
+				case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+					format = Image::FORMAT_ASTC_8x8;
+					break;
+				case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+					format = Image::FORMAT_ASTC_8x8_HDR;
+					break;
+				default:
+					ktxTexture_Destroy(ktx_texture);
+					ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported format " + itos(((ktxTexture2 *)ktx_texture)->vkFormat) + " of KTX2 texture file.");
+					break;
+			}
+			break;
+		}
+		default:
+			ktxTexture_Destroy(ktx_texture);
+			ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported version KTX texture file.");
+			break;
+	}
+
+	Vector<uint8_t> src_data;
+
+	// KTX use 4-bytes padding, don't use mipmaps if padding is effective
+	// TODO: unpad dynamically
+	int pixel_size = Image::get_format_pixel_size(format);
+	int pixel_rshift = Image::get_format_pixel_rshift(format);
+	int block = Image::get_format_block_size(format);
+	int minw, minh;
+	Image::get_format_min_pixel_size(format, minw, minh);
+	int w = width;
+	int h = height;
+	for (uint32_t i = 0; i < mipmaps; ++i) {
+		ktx_size_t mip_size = ktxTexture_GetImageSize(ktx_texture, i);
+		size_t bw = w % block != 0 ? w + (block - w % block) : w;
+		size_t bh = h % block != 0 ? h + (block - h % block) : h;
+		size_t s = bw * bh;
+		s *= pixel_size;
+		s >>= pixel_rshift;
+		if (mip_size != static_cast<ktx_size_t>(s)) {
+			if (!i) {
+				ktxTexture_Destroy(ktx_texture);
+				ERR_FAIL_V_MSG(Ref<Resource>(), "Unsupported padded KTX texture file.");
+			}
+			mipmaps = 1;
+			break;
+		}
+		w = MAX(minw, w >> 1);
+		h = MAX(minh, h >> 1);
+	}
+
+	for (uint32_t i = 0; i < mipmaps; ++i) {
+		ktx_size_t mip_size = ktxTexture_GetImageSize(ktx_texture, i);
+		ktx_size_t offset;
+		if (ktxTexture_GetImageOffset(ktx_texture, i, 0, 0, &offset) != KTX_SUCCESS) {
+			ktxTexture_Destroy(ktx_texture);
+			ERR_FAIL_V_MSG(Ref<Resource>(), "Invalid KTX texture file.");
+		}
+		int prev_size = src_data.size();
+		src_data.resize(prev_size + mip_size);
+		memcpy(src_data.ptrw() + prev_size, ktxTexture_GetData(ktx_texture) + offset, mip_size);
+	}
+
+	Ref<Image> img = memnew(Image(width, height, mipmaps - 1, format, src_data));
+	if (srgb) {
+		img->srgb_to_linear();
+	}
+
+	if (r_error) {
+		*r_error = OK;
+	}
+
+	ktxTexture_Destroy(ktx_texture);
+	return img;
+}
+
+Ref<Resource> ResourceFormatKTX::load(const String &p_path, const String &p_original_path, Error *r_error, bool p_use_sub_threads, float *r_progress, CacheMode p_cache_mode) {
+	if (r_error) {
+		*r_error = ERR_CANT_OPEN;
+	}
+
+	Error err;
+	Ref<FileAccess> f = FileAccess::open(p_path, FileAccess::READ, &err);
+	if (f.is_null()) {
+		return Ref<Resource>();
+	}
+
+	Ref<FileAccess> fref(f);
+	if (r_error) {
+		*r_error = ERR_FILE_CORRUPT;
+	}
+
+	ERR_FAIL_COND_V_MSG(err != OK, Ref<Resource>(), "Unable to open KTX texture file '" + p_path + "'.");
+	Ref<Image> img = load_from_file_access(f, r_error);
+	Ref<ImageTexture> texture = ImageTexture::create_from_image(img);
+	return texture;
+}
+
+static Ref<Image> _ktx_mem_loader_func(const uint8_t *p_ktx, int p_size) {
+	Ref<FileAccessMemory> f;
+	f.instantiate();
+	f->open_custom(p_ktx, p_size);
+	Error err;
+	Ref<Image> img = load_from_file_access(f, &err);
+	ERR_FAIL_COND_V(err, Ref<Image>());
+	return img;
+}
+
+void ResourceFormatKTX::get_recognized_extensions(List<String> *p_extensions) const {
+	p_extensions->push_back("ktx");
+	p_extensions->push_back("ktx2");
+}
+
+bool ResourceFormatKTX::handles_type(const String &p_type) const {
+	return ClassDB::is_parent_class(p_type, "Texture2D");
+}
+
+String ResourceFormatKTX::get_resource_type(const String &p_path) const {
+	if (p_path.get_extension().to_lower() == "ktx" || p_path.get_extension().to_lower() == "ktx2") {
+		return "ImageTexture";
+	}
+	return "";
+}
+
+ResourceFormatKTX::ResourceFormatKTX() {
+	Image::_ktx_mem_loader_func = _ktx_mem_loader_func;
+}
diff --git a/modules/ktx/texture_loader_ktx.h b/modules/ktx/texture_loader_ktx.h
new file mode 100644
index 00000000000..0ea676be6b2
--- /dev/null
+++ b/modules/ktx/texture_loader_ktx.h
@@ -0,0 +1,48 @@
+/**************************************************************************/
+/*  texture_loader_ktx.h                                                  */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* 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.                 */
+/**************************************************************************/
+
+#ifndef TEXTURE_LOADER_KTX_H
+#define TEXTURE_LOADER_KTX_H
+
+#include "core/io/resource_loader.h"
+#include "scene/resources/texture.h"
+
+class ResourceFormatKTX : public ResourceFormatLoader {
+public:
+	virtual Ref<Resource> load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr, bool p_use_sub_threads = false, float *r_progress = nullptr, CacheMode p_cache_mode = CACHE_MODE_REUSE);
+	virtual void get_recognized_extensions(List<String> *p_extensions) const;
+	virtual bool handles_type(const String &p_type) const;
+	virtual String get_resource_type(const String &p_path) const;
+
+	virtual ~ResourceFormatKTX() {}
+	ResourceFormatKTX();
+};
+
+#endif // TEXTURE_LOADER_KTX_H
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 50535029712..ac211138986 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -299,6 +299,25 @@ Files extracted from upstream source:
 - `jpge*.{c,h}`
 
 
+## libktx
+
+- Upstream: https://github.com/KhronosGroup/KTX-Software
+- Version: 4.1.0 (d7255fe73cd53b856731ceb9f2c279181d0dbbca, 2023)
+- License: Apache-2.0
+
+Files extracted from upstream source:
+
+- `LICENSE.md`
+- `include/*`
+- `lib/dfdutils/{LICENSES/Apache-2.0.txt,KHR,*.c,*.h,*.inl}`
+- `lib/{basis_sgd.h,basis_transcode.cpp,checkheader.c,filestream.*,formatsize.h,gl_format.h,hashlist.c,ktxint.h,memstream.*,swap.c,texture*,uthash.h,vk_format.h,vkformat_enum.h`
+- `utils/unused.h`
+- `other_include/KHR/*`
+- ifndef-protect NOMINMAX define in `lib/gl_format.h` (see godot.patch)
+- remove `basisu/` prefix from `thirdparty/libktx/lib/basis_transcode.cpp` basisu includes (see godot.patch)
+- comment `VK_FORMAT_ASTC_*x*x*_UNORM_BLOCK_EXT` cases in `lib/dfdutils/vk2dfd.inl` (see godot.patch)
+
+
 ## libogg
 
 - Upstream: https://www.xiph.org/ogg
diff --git a/thirdparty/libktx/Apache-2.0.txt b/thirdparty/libktx/Apache-2.0.txt
new file mode 100644
index 00000000000..4ed90b95224
--- /dev/null
+++ b/thirdparty/libktx/Apache-2.0.txt
@@ -0,0 +1,208 @@
+Apache License
+
+Version 2.0, January 2004
+
+http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
+AND DISTRIBUTION
+
+   1. Definitions.
+
+      
+
+"License" shall mean the terms and conditions for use, reproduction, and distribution
+as defined by Sections 1 through 9 of this document.
+
+      
+
+"Licensor" shall mean the copyright owner or entity authorized by the copyright
+owner that is granting the License.
+
+      
+
+"Legal Entity" shall mean the union of the acting entity and all other entities
+that control, are controlled by, or are under common control with that entity.
+For the purposes of this definition, "control" means (i) the power, direct
+or indirect, to cause the direction or management of such entity, whether
+by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
+of the outstanding shares, or (iii) beneficial ownership of such entity.
+
+      
+
+"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
+granted by this License.
+
+      
+
+"Source" form shall mean the preferred form for making modifications, including
+but not limited to software source code, documentation source, and configuration
+files.
+
+      
+
+"Object" form shall mean any form resulting from mechanical transformation
+or translation of a Source form, including but not limited to compiled object
+code, generated documentation, and conversions to other media types.
+
+      
+
+"Work" shall mean the work of authorship, whether in Source or Object form,
+made available under the License, as indicated by a copyright notice that
+is included in or attached to the work (an example is provided in the Appendix
+below).
+
+      
+
+"Derivative Works" shall mean any work, whether in Source or Object form,
+that is based on (or derived from) the Work and for which the editorial revisions,
+annotations, elaborations, or other modifications represent, as a whole, an
+original work of authorship. For the purposes of this License, Derivative
+Works shall not include works that remain separable from, or merely link (or
+bind by name) to the interfaces of, the Work and Derivative Works thereof.
+
+      
+
+"Contribution" shall mean any work of authorship, including the original version
+of the Work and any modifications or additions to that Work or Derivative
+Works thereof, that is intentionally submitted to Licensor for inclusion in
+the Work by the copyright owner or by an individual or Legal Entity authorized
+to submit on behalf of the copyright owner. For the purposes of this definition,
+"submitted" means any form of electronic, verbal, or written communication
+sent to the Licensor or its representatives, including but not limited to
+communication on electronic mailing lists, source code control systems, and
+issue tracking systems that are managed by, or on behalf of, the Licensor
+for the purpose of discussing and improving the Work, but excluding communication
+that is conspicuously marked or otherwise designated in writing by the copyright
+owner as "Not a Contribution."
+
+      
+
+"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
+of whom a Contribution has been received by Licensor and subsequently incorporated
+within the Work.
+
+2. Grant of Copyright License. Subject to the terms and conditions of this
+License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
+Derivative Works of, publicly display, publicly perform, sublicense, and distribute
+the Work and such Derivative Works in Source or Object form.
+
+3. Grant of Patent License. Subject to the terms and conditions of this License,
+each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this section) patent
+license to make, have made, use, offer to sell, sell, import, and otherwise
+transfer the Work, where such license applies only to those patent claims
+licensable by such Contributor that are necessarily infringed by their Contribution(s)
+alone or by combination of their Contribution(s) with the Work to which such
+Contribution(s) was submitted. If You institute patent litigation against
+any entity (including a cross-claim or counterclaim in a lawsuit) alleging
+that the Work or a Contribution incorporated within the Work constitutes direct
+or contributory patent infringement, then any patent licenses granted to You
+under this License for that Work shall terminate as of the date such litigation
+is filed.
+
+4. Redistribution. You may reproduce and distribute copies of the Work or
+Derivative Works thereof in any medium, with or without modifications, and
+in Source or Object form, provided that You meet the following conditions:
+
+(a) You must give any other recipients of the Work or Derivative Works a copy
+of this License; and
+
+(b) You must cause any modified files to carry prominent notices stating that
+You changed the files; and
+
+(c) You must retain, in the Source form of any Derivative Works that You distribute,
+all copyright, patent, trademark, and attribution notices from the Source
+form of the Work, excluding those notices that do not pertain to any part
+of the Derivative Works; and
+
+(d) If the Work includes a "NOTICE" text file as part of its distribution,
+then any Derivative Works that You distribute must include a readable copy
+of the attribution notices contained within such NOTICE file, excluding those
+notices that do not pertain to any part of the Derivative Works, in at least
+one of the following places: within a NOTICE text file distributed as part
+of the Derivative Works; within the Source form or documentation, if provided
+along with the Derivative Works; or, within a display generated by the Derivative
+Works, if and wherever such third-party notices normally appear. The contents
+of the NOTICE file are for informational purposes only and do not modify the
+License. You may add Your own attribution notices within Derivative Works
+that You distribute, alongside or as an addendum to the NOTICE text from the
+Work, provided that such additional attribution notices cannot be construed
+as modifying the License.
+
+You may add Your own copyright statement to Your modifications and may provide
+additional or different license terms and conditions for use, reproduction,
+or distribution of Your modifications, or for any such Derivative Works as
+a whole, provided Your use, reproduction, and distribution of the Work otherwise
+complies with the conditions stated in this License.
+
+5. Submission of Contributions. Unless You explicitly state otherwise, any
+Contribution intentionally submitted for inclusion in the Work by You to the
+Licensor shall be under the terms and conditions of this License, without
+any additional terms or conditions. Notwithstanding the above, nothing herein
+shall supersede or modify the terms of any separate license agreement you
+may have executed with Licensor regarding such Contributions.
+
+6. Trademarks. This License does not grant permission to use the trade names,
+trademarks, service marks, or product names of the Licensor, except as required
+for reasonable and customary use in describing the origin of the Work and
+reproducing the content of the NOTICE file.
+
+7. Disclaimer of Warranty. Unless required by applicable law or agreed to
+in writing, Licensor provides the Work (and each Contributor provides its
+Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+KIND, either express or implied, including, without limitation, any warranties
+or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
+A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
+of using or redistributing the Work and assume any risks associated with Your
+exercise of permissions under this License.
+
+8. Limitation of Liability. In no event and under no legal theory, whether
+in tort (including negligence), contract, or otherwise, unless required by
+applicable law (such as deliberate and grossly negligent acts) or agreed to
+in writing, shall any Contributor be liable to You for damages, including
+any direct, indirect, special, incidental, or consequential damages of any
+character arising as a result of this License or out of the use or inability
+to use the Work (including but not limited to damages for loss of goodwill,
+work stoppage, computer failure or malfunction, or any and all other commercial
+damages or losses), even if such Contributor has been advised of the possibility
+of such damages.
+
+9. Accepting Warranty or Additional Liability. While redistributing the Work
+or Derivative Works thereof, You may choose to offer, and charge a fee for,
+acceptance of support, warranty, indemnity, or other liability obligations
+and/or rights consistent with this License. However, in accepting such obligations,
+You may act only on Your own behalf and on Your sole responsibility, not on
+behalf of any other Contributor, and only if You agree to indemnify, defend,
+and hold each Contributor harmless for any liability incurred by, or claims
+asserted against, such Contributor by reason of your accepting any such warranty
+or additional liability. END OF TERMS AND CONDITIONS
+
+APPENDIX: How to apply the Apache License to your work.
+
+To apply the Apache License to your work, attach the following boilerplate
+notice, with the fields enclosed by brackets "[]" replaced with your own identifying
+information. (Don't include the brackets!) The text should be enclosed in
+the appropriate comment syntax for the file format. We also recommend that
+a file or class name and description of purpose be included on the same "printed
+page" as the copyright notice for easier identification within third-party
+archives.
+
+Copyright [yyyy] [name of copyright owner]
+
+Licensed under the Apache License, Version 2.0 (the "License");
+
+you may not use this file except in compliance with the License.
+
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+
+distributed under the License is distributed on an "AS IS" BASIS,
+
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+
+See the License for the specific language governing permissions and
+
+limitations under the License.
diff --git a/thirdparty/libktx/LICENSE.dfdutils.adoc b/thirdparty/libktx/LICENSE.dfdutils.adoc
new file mode 100644
index 00000000000..f206249940c
--- /dev/null
+++ b/thirdparty/libktx/LICENSE.dfdutils.adoc
@@ -0,0 +1,10 @@
+= LICENSE file for the KhronosGroup/dfdutils project
+
+Files in this repository fall under this license:
+
+  * SPDX license identifier: "`Apache-2.0`"
+  ** Apache License 2.0
+
+Full license text is available at:
+
+  * Apache-2.0: https://opensource.org/licenses/Apache-2.0
diff --git a/thirdparty/libktx/LICENSE.md b/thirdparty/libktx/LICENSE.md
new file mode 100644
index 00000000000..7b6d05fe480
--- /dev/null
+++ b/thirdparty/libktx/LICENSE.md
@@ -0,0 +1,36 @@
+LICENSE file for the KhronosGroup/KTX-Software project    {#license}
+======================================================
+
+<!--
+ Can't put at start. Doxygen requires page title on first line.
+ Copyright 2013-2020 Mark Callow 
+ SPDX-License-Identifier: Apache-2.0
+-->
+
+Files unique to this repository generally fall under the Apache 2.0 license
+with copyright holders including Mark Callow, the KTX-Software author; The
+Khronos Group Inc., which has supported KTX development; and other
+contributors to the KTX project.
+
+Because KTX-Software incorporates material and contributions from many other
+projects, which often have their own licenses, there are many other licenses
+in use in this repository. While there are many licenses in this repository,
+with rare exceptions all are open source licenses that we believe to be
+mutually compatible.
+
+The complete text of each of the licenses used in this repository is found
+in LICENSES/*.txt . Additionally, we have updated the repository to pass the
+REUSE compliance checker tool (see https://reuse.software/). REUSE verifies
+that every file in a git repository either incorporates a license, or that
+the license is present in auxiliary files such as .reuse/dep5 . To obtain a
+bill of materials for the repository identifying the license for each file,
+install the REUSE tool and run
+
+    reuse spdx
+
+inside the repository.
+
+## Special Cases
+
+The file lib/etcdec.cxx is not open source. It is made available under the
+terms of an Ericsson license, found in the file itself.
diff --git a/thirdparty/libktx/godot.patch b/thirdparty/libktx/godot.patch
new file mode 100644
index 00000000000..8a492ee27dd
--- /dev/null
+++ b/thirdparty/libktx/godot.patch
@@ -0,0 +1,45 @@
+--- thirdparty/libktx/lib/gl_format.h
++++ thirdparty/libktx/lib/gl_format.h
+@@ -92,7 +92,9 @@
+ #include "vkformat_enum.h"
+ 
+ #if defined(_WIN32) && !defined(__MINGW32__)
++#ifndef NOMINMAX
+ #define NOMINMAX
++#endif
+ #ifndef __cplusplus
+ #undef inline
+ #define inline __inline
+--- thirdparty/libktx/lib/basis_transcode.cpp
++++ thirdparty/libktx/lib/basis_transcode.cpp
+@@ -29,9 +29,9 @@
+ #include "vkformat_enum.h"
+ #include "vk_format.h"
+ #include "basis_sgd.h"
+-#include "basisu/transcoder/basisu_file_headers.h"
+-#include "basisu/transcoder/basisu_transcoder.h"
+-#include "basisu/transcoder/basisu_transcoder_internal.h"
++#include "transcoder/basisu_file_headers.h"
++#include "transcoder/basisu_transcoder.h"
++#include "transcoder/basisu_transcoder_internal.h"
+ 
+ #undef DECLARE_PRIVATE
+ #undef DECLARE_PROTECTED
+--- thirdparty/libktx/lib/dfdutils/vk2dfd.inl
++++ thirdparty/libktx/lib/dfdutils/vk2dfd.inl
+@@ -298,6 +298,7 @@
+ case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SFLOAT);
+ case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SFLOAT);
+ case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SFLOAT);
++#if 0
+ case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_UNORM);
+ case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SRGB);
+ case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SFLOAT);
+@@ -328,6 +329,7 @@
+ case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_UNORM);
+ case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SRGB);
+ case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SFLOAT);
++#endif
+ case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: {
+     int channels[] = {2,1,0,3}; int bits[] = {4,4,4,4};
+     return createDFDPacked(0, 4, bits, channels, s_UNORM);
diff --git a/thirdparty/libktx/include/KHR/khr_df.h b/thirdparty/libktx/include/KHR/khr_df.h
new file mode 100644
index 00000000000..bbd0d14bd90
--- /dev/null
+++ b/thirdparty/libktx/include/KHR/khr_df.h
@@ -0,0 +1,619 @@
+/* The Khronos Data Format Specification (version 1.3) */
+/*
+** Copyright 2015-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
+*/
+
+/* This header defines a structure that can describe the layout of image
+   formats in memory. This means that the data format is transparent to
+   the application, and the expectation is that this should be used when
+   the layout is defined external to the API. Many Khronos APIs deliberately
+   keep the internal layout of images opaque, to allow proprietary layouts
+   and optimisations. This structure is not appropriate for describing
+   opaque layouts. */
+
+/* We stick to standard C89 constructs for simplicity and portability. */
+
+#ifndef _KHR_DATA_FORMAT_H_
+#define _KHR_DATA_FORMAT_H_
+
+/* Accessors */
+typedef enum _khr_word_e {
+    KHR_DF_WORD_VENDORID = 0U,
+    KHR_DF_WORD_DESCRIPTORTYPE = 0U,
+    KHR_DF_WORD_VERSIONNUMBER = 1U,
+    KHR_DF_WORD_DESCRIPTORBLOCKSIZE = 1U,
+    KHR_DF_WORD_MODEL = 2U,
+    KHR_DF_WORD_PRIMARIES = 2U,
+    KHR_DF_WORD_TRANSFER = 2U,
+    KHR_DF_WORD_FLAGS = 2U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION0 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION1 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION2 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION3 = 3U,
+    KHR_DF_WORD_BYTESPLANE0 = 4U,
+    KHR_DF_WORD_BYTESPLANE1 = 4U,
+    KHR_DF_WORD_BYTESPLANE2 = 4U,
+    KHR_DF_WORD_BYTESPLANE3 = 4U,
+    KHR_DF_WORD_BYTESPLANE4 = 5U,
+    KHR_DF_WORD_BYTESPLANE5 = 5U,
+    KHR_DF_WORD_BYTESPLANE6 = 5U,
+    KHR_DF_WORD_BYTESPLANE7 = 5U,
+    KHR_DF_WORD_SAMPLESTART = 6U,
+    KHR_DF_WORD_SAMPLEWORDS = 4U
+} khr_df_word_e;
+
+typedef enum _khr_df_shift_e {
+    KHR_DF_SHIFT_VENDORID = 0U,
+    KHR_DF_SHIFT_DESCRIPTORTYPE = 17U,
+    KHR_DF_SHIFT_VERSIONNUMBER = 0U,
+    KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE = 16U,
+    KHR_DF_SHIFT_MODEL = 0U,
+    KHR_DF_SHIFT_PRIMARIES = 8U,
+    KHR_DF_SHIFT_TRANSFER = 16U,
+    KHR_DF_SHIFT_FLAGS = 24U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION0 = 0U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION1 = 8U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION2 = 16U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE0 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE1 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE2 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE4 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE5 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE6 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE7 = 24U
+} khr_df_shift_e;
+
+typedef enum _khr_df_mask_e {
+    KHR_DF_MASK_VENDORID = 0x1FFFFU,
+    KHR_DF_MASK_DESCRIPTORTYPE = 0x7FFFU,
+    KHR_DF_MASK_VERSIONNUMBER = 0xFFFFU,
+    KHR_DF_MASK_DESCRIPTORBLOCKSIZE = 0xFFFFU,
+    KHR_DF_MASK_MODEL = 0xFFU,
+    KHR_DF_MASK_PRIMARIES = 0xFFU,
+    KHR_DF_MASK_TRANSFER = 0xFFU,
+    KHR_DF_MASK_FLAGS = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION0 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION1 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION2 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE0 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE1 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE2 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE4 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE5 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE6 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE7 = 0xFFU
+} khr_df_mask_e;
+
+/* Helper macro:
+   Extract field X from basic descriptor block BDB */
+#define KHR_DFDVAL(BDB, X) \
+    (((BDB)[KHR_DF_WORD_ ## X] >> (KHR_DF_SHIFT_ ## X)) \
+     & (KHR_DF_MASK_ ## X))
+
+/* Helper macro:
+   Set field X of basic descriptor block BDB */
+#define KHR_DFDSETVAL(BDB, X, val) \
+    ((BDB)[KHR_DF_WORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_ ## X] & \
+      ~((KHR_DF_MASK_ ## X) << (KHR_DF_SHIFT_ ## X))) | \
+     (((val) & (KHR_DF_MASK_ ## X)) << (KHR_DF_SHIFT_ ## X)))
+
+/* Offsets relative to the start of a sample */
+typedef enum _khr_df_sampleword_e {
+    KHR_DF_SAMPLEWORD_BITOFFSET = 0U,
+    KHR_DF_SAMPLEWORD_BITLENGTH = 0U,
+    KHR_DF_SAMPLEWORD_CHANNELID = 0U,
+    KHR_DF_SAMPLEWORD_QUALIFIERS = 0U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION0 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION1 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION2 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION3 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLELOWER = 2U,
+    KHR_DF_SAMPLEWORD_SAMPLEUPPER = 3U
+} khr_df_sampleword_e;
+
+typedef enum _khr_df_sampleshift_e {
+    KHR_DF_SAMPLESHIFT_BITOFFSET = 0U,
+    KHR_DF_SAMPLESHIFT_BITLENGTH = 16U,
+    KHR_DF_SAMPLESHIFT_CHANNELID = 24U,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLESHIFT_QUALIFIERS = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION0 = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION1 = 8U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION2 = 16U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION3 = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION_ALL = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLELOWER = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEUPPER = 0U
+} khr_df_sampleshift_e;
+
+typedef enum _khr_df_samplemask_e {
+    KHR_DF_SAMPLEMASK_BITOFFSET = 0xFFFFU,
+    KHR_DF_SAMPLEMASK_BITLENGTH = 0xFF,
+    KHR_DF_SAMPLEMASK_CHANNELID = 0xF,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLEMASK_QUALIFIERS = 0xF0,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION0 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION1 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION2 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION3 = 0xFF,
+    /* ISO C restricts enum values to range of int hence the
+       cast. We do it verbosely instead of using -1 to ensure
+       it is a 32-bit value even if int is 64 bits. */
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION_ALL = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLELOWER = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLEUPPER = (int) 0xFFFFFFFFU
+} khr_df_samplemask_e;
+
+/* Helper macro:
+   Extract field X of sample S from basic descriptor block BDB */
+#define KHR_DFDSVAL(BDB, S, X) \
+    (((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] >> (KHR_DF_SAMPLESHIFT_ ## X)) \
+     & (KHR_DF_SAMPLEMASK_ ## X))
+
+/* Helper macro:
+   Set field X of sample S of basic descriptor block BDB */
+#define KHR_DFDSETSVAL(BDB, S, X, val) \
+    ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+           ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+           KHR_DF_SAMPLEWORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] & \
+      ~((uint32_t)(KHR_DF_SAMPLEMASK_ ## X) << (KHR_DF_SAMPLESHIFT_ ## X))) | \
+     (((val) & (uint32_t)(KHR_DF_SAMPLEMASK_ ## X)) << (KHR_DF_SAMPLESHIFT_ ## X)))
+
+/* Helper macro:
+   Number of samples in basic descriptor block BDB */
+#define KHR_DFDSAMPLECOUNT(BDB) \
+    (((KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) >> 2) - \
+      KHR_DF_WORD_SAMPLESTART) \
+     / KHR_DF_WORD_SAMPLEWORDS)
+
+/* Helper macro:
+   Size in words of basic descriptor block for S samples */
+#define KHR_DFDSIZEWORDS(S) \
+    (KHR_DF_WORD_SAMPLESTART + \
+     (S) * KHR_DF_WORD_SAMPLEWORDS)
+
+/* Vendor ids */
+typedef enum _khr_df_vendorid_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VENDORID_KHRONOS = 0U,
+    KHR_DF_VENDORID_MAX     = 0x1FFFFU
+} khr_df_vendorid_e;
+
+/* Descriptor types */
+typedef enum _khr_df_khr_descriptortype_e {
+    /* Default Khronos basic descriptor block */
+    KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT = 0U,
+    /* Extension descriptor block for additional planes */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES = 0x6001U,
+    /* Extension descriptor block for additional dimensions */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS = 0x6002U,
+    /* Bit indicates modifying requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_WRITE_BIT = 0x2000U,
+    /* Bit indicates processing requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_DECODE_BIT = 0x4000U,
+    KHR_DF_KHR_DESCRIPTORTYPE_MAX         = 0x7FFFU
+} khr_df_khr_descriptortype_e;
+
+/* Descriptor block version */
+typedef enum _khr_df_versionnumber_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VERSIONNUMBER_1_0 = 0U, /* Version 1.0 of the specification */
+    KHR_DF_VERSIONNUMBER_1_1 = 0U, /* Version 1.1 did not bump the version number */
+    KHR_DF_VERSIONNUMBER_1_2 = 1U, /* Version 1.2 increased the version number */
+    KHR_DF_VERSIONNUMBER_1_3 = 2U, /* Version 1.3 increased the version number */
+    KHR_DF_VERSIONNUMBER_LATEST = KHR_DF_VERSIONNUMBER_1_3,
+    KHR_DF_VERSIONNUMBER_MAX = 0xFFFFU
+} khr_df_versionnumber_e;
+
+/* Model in which the color coordinate space is defined.
+   There is no requirement that a color format use all the
+   channel types that are defined in the color model. */
+typedef enum _khr_df_model_e {
+    /* No interpretation of color channels defined */
+    KHR_DF_MODEL_UNSPECIFIED  = 0U,
+    /* Color primaries (red, green, blue) + alpha, depth and stencil */
+    KHR_DF_MODEL_RGBSDA       = 1U,
+    /* Color differences (Y', Cb, Cr) + alpha, depth and stencil */
+    KHR_DF_MODEL_YUVSDA       = 2U,
+    /* Color differences (Y', I, Q) + alpha, depth and stencil */
+    KHR_DF_MODEL_YIQSDA       = 3U,
+    /* Perceptual color (CIE L*a*b*) + alpha, depth and stencil */
+    KHR_DF_MODEL_LABSDA       = 4U,
+    /* Subtractive colors (cyan, magenta, yellow, black) + alpha */
+    KHR_DF_MODEL_CMYKA        = 5U,
+    /* Non-color coordinate data (X, Y, Z, W) */
+    KHR_DF_MODEL_XYZW         = 6U,
+    /* Hue, saturation, value, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSVA_ANG     = 7U,
+    /* Hue, saturation, lightness, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSLA_ANG     = 8U,
+    /* Hue, saturation, value, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSVA_HEX     = 9U,
+    /* Hue, saturation, lightness, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSLA_HEX     = 10U,
+    /* Lightweight approximate color difference (luma, orange, green) */
+    KHR_DF_MODEL_YCGCOA       = 11U,
+    /* ITU BT.2020 constant luminance YcCbcCrc */
+    KHR_DF_MODEL_YCCBCCRC     = 12U,
+    /* ITU BT.2100 constant intensity ICtCp */
+    KHR_DF_MODEL_ICTCP        = 13U,
+    /* CIE 1931 XYZ color coordinates (X, Y, Z) */
+    KHR_DF_MODEL_CIEXYZ       = 14U,
+    /* CIE 1931 xyY color coordinates (X, Y, Y) */
+    KHR_DF_MODEL_CIEXYY       = 15U,
+
+    /* Compressed formats start at 128. */
+    /* These compressed formats should generally have a single sample,
+       sited at the 0,0 position of the texel block. Where multiple
+       channels are used to distinguish formats, these should be cosited. */
+    /* Direct3D (and S3) compressed formats */
+    /* Note that premultiplied status is recorded separately */
+    /* DXT1 "channels" are RGB (0), Alpha (1) */
+    /* DXT1/BC1 with one channel is opaque */
+    /* DXT1/BC1 with a cosited alpha sample is transparent */
+    KHR_DF_MODEL_DXT1A         = 128U,
+    KHR_DF_MODEL_BC1A          = 128U,
+    /* DXT2/DXT3/BC2, with explicit 4-bit alpha */
+    KHR_DF_MODEL_DXT2          = 129U,
+    KHR_DF_MODEL_DXT3          = 129U,
+    KHR_DF_MODEL_BC2           = 129U,
+    /* DXT4/DXT5/BC3, with interpolated alpha */
+    KHR_DF_MODEL_DXT4          = 130U,
+    KHR_DF_MODEL_DXT5          = 130U,
+    KHR_DF_MODEL_BC3           = 130U,
+    /* BC4 - single channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC4           = 131U,
+    /* BC5 - two channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC5           = 132U,
+    /* BC6H - DX11 format for 16-bit float channels */
+    KHR_DF_MODEL_BC6H          = 133U,
+    /* BC7 - DX11 format */
+    KHR_DF_MODEL_BC7           = 134U,
+    /* Gap left for future desktop expansion */
+
+    /* Mobile compressed formats follow */
+    /* A format of ETC1 indicates that the format shall be decodable
+       by an ETC1-compliant decoder and not rely on ETC2 features */
+    KHR_DF_MODEL_ETC1          = 160U,
+    /* A format of ETC2 is permitted to use ETC2 encodings on top of
+       the baseline ETC1 specification */
+    /* The ETC2 format has channels "red", "green", "RGB" and "alpha",
+       which should be cosited samples */
+    /* Punch-through alpha can be distinguished from full alpha by
+       the plane size in bytes required for the texel block */
+    KHR_DF_MODEL_ETC2          = 161U,
+    /* Adaptive Scalable Texture Compression */
+    /* ASTC HDR vs LDR is determined by the float flag in the channel */
+    /* ASTC block size can be distinguished by texel block size */
+    KHR_DF_MODEL_ASTC          = 162U,
+    /* ETC1S is a simplified subset of ETC1 */
+    KHR_DF_MODEL_ETC1S         = 163U,
+    /* PowerVR Texture Compression */
+    KHR_DF_MODEL_PVRTC         = 164U,
+    KHR_DF_MODEL_PVRTC2        = 165U,
+    KHR_DF_MODEL_UASTC         = 166U,
+    /* Proprietary formats (ATITC, etc.) should follow */
+    KHR_DF_MODEL_MAX = 0xFFU
+} khr_df_model_e;
+
+/* Definition of channel names for each color model */
+typedef enum _khr_df_model_channels_e {
+    /* Unspecified format with nominal channel numbering */
+    KHR_DF_CHANNEL_UNSPECIFIED_0  = 0U,
+    KHR_DF_CHANNEL_UNSPECIFIED_1  = 1U,
+    KHR_DF_CHANNEL_UNSPECIFIED_2  = 2U,
+    KHR_DF_CHANNEL_UNSPECIFIED_3  = 3U,
+    KHR_DF_CHANNEL_UNSPECIFIED_4  = 4U,
+    KHR_DF_CHANNEL_UNSPECIFIED_5  = 5U,
+    KHR_DF_CHANNEL_UNSPECIFIED_6  = 6U,
+    KHR_DF_CHANNEL_UNSPECIFIED_7  = 7U,
+    KHR_DF_CHANNEL_UNSPECIFIED_8  = 8U,
+    KHR_DF_CHANNEL_UNSPECIFIED_9  = 9U,
+    KHR_DF_CHANNEL_UNSPECIFIED_10 = 10U,
+    KHR_DF_CHANNEL_UNSPECIFIED_11 = 11U,
+    KHR_DF_CHANNEL_UNSPECIFIED_12 = 12U,
+    KHR_DF_CHANNEL_UNSPECIFIED_13 = 13U,
+    KHR_DF_CHANNEL_UNSPECIFIED_14 = 14U,
+    KHR_DF_CHANNEL_UNSPECIFIED_15 = 15U,
+    /* MODEL_RGBSDA - red, green, blue, stencil, depth, alpha */
+    KHR_DF_CHANNEL_RGBSDA_RED     =  0U,
+    KHR_DF_CHANNEL_RGBSDA_R       =  0U,
+    KHR_DF_CHANNEL_RGBSDA_GREEN   =  1U,
+    KHR_DF_CHANNEL_RGBSDA_G       =  1U,
+    KHR_DF_CHANNEL_RGBSDA_BLUE    =  2U,
+    KHR_DF_CHANNEL_RGBSDA_B       =  2U,
+    KHR_DF_CHANNEL_RGBSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_RGBSDA_S       = 13U,
+    KHR_DF_CHANNEL_RGBSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_RGBSDA_D       = 14U,
+    KHR_DF_CHANNEL_RGBSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_RGBSDA_A       = 15U,
+    /* MODEL_YUVSDA - luma, Cb, Cr, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YUVSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YUVSDA_CB      =  1U,
+    KHR_DF_CHANNEL_YUVSDA_U       =  1U,
+    KHR_DF_CHANNEL_YUVSDA_CR      =  2U,
+    KHR_DF_CHANNEL_YUVSDA_V       =  2U,
+    KHR_DF_CHANNEL_YUVSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YUVSDA_S       = 13U,
+    KHR_DF_CHANNEL_YUVSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YUVSDA_D       = 14U,
+    KHR_DF_CHANNEL_YUVSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YUVSDA_A       = 15U,
+    /* MODEL_YIQSDA - luma, in-phase, quadrature, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YIQSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YIQSDA_I       =  1U,
+    KHR_DF_CHANNEL_YIQSDA_Q       =  2U,
+    KHR_DF_CHANNEL_YIQSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YIQSDA_S       = 13U,
+    KHR_DF_CHANNEL_YIQSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YIQSDA_D       = 14U,
+    KHR_DF_CHANNEL_YIQSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YIQSDA_A       = 15U,
+    /* MODEL_LABSDA - CIELAB/L*a*b* luma, red-green, blue-yellow, stencil, depth, alpha */
+    KHR_DF_CHANNEL_LABSDA_L       =  0U,
+    KHR_DF_CHANNEL_LABSDA_A       =  1U,
+    KHR_DF_CHANNEL_LABSDA_B       =  2U,
+    KHR_DF_CHANNEL_LABSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_LABSDA_S       = 13U,
+    KHR_DF_CHANNEL_LABSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_LABSDA_D       = 14U,
+    KHR_DF_CHANNEL_LABSDA_ALPHA   = 15U,
+    /* NOTE: KHR_DF_CHANNEL_LABSDA_A is not a synonym for alpha! */
+    /* MODEL_CMYKA - cyan, magenta, yellow, key/blacK, alpha */
+    KHR_DF_CHANNEL_CMYKSDA_CYAN    =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_C       =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_MAGENTA =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_M       =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_YELLOW  =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_Y       =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_KEY     =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_BLACK   =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_K       =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_CMYKSDA_A       = 15U,
+    /* MODEL_XYZW - coordinates x, y, z, w */
+    KHR_DF_CHANNEL_XYZW_X = 0U,
+    KHR_DF_CHANNEL_XYZW_Y = 1U,
+    KHR_DF_CHANNEL_XYZW_Z = 2U,
+    KHR_DF_CHANNEL_XYZW_W = 3U,
+    /* MODEL_HSVA_ANG - value (luma), saturation, hue, alpha, angular projection, conical space */
+    KHR_DF_CHANNEL_HSVA_ANG_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_ANG_A          = 15U,
+    /* MODEL_HSLA_ANG - lightness (luma), saturation, hue, alpha, angular projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_ANG_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_ANG_A          = 15U,
+    /* MODEL_HSVA_HEX - value (luma), saturation, hue, alpha, hexagonal projection, conical space */
+    KHR_DF_CHANNEL_HSVA_HEX_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_HEX_A          = 15U,
+    /* MODEL_HSLA_HEX - lightness (luma), saturation, hue, alpha, hexagonal projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_HEX_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_HEX_A          = 15U,
+    /* MODEL_YCGCOA - luma, green delta, orange delta, alpha */
+    KHR_DF_CHANNEL_YCGCOA_Y       =  0U,
+    KHR_DF_CHANNEL_YCGCOA_CG      =  1U,
+    KHR_DF_CHANNEL_YCGCOA_CO      =  2U,
+    KHR_DF_CHANNEL_YCGCOA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YCGCOA_A       = 15U,
+    /* MODEL_CIEXYZ - CIE 1931 X, Y, Z */
+    KHR_DF_CHANNEL_CIEXYZ_X = 0U,
+    KHR_DF_CHANNEL_CIEXYZ_Y = 1U,
+    KHR_DF_CHANNEL_CIEXYZ_Z = 2U,
+    /* MODEL_CIEXYY - CIE 1931 x, y, Y */
+    KHR_DF_CHANNEL_CIEXYY_X        = 0U,
+    KHR_DF_CHANNEL_CIEXYY_YCHROMA  = 1U,
+    KHR_DF_CHANNEL_CIEXYY_YLUMA    = 2U,
+
+    /* Compressed formats */
+    /* MODEL_DXT1A/MODEL_BC1A */
+    KHR_DF_CHANNEL_DXT1A_COLOR = 0U,
+    KHR_DF_CHANNEL_BC1A_COLOR  = 0U,
+    KHR_DF_CHANNEL_DXT1A_ALPHAPRESENT = 1U,
+    KHR_DF_CHANNEL_DXT1A_ALPHA = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHAPRESENT  = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHA  = 1U,
+    /* MODEL_DXT2/3/MODEL_BC2 */
+    KHR_DF_CHANNEL_DXT2_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT3_COLOR =  0U,
+    KHR_DF_CHANNEL_BC2_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT2_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT3_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC2_ALPHA  = 15U,
+    /* MODEL_DXT4/5/MODEL_BC3 */
+    KHR_DF_CHANNEL_DXT4_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT5_COLOR =  0U,
+    KHR_DF_CHANNEL_BC3_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT4_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT5_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC3_ALPHA  = 15U,
+    /* MODEL_BC4 */
+    KHR_DF_CHANNEL_BC4_DATA = 0U,
+    /* MODEL_BC5 */
+    KHR_DF_CHANNEL_BC5_RED   = 0U,
+    KHR_DF_CHANNEL_BC5_R     = 0U,
+    KHR_DF_CHANNEL_BC5_GREEN = 1U,
+    KHR_DF_CHANNEL_BC5_G     = 1U,
+    /* MODEL_BC6H */
+    KHR_DF_CHANNEL_BC6H_COLOR = 0U,
+    KHR_DF_CHANNEL_BC6H_DATA = 0U,
+    /* MODEL_BC7 */
+    KHR_DF_CHANNEL_BC7_DATA = 0U,
+    KHR_DF_CHANNEL_BC7_COLOR = 0U,
+    /* MODEL_ETC1 */
+    KHR_DF_CHANNEL_ETC1_DATA  = 0U,
+    KHR_DF_CHANNEL_ETC1_COLOR = 0U,
+    /* MODEL_ETC2 */
+    KHR_DF_CHANNEL_ETC2_RED   = 0U,
+    KHR_DF_CHANNEL_ETC2_R     = 0U,
+    KHR_DF_CHANNEL_ETC2_GREEN = 1U,
+    KHR_DF_CHANNEL_ETC2_G     = 1U,
+    KHR_DF_CHANNEL_ETC2_COLOR = 2U,
+    KHR_DF_CHANNEL_ETC2_ALPHA = 15U,
+    KHR_DF_CHANNEL_ETC2_A     = 15U,
+    /* MODEL_ASTC */
+    KHR_DF_CHANNEL_ASTC_DATA  = 0U,
+    /* MODEL_ETC1S */
+    KHR_DF_CHANNEL_ETC1S_RGB   = 0U,
+    KHR_DF_CHANNEL_ETC1S_RRR   = 3U,
+    KHR_DF_CHANNEL_ETC1S_GGG   = 4U,
+    KHR_DF_CHANNEL_ETC1S_AAA   = 15U,
+    /* MODEL_PVRTC */
+    KHR_DF_CHANNEL_PVRTC_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC_COLOR = 0U,
+    /* MODEL_PVRTC2 */
+    KHR_DF_CHANNEL_PVRTC2_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC2_COLOR = 0U,
+    /* MODEL UASTC */
+    KHR_DF_CHANNEL_UASTC_DATA  = 0U,
+    KHR_DF_CHANNEL_UASTC_RGB   = 0U,
+    KHR_DF_CHANNEL_UASTC_RGBA  = 3U,
+    KHR_DF_CHANNEL_UASTC_RRR   = 4U,
+    KHR_DF_CHANNEL_UASTC_RRRG  = 5U,
+    KHR_DF_CHANNEL_UASTC_RG    = 6U,
+
+    /* Common channel names shared by multiple formats */
+    KHR_DF_CHANNEL_COMMON_LUMA    =  0U,
+    KHR_DF_CHANNEL_COMMON_L       =  0U,
+    KHR_DF_CHANNEL_COMMON_STENCIL = 13U,
+    KHR_DF_CHANNEL_COMMON_S       = 13U,
+    KHR_DF_CHANNEL_COMMON_DEPTH   = 14U,
+    KHR_DF_CHANNEL_COMMON_D       = 14U,
+    KHR_DF_CHANNEL_COMMON_ALPHA   = 15U,
+    KHR_DF_CHANNEL_COMMON_A       = 15U
+} khr_df_model_channels_e;
+
+/* Definition of the primary colors in color coordinates.
+   This is implicitly responsible for defining the conversion
+   between RGB an YUV color spaces.
+   LAB and related absolute color models should use
+   KHR_DF_PRIMARIES_CIEXYZ. */
+typedef enum _khr_df_primaries_e {
+    /* No color primaries defined */
+    KHR_DF_PRIMARIES_UNSPECIFIED = 0U,
+    /* Color primaries of ITU-R BT.709 and sRGB */
+    KHR_DF_PRIMARIES_BT709       = 1U,
+    /* Synonym for KHR_DF_PRIMARIES_BT709 */
+    KHR_DF_PRIMARIES_SRGB        = 1U,
+    /* Color primaries of ITU-R BT.601 (625-line EBU variant) */
+    KHR_DF_PRIMARIES_BT601_EBU   = 2U,
+    /* Color primaries of ITU-R BT.601 (525-line SMPTE C variant) */
+    KHR_DF_PRIMARIES_BT601_SMPTE = 3U,
+    /* Color primaries of ITU-R BT.2020 */
+    KHR_DF_PRIMARIES_BT2020      = 4U,
+    /* CIE theoretical color coordinate space */
+    KHR_DF_PRIMARIES_CIEXYZ      = 5U,
+    /* Academy Color Encoding System primaries */
+    KHR_DF_PRIMARIES_ACES        = 6U,
+    /* Color primaries of ACEScc */
+    KHR_DF_PRIMARIES_ACESCC      = 7U,
+    /* Legacy NTSC 1953 primaries */
+    KHR_DF_PRIMARIES_NTSC1953    = 8U,
+    /* Legacy PAL 525-line primaries */
+    KHR_DF_PRIMARIES_PAL525      = 9U,
+    /* Color primaries of Display P3 */
+    KHR_DF_PRIMARIES_DISPLAYP3   = 10U,
+    /* Color primaries of Adobe RGB (1998) */
+    KHR_DF_PRIMARIES_ADOBERGB    = 11U,
+    KHR_DF_PRIMARIES_MAX         = 0xFFU
+} khr_df_primaries_e;
+
+/* Definition of the optical to digital transfer function
+   ("gamma correction"). Most transfer functions are not a pure
+   power function and also include a linear element.
+   LAB and related absolute color representations should use
+   KHR_DF_TRANSFER_UNSPECIFIED. */
+typedef enum _khr_df_transfer_e {
+    /* No transfer function defined */
+    KHR_DF_TRANSFER_UNSPECIFIED = 0U,
+    /* Linear transfer function (value proportional to intensity) */
+    KHR_DF_TRANSFER_LINEAR      = 1U,
+    /* Perceptually-linear transfer function of sRGH (~2.4) */
+    KHR_DF_TRANSFER_SRGB        = 2U,
+    /* Perceptually-linear transfer function of ITU BT.601, BT.709 and BT.2020 (~1/.45) */
+    KHR_DF_TRANSFER_ITU         = 3U,
+    /* SMTPE170M (digital NTSC) defines an alias for the ITU transfer function (~1/.45) */
+    KHR_DF_TRANSFER_SMTPE170M   = 3U,
+    /* Perceptually-linear gamma function of original NTSC (simple 2.2 gamma) */
+    KHR_DF_TRANSFER_NTSC        = 4U,
+    /* Sony S-log used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG        = 5U,
+    /* Sony S-log 2 used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG2       = 6U,
+    /* ITU BT.1886 EOTF */
+    KHR_DF_TRANSFER_BT1886      = 7U,
+    /* ITU BT.2100 HLG OETF */
+    KHR_DF_TRANSFER_HLG_OETF    = 8U,
+    /* ITU BT.2100 HLG EOTF */
+    KHR_DF_TRANSFER_HLG_EOTF    = 9U,
+    /* ITU BT.2100 PQ EOTF */
+    KHR_DF_TRANSFER_PQ_EOTF     = 10U,
+    /* ITU BT.2100 PQ OETF */
+    KHR_DF_TRANSFER_PQ_OETF     = 11U,
+    /* DCI P3 transfer function */
+    KHR_DF_TRANSFER_DCIP3       = 12U,
+    /* Legacy PAL OETF */
+    KHR_DF_TRANSFER_PAL_OETF    = 13U,
+    /* Legacy PAL 625-line EOTF */
+    KHR_DF_TRANSFER_PAL625_EOTF = 14U,
+    /* Legacy ST240 transfer function */
+    KHR_DF_TRANSFER_ST240       = 15U,
+    /* ACEScc transfer function */
+    KHR_DF_TRANSFER_ACESCC      = 16U,
+    /* ACEScct transfer function */
+    KHR_DF_TRANSFER_ACESCCT     = 17U,
+    /* Adobe RGB (1998) transfer function */
+    KHR_DF_TRANSFER_ADOBERGB    = 18U,
+    KHR_DF_TRANSFER_MAX         = 0xFFU
+} khr_df_transfer_e;
+
+typedef enum _khr_df_flags_e {
+    KHR_DF_FLAG_ALPHA_STRAIGHT      = 0U,
+    KHR_DF_FLAG_ALPHA_PREMULTIPLIED = 1U
+} khr_df_flags_e;
+
+typedef enum _khr_df_sample_datatype_qualifiers_e {
+    KHR_DF_SAMPLE_DATATYPE_LINEAR = 1U << 4U,
+    KHR_DF_SAMPLE_DATATYPE_EXPONENT = 1U << 5U,
+    KHR_DF_SAMPLE_DATATYPE_SIGNED = 1U << 6U,
+    KHR_DF_SAMPLE_DATATYPE_FLOAT = 1U << 7U
+} khr_df_sample_datatype_qualifiers_e;
+
+#endif
diff --git a/thirdparty/libktx/include/ktx.h b/thirdparty/libktx/include/ktx.h
new file mode 100644
index 00000000000..0af87f25190
--- /dev/null
+++ b/thirdparty/libktx/include/ktx.h
@@ -0,0 +1,1810 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+#ifndef KTX_H_A55A6F00956F42F3A137C11929827FE1
+#define KTX_H_A55A6F00956F42F3A137C11929827FE1
+
+/*
+ * Copyright 2010-2018 The Khronos Group, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * See the accompanying LICENSE.md for licensing details for all files in
+ * the KTX library and KTX loader tests.
+ */
+
+/**
+ * @file
+ * @~English
+ *
+ * @brief Declares the public functions and structures of the
+ *        KTX API.
+ *
+ * @author Mark Callow, Edgewise Consulting and while at HI Corporation
+ * @author Based on original work by Georg Kolling, Imagination Technology
+ *
+ * @snippet{doc} version.h API version
+ */
+
+#include <stdio.h>
+#include <stdbool.h>
+#include <sys/types.h>
+
+#include <KHR/khr_df.h>
+
+/*
+ * Don't use khrplatform.h in order not to break apps existing
+ * before these definitions were needed.
+ */
+#if defined(KHRONOS_STATIC)
+  #define KTX_API
+#elif defined(_WIN32) || defined(__CYGWIN__)
+  #if !defined(KTX_API)
+    #if __GNUC__
+      #define KTX_API __attribute__ ((dllimport))
+    #elif _MSC_VER
+      #define KTX_API __declspec(dllimport)
+    #else
+      #error "Your compiler's equivalent of dllimport is unknown"
+    #endif
+  #endif
+#elif defined(__ANDROID__)
+  #define KTX_API __attribute__((visibility("default")))
+#else
+  #define KTX_API
+#endif
+
+#if defined(_WIN32) && !defined(KHRONOS_STATIC)
+  #if !defined(KTX_APIENTRY)
+    #define KTX_APIENTRY __stdcall
+  #endif
+#else
+  #define KTX_APIENTRY
+#endif
+
+/* To avoid including <KHR/khrplatform.h> define our own types. */
+typedef unsigned char ktx_uint8_t;
+typedef bool ktx_bool_t;
+#ifdef _MSC_VER
+typedef unsigned __int16 ktx_uint16_t;
+typedef   signed __int16 ktx_int16_t;
+typedef unsigned __int32 ktx_uint32_t;
+typedef   signed __int32 ktx_int32_t;
+typedef          size_t  ktx_size_t;
+typedef unsigned __int64 ktx_uint64_t;
+typedef   signed __int64 ktx_int64_t;
+#else
+#include <stdint.h>
+typedef uint16_t ktx_uint16_t;
+typedef  int16_t ktx_int16_t;
+typedef uint32_t ktx_uint32_t;
+typedef  int32_t ktx_int32_t;
+typedef   size_t ktx_size_t;
+typedef uint64_t ktx_uint64_t;
+typedef  int64_t ktx_int64_t;
+#endif
+
+/* This will cause compilation to fail if size of uint32 != 4. */
+typedef unsigned char ktx_uint32_t_SIZE_ASSERT[sizeof(ktx_uint32_t) == 4];
+
+/*
+ * This #if allows libktx to be compiled with strict c99. It avoids
+ * compiler warnings or even errors when a gl.h is already included.
+ * "Redefinition of (type) is a c11 feature". Obviously this doesn't help if
+ * gl.h comes after. However nobody has complained about the unguarded typedefs
+ * since they were introduced so this is unlikely to be a problem in practice.
+ * Presumably everybody is using platform default compilers not c99 or else
+ * they are using C++.
+ */
+#if !defined(GL_NO_ERROR)
+  /*
+   * To avoid having to including gl.h ...
+   */
+  typedef unsigned char GLboolean;
+  typedef unsigned int GLenum;
+  typedef int GLint;
+  typedef int GLsizei;
+  typedef unsigned int GLuint;
+  typedef unsigned char GLubyte;
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @~English
+ * @brief Key string for standard writer metadata.
+ */
+#define KTX_ANIMDATA_KEY "KTXanimData"
+/**
+ * @~English
+ * @brief Key string for standard orientation metadata.
+ */
+#define KTX_ORIENTATION_KEY "KTXorientation"
+/**
+ * @~English
+ * @brief Key string for standard swizzle metadata.
+ */
+#define KTX_SWIZZLE_KEY "KTXswizzle"
+/**
+ * @~English
+ * @brief Key string for standard writer metadata.
+ */
+#define KTX_WRITER_KEY "KTXwriter"
+/**
+ * @~English
+ * @brief Key string for standard writer supercompression parameter metadata.
+ */
+#define KTX_WRITER_SCPARAMS_KEY "KTXwriterScParams"
+/**
+ * @~English
+ * @brief Standard KTX 1 format for 1D orientation value.
+ */
+#define KTX_ORIENTATION1_FMT "S=%c"
+/**
+ * @~English
+ * @brief Standard KTX 1 format for 2D orientation value.
+ */
+#define KTX_ORIENTATION2_FMT "S=%c,T=%c"
+/**
+ * @~English
+ * @brief Standard KTX 1 format for 3D orientation value.
+ */
+#define KTX_ORIENTATION3_FMT "S=%c,T=%c,R=%c"
+/**
+ * @~English
+ * @brief Required unpack alignment
+ */
+#define KTX_GL_UNPACK_ALIGNMENT 4
+
+#define KTX_TRUE  true
+#define KTX_FALSE false
+
+/**
+ * @~English
+ * @brief Error codes returned by library functions.
+ */
+typedef enum ktx_error_code_e {
+    KTX_SUCCESS = 0,         /*!< Operation was successful. */
+    KTX_FILE_DATA_ERROR,     /*!< The data in the file is inconsistent with the spec. */
+    KTX_FILE_ISPIPE,         /*!< The file is a pipe or named pipe. */
+    KTX_FILE_OPEN_FAILED,    /*!< The target file could not be opened. */
+    KTX_FILE_OVERFLOW,       /*!< The operation would exceed the max file size. */
+    KTX_FILE_READ_ERROR,     /*!< An error occurred while reading from the file. */
+    KTX_FILE_SEEK_ERROR,     /*!< An error occurred while seeking in the file. */
+    KTX_FILE_UNEXPECTED_EOF, /*!< File does not have enough data to satisfy request. */
+    KTX_FILE_WRITE_ERROR,    /*!< An error occurred while writing to the file. */
+    KTX_GL_ERROR,            /*!< GL operations resulted in an error. */
+    KTX_INVALID_OPERATION,   /*!< The operation is not allowed in the current state. */
+    KTX_INVALID_VALUE,       /*!< A parameter value was not valid */
+    KTX_NOT_FOUND,           /*!< Requested key was not found */
+    KTX_OUT_OF_MEMORY,       /*!< Not enough memory to complete the operation. */
+    KTX_TRANSCODE_FAILED,    /*!< Transcoding of block compressed texture failed. */
+    KTX_UNKNOWN_FILE_FORMAT, /*!< The file not a KTX file */
+    KTX_UNSUPPORTED_TEXTURE_TYPE, /*!< The KTX file specifies an unsupported texture type. */
+    KTX_UNSUPPORTED_FEATURE,  /*!< Feature not included in in-use library or not yet implemented. */
+    KTX_LIBRARY_NOT_LINKED,  /*!< Library dependency (OpenGL or Vulkan) not linked into application. */
+    KTX_ERROR_MAX_ENUM = KTX_LIBRARY_NOT_LINKED /*!< For safety checks. */
+} ktx_error_code_e;
+/**
+ * @deprecated
+ * @~English
+ * @brief For backward compatibility
+ */
+#define KTX_error_code ktx_error_code_e
+
+#define KTX_IDENTIFIER_REF  { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
+#define KTX_ENDIAN_REF      (0x04030201)
+#define KTX_ENDIAN_REF_REV  (0x01020304)
+#define KTX_HEADER_SIZE     (64)
+
+/**
+ * @~English
+ * @brief Result codes returned by library functions.
+ */
+ typedef enum ktx_error_code_e ktxResult;
+
+/**
+ * @class ktxHashList
+ * @~English
+ * @brief Opaque handle to a ktxHashList.
+ */
+typedef struct ktxKVListEntry* ktxHashList;
+
+typedef struct ktxStream ktxStream;
+
+#define KTX_APIENTRYP KTX_APIENTRY *
+/**
+ * @class ktxHashListEntry
+ * @~English
+ * @brief Opaque handle to an entry in a @ref ktxHashList.
+ */
+typedef struct ktxKVListEntry ktxHashListEntry;
+
+typedef enum ktxOrientationX {
+    KTX_ORIENT_X_LEFT = 'l', KTX_ORIENT_X_RIGHT = 'r'
+} ktxOrientationX;
+
+typedef enum ktxOrientationY {
+    KTX_ORIENT_Y_UP = 'u', KTX_ORIENT_Y_DOWN = 'd'
+} ktxOrientationY;
+
+typedef enum ktxOrientationZ {
+    KTX_ORIENT_Z_IN = 'i', KTX_ORIENT_Z_OUT = 'o'
+} ktxOrientationZ;
+
+typedef enum class_id {
+    ktxTexture1_c = 1,
+    ktxTexture2_c = 2
+} class_id;
+
+/**
+ * @~English
+ * @brief Struct describing the logical orientation of an image.
+ */
+struct ktxOrientation {
+    ktxOrientationX x;  /*!< Orientation in X */
+    ktxOrientationY y;  /*!< Orientation in Y */
+    ktxOrientationZ z;  /*!< Orientation in Z */
+};
+
+#define KTXTEXTURECLASSDEFN                   \
+    class_id classId;                         \
+    struct ktxTexture_vtbl* vtbl;             \
+    struct ktxTexture_vvtbl* vvtbl;           \
+    struct ktxTexture_protected* _protected;  \
+    ktx_bool_t   isArray;                     \
+    ktx_bool_t   isCubemap;                   \
+    ktx_bool_t   isCompressed;                \
+    ktx_bool_t   generateMipmaps;             \
+    ktx_uint32_t baseWidth;                   \
+    ktx_uint32_t baseHeight;                  \
+    ktx_uint32_t baseDepth;                   \
+    ktx_uint32_t numDimensions;               \
+    ktx_uint32_t numLevels;                   \
+    ktx_uint32_t numLayers;                   \
+    ktx_uint32_t numFaces;                    \
+    struct ktxOrientation orientation;        \
+    ktxHashList  kvDataHead;                  \
+    ktx_uint32_t kvDataLen;                   \
+    ktx_uint8_t* kvData;                      \
+    ktx_size_t dataSize;                      \
+    ktx_uint8_t* pData;
+
+
+/**
+ * @class ktxTexture
+ * @~English
+ * @brief Base class representing a texture.
+ *
+ * ktxTextures should be created only by one of the provided
+ * functions and these fields should be considered read-only.
+ */
+typedef struct ktxTexture {
+    KTXTEXTURECLASSDEFN
+} ktxTexture;
+/**
+ * @typedef ktxTexture::classId
+ * @~English
+ * @brief Identify the class type.
+ *
+ * Since there are no public ktxTexture constructors, this can only have
+ * values of ktxTexture1_c or ktxTexture2_c.
+ */
+/**
+ * @typedef ktxTexture::vtbl
+ * @~English
+ * @brief Pointer to the class's vtble.
+ */
+/**
+ * @typedef ktxTexture::vvtbl
+ * @~English
+ * @brief Pointer to the class's vtble for Vulkan functions.
+ *
+ * A separate vtble is used so this header does not need to include vulkan.h.
+ */
+/**
+ * @typedef ktxTexture::_protected
+ * @~English
+ * @brief Opaque pointer to the class's protected variables.
+ */
+/**
+ * @typedef ktxTexture::isArray
+ * @~English
+ *
+ * KTX_TRUE if the texture is an array texture, i.e,
+ * a GL_TEXTURE_*_ARRAY target is to be used.
+ */
+/**
+ * @typedef ktxTexture::isCubemap
+ * @~English
+ *
+ * KTX_TRUE if the texture is a cubemap or cubemap array.
+ */
+/**
+ * @typedef ktxTexture::isCubemap
+ * @~English
+ *
+ * KTX_TRUE if the texture's format is a block compressed format.
+ */
+/**
+ * @typedef ktxTexture::generateMipmaps
+ * @~English
+ *
+ * KTX_TRUE if mipmaps should be generated for the texture by
+ * ktxTexture_GLUpload() or ktxTexture_VkUpload().
+ */
+/**n
+ * @typedef ktxTexture::baseWidth
+ * @~English
+ * @brief Width of the texture's base level.
+ */
+/**
+ * @typedef ktxTexture::baseHeight
+ * @~English
+ * @brief Height of the texture's base level.
+ */
+/**
+ * @typedef ktxTexture::baseDepth
+ * @~English
+ * @brief Depth of the texture's base level.
+ */
+/**
+ * @typedef ktxTexture::numDimensions
+ * @~English
+ * @brief Number of dimensions in the texture: 1, 2 or 3.
+ */
+/**
+ * @typedef ktxTexture::numLevels
+ * @~English
+ * @brief Number of mip levels in the texture.
+ *
+ * Must be 1, if @c generateMipmaps is KTX_TRUE. Can be less than a
+ * full pyramid but always starts at the base level.
+ */
+/**
+ * @typedef ktxTexture::numLevels
+ * @~English
+ * @brief Number of array layers in the texture.
+ */
+/**
+ * @typedef ktxTexture::numFaces
+ * @~English
+ * @brief Number of faces: 6 for cube maps, 1 otherwise.
+ */
+/**
+ * @typedef ktxTexture::orientation
+ * @~English
+ * @brief Describes the logical orientation of the images in each dimension.
+ *
+ * ktxOrientationX for X, ktxOrientationY for Y and ktxOrientationZ for Z.
+ */
+/**
+ * @typedef ktxTexture::kvDataHead
+ * @~English
+ * @brief Head of the hash list of metadata.
+ */
+/**
+ * @typedef ktxTexture::kvDataLen
+ * @~English
+ * @brief Length of the metadata, if it has been extracted in its raw form,
+ *       otherwise 0.
+ */
+/**
+ * @typedef ktxTexture::kvData
+ * @~English
+ * @brief Pointer to the metadata, if it has been extracted in its raw form,
+ *       otherwise NULL.
+ */
+/**
+ * @typedef ktxTexture::dataSize
+ * @~English
+ * @brief Byte length of the texture's uncompressed image data.
+ */
+/**
+ * @typedef ktxTexture::pData
+ * @~English
+ * @brief Pointer to the start of the image data.
+ */
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Signature of function called by the <tt>ktxTexture_Iterate*</tt>
+ *        functions to receive image data.
+ *
+ * The function parameters are used to pass values which change for each image.
+ * Obtain values which are uniform across all images from the @c ktxTexture
+ * object.
+ *
+ * @param [in] miplevel        MIP level from 0 to the max level which is
+ *                             dependent on the texture size.
+ * @param [in] face            usually 0; for cube maps, one of the 6 cube
+ *                             faces in the order +X, -X, +Y, -Y, +Z, -Z,
+ *                             0 to 5.
+ * @param [in] width           width of the image.
+ * @param [in] height          height of the image or, for 1D textures
+ *                             textures, 1.
+ * @param [in] depth           depth of the image or, for 1D & 2D
+ *                             textures, 1.
+ * @param [in] faceLodSize     number of bytes of data pointed at by
+ *                             @p pixels.
+ * @param [in] pixels          pointer to the image data.
+ * @param [in,out] userdata    pointer for the application to pass data to and
+ *                             from the callback function.
+ */
+
+typedef KTX_error_code
+    (* PFNKTXITERCB)(int miplevel, int face,
+                     int width, int height, int depth,
+                     ktx_uint64_t faceLodSize,
+                     void* pixels, void* userdata);
+
+/* Don't use KTX_APIENTRYP to avoid a Doxygen bug. */
+typedef void (KTX_APIENTRY* PFNKTEXDESTROY)(ktxTexture* This);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXGETIMAGEOFFSET)(ktxTexture* This, ktx_uint32_t level,
+                                          ktx_uint32_t layer,
+                                          ktx_uint32_t faceSlice,
+                                          ktx_size_t* pOffset);
+typedef ktx_size_t
+    (KTX_APIENTRY* PFNKTEXGETDATASIZEUNCOMPRESSED)(ktxTexture* This);
+typedef ktx_size_t
+    (KTX_APIENTRY* PFNKTEXGETIMAGESIZE)(ktxTexture* This, ktx_uint32_t level);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXITERATELEVELS)(ktxTexture* This, PFNKTXITERCB iterCb,
+                                         void* userdata);
+
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXITERATELOADLEVELFACES)(ktxTexture* This,
+                                                 PFNKTXITERCB iterCb,
+                                                 void* userdata);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXLOADIMAGEDATA)(ktxTexture* This,
+                                         ktx_uint8_t* pBuffer,
+                                         ktx_size_t bufSize);
+typedef ktx_bool_t
+    (KTX_APIENTRY* PFNKTEXNEEDSTRANSCODING)(ktxTexture* This);
+
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXSETIMAGEFROMMEMORY)(ktxTexture* This,
+                                              ktx_uint32_t level,
+                                              ktx_uint32_t layer,
+                                              ktx_uint32_t faceSlice,
+                                              const ktx_uint8_t* src,
+                                              ktx_size_t srcSize);
+
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXSETIMAGEFROMSTDIOSTREAM)(ktxTexture* This,
+                                                   ktx_uint32_t level,
+                                                   ktx_uint32_t layer,
+                                                   ktx_uint32_t faceSlice,
+                                                   FILE* src, ktx_size_t srcSize);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXWRITETOSTDIOSTREAM)(ktxTexture* This, FILE* dstsstr);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXWRITETONAMEDFILE)(ktxTexture* This,
+                                            const char* const dstname);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXWRITETOMEMORY)(ktxTexture* This,
+                                         ktx_uint8_t** bytes, ktx_size_t* size);
+typedef KTX_error_code
+    (KTX_APIENTRY* PFNKTEXWRITETOSTREAM)(ktxTexture* This,
+                                         ktxStream* dststr);
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Table of virtual ktxTexture methods.
+ */
+ struct ktxTexture_vtbl {
+    PFNKTEXDESTROY Destroy;
+    PFNKTEXGETIMAGEOFFSET GetImageOffset;
+    PFNKTEXGETDATASIZEUNCOMPRESSED GetDataSizeUncompressed;
+    PFNKTEXGETIMAGESIZE GetImageSize;
+    PFNKTEXITERATELEVELS IterateLevels;
+    PFNKTEXITERATELOADLEVELFACES IterateLoadLevelFaces;
+    PFNKTEXNEEDSTRANSCODING NeedsTranscoding;
+    PFNKTEXLOADIMAGEDATA LoadImageData;
+    PFNKTEXSETIMAGEFROMMEMORY SetImageFromMemory;
+    PFNKTEXSETIMAGEFROMSTDIOSTREAM SetImageFromStdioStream;
+    PFNKTEXWRITETOSTDIOSTREAM WriteToStdioStream;
+    PFNKTEXWRITETONAMEDFILE WriteToNamedFile;
+    PFNKTEXWRITETOMEMORY WriteToMemory;
+    PFNKTEXWRITETOSTREAM WriteToStream;
+};
+
+/****************************************************************
+ * Macros to give some backward compatibility to the previous API
+ ****************************************************************/
+
+/**
+ * @~English
+ * @brief Helper for calling the Destroy virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_Destroy
+ */
+#define ktxTexture_Destroy(This) (This)->vtbl->Destroy(This)
+
+/**
+ * @~English
+ * @brief Helper for calling the GetImageOffset virtual method of a
+ *        ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_GetImageOffset
+ */
+#define ktxTexture_GetImageOffset(This, level, layer, faceSlice, pOffset) \
+            (This)->vtbl->GetImageOffset(This, level, layer, faceSlice, pOffset)
+
+/**
+ * @~English
+ * @brief Helper for calling the GetDataSizeUncompressed virtual method of a ktxTexture.
+ *
+ * For a ktxTexture1 this will always return the value of This->dataSize.
+ *
+ * @copydetails ktxTexture2.ktxTexture2_GetDataSizeUncompressed
+ */
+#define ktxTexture_GetDataSizeUncompressed(This) \
+                                (This)->vtbl->GetDataSizeUncompressed(This)
+
+/**
+ * @~English
+ * @brief Helper for calling the GetImageSize virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_GetImageSize
+ */
+#define ktxTexture_GetImageSize(This, level) \
+            (This)->vtbl->GetImageSize(This, level)
+
+/**
+ * @~English
+ * @brief Helper for calling the IterateLevels virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_IterateLevels
+ */
+#define ktxTexture_IterateLevels(This, iterCb, userdata) \
+                            (This)->vtbl->IterateLevels(This, iterCb, userdata)
+
+/**
+ * @~English
+ * @brief Helper for calling the IterateLoadLevelFaces virtual method of a
+ * ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_IterateLoadLevelFaces
+ */
+ #define ktxTexture_IterateLoadLevelFaces(This, iterCb, userdata) \
+                    (This)->vtbl->IterateLoadLevelFaces(This, iterCb, userdata)
+
+/**
+ * @~English
+ * @brief Helper for calling the LoadImageData virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_LoadImageData
+ */
+#define ktxTexture_LoadImageData(This, pBuffer, bufSize) \
+                    (This)->vtbl->LoadImageData(This, pBuffer, bufSize)
+
+/**
+ * @~English
+ * @brief Helper for calling the NeedsTranscoding virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_NeedsTranscoding
+ */
+#define ktxTexture_NeedsTranscoding(This) (This)->vtbl->NeedsTranscoding(This)
+
+/**
+ * @~English
+ * @brief Helper for calling the SetImageFromMemory virtual method of a
+ *        ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_SetImageFromMemory
+ */
+#define ktxTexture_SetImageFromMemory(This, level, layer, faceSlice, \
+                                      src, srcSize)                  \
+    (This)->vtbl->SetImageFromMemory(This, level, layer, faceSlice, src, srcSize)
+
+/**
+ * @~English
+ * @brief Helper for calling the SetImageFromStdioStream virtual method of a
+ *        ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_SetImageFromStdioStream
+ */
+#define ktxTexture_SetImageFromStdioStream(This, level, layer, faceSlice, \
+                                           src, srcSize)                  \
+    (This)->vtbl->SetImageFromStdioStream(This, level, layer, faceSlice,  \
+                                        src, srcSize)
+
+/**
+ * @~English
+ * @brief Helper for calling the WriteToStdioStream virtual method of a
+ *        ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_WriteToStdioStream
+ */
+#define ktxTexture_WriteToStdioStream(This, dstsstr) \
+                                (This)->vtbl->WriteToStdioStream(This, dstsstr)
+
+/**
+ * @~English
+ * @brief Helper for calling the WriteToNamedfile virtual method of a
+ *        ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_WriteToNamedFile
+ */
+#define ktxTexture_WriteToNamedFile(This, dstname) \
+                                (This)->vtbl->WriteToNamedFile(This, dstname)
+
+/**
+ * @~English
+ * @brief Helper for calling the WriteToMemory virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_WriteToMemory
+ */
+#define ktxTexture_WriteToMemory(This, ppDstBytes, pSize) \
+                  (This)->vtbl->WriteToMemory(This, ppDstBytes, pSize)
+
+/**
+ * @~English
+ * @brief Helper for calling the WriteToStream virtual method of a ktxTexture.
+ * @copydoc ktxTexture2.ktxTexture2_WriteToStream
+ */
+#define ktxTexture_WriteToStream(This, dststr) \
+                  (This)->vtbl->WriteToStream(This, dststr)
+
+
+/**
+ * @class ktxTexture1
+ * @~English
+ * @brief Class representing a KTX version 1 format texture.
+ *
+ * ktxTextures should be created only by one of the ktxTexture_Create*
+ * functions and these fields should be considered read-only.
+ */
+typedef struct ktxTexture1 {
+    KTXTEXTURECLASSDEFN
+    ktx_uint32_t glFormat; /*!< Format of the texture data, e.g., GL_RGB. */
+    ktx_uint32_t glInternalformat; /*!< Internal format of the texture data,
+                                        e.g., GL_RGB8. */
+    ktx_uint32_t glBaseInternalformat; /*!< Base format of the texture data,
+                                            e.g., GL_RGB. */
+    ktx_uint32_t glType; /*!< Type of the texture data, e.g, GL_UNSIGNED_BYTE.*/
+    struct ktxTexture1_private* _private; /*!< Private data. */
+} ktxTexture1;
+
+/*===========================================================*
+* KTX format version 2                                      *
+*===========================================================*/
+
+/**
+ * @~English
+ * @brief Enumerators identifying the supercompression scheme.
+ */
+typedef enum ktxSupercmpScheme {
+    KTX_SS_NONE = 0,            /*!< No supercompression. */
+    KTX_SS_BASIS_LZ = 1,        /*!< Basis LZ supercompression. */
+    KTX_SS_ZSTD = 2,            /*!< ZStd supercompression. */
+    KTX_SS_BEGIN_RANGE = KTX_SS_NONE,
+    KTX_SS_END_RANGE = KTX_SS_ZSTD,
+    KTX_SS_BEGIN_VENDOR_RANGE = 0x10000,
+    KTX_SS_END_VENDOR_RANGE = 0x1ffff,
+    KTX_SS_BEGIN_RESERVED = 0x20000,
+    KTX_SUPERCOMPRESSION_BASIS = KTX_SS_BASIS_LZ,
+        /*!< @deprecated Will be removed before v4 release. Use  KTX_SS_BASIS_LZ instead. */
+    KTX_SUPERCOMPRESSION_ZSTD = KTX_SS_ZSTD
+        /*!< @deprecated Will be removed before v4 release. Use  KTX_SS_ZSTD instead. */
+} ktxSupercmpScheme;
+
+/**
+ * @class ktxTexture2
+ * @~English
+ * @brief Class representing a KTX version 2 format texture.
+ *
+ * ktxTextures should be created only by one of the ktxTexture_Create*
+ * functions and these fields should be considered read-only.
+ */
+typedef struct ktxTexture2 {
+    KTXTEXTURECLASSDEFN
+    ktx_uint32_t  vkFormat;
+    ktx_uint32_t* pDfd;
+    ktxSupercmpScheme supercompressionScheme;
+    ktx_bool_t isVideo;
+    ktx_uint32_t duration;
+    ktx_uint32_t timescale;
+    ktx_uint32_t loopcount;
+    struct ktxTexture2_private* _private;  /*!< Private data. */
+} ktxTexture2;
+
+#define ktxTexture(t) ((ktxTexture*)t)
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Structure for passing texture information to ktxTexture1_Create() and
+ *        ktxTexture2_Create().
+ *
+ * @sa ktxTexture1_Create() and ktxTexture2_Create().
+ */
+typedef struct
+{
+    ktx_uint32_t glInternalformat; /*!< Internal format for the texture, e.g.,
+                                        GL_RGB8. Ignored when creating a
+                                        ktxTexture2. */
+    ktx_uint32_t vkFormat;   /*!< VkFormat for texture. Ignored when creating a
+                                  ktxTexture1. */
+    ktx_uint32_t* pDfd;      /*!< Pointer to DFD. Used only when creating a
+                                  ktxTexture2 and only if vkFormat is
+                                  VK_FORMAT_UNDEFINED. */
+    ktx_uint32_t baseWidth;  /*!< Width of the base level of the texture. */
+    ktx_uint32_t baseHeight; /*!< Height of the base level of the texture. */
+    ktx_uint32_t baseDepth;  /*!< Depth of the base level of the texture. */
+    ktx_uint32_t numDimensions; /*!< Number of dimensions in the texture, 1, 2
+                                     or 3. */
+    ktx_uint32_t numLevels; /*!< Number of mip levels in the texture. Should be
+                                 1 if @c generateMipmaps is KTX_TRUE; */
+    ktx_uint32_t numLayers; /*!< Number of array layers in the texture. */
+    ktx_uint32_t numFaces;  /*!< Number of faces: 6 for cube maps, 1 otherwise. */
+    ktx_bool_t   isArray;  /*!< Set to KTX_TRUE if the texture is to be an
+                                array texture. Means OpenGL will use a
+                                GL_TEXTURE_*_ARRAY target. */
+    ktx_bool_t   generateMipmaps; /*!< Set to KTX_TRUE if mipmaps should be
+                                       generated for the texture when loading
+                                       into a 3D API. */
+} ktxTextureCreateInfo;
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Enum for requesting, or not, allocation of storage for images.
+ *
+ * @sa ktxTexture1_Create() and ktxTexture2_Create().
+ */
+typedef enum {
+    KTX_TEXTURE_CREATE_NO_STORAGE = 0,  /*!< Don't allocate any image storage. */
+    KTX_TEXTURE_CREATE_ALLOC_STORAGE = 1 /*!< Allocate image storage. */
+} ktxTextureCreateStorageEnum;
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Flags for requesting services during creation.
+ *
+ * @sa ktxTexture_CreateFrom*
+ */
+enum ktxTextureCreateFlagBits {
+    KTX_TEXTURE_CREATE_NO_FLAGS = 0x00,
+    KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT = 0x01,
+                                   /*!< Load the images from the KTX source. */
+    KTX_TEXTURE_CREATE_RAW_KVDATA_BIT = 0x02,
+                                   /*!< Load the raw key-value data instead of
+                                        creating a @c ktxHashList from it. */
+    KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT = 0x04
+                                   /*!< Skip any key-value data. This overrides
+                                        the RAW_KVDATA_BIT. */
+};
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Type for TextureCreateFlags parameters.
+ *
+ * @sa ktxTexture_CreateFrom*()
+ */
+typedef ktx_uint32_t ktxTextureCreateFlags;
+
+/*===========================================================*
+* ktxStream
+*===========================================================*/
+
+/*
+ * This is unsigned to allow ktxmemstreams to use the
+ * full amount of memory available. Platforms will
+ * limit the size of ktxfilestreams to, e.g, MAX_LONG
+ * on 32-bit and ktxfilestreams raises errors if
+ * offset values exceed the limits. This choice may
+ * need to be revisited if we ever start needing -ve
+ * offsets.
+ *
+ * Should the 2GB file size handling limit on 32-bit
+ * platforms become a problem, ktxfilestream will have
+ * to be changed to explicitly handle large files by
+ * using the 64-bit stream functions.
+ */
+#if defined(_MSC_VER) && defined(_WIN64)
+  typedef unsigned __int64 ktx_off_t;
+#else
+  typedef   off_t ktx_off_t;
+#endif
+typedef struct ktxMem ktxMem;
+typedef struct ktxStream ktxStream;
+
+enum streamType { eStreamTypeFile = 1, eStreamTypeMemory = 2, eStreamTypeCustom = 3 };
+
+/**
+ * @~English
+ * @brief type for a pointer to a stream reading function
+ */
+typedef KTX_error_code (*ktxStream_read)(ktxStream* str, void* dst,
+                                         const ktx_size_t count);
+/**
+ * @~English
+ * @brief type for a pointer to a stream skipping function
+ */
+typedef KTX_error_code (*ktxStream_skip)(ktxStream* str,
+                                         const ktx_size_t count);
+
+/**
+ * @~English
+ * @brief type for a pointer to a stream writing function
+ */
+typedef KTX_error_code (*ktxStream_write)(ktxStream* str, const void *src,
+                                          const ktx_size_t size,
+                                          const ktx_size_t count);
+
+/**
+ * @~English
+ * @brief type for a pointer to a stream position query function
+ */
+typedef KTX_error_code (*ktxStream_getpos)(ktxStream* str, ktx_off_t* const offset);
+
+/**
+ * @~English
+ * @brief type for a pointer to a stream position query function
+ */
+typedef KTX_error_code (*ktxStream_setpos)(ktxStream* str, const ktx_off_t offset);
+
+/**
+ * @~English
+ * @brief type for a pointer to a stream size query function
+ */
+typedef KTX_error_code (*ktxStream_getsize)(ktxStream* str, ktx_size_t* const size);
+
+/**
+ * @~English
+ * @brief Destruct a stream
+ */
+typedef void (*ktxStream_destruct)(ktxStream* str);
+
+/**
+ * @~English
+ *
+ * @brief Interface of ktxStream.
+ *
+ * @author Maksim Kolesin
+ * @author Georg Kolling, Imagination Technology
+ * @author Mark Callow, HI Corporation
+ */
+struct ktxStream
+{
+    ktxStream_read read;   /*!< pointer to function for reading bytes. */
+    ktxStream_skip skip;   /*!< pointer to function for skipping bytes. */
+    ktxStream_write write; /*!< pointer to function for writing bytes. */
+    ktxStream_getpos getpos; /*!< pointer to function for getting current position in stream. */
+    ktxStream_setpos setpos; /*!< pointer to function for setting current position in stream. */
+    ktxStream_getsize getsize; /*!< pointer to function for querying size. */
+    ktxStream_destruct destruct; /*!< destruct the stream. */
+
+    enum streamType type;
+    union {
+        FILE* file;        /**< a stdio FILE pointer for a ktxFileStream. */
+        ktxMem* mem;       /**< a pointer to a ktxMem struct for a ktxMemStream. */
+        struct
+        {
+            void* address;           /**< pointer to the data. */
+            void* allocatorAddress;  /**< pointer to a memory allocator. */
+            ktx_size_t size;         /**< size of the data. */
+        } custom_ptr;      /**< pointer to a struct for custom streams. */
+    } data;                /**< pointer to the stream data. */
+    ktx_off_t readpos;     /**< used by FileStream for stdin. */
+    ktx_bool_t closeOnDestruct; /**< Close FILE* or dispose of memory on destruct. */
+};
+
+/*
+ * See the implementation files for the full documentation of the following
+ * functions.
+ */
+
+/*
+ * These four create a ktxTexture1 or ktxTexture2 according to the data
+ * header, and return a pointer to the base ktxTexture class.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_CreateFromStdioStream(FILE* stdioStream,
+                                 ktxTextureCreateFlags createFlags,
+                                 ktxTexture** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_CreateFromNamedFile(const char* const filename,
+                               ktxTextureCreateFlags createFlags,
+                               ktxTexture** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_CreateFromStream(ktxStream* stream,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture** newTex);
+
+/*
+ * Returns a pointer to the image data of a ktxTexture object.
+ */
+KTX_API ktx_uint8_t* KTX_APIENTRY
+ktxTexture_GetData(ktxTexture* This);
+
+/*
+ * Returns the pitch of a row of an image at the specified level.
+ * Similar to the rowPitch in a VkSubResourceLayout.
+ */
+KTX_API ktx_uint32_t KTX_APIENTRY
+ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level);
+
+ /*
+  * Return the element size of the texture's images.
+  */
+KTX_API ktx_uint32_t KTX_APIENTRY
+ktxTexture_GetElementSize(ktxTexture* This);
+
+/*
+ * Returns the size of all the image data of a ktxTexture object in bytes.
+ */
+KTX_API ktx_size_t KTX_APIENTRY
+ktxTexture_GetDataSize(ktxTexture* This);
+
+/* Uploads a texture to OpenGL {,ES}. */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_GLUpload(ktxTexture* This, GLuint* pTexture, GLenum* pTarget,
+                    GLenum* pGlerror);
+
+/*
+ * Iterate over the levels or faces in a ktxTexture object.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb,
+                             void* userdata);
+/*
+ * Create a new ktxTexture1.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_Create(ktxTextureCreateInfo* createInfo,
+                   ktxTextureCreateStorageEnum storageAllocation,
+                   ktxTexture1** newTex);
+
+/*
+ * These four create a ktxTexture1 provided the data is in KTX format.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_CreateFromStdioStream(FILE* stdioStream,
+                                 ktxTextureCreateFlags createFlags,
+                                 ktxTexture1** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_CreateFromNamedFile(const char* const filename,
+                               ktxTextureCreateFlags createFlags,
+                               ktxTexture1** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture1** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_CreateFromStream(ktxStream* stream,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture1** newTex);
+
+KTX_API ktx_bool_t KTX_APIENTRY
+ktxTexture1_NeedsTranscoding(ktxTexture1* This);
+
+/*
+ * Write a ktxTexture object to a stdio stream in KTX format.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_WriteKTX2ToStdioStream(ktxTexture1* This, FILE* dstsstr);
+
+/*
+ * Write a ktxTexture object to a named file in KTX format.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_WriteKTX2ToNamedFile(ktxTexture1* This, const char* const dstname);
+
+/*
+ * Write a ktxTexture object to a block of memory in KTX format.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_WriteKTX2ToMemory(ktxTexture1* This,
+                             ktx_uint8_t** bytes, ktx_size_t* size);
+
+/*
+ * Write a ktxTexture object to a ktxStream in KTX format.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_WriteKTX2ToStream(ktxTexture1* This, ktxStream *dststr);
+
+/*
+ * Create a new ktxTexture2.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_Create(ktxTextureCreateInfo* createInfo,
+                   ktxTextureCreateStorageEnum storageAllocation,
+                   ktxTexture2** newTex);
+
+/*
+ * Create a new ktxTexture2 as a copy of an existing texture.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex);
+
+ /*
+  * These four create a ktxTexture2 provided the data is in KTX2 format.
+  */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CreateFromStdioStream(FILE* stdioStream,
+                                 ktxTextureCreateFlags createFlags,
+                                 ktxTexture2** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CreateFromNamedFile(const char* const filename,
+                               ktxTextureCreateFlags createFlags,
+                               ktxTexture2** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture2** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CreateFromStream(ktxStream* stream,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture2** newTex);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CompressBasis(ktxTexture2* This, ktx_uint32_t quality);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_DeflateZstd(ktxTexture2* This, ktx_uint32_t level);
+
+KTX_API void KTX_APIENTRY
+ktxTexture2_GetComponentInfo(ktxTexture2* This, ktx_uint32_t* numComponents,
+                             ktx_uint32_t* componentByteLength);
+
+KTX_API ktx_uint32_t KTX_APIENTRY
+ktxTexture2_GetNumComponents(ktxTexture2* This);
+
+KTX_API khr_df_transfer_e KTX_APIENTRY
+ktxTexture2_GetOETF_e(ktxTexture2* This);
+
+// For backward compatibility
+KTX_API ktx_uint32_t KTX_APIENTRY
+ktxTexture2_GetOETF(ktxTexture2* This);
+
+KTX_API khr_df_model_e KTX_APIENTRY
+ktxTexture2_GetColorModel_e(ktxTexture2* This);
+
+KTX_API ktx_bool_t KTX_APIENTRY
+ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This);
+
+KTX_API ktx_bool_t KTX_APIENTRY
+ktxTexture2_NeedsTranscoding(ktxTexture2* This);
+
+/**
+ * @~English
+ * @brief Flags specifiying UASTC encoding options.
+ */
+typedef enum ktx_pack_uastc_flag_bits_e {
+    KTX_PACK_UASTC_LEVEL_FASTEST  = 0,
+        /*!< Fastest compression. 43.45dB. */
+    KTX_PACK_UASTC_LEVEL_FASTER   = 1,
+        /*!< Faster compression. 46.49dB. */
+    KTX_PACK_UASTC_LEVEL_DEFAULT  = 2,
+        /*!< Default compression. 47.47dB. */
+    KTX_PACK_UASTC_LEVEL_SLOWER   = 3,
+        /*!< Slower compression. 48.01dB. */
+    KTX_PACK_UASTC_LEVEL_VERYSLOW = 4,
+        /*!< Very slow compression. 48.24dB. */
+    KTX_PACK_UASTC_MAX_LEVEL = KTX_PACK_UASTC_LEVEL_VERYSLOW,
+        /*!< Maximum supported quality level. */
+    KTX_PACK_UASTC_LEVEL_MASK     = 0xF,
+        /*!< Mask to extract the level from the other bits. */
+    KTX_PACK_UASTC_FAVOR_UASTC_ERROR = 8,
+        /*!< Optimize for lowest UASTC error. */
+    KTX_PACK_UASTC_FAVOR_BC7_ERROR = 16,
+        /*!< Optimize for lowest BC7 error. */
+    KTX_PACK_UASTC_ETC1_FASTER_HINTS = 64,
+        /*!< Optimize for faster transcoding to ETC1. */
+    KTX_PACK_UASTC_ETC1_FASTEST_HINTS = 128,
+        /*!< Optimize for fastest transcoding to ETC1. */
+    KTX_PACK_UASTC__ETC1_DISABLE_FLIP_AND_INDIVIDUAL = 256
+        /*!< Not documented in BasisU code. */
+} ktx_pack_uastc_flag_bits_e;
+typedef ktx_uint32_t ktx_pack_uastc_flags;
+
+/**
+ * @~English
+ * @brief Options specifiying ASTC encoding quality levels.
+ */
+typedef enum ktx_pack_astc_quality_levels_e {
+    KTX_PACK_ASTC_QUALITY_LEVEL_FASTEST  = 0,
+        /*!< Fastest compression. */
+    KTX_PACK_ASTC_QUALITY_LEVEL_FAST   = 10,
+        /*!< Fast compression. */
+    KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM   = 60,
+        /*!< Medium compression. */
+    KTX_PACK_ASTC_QUALITY_LEVEL_THOROUGH   = 98,
+        /*!< Slower compression. */
+    KTX_PACK_ASTC_QUALITY_LEVEL_EXHAUSTIVE = 100,
+        /*!< Very slow compression. */
+    KTX_PACK_ASTC_QUALITY_LEVEL_MAX = KTX_PACK_ASTC_QUALITY_LEVEL_EXHAUSTIVE,
+        /*!< Maximum supported quality level. */
+} ktx_pack_astc_quality_levels_e;
+
+/**
+ * @~English
+ * @brief Options specifiying ASTC encoding block dimensions
+ */
+typedef enum ktx_pack_astc_block_dimension_e {
+    // 2D formats
+    KTX_PACK_ASTC_BLOCK_DIMENSION_4x4,                    //: 8.00 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_5x4,                    //: 6.40 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_5x5,                    //: 5.12 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_6x5,                    //: 4.27 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_6x6,                    //: 3.56 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_8x5,                    //: 3.20 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_8x6,                    //: 2.67 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_10x5,                   //: 2.56 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_10x6,                   //: 2.13 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_8x8,                    //: 2.00 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_10x8,                   //: 1.60 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_10x10,                  //: 1.28 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_12x10,                  //: 1.07 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_12x12,                  //: 0.89 bpp
+    // 3D formats
+    KTX_PACK_ASTC_BLOCK_DIMENSION_3x3x3,                  //: 4.74 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_4x3x3,                  //: 3.56 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_4x4x3,                  //: 2.67 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_4x4x4,                  //: 2.00 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_5x4x4,                  //: 1.60 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_5x5x4,                  //: 1.28 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_5x5x5,                  //: 1.02 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_6x5x5,                  //: 0.85 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x5,                  //: 0.71 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x6,                  //: 0.59 bpp
+    KTX_PACK_ASTC_BLOCK_DIMENSION_MAX = KTX_PACK_ASTC_BLOCK_DIMENSION_6x6x6
+        /*!< Maximum supported blocks. */
+} ktx_pack_astc_block_dimension_e;
+
+/**
+ * @~English
+ * @brief Options specifying ASTC encoder profile mode
+ *        This and function is used later to derive the profile.
+ */
+typedef enum ktx_pack_astc_encoder_mode_e {
+    KTX_PACK_ASTC_ENCODER_MODE_DEFAULT,
+    KTX_PACK_ASTC_ENCODER_MODE_LDR,
+    KTX_PACK_ASTC_ENCODER_MODE_HDR,
+    KTX_PACK_ASTC_ENCODER_MODE_MAX = KTX_PACK_ASTC_ENCODER_MODE_HDR
+} ktx_pack_astc_encoder_mode_e;
+
+extern KTX_API const ktx_uint32_t KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL;
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Structure for passing extended parameters to
+ *        ktxTexture_CompressAstc.
+ *
+ * Passing a struct initialized to 0 (e.g. " = {0};") will use blockDimension
+ * 4x4, mode LDR and qualityLevel FASTEST. Setting qualityLevel to
+ * KTX_PACK_ASTC_QUALITY_LEVEL_MEDIUM is recommended.
+ */
+typedef struct ktxAstcParams {
+    ktx_uint32_t structSize;
+        /*!< Size of this struct. Used so library can tell which version
+             of struct is being passed.
+         */
+
+    ktx_bool_t verbose;
+        /*!< If true, prints Astc encoder operation details to
+             @c stdout. Not recommended for GUI apps.
+         */
+
+    ktx_uint32_t threadCount;
+        /*!< Number of threads used for compression. Default is 1.
+         */
+
+    /* astcenc params */
+    ktx_uint32_t blockDimension;
+        /*!< Combinations of block dimensions that astcenc supports
+          i.e. 6x6, 8x8, 6x5 etc
+         */
+
+    ktx_uint32_t mode;
+        /*!< Can be {ldr/hdr} from astcenc
+         */
+
+    ktx_uint32_t qualityLevel;
+        /*!< astcenc supports -fastest, -fast, -medium, -thorough, -exhaustive
+         */
+
+    ktx_bool_t normalMap;
+        /*!< Tunes codec parameters for better quality on normal maps
+          In this mode normals are compressed to X,Y components
+          Discarding Z component, reader will need to generate Z
+          component in shaders.
+         */
+
+    ktx_bool_t perceptual;
+        /*!< The codec should optimize for perceptual error, instead of direct
+           RMS error. This aims to improves perceived image quality, but
+           typically lowers the measured PSNR score. Perceptual methods are
+           currently only available for normal maps and RGB color data.
+         */
+
+    char inputSwizzle[4];
+         /*!< A swizzle to provide as input to astcenc. It must match the regular
+             expression /^[rgba01]{4}$/.
+          */
+} ktxAstcParams;
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CompressAstcEx(ktxTexture2* This, ktxAstcParams* params);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CompressAstc(ktxTexture2* This, ktx_uint32_t quality);
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Structure for passing extended parameters to
+ *        ktxTexture2_CompressBasisEx().
+ *
+ * If you only want default values, use ktxTexture2_CompressBasis(). Here, at a minimum you
+ * must initialize the structure as follows:
+ * @code
+ *  ktxBasisParams params = {0};
+ *  params.structSize = sizeof(params);
+ *  params.compressionLevel = KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL;
+ * @endcode
+ *
+ * @e compressionLevel has to be explicitly set because 0 is a valid @e compressionLevel
+ * but is not the default used by the BasisU encoder when no value is set. Only the other
+ * settings that are to be non-default must be non-zero.
+ */
+typedef struct ktxBasisParams {
+    ktx_uint32_t structSize;
+        /*!< Size of this struct. Used so library can tell which version
+             of struct is being passed.
+         */
+    ktx_bool_t uastc;
+        /*!<  True to use UASTC base, false to use ETC1S base. */
+    ktx_bool_t verbose;
+        /*!< If true, prints Basis Universal encoder operation details to
+             @c stdout. Not recommended for GUI apps.
+         */
+    ktx_bool_t noSSE;
+        /*!< True to forbid use of the SSE instruction set. Ignored if CPU
+             does not support SSE. */
+    ktx_uint32_t threadCount;
+        /*!< Number of threads used for compression. Default is 1. */
+
+    /* ETC1S params */
+
+    ktx_uint32_t compressionLevel;
+        /*!< Encoding speed vs. quality tradeoff. Range is [0,5]. Higher values
+             are slower, but give higher quality. There is no default. Callers
+             must explicitly set this value. Callers can use
+             KTX_ETC1S_DEFAULT_COMPRESSION_LEVEL as a default value.
+             Currently this is 2.
+        */
+    ktx_uint32_t qualityLevel;
+        /*!< Compression quality. Range is [1,255].  Lower gives better
+             compression/lower quality/faster. Higher gives less compression
+             /higher quality/slower.  This automatically determines values for
+             @c maxEndpoints, @c maxSelectors,
+             @c endpointRDOThreshold and @c selectorRDOThreshold
+             for the target quality level. Setting these parameters overrides
+             the values determined by @c qualityLevel which defaults to
+             128 if neither it nor both of @c maxEndpoints and
+             @c maxSelectors have been set.
+             @note @e Both of @c maxEndpoints and @c maxSelectors
+             must be set for them to have any effect.
+             @note qualityLevel will only determine values for
+             @c endpointRDOThreshold and @c selectorRDOThreshold
+             when its value exceeds 128, otherwise their defaults will be used.
+        */
+    ktx_uint32_t maxEndpoints;
+        /*!< Manually set the max number of color endpoint clusters.
+             Range is [1,16128]. Default is 0, unset. If this is set, maxSelectors
+             must also be set, otherwise the value will be ignored.
+         */
+    float endpointRDOThreshold;
+        /*!< Set endpoint RDO quality threshold. The default is 1.25. Lower is
+             higher quality but less quality per output bit (try [1.0,3.0].
+             This will override the value chosen by @c qualityLevel.
+         */
+    ktx_uint32_t maxSelectors;
+        /*!< Manually set the max number of color selector clusters. Range
+             is [1,16128]. Default is 0, unset. If this is set, maxEndpoints
+             must also be set, otherwise the value will be ignored.
+         */
+    float selectorRDOThreshold;
+        /*!< Set selector RDO quality threshold. The default is 1.5. Lower is
+             higher quality but less quality per output bit (try [1.0,3.0]).
+             This will override the value chosen by @c qualityLevel.
+         */
+    char inputSwizzle[4];
+        /*!< A swizzle to apply before encoding. It must match the regular
+             expression /^[rgba01]{4}$/. If both this and preSwizzle
+             are specified ktxTexture_CompressBasisEx will raise
+             KTX_INVALID_OPERATION.
+         */
+    ktx_bool_t normalMap;
+        /*!< Tunes codec parameters for better quality on normal maps (no
+             selector RDO, no endpoint RDO) and sets the texture's DFD appropriately.
+             Only valid for linear textures.
+         */
+    ktx_bool_t separateRGToRGB_A;
+        /*!< @deprecated. This was and is a no-op. 2-component inputs have always been
+             automatically separated using an "rrrg" inputSwizzle. @sa inputSwizzle and normalMode.
+         */
+    ktx_bool_t preSwizzle;
+        /*!< If the texture has @c KTXswizzle metadata, apply it before
+             compressing. Swizzling, like @c rabb may yield drastically
+             different error metrics if done after supercompression.
+         */
+    ktx_bool_t noEndpointRDO;
+        /*!< Disable endpoint rate distortion optimizations. Slightly faster,
+             less noisy output, but lower quality per output bit. Default is
+             KTX_FALSE.
+         */
+    ktx_bool_t noSelectorRDO;
+        /*!< Disable selector rate distortion optimizations. Slightly faster,
+             less noisy output, but lower quality per output bit. Default is
+             KTX_FALSE.
+         */
+
+    /* UASTC params */
+
+    ktx_pack_uastc_flags uastcFlags;
+        /*!<  A set of ::ktx_pack_uastc_flag_bits_e controlling UASTC
+             encoding. The most important value is the level given in the
+             least-significant 4 bits which selects a speed vs quality tradeoff
+             as shown in the following table:
+
+                Level/Speed | Quality
+                :-----: | :-------:
+                KTX_PACK_UASTC_LEVEL_FASTEST | 43.45dB
+                KTX_PACK_UASTC_LEVEL_FASTER | 46.49dB
+                KTX_PACK_UASTC_LEVEL_DEFAULT | 47.47dB
+                KTX_PACK_UASTC_LEVEL_SLOWER  | 48.01dB
+                KTX_PACK_UASTC_LEVEL_VERYSLOW | 48.24dB
+         */
+    ktx_bool_t uastcRDO;
+        /*!< Enable Rate Distortion Optimization (RDO) post-processing.
+         */
+    float uastcRDOQualityScalar;
+        /*!< UASTC RDO quality scalar (lambda). Lower values yield higher
+             quality/larger LZ compressed files, higher values yield lower
+             quality/smaller LZ compressed files. A good range to try is [.2,4].
+             Full range is [.001,50.0]. Default is 1.0.
+         */
+    ktx_uint32_t uastcRDODictSize;
+        /*!< UASTC RDO dictionary size in bytes. Default is 4096. Lower
+             values=faster, but give less compression. Range is [64,65536].
+         */
+    float uastcRDOMaxSmoothBlockErrorScale;
+        /*!< UASTC RDO max smooth block error scale. Range is [1,300].
+             Default is 10.0, 1.0 is disabled. Larger values suppress more
+             artifacts (and allocate more bits) on smooth blocks.
+         */
+    float uastcRDOMaxSmoothBlockStdDev;
+        /*!< UASTC RDO max smooth block standard deviation. Range is
+             [.01,65536.0]. Default is 18.0. Larger values expand the range of
+             blocks considered smooth.
+         */
+    ktx_bool_t uastcRDODontFavorSimplerModes;
+        /*!< Do not favor simpler UASTC modes in RDO mode.
+         */
+    ktx_bool_t uastcRDONoMultithreading;
+        /*!< Disable RDO multithreading (slightly higher compression,
+             deterministic).
+         */
+
+} ktxBasisParams;
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_CompressBasisEx(ktxTexture2* This, ktxBasisParams* params);
+
+/**
+ * @~English
+ * @brief Enumerators for specifying the transcode target format.
+ *
+ * For BasisU/ETC1S format, @e Opaque and @e alpha here refer to 2 separate
+ * RGB images, a.k.a slices within the BasisU compressed data. For UASTC
+ * format they refer to the RGB and the alpha components of the UASTC data. If
+ * the original image had only 2 components, R will be in the opaque portion
+ * and G in the alpha portion. The R value will be replicated in the RGB
+ * components. In the case of BasisU the G value will be replicated in all 3
+ * components of the alpha slice. If the original image had only 1 component
+ * it's value is replicated in all 3 components of the opaque portion and
+ * there is no alpha.
+ *
+ * @note You should not transcode sRGB encoded data to @c KTX_TTF_BC4_R,
+ * @c KTX_TTF_BC5_RG, @c KTX_TTF_ETC2_EAC_R{,G}11, @c KTX_TTF_RGB565,
+ * @c KTX_TTF_BGR565 or @c KTX_TTF_RGBA4444 formats as neither OpenGL nor
+ * Vulkan support sRGB variants of these. Doing sRGB decoding in the shader
+ * will not produce correct results if any texture filtering is being used.
+ */
+typedef enum ktx_transcode_fmt_e {
+        // Compressed formats
+
+        // ETC1-2
+        KTX_TTF_ETC1_RGB = 0,
+            /*!< Opaque only. Returns RGB or alpha data, if
+                 KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is
+                 specified. */
+        KTX_TTF_ETC2_RGBA = 1,
+            /*!< Opaque+alpha. EAC_A8 block followed by an ETC1 block. The
+                 alpha channel will be opaque for textures without an alpha
+                 channel. */
+
+        // BC1-5, BC7 (desktop, some mobile devices)
+        KTX_TTF_BC1_RGB = 2,
+            /*!< Opaque only, no punchthrough alpha support yet.  Returns RGB
+                 or alpha data, if KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS
+                 flag is specified. */
+        KTX_TTF_BC3_RGBA = 3,
+            /*!< Opaque+alpha. BC4 block with alpha followed by a BC1 block. The
+                 alpha channel will be opaque for textures without an alpha
+                 channel. */
+        KTX_TTF_BC4_R = 4,
+            /*!< One BC4 block. R = opaque.g or alpha.g, if
+                 KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is
+                 specified. */
+        KTX_TTF_BC5_RG = 5,
+            /*!< Two BC4 blocks, R=opaque.g and G=alpha.g The texture should
+                 have an alpha channel (if not G will be all 255's. For tangent
+                 space normal maps. */
+        KTX_TTF_BC7_RGBA = 6,
+            /*!< RGB or RGBA mode 5 for ETC1S, modes 1, 2, 3, 4, 5, 6, 7 for
+                 UASTC. */
+
+        // PVRTC1 4bpp (mobile, PowerVR devices)
+        KTX_TTF_PVRTC1_4_RGB = 8,
+            /*!< Opaque only. Returns RGB or alpha data, if
+                 KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is
+                 specified. */
+        KTX_TTF_PVRTC1_4_RGBA = 9,
+            /*!< Opaque+alpha. Most useful for simple opacity maps. If the
+                 texture doesn't have an alpha channel KTX_TTF_PVRTC1_4_RGB
+                 will be used instead. Lowest quality of any supported
+                 texture format. */
+
+        // ASTC (mobile, Intel devices, hopefully all desktop GPU's one day)
+        KTX_TTF_ASTC_4x4_RGBA = 10,
+            /*!< Opaque+alpha, ASTC 4x4. The alpha channel will be opaque for
+                 textures without an alpha channel.  The transcoder uses
+                 RGB/RGBA/L/LA modes, void extent, and up to two ([0,47] and
+                 [0,255]) endpoint precisions. */
+
+        // ATC and FXT1 formats are not supported by KTX2 as there
+        // are no equivalent VkFormats.
+
+        KTX_TTF_PVRTC2_4_RGB = 18,
+            /*!< Opaque-only. Almost BC1 quality, much faster to transcode
+                 and supports arbitrary texture dimensions (unlike
+                 PVRTC1 RGB). */
+        KTX_TTF_PVRTC2_4_RGBA = 19,
+            /*!< Opaque+alpha. Slower to transcode than cTFPVRTC2_4_RGB.
+                 Premultiplied alpha is highly recommended, otherwise the
+                 color channel can leak into the alpha channel on transparent
+                 blocks. */
+
+        KTX_TTF_ETC2_EAC_R11 = 20,
+            /*!< R only (ETC2 EAC R11 unsigned). R = opaque.g or alpha.g, if
+                 KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS flag is
+                 specified. */
+        KTX_TTF_ETC2_EAC_RG11 = 21,
+            /*!< RG only (ETC2 EAC RG11 unsigned), R=opaque.g, G=alpha.g. The
+                 texture should have an alpha channel (if not G will be all
+                 255's. For tangent space normal maps. */
+
+        // Uncompressed (raw pixel) formats
+        KTX_TTF_RGBA32 = 13,
+            /*!< 32bpp RGBA image stored in raster (not block) order in
+                 memory, R is first byte, A is last byte. */
+        KTX_TTF_RGB565 = 14,
+            /*!< 16bpp RGB image stored in raster (not block) order in memory,
+                 R at bit position 11. */
+        KTX_TTF_BGR565 = 15,
+            /*!< 16bpp RGB image stored in raster (not block) order in memory,
+                 R at bit position 0. */
+        KTX_TTF_RGBA4444 = 16,
+            /*!< 16bpp RGBA image stored in raster (not block) order in memory,
+                 R at bit position 12, A at bit position 0. */
+
+        // Values for automatic selection of RGB or RGBA depending if alpha
+        // present.
+        KTX_TTF_ETC = 22,
+            /*!< Automatically selects @c KTX_TTF_ETC1_RGB or
+                 @c KTX_TTF_ETC2_RGBA according to presence of alpha. */
+        KTX_TTF_BC1_OR_3 = 23,
+            /*!< Automatically selects @c KTX_TTF_BC1_RGB or
+                 @c KTX_TTF_BC3_RGBA according to presence of alpha. */
+
+        KTX_TTF_NOSELECTION = 0x7fffffff,
+
+        // Old enums for compatibility with code compiled against previous
+        // versions of libktx.
+        KTX_TF_ETC1 = KTX_TTF_ETC1_RGB,
+            //!< @deprecated. Use #KTX_TTF_ETC1_RGB.
+        KTX_TF_ETC2 = KTX_TTF_ETC,
+            //!< @deprecated. Use #KTX_TTF_ETC.
+        KTX_TF_BC1 = KTX_TTF_BC1_RGB,
+            //!< @deprecated. Use #KTX_TTF_BC1_RGB.
+        KTX_TF_BC3 = KTX_TTF_BC3_RGBA,
+            //!< @deprecated. Use #KTX_TTF_BC3_RGBA.
+        KTX_TF_BC4 = KTX_TTF_BC4_R,
+            //!< @deprecated. Use #KTX_TTF_BC4_R.
+        KTX_TF_BC5 = KTX_TTF_BC5_RG,
+            //!< @deprecated. Use #KTX_TTF_BC5_RG.
+        KTX_TTF_BC7_M6_RGB = KTX_TTF_BC7_RGBA,
+            //!< @deprecated. Use #KTX_TTF_BC7_RGBA.
+        KTX_TTF_BC7_M5_RGBA = KTX_TTF_BC7_RGBA,
+            //!< @deprecated. Use #KTX_TTF_BC7_RGBA.
+        KTX_TF_BC7_M6_OPAQUE_ONLY = KTX_TTF_BC7_RGBA,
+            //!< @deprecated. Use #KTX_TTF_BC7_RGBA
+        KTX_TF_PVRTC1_4_OPAQUE_ONLY = KTX_TTF_PVRTC1_4_RGB
+            //!< @deprecated. Use #KTX_TTF_PVRTC1_4_RGB.
+} ktx_transcode_fmt_e;
+
+/**
+ * @~English
+ * @brief Flags guiding transcoding of Basis Universal compressed textures.
+ */
+typedef enum ktx_transcode_flag_bits_e {
+    KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 = 2,
+        /*!< PVRTC1: decode non-pow2 ETC1S texture level to the next larger
+             power of 2 (not implemented yet, but we're going to support it).
+             Ignored if the slice's dimensions are already a power of 2.
+         */
+    KTX_TF_TRANSCODE_ALPHA_DATA_TO_OPAQUE_FORMATS = 4,
+        /*!< When decoding to an opaque texture format, if the Basis data has
+             alpha, decode the alpha slice instead of the color slice to the
+             output texture format. Has no effect if there is no alpha data.
+         */
+    KTX_TF_HIGH_QUALITY = 32,
+        /*!< Request higher quality transcode of UASTC to BC1, BC3, ETC2_EAC_R11 and
+             ETC2_EAC_RG11. The flag is unused by other UASTC transcoders.
+         */
+} ktx_transcode_flag_bits_e;
+typedef ktx_uint32_t ktx_transcode_flags;
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_TranscodeBasis(ktxTexture2* This, ktx_transcode_fmt_e fmt,
+                           ktx_transcode_flags transcodeFlags);
+
+/*
+ * Returns a string corresponding to a KTX error code.
+ */
+KTX_API const char* KTX_APIENTRY
+ktxErrorString(KTX_error_code error);
+
+/*
+ * Returns a string corresponding to a supercompression scheme.
+ */
+KTX_API const char* KTX_APIENTRY
+ktxSupercompressionSchemeString(ktxSupercmpScheme scheme);
+
+/*
+ * Returns a string corresponding to a transcode target format.
+ */
+KTX_API const char* KTX_APIENTRY
+ktxTranscodeFormatString(ktx_transcode_fmt_e format);
+
+KTX_API KTX_error_code KTX_APIENTRY ktxHashList_Create(ktxHashList** ppHl);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_CreateCopy(ktxHashList** ppHl, ktxHashList orig);
+KTX_API void KTX_APIENTRY ktxHashList_Construct(ktxHashList* pHl);
+KTX_API void KTX_APIENTRY
+ktxHashList_ConstructCopy(ktxHashList* pHl, ktxHashList orig);
+KTX_API void KTX_APIENTRY ktxHashList_Destroy(ktxHashList* head);
+KTX_API void KTX_APIENTRY ktxHashList_Destruct(ktxHashList* head);
+
+/*
+ * Adds a key-value pair to a hash list.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_AddKVPair(ktxHashList* pHead, const char* key,
+                      unsigned int valueLen, const void* value);
+
+/*
+ * Deletes a ktxHashListEntry from a ktxHashList.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_DeleteEntry(ktxHashList* pHead,  ktxHashListEntry* pEntry);
+
+/*
+ * Finds the entry for a key in a ktxHashList and deletes it.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_DeleteKVPair(ktxHashList* pHead, const char* key);
+
+/*
+ * Looks up a key and returns the ktxHashListEntry.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_FindEntry(ktxHashList* pHead, const char* key,
+                      ktxHashListEntry** ppEntry);
+
+/*
+ * Looks up a key and returns the value.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_FindValue(ktxHashList* pHead, const char* key,
+                      unsigned int* pValueLen, void** pValue);
+
+/*
+ * Return the next entry in a ktxHashList.
+ */
+KTX_API ktxHashListEntry* KTX_APIENTRY
+ktxHashList_Next(ktxHashListEntry* entry);
+
+/*
+ * Sorts a ktxHashList into order of the key codepoints.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_Sort(ktxHashList* pHead);
+
+/*
+ * Serializes a ktxHashList to a block of memory suitable for
+ * writing to a KTX file.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_Serialize(ktxHashList* pHead,
+                      unsigned int* kvdLen, unsigned char** kvd);
+
+/*
+ * Creates a hash table from the serialized data read from a
+ * a KTX file.
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashList_Deserialize(ktxHashList* pHead, unsigned int kvdLen, void* kvd);
+
+/*
+ * Get the key from a ktxHashListEntry
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashListEntry_GetKey(ktxHashListEntry* This,
+                        unsigned int* pKeyLen, char** ppKey);
+
+/*
+ * Get the value from a ktxHashListEntry
+ */
+KTX_API KTX_error_code KTX_APIENTRY
+ktxHashListEntry_GetValue(ktxHashListEntry* This,
+                          unsigned int* pValueLen, void** ppValue);
+
+/*===========================================================*
+ * Utilities for printing info about a KTX file.             *
+ *===========================================================*/
+
+KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForStdioStream(FILE* stdioStream);
+KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForNamedFile(const char* const filename);
+KTX_API KTX_error_code KTX_APIENTRY ktxPrintInfoForMemory(const ktx_uint8_t* bytes, ktx_size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+/*========================================================================*
+ * For backward compatibilty with the V3 & early versions of the V4 APIs. *
+ *========================================================================*/
+
+/**
+ * @deprecated Will be dropped before V4 release.
+ */
+#define ktx_texture_transcode_fmt_e ktx_transcode_fmt_e
+
+/**
+ * @deprecated Will be dropped before V4 release.
+ */
+#define ktx_texture_decode_flags ktx_transcode_flag_bits
+
+/**
+ * @deprecated Will be dropped before V4 release.
+ */
+#define ktxTexture_GetSize ktxTexture_GetDatasize
+
+/**
+@~English
+@page libktx_history Revision History
+
+@section v8 Version 4.0
+Added:
+@li Support for KTX Version 2.
+@li Support for encoding and transcoding Basis Universal images in KTX Version 2 files.
+@li Function to print info about a KTX file.
+
+@section v7 Version 3.0.1
+Fixed:
+@li GitHub issue #159: compile failure with recent Vulkan SDKs.
+@li Incorrect mapping of GL DXT3 and DXT5 formats to Vulkan equivalents.
+@li Incorrect BC4 blocksize.
+@li Missing mapping of PVRTC formats from GL to Vulkan.
+@li Incorrect block width and height calculations for sizes that are not
+    a multiple of the block size.
+@li Incorrect KTXorientation key in test images.
+
+@section v6 Version 3.0
+Added:
+@li new ktxTexture object based API for reading KTX files without an OpenGL context.
+@li Vulkan loader. @#include <ktxvulkan.h> to use it.
+
+Changed:
+@li ktx.h to not depend on KHR/khrplatform.h and GL{,ES*}/gl{corearb,}.h.
+    Applications using OpenGL must now include these files themselves.
+@li ktxLoadTexture[FMN], removing the hack of loading 1D textures as 2D textures
+    when the OpenGL context does not support 1D textures.
+    KTX_UNSUPPORTED_TEXTURE_TYPE is now returned.
+
+@section v5 Version 2.0.2
+Added:
+@li Support for cubemap arrays.
+
+Changed:
+@li New build system
+
+Fixed:
+@li GitHub issue #40: failure to byte-swap key-value lengths.
+@li GitHub issue #33: returning incorrect target when loading cubemaps.
+@li GitHub PR #42: loading of texture arrays.
+@li GitHub PR #41: compilation error when KTX_OPENGL_ES2=1 defined.
+@li GitHub issue #39: stack-buffer-overflow in toktx
+@li Don't use GL_EXTENSIONS on recent OpenGL versions.
+
+@section v4 Version 2.0.1
+Added:
+@li CMake build files. Thanks to Pavel Rotjberg for the initial version.
+
+Changed:
+@li ktxWriteKTXF to check the validity of the type & format combinations
+    passed to it.
+
+Fixed:
+@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=999">999</a>: 16-bit luminance texture cannot be written.
+@li compile warnings from compilers stricter than MS Visual C++. Thanks to
+    Pavel Rotjberg.
+
+@section v3 Version 2.0
+Added:
+@li support for decoding ETC2 and EAC formats in the absence of a hardware
+    decoder.
+@li support for converting textures with legacy LUMINANCE, LUMINANCE_ALPHA,
+    etc. formats to the equivalent R, RG, etc. format with an
+    appropriate swizzle, when loading in OpenGL Core Profile contexts.
+@li ktxErrorString function to return a string corresponding to an error code.
+@li tests for ktxLoadTexture[FN] that run under OpenGL ES 3.0 and OpenGL 3.3.
+    The latter includes an EGL on WGL wrapper that makes porting apps between
+    OpenGL ES and OpenGL easier on Windows.
+@li more texture formats to ktxLoadTexture[FN] and toktx tests.
+
+Changed:
+@li ktxLoadTexture[FMN] to discover the capabilities of the GL context at
+    run time and load textures, or not, according to those capabilities.
+
+Fixed:
+@li failure of ktxWriteKTXF to pad image rows to 4 bytes as required by the KTX
+    format.
+@li ktxWriteKTXF exiting with KTX_FILE_WRITE_ERROR when attempting to write
+    more than 1 byte of face-LOD padding.
+
+Although there is only a very minor API change, the addition of ktxErrorString,
+the functional changes are large enough to justify bumping the major revision
+number.
+
+@section v2 Version 1.0.1
+Implemented ktxLoadTextureM.
+Fixed the following:
+@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=571">571</a>: crash when null passed for pIsMipmapped.
+@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=572">572</a>: memory leak when unpacking ETC textures.
+@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=573">573</a>: potential crash when unpacking ETC textures with unused padding pixels.
+@li Public Bugzilla <a href="http://www.khronos.org/bugzilla/show_bug.cgi?id=576">576</a>: various small fixes.
+
+Thanks to Krystian Bigaj for the ktxLoadTextureM implementation and these fixes.
+
+@section v1 Version 1.0
+Initial release.
+
+*/
+
+#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */
diff --git a/thirdparty/libktx/include/ktxvulkan.h b/thirdparty/libktx/include/ktxvulkan.h
new file mode 100644
index 00000000000..e695ee6863d
--- /dev/null
+++ b/thirdparty/libktx/include/ktxvulkan.h
@@ -0,0 +1,253 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+#ifndef KTX_H_C54B42AEE39611E68E1E4FF8C51D1C66
+#define KTX_H_C54B42AEE39611E68E1E4FF8C51D1C66
+
+/*
+ * Copyright 2017-2020 The Khronos Group, Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file
+ * @~English
+ *
+ * @brief Declares the public functions and structures of the
+ *        KTX Vulkan texture loading API.
+ *
+ * A separate header file is used to avoid extra dependencies for those not
+ * using Vulkan. The nature of the Vulkan API, rampant structures and enums,
+ * means that vulkan.h must be included @e before including this file. The
+ * alternative is duplicating unattractively large parts of it.
+ *
+ * @author Mark Callow, Edgewise Consulting
+ *
+ * $Date$
+ */
+
+#include <ktx.h>
+
+#if 0
+/* Avoid Vulkan include file */
+#define VK_DEFINE_HANDLE(object) typedef struct object##_T* object;
+
+#if defined(__LP64__) || defined(_WIN64) || defined(__x86_64__) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__)
+        #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef struct object##_T *object;
+#else
+        #define VK_DEFINE_NON_DISPATCHABLE_HANDLE(object) typedef uint64_t object;
+#endif
+
+VK_DEFINE_HANDLE(VkPhysicalDevice)
+VK_DEFINE_HANDLE(VkDevice)
+VK_DEFINE_HANDLE(VkQueue)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkCommandPool)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkDeviceMemory)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImage)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkImageView)
+VK_DEFINE_NON_DISPATCHABLE_HANDLE(VkSampler)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @struct ktxVulkanFunctions
+ * @~English
+ * @brief Struct for applications to pass Vulkan function pointers to the
+ *        ktxTexture_VkUpload functions via a ktxVulkanDeviceInfo struct.
+ *
+ * @c vkGetInstanceProcAddr and @c vkGetDeviceProcAddr should be set, others
+ * are optional.
+ */
+typedef struct ktxVulkanFunctions {
+    // These are functions pointers we need to perform our vulkan duties.
+    PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr;
+    PFN_vkGetDeviceProcAddr vkGetDeviceProcAddr;
+
+    // These we optionally specify
+    PFN_vkAllocateCommandBuffers vkAllocateCommandBuffers;
+    PFN_vkAllocateMemory vkAllocateMemory;
+    PFN_vkBeginCommandBuffer vkBeginCommandBuffer;
+    PFN_vkBindBufferMemory vkBindBufferMemory;
+    PFN_vkBindImageMemory vkBindImageMemory;
+    PFN_vkCmdBlitImage vkCmdBlitImage;
+    PFN_vkCmdCopyBufferToImage vkCmdCopyBufferToImage;
+    PFN_vkCmdPipelineBarrier vkCmdPipelineBarrier;
+    PFN_vkCreateImage vkCreateImage;
+    PFN_vkDestroyImage vkDestroyImage;
+    PFN_vkCreateBuffer vkCreateBuffer;
+    PFN_vkDestroyBuffer vkDestroyBuffer;
+    PFN_vkCreateFence vkCreateFence;
+    PFN_vkDestroyFence vkDestroyFence;
+    PFN_vkEndCommandBuffer vkEndCommandBuffer;
+    PFN_vkFreeCommandBuffers vkFreeCommandBuffers;
+    PFN_vkFreeMemory vkFreeMemory;
+    PFN_vkGetBufferMemoryRequirements vkGetBufferMemoryRequirements;
+    PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
+    PFN_vkGetImageSubresourceLayout vkGetImageSubresourceLayout;
+    PFN_vkGetPhysicalDeviceImageFormatProperties vkGetPhysicalDeviceImageFormatProperties;
+    PFN_vkGetPhysicalDeviceFormatProperties vkGetPhysicalDeviceFormatProperties;
+    PFN_vkGetPhysicalDeviceMemoryProperties vkGetPhysicalDeviceMemoryProperties;
+    PFN_vkMapMemory vkMapMemory;
+    PFN_vkQueueSubmit vkQueueSubmit;
+    PFN_vkQueueWaitIdle vkQueueWaitIdle;
+    PFN_vkUnmapMemory vkUnmapMemory;
+    PFN_vkWaitForFences vkWaitForFences;
+} ktxVulkanFunctions;
+
+/**
+ * @class ktxVulkanTexture
+ * @~English
+ * @brief Struct for returning information about the Vulkan texture image
+ *        created by the ktxTexture_VkUpload* functions.
+ *
+ * Creation of these objects is internal to the upload functions.
+ */
+typedef struct ktxVulkanTexture
+{
+    PFN_vkDestroyImage vkDestroyImage; /*!< Pointer to vkDestroyImage function */
+    PFN_vkFreeMemory vkFreeMemory; /*!< Pointer to vkFreeMemory function */
+
+    VkImage image; /*!< Handle to the Vulkan image created by the loader. */
+    VkFormat imageFormat;     /*!< Format of the image data. */
+    VkImageLayout imageLayout; /*!< Layout of the created image. Has the same
+                                    value as @p layout parameter passed to the
+                                    loader. */
+    VkDeviceMemory deviceMemory; /*!< The memory allocated for the image on
+                                  the Vulkan device. */
+    VkImageViewType viewType; /*!< ViewType corresponding to @p image. Reflects
+                                   the dimensionality, cubeness and arrayness
+                                   of the image. */
+    uint32_t width; /*!< The width of the image. */
+    uint32_t height; /*!< The height of the image. */
+    uint32_t depth; /*!< The depth of the image. */
+    uint32_t levelCount; /*!< The number of MIP levels in the image. */
+    uint32_t layerCount; /*!< The number of array layers in the image. */
+} ktxVulkanTexture;
+
+KTX_API void KTX_APIENTRY
+ktxVulkanTexture_Destruct(ktxVulkanTexture* This, VkDevice device,
+                          const VkAllocationCallbacks* pAllocator);
+
+
+
+
+/**
+ * @class ktxVulkanDeviceInfo
+ * @~English
+ * @brief Struct for passing information about the Vulkan device on which
+ *        to create images to the texture image loading functions.
+ *
+ * Avoids passing a large number of parameters to each loading function.
+ * Use of ktxVulkanDeviceInfo_create() or ktxVulkanDeviceInfo_construct() to
+ * populate this structure is highly recommended.
+ *
+ * @code
+    ktxVulkanDeviceInfo vdi;
+    ktxVulkanTexture texture;
+ 
+    vdi = ktxVulkanDeviceInfo_create(physicalDevice,
+                                     device,
+                                     queue,
+                                     cmdPool,
+                                     &allocator);
+    ktxLoadVkTextureN("texture_1.ktx", vdi, &texture, NULL, NULL);
+    // ...
+    ktxLoadVkTextureN("texture_n.ktx", vdi, &texture, NULL, NULL);
+    ktxVulkanDeviceInfo_destroy(vdi);
+ * @endcode
+ */
+typedef struct ktxVulkanDeviceInfo {
+    VkInstance instance; /*!< Instance used to communicate with vulkan. */
+    VkPhysicalDevice physicalDevice; /*!< Handle of the physical device. */
+    VkDevice device; /*!< Handle of the logical device. */
+    VkQueue queue; /*!< Handle to the queue to which to submit commands. */
+    VkCommandBuffer cmdBuffer; /*!< Handle of the cmdBuffer to use. */
+    /** Handle of the command pool from which to allocate the command buffer. */
+    VkCommandPool cmdPool;
+    /** Pointer to the allocator to use for the command buffer and created
+     * images.
+     */
+    const VkAllocationCallbacks* pAllocator;
+    /** Memory properties of the Vulkan physical device. */
+    VkPhysicalDeviceMemoryProperties deviceMemoryProperties;
+
+    /** The functions needed to operate functions */
+    ktxVulkanFunctions vkFuncs;
+} ktxVulkanDeviceInfo;
+
+
+KTX_API ktxVulkanDeviceInfo* KTX_APIENTRY
+ktxVulkanDeviceInfo_CreateEx(VkInstance instance, VkPhysicalDevice physicalDevice, VkDevice device,
+                           VkQueue queue, VkCommandPool cmdPool,
+                           const VkAllocationCallbacks* pAllocator,
+                           const ktxVulkanFunctions* pFunctions);
+
+KTX_API ktxVulkanDeviceInfo* KTX_APIENTRY
+ktxVulkanDeviceInfo_Create(VkPhysicalDevice physicalDevice, VkDevice device,
+                           VkQueue queue, VkCommandPool cmdPool,
+                           const VkAllocationCallbacks* pAllocator);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxVulkanDeviceInfo_Construct(ktxVulkanDeviceInfo* This,
+                         VkPhysicalDevice physicalDevice, VkDevice device,
+                         VkQueue queue, VkCommandPool cmdPool,
+                         const VkAllocationCallbacks* pAllocator);
+
+KTX_API KTX_error_code KTX_APIENTRY
+ktxVulkanDeviceInfo_ConstructEx(ktxVulkanDeviceInfo* This,
+                              VkInstance instance,
+                              VkPhysicalDevice physicalDevice, VkDevice device,
+                              VkQueue queue, VkCommandPool cmdPool,
+                              const VkAllocationCallbacks* pAllocator,
+                              const ktxVulkanFunctions* pFunctions);
+
+KTX_API void KTX_APIENTRY
+ktxVulkanDeviceInfo_Destruct(ktxVulkanDeviceInfo* This);
+KTX_API void KTX_APIENTRY
+ktxVulkanDeviceInfo_Destroy(ktxVulkanDeviceInfo* This);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_VkUploadEx(ktxTexture* This, ktxVulkanDeviceInfo* vdi,
+                      ktxVulkanTexture* vkTexture,
+                      VkImageTiling tiling,
+                      VkImageUsageFlags usageFlags,
+                      VkImageLayout finalLayout);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture_VkUpload(ktxTexture* texture, ktxVulkanDeviceInfo* vdi,
+                    ktxVulkanTexture *vkTexture);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_VkUploadEx(ktxTexture1* This, ktxVulkanDeviceInfo* vdi,
+                       ktxVulkanTexture* vkTexture,
+                       VkImageTiling tiling,
+                       VkImageUsageFlags usageFlags,
+                       VkImageLayout finalLayout);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture1_VkUpload(ktxTexture1* texture, ktxVulkanDeviceInfo* vdi,
+                    ktxVulkanTexture *vkTexture);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_VkUploadEx(ktxTexture2* This, ktxVulkanDeviceInfo* vdi,
+                       ktxVulkanTexture* vkTexture,
+                       VkImageTiling tiling,
+                       VkImageUsageFlags usageFlags,
+                       VkImageLayout finalLayout);
+KTX_API KTX_error_code KTX_APIENTRY
+ktxTexture2_VkUpload(ktxTexture2* texture, ktxVulkanDeviceInfo* vdi,
+                     ktxVulkanTexture *vkTexture);
+
+KTX_API VkFormat KTX_APIENTRY
+ktxTexture_GetVkFormat(ktxTexture* This);
+
+KTX_API VkFormat KTX_APIENTRY
+ktxTexture1_GetVkFormat(ktxTexture1* This);
+
+KTX_API VkFormat KTX_APIENTRY
+ktxTexture2_GetVkFormat(ktxTexture2* This);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KTX_H_A55A6F00956F42F3A137C11929827FE1 */
diff --git a/thirdparty/libktx/lib/basis_sgd.h b/thirdparty/libktx/lib/basis_sgd.h
new file mode 100644
index 00000000000..6c559096529
--- /dev/null
+++ b/thirdparty/libktx/lib/basis_sgd.h
@@ -0,0 +1,85 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab textwidth=70: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file basisu_sgd.h
+ * @~English
+ *
+ * @brief Declare global data for Basis LZ supercompression with ETC1S.
+ *
+ * These functions are private and should not be used outside the library.
+ */
+
+#ifndef _BASIS_SGD_H_
+#define _BASIS_SGD_H_
+
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// This must be the same value as cSliceDescFlagsFrameIsIFrame so we can just
+// invert the bit when passing back & forth. As FrameIsIFrame is within
+// a C namespace it can't easily be accessed from a c header.
+enum bu_image_flags__bits_e { eBUImageIsPframe = 0x02 };
+
+typedef uint32_t buFlags;
+
+typedef struct ktxBasisLzGlobalHeader {
+    uint16_t endpointCount;
+    uint16_t selectorCount;
+    uint32_t endpointsByteLength;
+    uint32_t selectorsByteLength;
+    uint32_t tablesByteLength;
+    uint32_t extendedByteLength;
+} ktxBasisLzGlobalHeader;
+
+// This header is followed by imageCount "slice" descriptions.
+
+// 1, or 2 slices per image (i.e. layer, face & slice).
+// These offsets are relative to start of a mip level as given by the
+// main levelIndex.
+typedef struct ktxBasisLzEtc1sImageDesc {
+    buFlags imageFlags;
+    uint32_t rgbSliceByteOffset;
+    uint32_t rgbSliceByteLength;
+    uint32_t alphaSliceByteOffset;
+    uint32_t alphaSliceByteLength;
+} ktxBasisLzEtc1sImageDesc;
+
+#define BGD_ETC1S_IMAGE_DESCS(bgd) \
+        reinterpret_cast<ktxBasisLzEtc1sImageDesc*>(bgd + sizeof(ktxBasisLzGlobalHeader))
+
+// The are followed in the global data by these ...
+//    uint8_t[endpointsByteLength] endpointsData;
+//    uint8_t[selectorsByteLength] selectorsData;
+//    uint8_t[tablesByteLength] tablesData;
+
+#define BGD_ENDPOINTS_ADDR(bgd, imageCount) \
+    (bgd + sizeof(ktxBasisLzGlobalHeader) + sizeof(ktxBasisLzEtc1sImageDesc) * imageCount)
+
+#define BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) (BGD_ENDPOINTS_ADDR(bgd, imageCount) + bgdh.endpointsByteLength)
+
+#define BGD_TABLES_ADDR(bgd, bgdh, imageCount) (BGD_SELECTORS_ADDR(bgd, bgdh, imageCount) + bgdh.selectorsByteLength)
+
+#define BGD_EXTENDED_ADDR(bgd, bgdh, imageCount) (BGD_TABLES_ADDR(bgd, bgdh, imageCount) + bgdh.tablesByteLength)
+
+// Just because this is a convenient place to put it for basis_{en,trans}code.
+enum alpha_content_e {
+    eNone,
+    eAlpha,
+    eGreen
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _BASIS_SGD_H_ */
diff --git a/thirdparty/libktx/lib/basis_transcode.cpp b/thirdparty/libktx/lib/basis_transcode.cpp
new file mode 100644
index 00000000000..8df65bcb68f
--- /dev/null
+++ b/thirdparty/libktx/lib/basis_transcode.cpp
@@ -0,0 +1,733 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file basis_transcode.cpp
+ * @~English
+ *
+ * @brief Functions for transcoding Basis Universal BasisLZ/ETC1S and UASTC textures.
+ *
+ * Two worlds collide here too. More uglyness!
+ *
+ * @author Mark Callow, www.edgewise-consulting.com
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <KHR/khr_df.h>
+
+#include "dfdutils/dfd.h"
+#include "ktx.h"
+#include "ktxint.h"
+#include "texture2.h"
+#include "vkformat_enum.h"
+#include "vk_format.h"
+#include "basis_sgd.h"
+#include "transcoder/basisu_file_headers.h"
+#include "transcoder/basisu_transcoder.h"
+#include "transcoder/basisu_transcoder_internal.h"
+
+#undef DECLARE_PRIVATE
+#undef DECLARE_PROTECTED
+#define DECLARE_PRIVATE(n,t2) ktxTexture2_private& n = *(t2->_private)
+#define DECLARE_PROTECTED(n,t2) ktxTexture_protected& n = *(t2->_protected)
+
+using namespace basisu;
+using namespace basist;
+
+inline bool isPow2(uint32_t x) { return x && ((x & (x - 1U)) == 0U); }
+
+inline bool isPow2(uint64_t x) { return x && ((x & (x - 1U)) == 0U); }
+
+KTX_error_code
+ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
+                           alpha_content_e alphaContent,
+                           ktxTexture2* prototype,
+                           ktx_transcode_fmt_e outputFormat,
+                           ktx_transcode_flags transcodeFlags);
+KTX_error_code
+ktxTexture2_transcodeUastc(ktxTexture2* This,
+                           alpha_content_e alphaContent,
+                           ktxTexture2* prototype,
+                           ktx_transcode_fmt_e outputFormat,
+                           ktx_transcode_flags transcodeFlags);
+
+/**
+ * @memberof ktxTexture2
+ * @ingroup reader
+ * @~English
+ * @brief Transcode a KTX2 texture with BasisLZ/ETC1S or UASTC images.
+ *
+ * If the texture contains BasisLZ supercompressed images, Inflates them from
+ * back to ETC1S then transcodes them to the specified block-compressed
+ * format. If the texture contains UASTC images, inflates them, if they have been
+ * supercompressed with zstd, then transcodes then to the specified format, The
+ * transcoded images replace the original images and the texture's fields including
+ * the DFD are modified to reflect the new format.
+ *
+ * These types of textures must be transcoded to a desired target
+ * block-compressed format before they can be uploaded to a GPU via a
+ * graphics API.
+ *
+ * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
+ * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
+ * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
+ * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
+ * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
+ * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
+ * @c KTX_TTF_BC1_OR_3.
+ *
+ * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
+ * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
+ * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
+ * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
+ * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
+ *
+ * Transcoding to ATC & FXT1 formats is not supported by libktx as there
+ * are no equivalent Vulkan formats.
+ *
+ * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
+ * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
+ *
+ * The following @p transcodeFlags are available.
+ *
+ * @sa ktxtexture2_CompressBasis().
+ *
+ * @param[in]   This         pointer to the ktxTexture2 object of interest.
+ * @param[in]   outputFormat a value from the ktx_texture_transcode_fmt_e enum
+ *                                             specifying the target format.
+ * @param[in]   transcodeFlags  bitfield of flags modifying the transcode
+ *                                                operation. @sa ktx_texture_decode_flags_e.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Supercompression global data is corrupted.
+ * @exception KTX_INVALID_OPERATION
+ *                              The texture's format is not transcodable (not
+ *                              ETC1S/BasisLZ or UASTC).
+ * @exception KTX_INVALID_OPERATION
+ *                              Supercompression global data is missing, i.e.,
+ *                              the texture object is invalid.
+ * @exception KTX_INVALID_OPERATION
+ *                              Image data is missing, i.e., the texture object
+ *                              is invalid.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p outputFormat is PVRTC1 but the texture does
+ *                              does not have power-of-two dimensions.
+ * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
+ * @exception KTX_TRANSCODE_FAILED
+ *                              Something went wrong during transcoding.
+ * @exception KTX_UNSUPPORTED_FEATURE
+ *                              KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
+ *                              or the specified transcode target has not been
+ *                              included in the library being used.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
+ */
+ KTX_error_code
+ ktxTexture2_TranscodeBasis(ktxTexture2* This,
+                            ktx_transcode_fmt_e outputFormat,
+                            ktx_transcode_flags transcodeFlags)
+{
+    uint32_t* BDB = This->pDfd + 1;
+    khr_df_model_e colorModel = (khr_df_model_e)KHR_DFDVAL(BDB, MODEL);
+    if (colorModel != KHR_DF_MODEL_UASTC
+        // Constructor has checked color model matches BASIS_LZ.
+        && This->supercompressionScheme != KTX_SS_BASIS_LZ)
+    {
+        return KTX_INVALID_OPERATION; // Not in a transcodable format.
+    }
+
+    DECLARE_PRIVATE(priv, This);
+    if (This->supercompressionScheme == KTX_SS_BASIS_LZ) {
+        if (!priv._supercompressionGlobalData || priv._sgdByteLength == 0)
+            return KTX_INVALID_OPERATION;
+    }
+
+    if (transcodeFlags & KTX_TF_PVRTC_DECODE_TO_NEXT_POW2) {
+         debug_printf("ktxTexture_TranscodeBasis: KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 currently unsupported\n");
+         return KTX_UNSUPPORTED_FEATURE;
+    }
+
+    if (outputFormat == KTX_TTF_PVRTC1_4_RGB
+        || outputFormat == KTX_TTF_PVRTC1_4_RGBA) {
+         if ((!isPow2(This->baseWidth)) || (!isPow2(This->baseHeight))) {
+             debug_printf("ktxTexture_TranscodeBasis: PVRTC1 only supports power of 2 dimensions\n");
+             return KTX_INVALID_OPERATION;
+        }
+    }
+
+    const bool srgb = (KHR_DFDVAL(BDB, TRANSFER) == KHR_DF_TRANSFER_SRGB);
+    alpha_content_e alphaContent = eNone;
+    if (colorModel == KHR_DF_MODEL_ETC1S) {
+        if (KHR_DFDSAMPLECOUNT(BDB) == 2) {
+            uint32_t channelId = KHR_DFDSVAL(BDB, 1, CHANNELID);
+            if (channelId == KHR_DF_CHANNEL_ETC1S_AAA) {
+                alphaContent = eAlpha;
+            } else if (channelId == KHR_DF_CHANNEL_ETC1S_GGG){
+                alphaContent = eGreen;
+            } else {
+                return KTX_FILE_DATA_ERROR;
+            }
+        }
+    } else {
+        uint32_t channelId = KHR_DFDSVAL(BDB, 0, CHANNELID);
+        if (channelId == KHR_DF_CHANNEL_UASTC_RGBA)
+            alphaContent = eAlpha;
+        else if (channelId == KHR_DF_CHANNEL_UASTC_RRRG)
+            alphaContent = eGreen;
+    }
+
+    VkFormat vkFormat;
+
+    // Do some format mapping.
+    switch (outputFormat) {
+      case KTX_TTF_BC1_OR_3:
+        outputFormat = alphaContent != eNone ? KTX_TTF_BC3_RGBA
+                                             : KTX_TTF_BC1_RGB;
+        break;
+      case KTX_TTF_ETC:
+        outputFormat = alphaContent != eNone ? KTX_TTF_ETC2_RGBA
+                                             : KTX_TTF_ETC1_RGB;
+        break;
+      case KTX_TTF_PVRTC1_4_RGBA:
+        // This transcoder does not write opaque alpha blocks.
+        outputFormat = alphaContent != eNone  ? KTX_TTF_PVRTC1_4_RGBA
+                                              : KTX_TTF_PVRTC1_4_RGB;
+        break;
+      case KTX_TTF_PVRTC2_4_RGBA:
+        // This transcoder does not write opaque alpha blocks.
+        outputFormat = alphaContent != eNone ? KTX_TTF_PVRTC2_4_RGBA
+                                              : KTX_TTF_PVRTC2_4_RGB;
+        break;
+      default:
+        /*NOP*/;
+    }
+
+    switch (outputFormat) {
+      case KTX_TTF_ETC1_RGB:
+        vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK
+                        : VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
+        break;
+      case KTX_TTF_ETC2_RGBA:
+        vkFormat = srgb ? VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK
+                        : VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+        break;
+      case KTX_TTF_ETC2_EAC_R11:
+        vkFormat = VK_FORMAT_EAC_R11_UNORM_BLOCK;
+        break;
+      case KTX_TTF_ETC2_EAC_RG11:
+        vkFormat = VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
+        break;
+      case KTX_TTF_BC1_RGB:
+        // Transcoding doesn't support BC1 alpha.
+        vkFormat = srgb ? VK_FORMAT_BC1_RGB_SRGB_BLOCK
+                        : VK_FORMAT_BC1_RGB_UNORM_BLOCK;
+        break;
+      case KTX_TTF_BC3_RGBA:
+        vkFormat = srgb ? VK_FORMAT_BC3_SRGB_BLOCK
+                        : VK_FORMAT_BC3_UNORM_BLOCK;
+        break;
+      case KTX_TTF_BC4_R:
+        vkFormat = VK_FORMAT_BC4_UNORM_BLOCK;
+        break;
+      case KTX_TTF_BC5_RG:
+        vkFormat = VK_FORMAT_BC5_UNORM_BLOCK;
+        break;
+      case KTX_TTF_PVRTC1_4_RGB:
+      case KTX_TTF_PVRTC1_4_RGBA:
+        vkFormat = srgb ? VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG
+                        : VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
+        break;
+      case KTX_TTF_PVRTC2_4_RGB:
+      case KTX_TTF_PVRTC2_4_RGBA:
+        vkFormat = srgb ? VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG
+                        : VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
+        break;
+      case KTX_TTF_BC7_RGBA:
+        vkFormat = srgb ? VK_FORMAT_BC7_SRGB_BLOCK
+                        : VK_FORMAT_BC7_UNORM_BLOCK;
+        break;
+      case KTX_TTF_ASTC_4x4_RGBA:
+        vkFormat = srgb ? VK_FORMAT_ASTC_4x4_SRGB_BLOCK
+                        : VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
+        break;
+      case KTX_TTF_RGB565:
+        vkFormat = VK_FORMAT_R5G6B5_UNORM_PACK16;
+        break;
+      case KTX_TTF_BGR565:
+        vkFormat = VK_FORMAT_B5G6R5_UNORM_PACK16;
+        break;
+      case KTX_TTF_RGBA4444:
+        vkFormat = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+        break;
+      case KTX_TTF_RGBA32:
+        vkFormat = srgb ? VK_FORMAT_R8G8B8A8_SRGB
+                        : VK_FORMAT_R8G8B8A8_UNORM;
+        break;
+      default:
+        return KTX_INVALID_VALUE;
+    }
+
+    basis_tex_format textureFormat;
+    if (colorModel == KHR_DF_MODEL_UASTC)
+        textureFormat = basis_tex_format::cUASTC4x4;
+    else
+        textureFormat = basis_tex_format::cETC1S;
+
+    if (!basis_is_format_supported((transcoder_texture_format)outputFormat,
+                                    textureFormat)) {
+        return KTX_UNSUPPORTED_FEATURE;
+    }
+
+
+    // Create a prototype texture to use for calculating sizes in the target
+    // format and, as useful side effects, provide us with a properly sized
+    // data allocation and the DFD for the target format.
+    ktxTextureCreateInfo createInfo;
+    createInfo.glInternalformat = 0;
+    createInfo.vkFormat = vkFormat;
+    createInfo.baseWidth = This->baseWidth;
+    createInfo.baseHeight = This->baseHeight;
+    createInfo.baseDepth = This->baseDepth;
+    createInfo.generateMipmaps = This->generateMipmaps;
+    createInfo.isArray = This->isArray;
+    createInfo.numDimensions = This->numDimensions;
+    createInfo.numFaces = This->numFaces;
+    createInfo.numLayers = This->numLayers;
+    createInfo.numLevels = This->numLevels;
+    createInfo.pDfd = nullptr;
+
+    KTX_error_code result;
+    ktxTexture2* prototype;
+    result = ktxTexture2_Create(&createInfo, KTX_TEXTURE_CREATE_ALLOC_STORAGE,
+                                &prototype);
+
+    if (result != KTX_SUCCESS) {
+        assert(result == KTX_OUT_OF_MEMORY); // The only run time error
+        return result;
+    }
+
+    if (!This->pData) {
+        if (ktxTexture_isActiveStream((ktxTexture*)This)) {
+             // Load pending. Complete it.
+            result = ktxTexture2_LoadImageData(This, NULL, 0);
+            if (result != KTX_SUCCESS)
+            {
+                ktxTexture2_Destroy(prototype);
+                return result;
+            }
+        } else {
+            // No data to transcode.
+            ktxTexture2_Destroy(prototype);
+            return KTX_INVALID_OPERATION;
+        }
+    }
+
+    // Transcoder global initialization. Requires ~9 milliseconds when compiled
+    // and executed natively on a Core i7 2.2 GHz. If this is too slow, the
+    // tables it computes can easily be moved to be compiled in.
+    static bool transcoderInitialized;
+    if (!transcoderInitialized) {
+        basisu_transcoder_init();
+        transcoderInitialized = true;
+    }
+
+    if (textureFormat == basis_tex_format::cETC1S) {
+        result = ktxTexture2_transcodeLzEtc1s(This, alphaContent,
+                                            prototype, outputFormat,
+                                            transcodeFlags);
+    } else {
+        result = ktxTexture2_transcodeUastc(This, alphaContent,
+                                            prototype, outputFormat,
+                                            transcodeFlags);
+    }
+
+    if (result == KTX_SUCCESS) {
+        // Fix up the current texture
+        DECLARE_PROTECTED(thisPrtctd, This);
+        DECLARE_PRIVATE(protoPriv, prototype);
+        DECLARE_PROTECTED(protoPrtctd, prototype);
+        memcpy(&thisPrtctd._formatSize, &protoPrtctd._formatSize,
+               sizeof(ktxFormatSize));
+        This->vkFormat = vkFormat;
+        This->isCompressed = prototype->isCompressed;
+        This->supercompressionScheme = KTX_SS_NONE;
+        priv._requiredLevelAlignment = protoPriv._requiredLevelAlignment;
+        // Copy the levelIndex from the prototype to This.
+        memcpy(priv._levelIndex, protoPriv._levelIndex,
+               This->numLevels * sizeof(ktxLevelIndexEntry));
+        // Move the DFD and data from the prototype to This.
+        free(This->pDfd);
+        This->pDfd = prototype->pDfd;
+        prototype->pDfd = 0;
+        free(This->pData);
+        This->pData = prototype->pData;
+        This->dataSize = prototype->dataSize;
+        prototype->pData = 0;
+        prototype->dataSize = 0;
+    }
+    ktxTexture2_Destroy(prototype);
+    return result;
+ }
+
+/**
+ * @memberof ktxTexture2 @private
+ * @ingroup reader
+ * @~English
+ * @brief Transcode a KTX2 texture with BasisLZ supercompressed ETC1S images.
+ *
+ * Inflates the images from BasisLZ supercompression back to ETC1S
+ * then transcodes them to the specified block-compressed format. The
+ * transcoded images replace the original images and the texture's fields
+ * including the DFD are modified to reflect the new format.
+ *
+ * BasisLZ supercompressed textures must be transcoded to a desired target
+ * block-compressed format before they can be uploaded to a GPU via a graphics
+ * API.
+ *
+ * The following block compressed transcode targets are available: @c KTX_TTF_ETC1_RGB,
+ * @c KTX_TTF_ETC2_RGBA, @c KTX_TTF_BC1_RGB, @c KTX_TTF_BC3_RGBA,
+ * @c KTX_TTF_BC4_R, @c KTX_TTF_BC5_RG, @c KTX_TTF_BC7_RGBA,
+ * @c @c KTX_TTF_PVRTC1_4_RGB, @c KTX_TTF_PVRTC1_4_RGBA,
+ * @c KTX_TTF_PVRTC2_4_RGB, @c KTX_TTF_PVRTC2_4_RGBA, @c KTX_TTF_ASTC_4x4_RGBA,
+ * @c KTX_TTF_ETC2_EAC_R11, @c KTX_TTF_ETC2_EAC_RG11, @c KTX_TTF_ETC and
+ * @c KTX_TTF_BC1_OR_3.
+ *
+ * @c KTX_TTF_ETC automatically selects between @c KTX_TTF_ETC1_RGB and
+ * @c KTX_TTF_ETC2_RGBA according to whether an alpha channel is available. @c KTX_TTF_BC1_OR_3
+ * does likewise between @c KTX_TTF_BC1_RGB and @c KTX_TTF_BC3_RGBA. Note that if
+ * @c KTX_TTF_PVRTC1_4_RGBA or @c KTX_TTF_PVRTC2_4_RGBA is specified and there is no alpha
+ * channel @c KTX_TTF_PVRTC1_4_RGB or @c KTX_TTF_PVRTC2_4_RGB respectively will be selected.
+ *
+ * ATC & FXT1 formats are not supported by KTX2 & libktx as there are no equivalent Vulkan formats.
+ *
+ * The following uncompressed transcode targets are also available: @c KTX_TTF_RGBA32,
+ * @c KTX_TTF_RGB565, KTX_TTF_BGR565 and KTX_TTF_RGBA4444.
+ *
+ * The following @p transcodeFlags are available.
+ *
+ * @sa ktxtexture2_CompressBasis().
+ *
+ * @param[in]   This         pointer to the ktxTexture2 object of interest.
+ * @param[in]   outputFormat a value from the ktx_texture_transcode_fmt_e enum
+ *                           specifying the target format.
+ * @param[in]   transcodeFlags  bitfield of flags modifying the transcode
+ *                           operation. @sa ktx_texture_decode_flags_e.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Supercompression global data is corrupted.
+ * @exception KTX_INVALID_OPERATION
+ *                              The texture's format is not transcodable (not
+ *                              ETC1S/BasisLZ or UASTC).
+ * @exception KTX_INVALID_OPERATION
+ *                              Supercompression global data is missing, i.e.,
+ *                              the texture object is invalid.
+ * @exception KTX_INVALID_OPERATION
+ *                              Image data is missing, i.e., the texture object
+ *                              is invalid.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p outputFormat is PVRTC1 but the texture does
+ *                              does not have power-of-two dimensions.
+ * @exception KTX_INVALID_VALUE @p outputFormat is invalid.
+ * @exception KTX_TRANSCODE_FAILED
+ *                              Something went wrong during transcoding. The
+ *                              texture object will be corrupted.
+ * @exception KTX_UNSUPPORTED_FEATURE
+ *                              KTX_TF_PVRTC_DECODE_TO_NEXT_POW2 was requested
+ *                              or the specified transcode target has not been
+ *                              included in the library being used.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to carry out transcoding.
+ */
+KTX_error_code
+ktxTexture2_transcodeLzEtc1s(ktxTexture2* This,
+                             alpha_content_e alphaContent,
+                             ktxTexture2* prototype,
+                             ktx_transcode_fmt_e outputFormat,
+                             ktx_transcode_flags transcodeFlags)
+{
+    DECLARE_PRIVATE(priv, This);
+    DECLARE_PRIVATE(protoPriv, prototype);
+    KTX_error_code result = KTX_SUCCESS;
+
+    assert(This->supercompressionScheme == KTX_SS_BASIS_LZ);
+
+    uint8_t* bgd = priv._supercompressionGlobalData;
+    ktxBasisLzGlobalHeader& bgdh = *reinterpret_cast<ktxBasisLzGlobalHeader*>(bgd);
+    if (!(bgdh.endpointsByteLength && bgdh.selectorsByteLength && bgdh.tablesByteLength)) {
+        debug_printf("ktxTexture_TranscodeBasis: missing endpoints, selectors or tables");
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    // Compute some helpful numbers.
+    //
+    // firstImages contains the indices of the first images for each level to
+    // ease finding the correct slice description when iterating from smallest
+    // level to largest or when randomly accessing them (t.b.c). The last array
+    // entry contains the total number of images, for calculating the offsets
+    // of the endpoints, etc.
+    uint32_t* firstImages = new uint32_t[This->numLevels+1];
+
+    // Temporary invariant value
+    uint32_t layersFaces = This->numLayers * This->numFaces;
+    firstImages[0] = 0;
+    for (uint32_t level = 1; level <= This->numLevels; level++) {
+        // NOTA BENE: numFaces * depth is only reasonable because they can't
+        // both be > 1. I.e there are no 3d cubemaps.
+        firstImages[level] = firstImages[level - 1]
+                           + layersFaces * MAX(This->baseDepth >> (level - 1), 1);
+    }
+    uint32_t& imageCount = firstImages[This->numLevels];
+
+    if (BGD_TABLES_ADDR(0, bgdh, imageCount) + bgdh.tablesByteLength > priv._sgdByteLength) {
+        return KTX_FILE_DATA_ERROR;
+    }
+    // FIXME: Do more validation.
+
+    // Prepare low-level transcoder for transcoding slices.
+    basist::basisu_lowlevel_etc1s_transcoder bit;
+
+    // basisu_transcoder_state is used to find the previous frame when
+    // decoding a video P-Frame. It tracks the previous frame for each mip
+    // level. For cube map array textures we need to find the previous frame
+    // for each face so we a state per face. Although providing this is only
+    // needed for video, it is easier to always pass our own.
+    std::vector<basisu_transcoder_state> xcoderStates;
+    xcoderStates.resize(This->isVideo ? This->numFaces : 1);
+
+    bit.decode_palettes(bgdh.endpointCount, BGD_ENDPOINTS_ADDR(bgd, imageCount),
+                        bgdh.endpointsByteLength,
+                        bgdh.selectorCount, BGD_SELECTORS_ADDR(bgd, bgdh, imageCount),
+                        bgdh.selectorsByteLength);
+
+    bit.decode_tables(BGD_TABLES_ADDR(bgd, bgdh, imageCount),
+                      bgdh.tablesByteLength);
+
+    // Find matching VkFormat and calculate output sizes.
+
+    const bool isVideo = This->isVideo;
+
+    ktx_uint8_t* pXcodedData = prototype->pData;
+    // Inconveniently, the output buffer size parameter of transcode_image
+    // has to be in pixels for uncompressed output and in blocks for
+    // compressed output. The only reason for humouring the API is so
+    // its buffer size tests provide a real check. An alternative is to
+    // always provide the size in bytes which will always pass.
+    ktx_uint32_t outputBlockByteLength
+                      = prototype->_protected->_formatSize.blockSizeInBits / 8;
+    ktx_size_t xcodedDataLength
+                      = prototype->dataSize / outputBlockByteLength;
+    ktxLevelIndexEntry* protoLevelIndex;
+    uint64_t levelOffsetWrite;
+    const ktxBasisLzEtc1sImageDesc* imageDescs = BGD_ETC1S_IMAGE_DESCS(bgd);
+
+    // Finally we're ready to transcode the slices.
+
+    // FIXME: Iframe flag needs to be queryable by the application. In Basis
+    // the app can query file_info and image_info from the transcoder which
+    // returns a structure with lots of info about the image.
+
+    protoLevelIndex = protoPriv._levelIndex;
+    levelOffsetWrite = 0;
+    for (int32_t level = This->numLevels - 1; level >= 0; level--) {
+        uint64_t levelOffset = ktxTexture2_levelDataOffset(This, level);
+        uint64_t writeOffset = levelOffsetWrite;
+        uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
+        uint32_t levelWidth = MAX(1, This->baseWidth >> level);
+        uint32_t levelHeight = MAX(1, This->baseHeight >> level);
+        // ETC1S texel block dimensions
+        const uint32_t bw = 4, bh = 4;
+        uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
+        uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
+        uint32_t depth = MAX(1, This->baseDepth >> level);
+        //uint32_t faceSlices = This->numFaces == 1 ? depth : This->numFaces;
+        uint32_t faceSlices = This->numFaces * depth;
+        uint32_t numImages = This->numLayers * faceSlices;
+        uint32_t image = firstImages[level];
+        uint32_t endImage = image + numImages;
+        ktx_size_t levelImageSizeOut, levelSizeOut;
+        uint32_t stateIndex = 0;
+
+        levelSizeOut = 0;
+        // FIXME: Figure out a way to get the size out of the transcoder.
+        levelImageSizeOut = ktxTexture2_GetImageSize(prototype, level);
+        for (; image < endImage; image++) {
+            const ktxBasisLzEtc1sImageDesc& imageDesc = imageDescs[image];
+
+            basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
+            // We have face0 [face1 ...] within each layer. Use `stateIndex`
+            // rather than a double loop of layers and faceSlices as this
+            // works for 3d texture and non-array cube maps as well as
+            // cube map arrays without special casing.
+            if (++stateIndex == xcoderStates.size())
+                stateIndex = 0;
+
+            if (alphaContent != eNone)
+            {
+                // The slice descriptions should have alpha information.
+                if (imageDesc.alphaSliceByteOffset == 0
+                    || imageDesc.alphaSliceByteLength == 0)
+                    return KTX_FILE_DATA_ERROR;
+            }
+
+            bool status;
+            status = bit.transcode_image(
+                      (transcoder_texture_format)outputFormat,
+                      pXcodedData + writeOffset,
+                      (uint32_t)(xcodedDataLength - writeOffsetBlocks),
+                      This->pData,
+                      (uint32_t)This->dataSize,
+                      levelBlocksX,
+                      levelBlocksY,
+                      levelWidth,
+                      levelHeight,
+                      level,
+                      (uint32_t)(levelOffset + imageDesc.rgbSliceByteOffset),
+                      imageDesc.rgbSliceByteLength,
+                      (uint32_t)(levelOffset + imageDesc.alphaSliceByteOffset),
+                      imageDesc.alphaSliceByteLength,
+                      transcodeFlags,
+                      alphaContent != eNone,
+                      isVideo,
+                      // Our P-Frame flag is in the same bit as
+                      // cSliceDescFlagsFrameIsIFrame. We have to
+                      // invert it to make it an I-Frame flag.
+                      //
+                      // API currently doesn't have any way to pass
+                      // the I-Frame flag.
+                      //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
+                      0, // output_row_pitch_in_blocks_or_pixels
+                      &xcoderState,
+                      0  // output_rows_in_pixels
+                      );
+            if (!status) {
+                result = KTX_TRANSCODE_FAILED;
+                goto cleanup;
+            }
+
+            writeOffset += levelImageSizeOut;
+            levelSizeOut += levelImageSizeOut;
+        } // end images loop
+        protoLevelIndex[level].byteOffset = levelOffsetWrite;
+        protoLevelIndex[level].byteLength = levelSizeOut;
+        protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
+        levelOffsetWrite += levelSizeOut;
+        assert(levelOffsetWrite == writeOffset);
+        // In case of transcoding to uncompressed.
+        levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
+                                     levelOffsetWrite);
+    } // level loop
+
+    result = KTX_SUCCESS;
+
+cleanup:
+    delete[] firstImages;
+    return result;
+}
+
+
+KTX_error_code
+ktxTexture2_transcodeUastc(ktxTexture2* This,
+                           alpha_content_e alphaContent,
+                           ktxTexture2* prototype,
+                           ktx_transcode_fmt_e outputFormat,
+                           ktx_transcode_flags transcodeFlags)
+{
+    assert(This->supercompressionScheme != KTX_SS_BASIS_LZ);
+
+    ktx_uint8_t* pXcodedData = prototype->pData;
+    ktx_uint32_t outputBlockByteLength
+                      = prototype->_protected->_formatSize.blockSizeInBits / 8;
+    ktx_size_t xcodedDataLength
+                      = prototype->dataSize / outputBlockByteLength;
+    DECLARE_PRIVATE(protoPriv, prototype);
+    ktxLevelIndexEntry* protoLevelIndex = protoPriv._levelIndex;
+    ktx_size_t levelOffsetWrite = 0;
+
+    basisu_lowlevel_uastc_transcoder uit;
+    // See comment on same declaration in transcodeEtc1s.
+    std::vector<basisu_transcoder_state> xcoderStates;
+    xcoderStates.resize(This->isVideo ? This->numFaces : 1);
+
+    for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
+    {
+        ktx_uint32_t depth;
+        uint64_t writeOffset = levelOffsetWrite;
+        uint64_t writeOffsetBlocks = levelOffsetWrite / outputBlockByteLength;
+        ktx_size_t levelImageSizeIn, levelImageOffsetIn;
+        ktx_size_t levelImageSizeOut, levelSizeOut;
+        ktx_uint32_t levelImageCount;
+        uint32_t levelWidth = MAX(1, This->baseWidth >> level);
+        uint32_t levelHeight = MAX(1, This->baseHeight >> level);
+        // UASTC texel block dimensions
+        const uint32_t bw = 4, bh = 4;
+        uint32_t levelBlocksX = (levelWidth + (bw - 1)) / bw;
+        uint32_t levelBlocksY = (levelHeight + (bh - 1)) / bh;
+        uint32_t stateIndex = 0;
+
+        depth = MAX(1, This->baseDepth  >> level);
+
+        levelImageCount = This->numLayers * This->numFaces * depth;
+        levelImageSizeIn = ktxTexture_calcImageSize(ktxTexture(This), level,
+                                                    KTX_FORMAT_VERSION_TWO);
+        levelImageSizeOut = ktxTexture_calcImageSize(ktxTexture(prototype),
+                                                     level,
+                                                     KTX_FORMAT_VERSION_TWO);
+
+        levelImageOffsetIn = ktxTexture2_levelDataOffset(This, level);
+        levelSizeOut = 0;
+        bool status;
+        for (uint32_t image = 0; image < levelImageCount; image++) {
+            basisu_transcoder_state& xcoderState = xcoderStates[stateIndex];
+            // See comment before same lines in transcodeEtc1s.
+            if (++stateIndex == xcoderStates.size())
+                stateIndex = 0;
+
+            status = uit.transcode_image(
+                          (transcoder_texture_format)outputFormat,
+                          pXcodedData + writeOffset,
+                          (uint32_t)(xcodedDataLength - writeOffsetBlocks),
+                          This->pData,
+                          (uint32_t)This->dataSize,
+                          levelBlocksX,
+                          levelBlocksY,
+                          levelWidth,
+                          levelHeight,
+                          level,
+                          (uint32_t)levelImageOffsetIn,
+                          (uint32_t)levelImageSizeIn,
+                          transcodeFlags,
+                          alphaContent != eNone,
+                          This->isVideo, // is_video
+                          //imageDesc.imageFlags ^ cSliceDescFlagsFrameIsIFrame,
+                          0, // output_row_pitch_in_blocks_or_pixels
+                          &xcoderState, // pState
+                          0, // output_rows_in_pixels,
+                          -1, // channel0
+                          -1  // channel1
+                          );
+            if (!status)
+                return KTX_TRANSCODE_FAILED;
+            writeOffset += levelImageSizeOut;
+            levelSizeOut += levelImageSizeOut;
+            levelImageOffsetIn += levelImageSizeIn;
+        }
+        protoLevelIndex[level].byteOffset = levelOffsetWrite;
+        // writeOffset will be equal to total size of the images in the level.
+        protoLevelIndex[level].byteLength = levelSizeOut;
+        protoLevelIndex[level].uncompressedByteLength = levelSizeOut;
+        levelOffsetWrite += levelSizeOut;
+    }
+    // In case of transcoding to uncompressed.
+    levelOffsetWrite = _KTX_PADN(protoPriv._requiredLevelAlignment,
+                                 levelOffsetWrite);
+    return KTX_SUCCESS;
+}
diff --git a/thirdparty/libktx/lib/checkheader.c b/thirdparty/libktx/lib/checkheader.c
new file mode 100644
index 00000000000..07e5d919c8c
--- /dev/null
+++ b/thirdparty/libktx/lib/checkheader.c
@@ -0,0 +1,259 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* $Id: ee6f7be4d43390de78e1815ed158012c78ddeff1 $ */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file checkheader.c
+ * @~English
+ *
+ * @brief Function to verify a KTX file header
+ *
+ * @author Mark Callow, HI Corporation
+ */
+
+/*
+ * Author: Georg Kolling, Imagination Technology with modifications
+ * by Mark Callow, HI Corporation.
+ */
+#include <assert.h>
+#include <string.h>
+
+#include "ktx.h"
+#include "ktxint.h"
+
+/**
+ * @internal
+ * @~English
+ * @brief Check a KTX file header.
+ *
+ * As well as checking that the header identifies a KTX file, the function
+ * sanity checks the values and returns information about the texture in a
+ * struct KTX_supplementary_info.
+ *
+ * @param pHeader   pointer to the KTX header to check
+ * @param pSuppInfo pointer to a KTX_supplementary_info structure in which to
+ *                  return information about the texture.
+ *
+ * @author Georg Kolling, Imagination Technology
+ * @author Mark Callow, HI Corporation
+ */
+
+KTX_error_code  ktxCheckHeader1_(KTX_header* pHeader,
+                                 KTX_supplemental_info* pSuppInfo)
+{
+    ktx_uint8_t identifier_reference[12] = KTX_IDENTIFIER_REF;
+    ktx_uint32_t max_dim;
+
+    assert(pHeader != NULL && pSuppInfo != NULL);
+
+    /* Compare identifier, is this a KTX file? */
+    if (memcmp(pHeader->identifier, identifier_reference, 12) != 0)
+    {
+        return KTX_UNKNOWN_FILE_FORMAT;
+    }
+
+    if (pHeader->endianness == KTX_ENDIAN_REF_REV)
+    {
+        /* Convert endianness of pHeader fields. */
+        _ktxSwapEndian32(&pHeader->glType, 12);
+
+        if (pHeader->glTypeSize != 1 &&
+            pHeader->glTypeSize != 2 &&
+            pHeader->glTypeSize != 4)
+        {
+            /* Only 8-, 16-, and 32-bit types supported so far. */
+            return KTX_FILE_DATA_ERROR;
+        }
+    }
+    else if (pHeader->endianness != KTX_ENDIAN_REF)
+    {
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    /* Check glType and glFormat */
+    pSuppInfo->compressed = 0;
+    if (pHeader->glType == 0 || pHeader->glFormat == 0)
+    {
+        if (pHeader->glType + pHeader->glFormat != 0)
+        {
+            /* either both or none of glType, glFormat must be zero */
+            return KTX_FILE_DATA_ERROR;
+        }
+        pSuppInfo->compressed = 1;
+    }
+
+    if (pHeader->glFormat == pHeader->glInternalformat) {
+        // glInternalFormat is either unsized (which is no longer and should
+        // never have been supported by libktx) or glFormat is sized.
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    /* Check texture dimensions. KTX files can store 8 types of textures:
+       1D, 2D, 3D, cube, and array variants of these. There is currently
+       no GL extension for 3D array textures. */
+    if ((pHeader->pixelWidth == 0) ||
+        (pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0))
+    {
+        /* texture must have width */
+        /* texture must have height if it has depth */
+        return KTX_FILE_DATA_ERROR;
+    }
+
+
+    if (pHeader->pixelDepth > 0)
+    {
+        if (pHeader->numberOfArrayElements > 0)
+        {
+            /* No 3D array textures yet. */
+            return KTX_UNSUPPORTED_TEXTURE_TYPE;
+        }
+        pSuppInfo->textureDimension = 3;
+    }
+    else if (pHeader->pixelHeight > 0)
+    {
+        pSuppInfo->textureDimension = 2;
+    }
+    else
+    {
+        pSuppInfo->textureDimension = 1;
+    }
+
+    if (pHeader->numberOfFaces == 6)
+    {
+        if (pSuppInfo->textureDimension != 2)
+        {
+            /* cube map needs 2D faces */
+            return KTX_FILE_DATA_ERROR;
+        }
+    }
+    else if (pHeader->numberOfFaces != 1)
+    {
+        /* numberOfFaces must be either 1 or 6 */
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    /* Check number of mipmap levels */
+    if (pHeader->numberOfMipLevels == 0)
+    {
+        pSuppInfo->generateMipmaps = 1;
+        pHeader->numberOfMipLevels = 1;
+    }
+    else
+    {
+        pSuppInfo->generateMipmaps = 0;
+    }
+
+    /* This test works for arrays too because height or depth will be 0. */
+    max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);
+    if (max_dim < ((ktx_uint32_t)1 << (pHeader->numberOfMipLevels - 1)))
+    {
+        /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @internal
+ * @~English
+ * @brief Check a KTX2 file header.
+ *
+ * As well as checking that the header identifies a KTX 2 file, the function
+ * sanity checks the values and returns information about the texture in a
+ * struct KTX_supplementary_info.
+ *
+ * @param pHeader   pointer to the KTX header to check
+ * @param pSuppInfo pointer to a KTX_supplementary_info structure in which to
+ *                  return information about the texture.
+ *
+ * @author Mark Callow, HI Corporation
+ */
+KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader,
+                                KTX_supplemental_info* pSuppInfo)
+{
+// supp info is compressed, generateMipmaps and num dimensions. Don't need
+// compressed as formatSize gives us that. I think the other 2 aren't needed.
+    ktx_uint8_t identifier_reference[12] = KTX2_IDENTIFIER_REF;
+
+    assert(pHeader != NULL && pSuppInfo != NULL);
+    ktx_uint32_t max_dim;
+
+    /* Compare identifier, is this a KTX file? */
+    if (memcmp(pHeader->identifier, identifier_reference, 12) != 0)
+    {
+        return KTX_UNKNOWN_FILE_FORMAT;
+    }
+
+    /* Check texture dimensions. KTX files can store 8 types of textures:
+       1D, 2D, 3D, cube, and array variants of these. There is currently
+       no extension for 3D array textures in any 3D API. */
+    if ((pHeader->pixelWidth == 0) ||
+        (pHeader->pixelDepth > 0 && pHeader->pixelHeight == 0))
+    {
+        /* texture must have width */
+        /* texture must have height if it has depth */
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    if (pHeader->pixelDepth > 0)
+    {
+        if (pHeader->layerCount > 0)
+        {
+            /* No 3D array textures yet. */
+            return KTX_UNSUPPORTED_TEXTURE_TYPE;
+        }
+        pSuppInfo->textureDimension = 3;
+    }
+    else if (pHeader->pixelHeight > 0)
+    {
+        pSuppInfo->textureDimension = 2;
+    }
+    else
+    {
+        pSuppInfo->textureDimension = 1;
+    }
+
+    if (pHeader->faceCount == 6)
+    {
+        if (pSuppInfo->textureDimension != 2)
+        {
+            /* cube map needs 2D faces */
+            return KTX_FILE_DATA_ERROR;
+        }
+    }
+    else if (pHeader->faceCount != 1)
+    {
+        /* numberOfFaces must be either 1 or 6 */
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    // Check number of mipmap levels
+    if (pHeader->levelCount == 0)
+    {
+        pSuppInfo->generateMipmaps = 1;
+        pHeader->levelCount = 1;
+    }
+    else
+    {
+        pSuppInfo->generateMipmaps = 0;
+    }
+
+    // This test works for arrays too because height or depth will be 0.
+    max_dim = MAX(MAX(pHeader->pixelWidth, pHeader->pixelHeight), pHeader->pixelDepth);
+    if (max_dim < ((ktx_uint32_t)1 << (pHeader->levelCount - 1)))
+    {
+        // Can't have more mip levels than 1 + log2(max(width, height, depth))
+        return KTX_FILE_DATA_ERROR;
+    }
+
+    return KTX_SUCCESS;
+
+}
diff --git a/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h b/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h
new file mode 100644
index 00000000000..bbd0d14bd90
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/KHR/khr_df.h
@@ -0,0 +1,619 @@
+/* The Khronos Data Format Specification (version 1.3) */
+/*
+** Copyright 2015-2020 The Khronos Group Inc.
+** SPDX-License-Identifier: Apache-2.0
+*/
+
+/* This header defines a structure that can describe the layout of image
+   formats in memory. This means that the data format is transparent to
+   the application, and the expectation is that this should be used when
+   the layout is defined external to the API. Many Khronos APIs deliberately
+   keep the internal layout of images opaque, to allow proprietary layouts
+   and optimisations. This structure is not appropriate for describing
+   opaque layouts. */
+
+/* We stick to standard C89 constructs for simplicity and portability. */
+
+#ifndef _KHR_DATA_FORMAT_H_
+#define _KHR_DATA_FORMAT_H_
+
+/* Accessors */
+typedef enum _khr_word_e {
+    KHR_DF_WORD_VENDORID = 0U,
+    KHR_DF_WORD_DESCRIPTORTYPE = 0U,
+    KHR_DF_WORD_VERSIONNUMBER = 1U,
+    KHR_DF_WORD_DESCRIPTORBLOCKSIZE = 1U,
+    KHR_DF_WORD_MODEL = 2U,
+    KHR_DF_WORD_PRIMARIES = 2U,
+    KHR_DF_WORD_TRANSFER = 2U,
+    KHR_DF_WORD_FLAGS = 2U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION0 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION1 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION2 = 3U,
+    KHR_DF_WORD_TEXELBLOCKDIMENSION3 = 3U,
+    KHR_DF_WORD_BYTESPLANE0 = 4U,
+    KHR_DF_WORD_BYTESPLANE1 = 4U,
+    KHR_DF_WORD_BYTESPLANE2 = 4U,
+    KHR_DF_WORD_BYTESPLANE3 = 4U,
+    KHR_DF_WORD_BYTESPLANE4 = 5U,
+    KHR_DF_WORD_BYTESPLANE5 = 5U,
+    KHR_DF_WORD_BYTESPLANE6 = 5U,
+    KHR_DF_WORD_BYTESPLANE7 = 5U,
+    KHR_DF_WORD_SAMPLESTART = 6U,
+    KHR_DF_WORD_SAMPLEWORDS = 4U
+} khr_df_word_e;
+
+typedef enum _khr_df_shift_e {
+    KHR_DF_SHIFT_VENDORID = 0U,
+    KHR_DF_SHIFT_DESCRIPTORTYPE = 17U,
+    KHR_DF_SHIFT_VERSIONNUMBER = 0U,
+    KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE = 16U,
+    KHR_DF_SHIFT_MODEL = 0U,
+    KHR_DF_SHIFT_PRIMARIES = 8U,
+    KHR_DF_SHIFT_TRANSFER = 16U,
+    KHR_DF_SHIFT_FLAGS = 24U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION0 = 0U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION1 = 8U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION2 = 16U,
+    KHR_DF_SHIFT_TEXELBLOCKDIMENSION3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE0 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE1 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE2 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE3 = 24U,
+    KHR_DF_SHIFT_BYTESPLANE4 = 0U,
+    KHR_DF_SHIFT_BYTESPLANE5 = 8U,
+    KHR_DF_SHIFT_BYTESPLANE6 = 16U,
+    KHR_DF_SHIFT_BYTESPLANE7 = 24U
+} khr_df_shift_e;
+
+typedef enum _khr_df_mask_e {
+    KHR_DF_MASK_VENDORID = 0x1FFFFU,
+    KHR_DF_MASK_DESCRIPTORTYPE = 0x7FFFU,
+    KHR_DF_MASK_VERSIONNUMBER = 0xFFFFU,
+    KHR_DF_MASK_DESCRIPTORBLOCKSIZE = 0xFFFFU,
+    KHR_DF_MASK_MODEL = 0xFFU,
+    KHR_DF_MASK_PRIMARIES = 0xFFU,
+    KHR_DF_MASK_TRANSFER = 0xFFU,
+    KHR_DF_MASK_FLAGS = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION0 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION1 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION2 = 0xFFU,
+    KHR_DF_MASK_TEXELBLOCKDIMENSION3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE0 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE1 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE2 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE3 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE4 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE5 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE6 = 0xFFU,
+    KHR_DF_MASK_BYTESPLANE7 = 0xFFU
+} khr_df_mask_e;
+
+/* Helper macro:
+   Extract field X from basic descriptor block BDB */
+#define KHR_DFDVAL(BDB, X) \
+    (((BDB)[KHR_DF_WORD_ ## X] >> (KHR_DF_SHIFT_ ## X)) \
+     & (KHR_DF_MASK_ ## X))
+
+/* Helper macro:
+   Set field X of basic descriptor block BDB */
+#define KHR_DFDSETVAL(BDB, X, val) \
+    ((BDB)[KHR_DF_WORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_ ## X] & \
+      ~((KHR_DF_MASK_ ## X) << (KHR_DF_SHIFT_ ## X))) | \
+     (((val) & (KHR_DF_MASK_ ## X)) << (KHR_DF_SHIFT_ ## X)))
+
+/* Offsets relative to the start of a sample */
+typedef enum _khr_df_sampleword_e {
+    KHR_DF_SAMPLEWORD_BITOFFSET = 0U,
+    KHR_DF_SAMPLEWORD_BITLENGTH = 0U,
+    KHR_DF_SAMPLEWORD_CHANNELID = 0U,
+    KHR_DF_SAMPLEWORD_QUALIFIERS = 0U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION0 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION1 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION2 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION3 = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL = 1U,
+    KHR_DF_SAMPLEWORD_SAMPLELOWER = 2U,
+    KHR_DF_SAMPLEWORD_SAMPLEUPPER = 3U
+} khr_df_sampleword_e;
+
+typedef enum _khr_df_sampleshift_e {
+    KHR_DF_SAMPLESHIFT_BITOFFSET = 0U,
+    KHR_DF_SAMPLESHIFT_BITLENGTH = 16U,
+    KHR_DF_SAMPLESHIFT_CHANNELID = 24U,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLESHIFT_QUALIFIERS = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION0 = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION1 = 8U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION2 = 16U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION3 = 24U,
+    KHR_DF_SAMPLESHIFT_SAMPLEPOSITION_ALL = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLELOWER = 0U,
+    KHR_DF_SAMPLESHIFT_SAMPLEUPPER = 0U
+} khr_df_sampleshift_e;
+
+typedef enum _khr_df_samplemask_e {
+    KHR_DF_SAMPLEMASK_BITOFFSET = 0xFFFFU,
+    KHR_DF_SAMPLEMASK_BITLENGTH = 0xFF,
+    KHR_DF_SAMPLEMASK_CHANNELID = 0xF,
+    /* N.B. Qualifiers are defined as an offset into a byte */
+    KHR_DF_SAMPLEMASK_QUALIFIERS = 0xF0,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION0 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION1 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION2 = 0xFF,
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION3 = 0xFF,
+    /* ISO C restricts enum values to range of int hence the
+       cast. We do it verbosely instead of using -1 to ensure
+       it is a 32-bit value even if int is 64 bits. */
+    KHR_DF_SAMPLEMASK_SAMPLEPOSITION_ALL = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLELOWER = (int) 0xFFFFFFFFU,
+    KHR_DF_SAMPLEMASK_SAMPLEUPPER = (int) 0xFFFFFFFFU
+} khr_df_samplemask_e;
+
+/* Helper macro:
+   Extract field X of sample S from basic descriptor block BDB */
+#define KHR_DFDSVAL(BDB, S, X) \
+    (((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] >> (KHR_DF_SAMPLESHIFT_ ## X)) \
+     & (KHR_DF_SAMPLEMASK_ ## X))
+
+/* Helper macro:
+   Set field X of sample S of basic descriptor block BDB */
+#define KHR_DFDSETSVAL(BDB, S, X, val) \
+    ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+           ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+           KHR_DF_SAMPLEWORD_ ## X] = \
+     ((BDB)[KHR_DF_WORD_SAMPLESTART + \
+            ((S) * KHR_DF_WORD_SAMPLEWORDS) + \
+            KHR_DF_SAMPLEWORD_ ## X] & \
+      ~((uint32_t)(KHR_DF_SAMPLEMASK_ ## X) << (KHR_DF_SAMPLESHIFT_ ## X))) | \
+     (((val) & (uint32_t)(KHR_DF_SAMPLEMASK_ ## X)) << (KHR_DF_SAMPLESHIFT_ ## X)))
+
+/* Helper macro:
+   Number of samples in basic descriptor block BDB */
+#define KHR_DFDSAMPLECOUNT(BDB) \
+    (((KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) >> 2) - \
+      KHR_DF_WORD_SAMPLESTART) \
+     / KHR_DF_WORD_SAMPLEWORDS)
+
+/* Helper macro:
+   Size in words of basic descriptor block for S samples */
+#define KHR_DFDSIZEWORDS(S) \
+    (KHR_DF_WORD_SAMPLESTART + \
+     (S) * KHR_DF_WORD_SAMPLEWORDS)
+
+/* Vendor ids */
+typedef enum _khr_df_vendorid_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VENDORID_KHRONOS = 0U,
+    KHR_DF_VENDORID_MAX     = 0x1FFFFU
+} khr_df_vendorid_e;
+
+/* Descriptor types */
+typedef enum _khr_df_khr_descriptortype_e {
+    /* Default Khronos basic descriptor block */
+    KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT = 0U,
+    /* Extension descriptor block for additional planes */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_PLANES = 0x6001U,
+    /* Extension descriptor block for additional dimensions */
+    KHR_DF_KHR_DESCRIPTORTYPE_ADDITIONAL_DIMENSIONS = 0x6002U,
+    /* Bit indicates modifying requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_WRITE_BIT = 0x2000U,
+    /* Bit indicates processing requires understanding this extension */
+    KHR_DF_KHR_DESCRIPTORTYPE_NEEDED_FOR_DECODE_BIT = 0x4000U,
+    KHR_DF_KHR_DESCRIPTORTYPE_MAX         = 0x7FFFU
+} khr_df_khr_descriptortype_e;
+
+/* Descriptor block version */
+typedef enum _khr_df_versionnumber_e {
+    /* Standard Khronos descriptor */
+    KHR_DF_VERSIONNUMBER_1_0 = 0U, /* Version 1.0 of the specification */
+    KHR_DF_VERSIONNUMBER_1_1 = 0U, /* Version 1.1 did not bump the version number */
+    KHR_DF_VERSIONNUMBER_1_2 = 1U, /* Version 1.2 increased the version number */
+    KHR_DF_VERSIONNUMBER_1_3 = 2U, /* Version 1.3 increased the version number */
+    KHR_DF_VERSIONNUMBER_LATEST = KHR_DF_VERSIONNUMBER_1_3,
+    KHR_DF_VERSIONNUMBER_MAX = 0xFFFFU
+} khr_df_versionnumber_e;
+
+/* Model in which the color coordinate space is defined.
+   There is no requirement that a color format use all the
+   channel types that are defined in the color model. */
+typedef enum _khr_df_model_e {
+    /* No interpretation of color channels defined */
+    KHR_DF_MODEL_UNSPECIFIED  = 0U,
+    /* Color primaries (red, green, blue) + alpha, depth and stencil */
+    KHR_DF_MODEL_RGBSDA       = 1U,
+    /* Color differences (Y', Cb, Cr) + alpha, depth and stencil */
+    KHR_DF_MODEL_YUVSDA       = 2U,
+    /* Color differences (Y', I, Q) + alpha, depth and stencil */
+    KHR_DF_MODEL_YIQSDA       = 3U,
+    /* Perceptual color (CIE L*a*b*) + alpha, depth and stencil */
+    KHR_DF_MODEL_LABSDA       = 4U,
+    /* Subtractive colors (cyan, magenta, yellow, black) + alpha */
+    KHR_DF_MODEL_CMYKA        = 5U,
+    /* Non-color coordinate data (X, Y, Z, W) */
+    KHR_DF_MODEL_XYZW         = 6U,
+    /* Hue, saturation, value, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSVA_ANG     = 7U,
+    /* Hue, saturation, lightness, hue angle on color circle, plus alpha */
+    KHR_DF_MODEL_HSLA_ANG     = 8U,
+    /* Hue, saturation, value, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSVA_HEX     = 9U,
+    /* Hue, saturation, lightness, hue on color hexagon, plus alpha */
+    KHR_DF_MODEL_HSLA_HEX     = 10U,
+    /* Lightweight approximate color difference (luma, orange, green) */
+    KHR_DF_MODEL_YCGCOA       = 11U,
+    /* ITU BT.2020 constant luminance YcCbcCrc */
+    KHR_DF_MODEL_YCCBCCRC     = 12U,
+    /* ITU BT.2100 constant intensity ICtCp */
+    KHR_DF_MODEL_ICTCP        = 13U,
+    /* CIE 1931 XYZ color coordinates (X, Y, Z) */
+    KHR_DF_MODEL_CIEXYZ       = 14U,
+    /* CIE 1931 xyY color coordinates (X, Y, Y) */
+    KHR_DF_MODEL_CIEXYY       = 15U,
+
+    /* Compressed formats start at 128. */
+    /* These compressed formats should generally have a single sample,
+       sited at the 0,0 position of the texel block. Where multiple
+       channels are used to distinguish formats, these should be cosited. */
+    /* Direct3D (and S3) compressed formats */
+    /* Note that premultiplied status is recorded separately */
+    /* DXT1 "channels" are RGB (0), Alpha (1) */
+    /* DXT1/BC1 with one channel is opaque */
+    /* DXT1/BC1 with a cosited alpha sample is transparent */
+    KHR_DF_MODEL_DXT1A         = 128U,
+    KHR_DF_MODEL_BC1A          = 128U,
+    /* DXT2/DXT3/BC2, with explicit 4-bit alpha */
+    KHR_DF_MODEL_DXT2          = 129U,
+    KHR_DF_MODEL_DXT3          = 129U,
+    KHR_DF_MODEL_BC2           = 129U,
+    /* DXT4/DXT5/BC3, with interpolated alpha */
+    KHR_DF_MODEL_DXT4          = 130U,
+    KHR_DF_MODEL_DXT5          = 130U,
+    KHR_DF_MODEL_BC3           = 130U,
+    /* BC4 - single channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC4           = 131U,
+    /* BC5 - two channel interpolated 8-bit data */
+    /* (The UNORM/SNORM variation is recorded in the channel data) */
+    KHR_DF_MODEL_BC5           = 132U,
+    /* BC6H - DX11 format for 16-bit float channels */
+    KHR_DF_MODEL_BC6H          = 133U,
+    /* BC7 - DX11 format */
+    KHR_DF_MODEL_BC7           = 134U,
+    /* Gap left for future desktop expansion */
+
+    /* Mobile compressed formats follow */
+    /* A format of ETC1 indicates that the format shall be decodable
+       by an ETC1-compliant decoder and not rely on ETC2 features */
+    KHR_DF_MODEL_ETC1          = 160U,
+    /* A format of ETC2 is permitted to use ETC2 encodings on top of
+       the baseline ETC1 specification */
+    /* The ETC2 format has channels "red", "green", "RGB" and "alpha",
+       which should be cosited samples */
+    /* Punch-through alpha can be distinguished from full alpha by
+       the plane size in bytes required for the texel block */
+    KHR_DF_MODEL_ETC2          = 161U,
+    /* Adaptive Scalable Texture Compression */
+    /* ASTC HDR vs LDR is determined by the float flag in the channel */
+    /* ASTC block size can be distinguished by texel block size */
+    KHR_DF_MODEL_ASTC          = 162U,
+    /* ETC1S is a simplified subset of ETC1 */
+    KHR_DF_MODEL_ETC1S         = 163U,
+    /* PowerVR Texture Compression */
+    KHR_DF_MODEL_PVRTC         = 164U,
+    KHR_DF_MODEL_PVRTC2        = 165U,
+    KHR_DF_MODEL_UASTC         = 166U,
+    /* Proprietary formats (ATITC, etc.) should follow */
+    KHR_DF_MODEL_MAX = 0xFFU
+} khr_df_model_e;
+
+/* Definition of channel names for each color model */
+typedef enum _khr_df_model_channels_e {
+    /* Unspecified format with nominal channel numbering */
+    KHR_DF_CHANNEL_UNSPECIFIED_0  = 0U,
+    KHR_DF_CHANNEL_UNSPECIFIED_1  = 1U,
+    KHR_DF_CHANNEL_UNSPECIFIED_2  = 2U,
+    KHR_DF_CHANNEL_UNSPECIFIED_3  = 3U,
+    KHR_DF_CHANNEL_UNSPECIFIED_4  = 4U,
+    KHR_DF_CHANNEL_UNSPECIFIED_5  = 5U,
+    KHR_DF_CHANNEL_UNSPECIFIED_6  = 6U,
+    KHR_DF_CHANNEL_UNSPECIFIED_7  = 7U,
+    KHR_DF_CHANNEL_UNSPECIFIED_8  = 8U,
+    KHR_DF_CHANNEL_UNSPECIFIED_9  = 9U,
+    KHR_DF_CHANNEL_UNSPECIFIED_10 = 10U,
+    KHR_DF_CHANNEL_UNSPECIFIED_11 = 11U,
+    KHR_DF_CHANNEL_UNSPECIFIED_12 = 12U,
+    KHR_DF_CHANNEL_UNSPECIFIED_13 = 13U,
+    KHR_DF_CHANNEL_UNSPECIFIED_14 = 14U,
+    KHR_DF_CHANNEL_UNSPECIFIED_15 = 15U,
+    /* MODEL_RGBSDA - red, green, blue, stencil, depth, alpha */
+    KHR_DF_CHANNEL_RGBSDA_RED     =  0U,
+    KHR_DF_CHANNEL_RGBSDA_R       =  0U,
+    KHR_DF_CHANNEL_RGBSDA_GREEN   =  1U,
+    KHR_DF_CHANNEL_RGBSDA_G       =  1U,
+    KHR_DF_CHANNEL_RGBSDA_BLUE    =  2U,
+    KHR_DF_CHANNEL_RGBSDA_B       =  2U,
+    KHR_DF_CHANNEL_RGBSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_RGBSDA_S       = 13U,
+    KHR_DF_CHANNEL_RGBSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_RGBSDA_D       = 14U,
+    KHR_DF_CHANNEL_RGBSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_RGBSDA_A       = 15U,
+    /* MODEL_YUVSDA - luma, Cb, Cr, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YUVSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YUVSDA_CB      =  1U,
+    KHR_DF_CHANNEL_YUVSDA_U       =  1U,
+    KHR_DF_CHANNEL_YUVSDA_CR      =  2U,
+    KHR_DF_CHANNEL_YUVSDA_V       =  2U,
+    KHR_DF_CHANNEL_YUVSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YUVSDA_S       = 13U,
+    KHR_DF_CHANNEL_YUVSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YUVSDA_D       = 14U,
+    KHR_DF_CHANNEL_YUVSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YUVSDA_A       = 15U,
+    /* MODEL_YIQSDA - luma, in-phase, quadrature, stencil, depth, alpha */
+    KHR_DF_CHANNEL_YIQSDA_Y       =  0U,
+    KHR_DF_CHANNEL_YIQSDA_I       =  1U,
+    KHR_DF_CHANNEL_YIQSDA_Q       =  2U,
+    KHR_DF_CHANNEL_YIQSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_YIQSDA_S       = 13U,
+    KHR_DF_CHANNEL_YIQSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_YIQSDA_D       = 14U,
+    KHR_DF_CHANNEL_YIQSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YIQSDA_A       = 15U,
+    /* MODEL_LABSDA - CIELAB/L*a*b* luma, red-green, blue-yellow, stencil, depth, alpha */
+    KHR_DF_CHANNEL_LABSDA_L       =  0U,
+    KHR_DF_CHANNEL_LABSDA_A       =  1U,
+    KHR_DF_CHANNEL_LABSDA_B       =  2U,
+    KHR_DF_CHANNEL_LABSDA_STENCIL = 13U,
+    KHR_DF_CHANNEL_LABSDA_S       = 13U,
+    KHR_DF_CHANNEL_LABSDA_DEPTH   = 14U,
+    KHR_DF_CHANNEL_LABSDA_D       = 14U,
+    KHR_DF_CHANNEL_LABSDA_ALPHA   = 15U,
+    /* NOTE: KHR_DF_CHANNEL_LABSDA_A is not a synonym for alpha! */
+    /* MODEL_CMYKA - cyan, magenta, yellow, key/blacK, alpha */
+    KHR_DF_CHANNEL_CMYKSDA_CYAN    =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_C       =  0U,
+    KHR_DF_CHANNEL_CMYKSDA_MAGENTA =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_M       =  1U,
+    KHR_DF_CHANNEL_CMYKSDA_YELLOW  =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_Y       =  2U,
+    KHR_DF_CHANNEL_CMYKSDA_KEY     =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_BLACK   =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_K       =  3U,
+    KHR_DF_CHANNEL_CMYKSDA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_CMYKSDA_A       = 15U,
+    /* MODEL_XYZW - coordinates x, y, z, w */
+    KHR_DF_CHANNEL_XYZW_X = 0U,
+    KHR_DF_CHANNEL_XYZW_Y = 1U,
+    KHR_DF_CHANNEL_XYZW_Z = 2U,
+    KHR_DF_CHANNEL_XYZW_W = 3U,
+    /* MODEL_HSVA_ANG - value (luma), saturation, hue, alpha, angular projection, conical space */
+    KHR_DF_CHANNEL_HSVA_ANG_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_ANG_A          = 15U,
+    /* MODEL_HSLA_ANG - lightness (luma), saturation, hue, alpha, angular projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_ANG_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_ANG_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_ANG_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_ANG_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_ANG_A          = 15U,
+    /* MODEL_HSVA_HEX - value (luma), saturation, hue, alpha, hexagonal projection, conical space */
+    KHR_DF_CHANNEL_HSVA_HEX_VALUE      = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_V          = 0U,
+    KHR_DF_CHANNEL_HSVA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSVA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSVA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSVA_HEX_A          = 15U,
+    /* MODEL_HSLA_HEX - lightness (luma), saturation, hue, alpha, hexagonal projection, double conical space */
+    KHR_DF_CHANNEL_HSLA_HEX_LIGHTNESS  = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_L          = 0U,
+    KHR_DF_CHANNEL_HSLA_HEX_SATURATION = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_S          = 1U,
+    KHR_DF_CHANNEL_HSLA_HEX_HUE        = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_H          = 2U,
+    KHR_DF_CHANNEL_HSLA_HEX_ALPHA      = 15U,
+    KHR_DF_CHANNEL_HSLA_HEX_A          = 15U,
+    /* MODEL_YCGCOA - luma, green delta, orange delta, alpha */
+    KHR_DF_CHANNEL_YCGCOA_Y       =  0U,
+    KHR_DF_CHANNEL_YCGCOA_CG      =  1U,
+    KHR_DF_CHANNEL_YCGCOA_CO      =  2U,
+    KHR_DF_CHANNEL_YCGCOA_ALPHA   = 15U,
+    KHR_DF_CHANNEL_YCGCOA_A       = 15U,
+    /* MODEL_CIEXYZ - CIE 1931 X, Y, Z */
+    KHR_DF_CHANNEL_CIEXYZ_X = 0U,
+    KHR_DF_CHANNEL_CIEXYZ_Y = 1U,
+    KHR_DF_CHANNEL_CIEXYZ_Z = 2U,
+    /* MODEL_CIEXYY - CIE 1931 x, y, Y */
+    KHR_DF_CHANNEL_CIEXYY_X        = 0U,
+    KHR_DF_CHANNEL_CIEXYY_YCHROMA  = 1U,
+    KHR_DF_CHANNEL_CIEXYY_YLUMA    = 2U,
+
+    /* Compressed formats */
+    /* MODEL_DXT1A/MODEL_BC1A */
+    KHR_DF_CHANNEL_DXT1A_COLOR = 0U,
+    KHR_DF_CHANNEL_BC1A_COLOR  = 0U,
+    KHR_DF_CHANNEL_DXT1A_ALPHAPRESENT = 1U,
+    KHR_DF_CHANNEL_DXT1A_ALPHA = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHAPRESENT  = 1U,
+    KHR_DF_CHANNEL_BC1A_ALPHA  = 1U,
+    /* MODEL_DXT2/3/MODEL_BC2 */
+    KHR_DF_CHANNEL_DXT2_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT3_COLOR =  0U,
+    KHR_DF_CHANNEL_BC2_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT2_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT3_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC2_ALPHA  = 15U,
+    /* MODEL_DXT4/5/MODEL_BC3 */
+    KHR_DF_CHANNEL_DXT4_COLOR =  0U,
+    KHR_DF_CHANNEL_DXT5_COLOR =  0U,
+    KHR_DF_CHANNEL_BC3_COLOR  =  0U,
+    KHR_DF_CHANNEL_DXT4_ALPHA = 15U,
+    KHR_DF_CHANNEL_DXT5_ALPHA = 15U,
+    KHR_DF_CHANNEL_BC3_ALPHA  = 15U,
+    /* MODEL_BC4 */
+    KHR_DF_CHANNEL_BC4_DATA = 0U,
+    /* MODEL_BC5 */
+    KHR_DF_CHANNEL_BC5_RED   = 0U,
+    KHR_DF_CHANNEL_BC5_R     = 0U,
+    KHR_DF_CHANNEL_BC5_GREEN = 1U,
+    KHR_DF_CHANNEL_BC5_G     = 1U,
+    /* MODEL_BC6H */
+    KHR_DF_CHANNEL_BC6H_COLOR = 0U,
+    KHR_DF_CHANNEL_BC6H_DATA = 0U,
+    /* MODEL_BC7 */
+    KHR_DF_CHANNEL_BC7_DATA = 0U,
+    KHR_DF_CHANNEL_BC7_COLOR = 0U,
+    /* MODEL_ETC1 */
+    KHR_DF_CHANNEL_ETC1_DATA  = 0U,
+    KHR_DF_CHANNEL_ETC1_COLOR = 0U,
+    /* MODEL_ETC2 */
+    KHR_DF_CHANNEL_ETC2_RED   = 0U,
+    KHR_DF_CHANNEL_ETC2_R     = 0U,
+    KHR_DF_CHANNEL_ETC2_GREEN = 1U,
+    KHR_DF_CHANNEL_ETC2_G     = 1U,
+    KHR_DF_CHANNEL_ETC2_COLOR = 2U,
+    KHR_DF_CHANNEL_ETC2_ALPHA = 15U,
+    KHR_DF_CHANNEL_ETC2_A     = 15U,
+    /* MODEL_ASTC */
+    KHR_DF_CHANNEL_ASTC_DATA  = 0U,
+    /* MODEL_ETC1S */
+    KHR_DF_CHANNEL_ETC1S_RGB   = 0U,
+    KHR_DF_CHANNEL_ETC1S_RRR   = 3U,
+    KHR_DF_CHANNEL_ETC1S_GGG   = 4U,
+    KHR_DF_CHANNEL_ETC1S_AAA   = 15U,
+    /* MODEL_PVRTC */
+    KHR_DF_CHANNEL_PVRTC_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC_COLOR = 0U,
+    /* MODEL_PVRTC2 */
+    KHR_DF_CHANNEL_PVRTC2_DATA  = 0U,
+    KHR_DF_CHANNEL_PVRTC2_COLOR = 0U,
+    /* MODEL UASTC */
+    KHR_DF_CHANNEL_UASTC_DATA  = 0U,
+    KHR_DF_CHANNEL_UASTC_RGB   = 0U,
+    KHR_DF_CHANNEL_UASTC_RGBA  = 3U,
+    KHR_DF_CHANNEL_UASTC_RRR   = 4U,
+    KHR_DF_CHANNEL_UASTC_RRRG  = 5U,
+    KHR_DF_CHANNEL_UASTC_RG    = 6U,
+
+    /* Common channel names shared by multiple formats */
+    KHR_DF_CHANNEL_COMMON_LUMA    =  0U,
+    KHR_DF_CHANNEL_COMMON_L       =  0U,
+    KHR_DF_CHANNEL_COMMON_STENCIL = 13U,
+    KHR_DF_CHANNEL_COMMON_S       = 13U,
+    KHR_DF_CHANNEL_COMMON_DEPTH   = 14U,
+    KHR_DF_CHANNEL_COMMON_D       = 14U,
+    KHR_DF_CHANNEL_COMMON_ALPHA   = 15U,
+    KHR_DF_CHANNEL_COMMON_A       = 15U
+} khr_df_model_channels_e;
+
+/* Definition of the primary colors in color coordinates.
+   This is implicitly responsible for defining the conversion
+   between RGB an YUV color spaces.
+   LAB and related absolute color models should use
+   KHR_DF_PRIMARIES_CIEXYZ. */
+typedef enum _khr_df_primaries_e {
+    /* No color primaries defined */
+    KHR_DF_PRIMARIES_UNSPECIFIED = 0U,
+    /* Color primaries of ITU-R BT.709 and sRGB */
+    KHR_DF_PRIMARIES_BT709       = 1U,
+    /* Synonym for KHR_DF_PRIMARIES_BT709 */
+    KHR_DF_PRIMARIES_SRGB        = 1U,
+    /* Color primaries of ITU-R BT.601 (625-line EBU variant) */
+    KHR_DF_PRIMARIES_BT601_EBU   = 2U,
+    /* Color primaries of ITU-R BT.601 (525-line SMPTE C variant) */
+    KHR_DF_PRIMARIES_BT601_SMPTE = 3U,
+    /* Color primaries of ITU-R BT.2020 */
+    KHR_DF_PRIMARIES_BT2020      = 4U,
+    /* CIE theoretical color coordinate space */
+    KHR_DF_PRIMARIES_CIEXYZ      = 5U,
+    /* Academy Color Encoding System primaries */
+    KHR_DF_PRIMARIES_ACES        = 6U,
+    /* Color primaries of ACEScc */
+    KHR_DF_PRIMARIES_ACESCC      = 7U,
+    /* Legacy NTSC 1953 primaries */
+    KHR_DF_PRIMARIES_NTSC1953    = 8U,
+    /* Legacy PAL 525-line primaries */
+    KHR_DF_PRIMARIES_PAL525      = 9U,
+    /* Color primaries of Display P3 */
+    KHR_DF_PRIMARIES_DISPLAYP3   = 10U,
+    /* Color primaries of Adobe RGB (1998) */
+    KHR_DF_PRIMARIES_ADOBERGB    = 11U,
+    KHR_DF_PRIMARIES_MAX         = 0xFFU
+} khr_df_primaries_e;
+
+/* Definition of the optical to digital transfer function
+   ("gamma correction"). Most transfer functions are not a pure
+   power function and also include a linear element.
+   LAB and related absolute color representations should use
+   KHR_DF_TRANSFER_UNSPECIFIED. */
+typedef enum _khr_df_transfer_e {
+    /* No transfer function defined */
+    KHR_DF_TRANSFER_UNSPECIFIED = 0U,
+    /* Linear transfer function (value proportional to intensity) */
+    KHR_DF_TRANSFER_LINEAR      = 1U,
+    /* Perceptually-linear transfer function of sRGH (~2.4) */
+    KHR_DF_TRANSFER_SRGB        = 2U,
+    /* Perceptually-linear transfer function of ITU BT.601, BT.709 and BT.2020 (~1/.45) */
+    KHR_DF_TRANSFER_ITU         = 3U,
+    /* SMTPE170M (digital NTSC) defines an alias for the ITU transfer function (~1/.45) */
+    KHR_DF_TRANSFER_SMTPE170M   = 3U,
+    /* Perceptually-linear gamma function of original NTSC (simple 2.2 gamma) */
+    KHR_DF_TRANSFER_NTSC        = 4U,
+    /* Sony S-log used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG        = 5U,
+    /* Sony S-log 2 used by Sony video cameras */
+    KHR_DF_TRANSFER_SLOG2       = 6U,
+    /* ITU BT.1886 EOTF */
+    KHR_DF_TRANSFER_BT1886      = 7U,
+    /* ITU BT.2100 HLG OETF */
+    KHR_DF_TRANSFER_HLG_OETF    = 8U,
+    /* ITU BT.2100 HLG EOTF */
+    KHR_DF_TRANSFER_HLG_EOTF    = 9U,
+    /* ITU BT.2100 PQ EOTF */
+    KHR_DF_TRANSFER_PQ_EOTF     = 10U,
+    /* ITU BT.2100 PQ OETF */
+    KHR_DF_TRANSFER_PQ_OETF     = 11U,
+    /* DCI P3 transfer function */
+    KHR_DF_TRANSFER_DCIP3       = 12U,
+    /* Legacy PAL OETF */
+    KHR_DF_TRANSFER_PAL_OETF    = 13U,
+    /* Legacy PAL 625-line EOTF */
+    KHR_DF_TRANSFER_PAL625_EOTF = 14U,
+    /* Legacy ST240 transfer function */
+    KHR_DF_TRANSFER_ST240       = 15U,
+    /* ACEScc transfer function */
+    KHR_DF_TRANSFER_ACESCC      = 16U,
+    /* ACEScct transfer function */
+    KHR_DF_TRANSFER_ACESCCT     = 17U,
+    /* Adobe RGB (1998) transfer function */
+    KHR_DF_TRANSFER_ADOBERGB    = 18U,
+    KHR_DF_TRANSFER_MAX         = 0xFFU
+} khr_df_transfer_e;
+
+typedef enum _khr_df_flags_e {
+    KHR_DF_FLAG_ALPHA_STRAIGHT      = 0U,
+    KHR_DF_FLAG_ALPHA_PREMULTIPLIED = 1U
+} khr_df_flags_e;
+
+typedef enum _khr_df_sample_datatype_qualifiers_e {
+    KHR_DF_SAMPLE_DATATYPE_LINEAR = 1U << 4U,
+    KHR_DF_SAMPLE_DATATYPE_EXPONENT = 1U << 5U,
+    KHR_DF_SAMPLE_DATATYPE_SIGNED = 1U << 6U,
+    KHR_DF_SAMPLE_DATATYPE_FLOAT = 1U << 7U
+} khr_df_sample_datatype_qualifiers_e;
+
+#endif
diff --git a/thirdparty/libktx/lib/dfdutils/colourspaces.c b/thirdparty/libktx/lib/dfdutils/colourspaces.c
new file mode 100644
index 00000000000..0d998a71aa7
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/colourspaces.c
@@ -0,0 +1,51 @@
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Helper functions for colourspaces.
+ */
+
+#include <KHR/khr_df.h>
+#include "dfd.h"
+
+typedef struct s_PrimaryMapping {
+    khr_df_primaries_e  dfPrimaryEnum;
+    Primaries           primaries;
+} sPrimaryMapping;
+
+sPrimaryMapping primaryMap[] = {
+    { KHR_DF_PRIMARIES_BT709,       { 0.640f,0.330f,   0.300f,0.600f, 0.150f,0.060f,   0.3127f,0.3290f}},
+    { KHR_DF_PRIMARIES_BT601_EBU,   { 0.640f,0.330f,   0.290f,0.600f, 0.150f,0.060f,   0.3127f,0.3290f}},
+    { KHR_DF_PRIMARIES_BT601_SMPTE, { 0.630f,0.340f,   0.310f,0.595f, 0.155f,0.070f,   0.3127f,0.3290f}},
+    { KHR_DF_PRIMARIES_BT2020,      { 0.708f,0.292f,   0.170f,0.797f, 0.131f,0.046f,   0.3127f,0.3290f}},
+    { KHR_DF_PRIMARIES_CIEXYZ,      { 1.0f,0.0f,       0.0f,1.0f,     0.0f,0.0f,       0.0f,1.0f}},
+    { KHR_DF_PRIMARIES_ACES,        { 0.7347f,0.2653f, 0.0f,1.0f,     0.0001f,-0.077f, 0.32168f,0.33767f}},
+    { KHR_DF_PRIMARIES_ACESCC,      { 0.713f,0.293f,   0.165f,0.830f, 0.128f,0.044f,   0.32168f,0.33767f}},
+    { KHR_DF_PRIMARIES_NTSC1953,    { 0.67f,0.33f,     0.21f,0.71f,   0.14f,0.08f,     0.310f,0.316f}},
+    { KHR_DF_PRIMARIES_PAL525,      { 0.630f,0.340f,   0.310f,0.595f, 0.155f,0.070f,   0.3101f,0.3162f}},
+    { KHR_DF_PRIMARIES_DISPLAYP3,   { 0.6800f,0.3200f, 0.2650f,0.69f, 0.1500f,0.0600f, 0.3127f,0.3290f}},
+    { KHR_DF_PRIMARIES_ADOBERGB,    { 0.6400f,0.3300f, 0.2100f,0.71f, 0.1500f,0.0600f, 0.3127f,0.3290f}}};
+
+/**
+ * @brief Map a set of primaries to a KDFS primaries enum.
+ *
+ * @param[in] p           pointer to a Primaries struct filled in with the primary values.
+ * @param[in] latitude tolerance to use while matching. A suitable value might be 0.002
+ *                 but it depends on the application.
+ */
+khr_df_primaries_e findMapping(Primaries *p, float latitude) {
+    unsigned int i;
+    for (i = 0; i < sizeof(primaryMap)/sizeof(sPrimaryMapping); ++i) {
+        if (primaryMap[i].primaries.Rx - p->Rx <= latitude && p->Rx - primaryMap[i].primaries.Rx <= latitude &&
+            primaryMap[i].primaries.Gx - p->Gx <= latitude && p->Gx - primaryMap[i].primaries.Gx <= latitude &&
+            primaryMap[i].primaries.Bx - p->Bx <= latitude && p->Bx - primaryMap[i].primaries.Bx <= latitude &&
+            primaryMap[i].primaries.Wx - p->Wx <= latitude && p->Wx - primaryMap[i].primaries.Wx <= latitude) {
+            return primaryMap[i].dfPrimaryEnum;
+        }
+    }
+    /* No match */
+    return KHR_DF_PRIMARIES_UNSPECIFIED;
+}
diff --git a/thirdparty/libktx/lib/dfdutils/createdfd.c b/thirdparty/libktx/lib/dfdutils/createdfd.c
new file mode 100644
index 00000000000..ea00d8d7458
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/createdfd.c
@@ -0,0 +1,659 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Utilities for creating data format descriptors.
+ */
+
+/*
+ * Author: Andrew Garrard
+ */
+
+#include <stdlib.h>
+#include <KHR/khr_df.h>
+
+#include "dfd.h"
+
+typedef enum { i_COLOR, i_NON_COLOR } channels_infotype;
+
+static uint32_t *writeHeader(int numSamples, int bytes, int suffix,
+                             channels_infotype infotype)
+{
+    uint32_t *DFD = (uint32_t *) malloc(sizeof(uint32_t) *
+                                        (1 + KHR_DF_WORD_SAMPLESTART +
+                                         numSamples * KHR_DF_WORD_SAMPLEWORDS));
+    uint32_t* BDFD = DFD+1;
+    DFD[0] = sizeof(uint32_t) *
+        (1 + KHR_DF_WORD_SAMPLESTART +
+         numSamples * KHR_DF_WORD_SAMPLEWORDS);
+    BDFD[KHR_DF_WORD_VENDORID] =
+        (KHR_DF_VENDORID_KHRONOS << KHR_DF_SHIFT_VENDORID) |
+        (KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT << KHR_DF_SHIFT_DESCRIPTORTYPE);
+    BDFD[KHR_DF_WORD_VERSIONNUMBER] =
+        (KHR_DF_VERSIONNUMBER_LATEST << KHR_DF_SHIFT_VERSIONNUMBER) |
+        (((uint32_t)sizeof(uint32_t) *
+          (KHR_DF_WORD_SAMPLESTART +
+           numSamples * KHR_DF_WORD_SAMPLEWORDS)
+          << KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE));
+    BDFD[KHR_DF_WORD_MODEL] =
+        ((KHR_DF_MODEL_RGBSDA << KHR_DF_SHIFT_MODEL) | /* Only supported model */
+         (KHR_DF_FLAG_ALPHA_STRAIGHT << KHR_DF_SHIFT_FLAGS));
+    if (infotype == i_COLOR) {
+        BDFD[KHR_DF_WORD_PRIMARIES] |= KHR_DF_PRIMARIES_BT709 << KHR_DF_SHIFT_PRIMARIES; /* Assumed */
+    } else {
+        BDFD[KHR_DF_WORD_PRIMARIES] |= KHR_DF_PRIMARIES_UNSPECIFIED << KHR_DF_SHIFT_PRIMARIES;
+    }
+    if (suffix == s_SRGB) {
+        BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_SRGB << KHR_DF_SHIFT_TRANSFER;
+    } else {
+        BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_LINEAR << KHR_DF_SHIFT_TRANSFER;
+    }
+    BDFD[KHR_DF_WORD_TEXELBLOCKDIMENSION0] = 0; /* Only 1x1x1x1 texel blocks supported */
+    BDFD[KHR_DF_WORD_BYTESPLANE0] = bytes; /* bytesPlane0 = bytes, bytesPlane3..1 = 0 */
+    BDFD[KHR_DF_WORD_BYTESPLANE4] = 0; /* bytesPlane7..5 = 0 */
+    return DFD;
+}
+
+static uint32_t setChannelFlags(uint32_t channel, enum VkSuffix suffix)
+{
+    switch (suffix) {
+    case s_UNORM: break;
+    case s_SNORM:
+        channel |=
+            KHR_DF_SAMPLE_DATATYPE_SIGNED;
+        break;
+    case s_USCALED: break;
+    case s_SSCALED:
+        channel |=
+            KHR_DF_SAMPLE_DATATYPE_SIGNED;
+        break;
+    case s_UINT: break;
+    case s_SINT:
+        channel |=
+            KHR_DF_SAMPLE_DATATYPE_SIGNED;
+        break;
+    case s_SFLOAT:
+        channel |=
+            KHR_DF_SAMPLE_DATATYPE_FLOAT |
+            KHR_DF_SAMPLE_DATATYPE_SIGNED;
+        break;
+    case s_UFLOAT:
+        channel |=
+            KHR_DF_SAMPLE_DATATYPE_FLOAT;
+        break;
+    case s_SRGB:
+        if (channel == KHR_DF_CHANNEL_RGBSDA_ALPHA) {
+            channel |= KHR_DF_SAMPLE_DATATYPE_LINEAR;
+        }
+        break;
+    }
+    return channel;
+}
+
+static void writeSample(uint32_t *DFD, int sampleNo, int channel,
+                        int bits, int offset,
+                        int topSample, int bottomSample, enum VkSuffix suffix)
+{
+    // Use this to avoid type-punning complaints from the gcc optimizer
+    // with -Wall.
+    union {
+        uint32_t i;
+        float f;
+    } lower, upper;
+    uint32_t *sample = DFD + 1 + KHR_DF_WORD_SAMPLESTART + sampleNo * KHR_DF_WORD_SAMPLEWORDS;
+    if (channel == 3) channel = KHR_DF_CHANNEL_RGBSDA_ALPHA;
+
+    if (channel == 3) channel = KHR_DF_CHANNEL_RGBSDA_ALPHA;
+    channel = setChannelFlags(channel, suffix);
+
+    sample[KHR_DF_SAMPLEWORD_BITOFFSET] =
+        (offset << KHR_DF_SAMPLESHIFT_BITOFFSET) |
+        ((bits - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) |
+        (channel << KHR_DF_SAMPLESHIFT_CHANNELID);
+
+    sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0;
+
+    switch (suffix) {
+    case s_UNORM:
+    case s_SRGB:
+    default:
+        if (bits > 32) {
+            upper.i = 0xFFFFFFFFU;
+        } else {
+            upper.i = (uint32_t)((1U << bits) - 1U);
+        }
+        lower.i = 0U;
+        break;
+    case s_SNORM:
+        if (bits > 32) {
+            upper.i = 0x7FFFFFFF;
+        } else {
+            upper.i = topSample ? (1U << (bits - 1)) - 1 : (1U << bits) - 1;
+        }
+        lower.i = ~upper.i;
+        if (bottomSample) lower.i += 1;
+        break;
+    case s_USCALED:
+    case s_UINT:
+        upper.i = bottomSample ? 1U : 0U;
+        lower.i = 0U;
+        break;
+    case s_SSCALED:
+    case s_SINT:
+        upper.i = bottomSample ? 1U : 0U;
+        lower.i = ~0U;
+        break;
+    case s_SFLOAT:
+        upper.f = 1.0f;
+        lower.f = -1.0f;
+        break;
+    case s_UFLOAT:
+        upper.f = 1.0f;
+        lower.f = 0.0f;
+        break;
+    }
+    sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i;
+    sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i;
+}
+
+/**
+ * @~English
+ * @brief Create a Data Format Descriptor for an unpacked format.
+ *
+ * @param bigEndian Set to 1 for big-endian byte ordering and
+                    0 for little-endian byte ordering.
+ * @param numChannels The number of color channels.
+ * @param bytes The number of bytes per channel.
+ * @param redBlueSwap Normally channels appear in consecutive R, G, B, A order
+ *                    in memory; redBlueSwap inverts red and blue, allowing
+ *                    B, G, R, A.
+ * @param suffix Indicates the format suffix for the type.
+ *
+ * @return A data format descriptor in malloc'd data. The caller is responsible
+ *         for freeing the descriptor.
+ **/
+uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
+                            int redBlueSwap, enum VkSuffix suffix)
+{
+    uint32_t *DFD;
+    if (bigEndian) {
+        int channelCounter, channelByte;
+        /* Number of samples = number of channels * bytes per channel */
+        DFD = writeHeader(numChannels * bytes, numChannels * bytes, suffix, i_COLOR);
+        /* First loop over the channels */
+        for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) {
+            int channel = channelCounter;
+            if (redBlueSwap && (channel == 0 || channel == 2)) {
+                channel ^= 2;
+            }
+            /* Loop over the bytes that constitute a channel */
+            for (channelByte = 0; channelByte < bytes; ++channelByte) {
+                writeSample(DFD, channelCounter * bytes + channelByte, channel,
+                            8, 8 * (channelCounter * bytes + bytes - channelByte - 1),
+                            channelByte == bytes-1, channelByte == 0, suffix);
+            }
+        }
+
+    } else { /* Little-endian */
+
+        int sampleCounter;
+        /* One sample per channel */
+        DFD = writeHeader(numChannels, numChannels * bytes, suffix, i_COLOR);
+        for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) {
+            int channel = sampleCounter;
+            if (redBlueSwap && (channel == 0 || channel == 2)) {
+                channel ^= 2;
+            }
+            writeSample(DFD, sampleCounter, channel,
+                        8 * bytes, 8 * sampleCounter * bytes,
+                        1, 1, suffix);
+        }
+    }
+    return DFD;
+}
+
+/**
+ * @~English
+ * @brief Create a Data Format Descriptor for a packed format.
+ *
+ * @param bigEndian Big-endian flag: Set to 1 for big-endian byte ordering and
+ *                  0 for little-endian byte ordering.
+ * @param numChannels The number of color channels.
+ * @param bits[] An array of length numChannels.
+ *               Each entry is the number of bits composing the channel, in
+ *               order starting at bit 0 of the packed type.
+ * @param channels[] An array of length numChannels.
+ *                   Each entry enumerates the channel type: 0 = red, 1 = green,
+ *                   2 = blue, 15 = alpha, in order starting at bit 0 of the
+ *                   packed type. These values match channel IDs for RGBSDA in
+ *                   the Khronos Data Format header. To simplify iteration
+ *                   through channels, channel id 3 is a synonym for alpha.
+ * @param suffix Indicates the format suffix for the type.
+ *
+ * @return A data format descriptor in malloc'd data. The caller is responsible
+ *         for freeing the descriptor.
+ **/
+uint32_t *createDFDPacked(int bigEndian, int numChannels,
+                          int bits[], int channels[],
+                          enum VkSuffix suffix)
+{
+    uint32_t *DFD = 0;
+    if (numChannels == 6) {
+        /* Special case E5B9G9R9 */
+        DFD = writeHeader(numChannels, 4, s_UFLOAT, i_COLOR);
+        writeSample(DFD, 0, 0,
+                    9, 0,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 0, SAMPLEUPPER, 8448);
+        writeSample(DFD, 1, 0 | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+                    5, 27,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 1, SAMPLELOWER, 15);
+        KHR_DFDSETSVAL((DFD+1), 1, SAMPLEUPPER, 31);
+        writeSample(DFD, 2, 1,
+                    9, 9,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 2, SAMPLEUPPER, 8448);
+        writeSample(DFD, 3, 1 | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+                    5, 27,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 3, SAMPLELOWER, 15);
+        KHR_DFDSETSVAL((DFD+1), 3, SAMPLEUPPER, 31);
+        writeSample(DFD, 4, 2,
+                    9, 18,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 4, SAMPLEUPPER, 8448);
+        writeSample(DFD, 5, 2 | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+                    5, 27,
+                    1, 1, s_UNORM);
+        KHR_DFDSETSVAL((DFD+1), 5, SAMPLELOWER, 15);
+        KHR_DFDSETSVAL((DFD+1), 5, SAMPLEUPPER, 31);
+    } else if (bigEndian) {
+        /* No packed format is larger than 32 bits. */
+        /* No packed channel crosses more than two bytes. */
+        int totalBits = 0;
+        int bitChannel[32];
+        int beChannelStart[4];
+        int channelCounter;
+        int bitOffset = 0;
+        int BEMask;
+        int numSamples = numChannels;
+        int sampleCounter;
+        for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) {
+            beChannelStart[channelCounter] = totalBits;
+            totalBits += bits[channelCounter];
+        }
+        BEMask = (totalBits - 1) & 0x18;
+        for (channelCounter = 0; channelCounter < numChannels; ++channelCounter) {
+            bitChannel[bitOffset ^ BEMask] = channelCounter;
+            if (((bitOffset + bits[channelCounter] - 1) & ~7) != (bitOffset & ~7)) {
+                /* Continuation sample */
+                bitChannel[((bitOffset + bits[channelCounter] - 1) & ~7) ^ BEMask] = channelCounter;
+                numSamples++;
+            }
+            bitOffset += bits[channelCounter];
+        }
+        DFD = writeHeader(numSamples, totalBits >> 3, suffix, i_COLOR);
+
+        sampleCounter = 0;
+        for (bitOffset = 0; bitOffset < totalBits;) {
+            if (bitChannel[bitOffset] == -1) {
+                /* Done this bit, so this is the lower half of something. */
+                /* We must therefore jump to the end of the byte and continue. */
+                bitOffset = (bitOffset + 8) & ~7;
+            } else {
+                /* Start of a channel? */
+                int thisChannel = bitChannel[bitOffset];
+                if ((beChannelStart[thisChannel] ^ BEMask) == bitOffset) {
+                    /* Must be just one sample if we hit it first. */
+                    writeSample(DFD, sampleCounter++, channels[thisChannel],
+                                    bits[thisChannel], bitOffset,
+                                    1, 1, suffix);
+                    bitOffset += bits[thisChannel];
+                } else {
+                    /* Two samples. Move to the end of the first one we hit when we're done. */
+                    int firstSampleBits = 8 - (beChannelStart[thisChannel] & 0x7); /* Rest of the byte */
+                    int secondSampleBits = bits[thisChannel] - firstSampleBits; /* Rest of the bits */
+                    writeSample(DFD, sampleCounter++, channels[thisChannel],
+                                firstSampleBits, beChannelStart[thisChannel] ^ BEMask,
+                                0, 1, suffix);
+                    /* Mark that we've already handled this sample */
+                    bitChannel[beChannelStart[thisChannel] ^ BEMask] = -1;
+                    writeSample(DFD, sampleCounter++, channels[thisChannel],
+                                secondSampleBits, bitOffset,
+                                1, 0, suffix);
+                    bitOffset += secondSampleBits;
+                }
+            }
+        }
+
+    } else { /* Little-endian */
+
+        int sampleCounter;
+        int totalBits = 0;
+        int bitOffset = 0;
+        for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) {
+            totalBits += bits[sampleCounter];
+        }
+
+        /* One sample per channel */
+        DFD = writeHeader(numChannels, totalBits >> 3, suffix, i_COLOR);
+        for (sampleCounter = 0; sampleCounter < numChannels; ++sampleCounter) {
+            writeSample(DFD, sampleCounter, channels[sampleCounter],
+                        bits[sampleCounter], bitOffset,
+                        1, 1, suffix);
+            bitOffset += bits[sampleCounter];
+        }
+    }
+    return DFD;
+}
+
+static khr_df_model_e compModelMapping[] = {
+    KHR_DF_MODEL_BC1A,   /*!< BC1, aka DXT1, no alpha. */
+    KHR_DF_MODEL_BC1A,   /*!< BC1, aka DXT1, punch-through alpha. */
+    KHR_DF_MODEL_BC2,    /*!< BC2, aka DXT2 and DXT3. */
+    KHR_DF_MODEL_BC3,    /*!< BC3, aka DXT4 and DXT5. */
+    KHR_DF_MODEL_BC4,    /*!< BC4. */
+    KHR_DF_MODEL_BC5,    /*!< BC5. */
+    KHR_DF_MODEL_BC6H,   /*!< BC6h HDR format. */
+    KHR_DF_MODEL_BC7,    /*!< BC7. */
+    KHR_DF_MODEL_ETC2,   /*!< ETC2 no alpha. */
+    KHR_DF_MODEL_ETC2,   /*!< ETC2 punch-through alpha. */
+    KHR_DF_MODEL_ETC2,   /*!< ETC2 independent alpha. */
+    KHR_DF_MODEL_ETC2,   /*!< R11 ETC2 single-channel. */
+    KHR_DF_MODEL_ETC2,   /*!< R11G11 ETC2 dual-channel. */
+    KHR_DF_MODEL_ASTC,   /*!< ASTC. */
+    KHR_DF_MODEL_ETC1S,  /*!< ETC1S. */
+    KHR_DF_MODEL_PVRTC,  /*!< PVRTC(1). */
+    KHR_DF_MODEL_PVRTC2  /*!< PVRTC2. */
+};
+
+static uint32_t compSampleCount[] = {
+    1U, /*!< BC1, aka DXT1, no alpha. */
+    1U, /*!< BC1, aka DXT1, punch-through alpha. */
+    2U, /*!< BC2, aka DXT2 and DXT3. */
+    2U, /*!< BC3, aka DXT4 and DXT5. */
+    1U, /*!< BC4. */
+    2U, /*!< BC5. */
+    1U, /*!< BC6h HDR format. */
+    1U, /*!< BC7. */
+    1U, /*!< ETC2 no alpha. */
+    2U, /*!< ETC2 punch-through alpha. */
+    2U, /*!< ETC2 independent alpha. */
+    1U, /*!< R11 ETC2 single-channel. */
+    2U, /*!< R11G11 ETC2 dual-channel. */
+    1U, /*!< ASTC. */
+    1U, /*!< ETC1S. */
+    1U, /*!< PVRTC. */
+    1U  /*!< PVRTC2. */
+};
+
+static khr_df_model_channels_e compFirstChannel[] = {
+    KHR_DF_CHANNEL_BC1A_COLOR,        /*!< BC1, aka DXT1, no alpha. */
+    KHR_DF_CHANNEL_BC1A_ALPHAPRESENT, /*!< BC1, aka DXT1, punch-through alpha. */
+    KHR_DF_CHANNEL_BC2_ALPHA,         /*!< BC2, aka DXT2 and DXT3. */
+    KHR_DF_CHANNEL_BC3_ALPHA,         /*!< BC3, aka DXT4 and DXT5. */
+    KHR_DF_CHANNEL_BC4_DATA,          /*!< BC4. */
+    KHR_DF_CHANNEL_BC5_RED,           /*!< BC5. */
+    KHR_DF_CHANNEL_BC6H_COLOR,        /*!< BC6h HDR format. */
+    KHR_DF_CHANNEL_BC7_COLOR,         /*!< BC7. */
+    KHR_DF_CHANNEL_ETC2_COLOR,        /*!< ETC2 no alpha. */
+    KHR_DF_CHANNEL_ETC2_COLOR,        /*!< ETC2 punch-through alpha. */
+    KHR_DF_CHANNEL_ETC2_ALPHA,        /*!< ETC2 independent alpha. */
+    KHR_DF_CHANNEL_ETC2_RED,          /*!< R11 ETC2 single-channel. */
+    KHR_DF_CHANNEL_ETC2_RED,          /*!< R11G11 ETC2 dual-channel. */
+    KHR_DF_CHANNEL_ASTC_DATA,         /*!< ASTC. */
+    KHR_DF_CHANNEL_ETC1S_RGB,         /*!< ETC1S. */
+    KHR_DF_CHANNEL_PVRTC_COLOR,       /*!< PVRTC. */
+    KHR_DF_CHANNEL_PVRTC2_COLOR       /*!< PVRTC2. */
+};
+
+static khr_df_model_channels_e compSecondChannel[] = {
+    KHR_DF_CHANNEL_BC1A_COLOR,        /*!< BC1, aka DXT1, no alpha. */
+    KHR_DF_CHANNEL_BC1A_ALPHAPRESENT, /*!< BC1, aka DXT1, punch-through alpha. */
+    KHR_DF_CHANNEL_BC2_COLOR,         /*!< BC2, aka DXT2 and DXT3. */
+    KHR_DF_CHANNEL_BC3_COLOR,         /*!< BC3, aka DXT4 and DXT5. */
+    KHR_DF_CHANNEL_BC4_DATA,          /*!< BC4. */
+    KHR_DF_CHANNEL_BC5_GREEN,         /*!< BC5. */
+    KHR_DF_CHANNEL_BC6H_COLOR,        /*!< BC6h HDR format. */
+    KHR_DF_CHANNEL_BC7_COLOR,         /*!< BC7. */
+    KHR_DF_CHANNEL_ETC2_COLOR,        /*!< ETC2 no alpha. */
+    KHR_DF_CHANNEL_ETC2_ALPHA,        /*!< ETC2 punch-through alpha. */
+    KHR_DF_CHANNEL_ETC2_COLOR,        /*!< ETC2 independent alpha. */
+    KHR_DF_CHANNEL_ETC2_RED,          /*!< R11 ETC2 single-channel. */
+    KHR_DF_CHANNEL_ETC2_GREEN,        /*!< R11G11 ETC2 dual-channel. */
+    KHR_DF_CHANNEL_ASTC_DATA,         /*!< ASTC. */
+    KHR_DF_CHANNEL_ETC1S_RGB,         /*!< ETC1S. */
+    KHR_DF_CHANNEL_PVRTC_COLOR,       /*!< PVRTC. */
+    KHR_DF_CHANNEL_PVRTC2_COLOR       /*!< PVRTC2. */
+};
+
+static uint32_t compSecondChannelOffset[] = {
+    0U,  /*!< BC1, aka DXT1, no alpha. */
+    0U,  /*!< BC1, aka DXT1, punch-through alpha. */
+    64U, /*!< BC2, aka DXT2 and DXT3. */
+    64U, /*!< BC3, aka DXT4 and DXT5. */
+    0U,  /*!< BC4. */
+    64U, /*!< BC5. */
+    0U,  /*!< BC6h HDR format. */
+    0U,  /*!< BC7. */
+    0U,  /*!< ETC2 no alpha. */
+    0U,  /*!< ETC2 punch-through alpha. */
+    64U, /*!< ETC2 independent alpha. */
+    0U,  /*!< R11 ETC2 single-channel. */
+    64U, /*!< R11G11 ETC2 dual-channel. */
+    0U,  /*!< ASTC. */
+    0U,  /*!< ETC1S. */
+    0U,  /*!< PVRTC. */
+    0U   /*!< PVRTC2. */
+};
+
+static uint32_t compChannelBits[] = {
+    64U,  /*!< BC1, aka DXT1, no alpha. */
+    64U,  /*!< BC1, aka DXT1, punch-through alpha. */
+    64U,  /*!< BC2, aka DXT2 and DXT3. */
+    64U,  /*!< BC3, aka DXT4 and DXT5. */
+    64U,  /*!< BC4. */
+    64U,  /*!< BC5. */
+    128U, /*!< BC6h HDR format. */
+    128U, /*!< BC7. */
+    64U,  /*!< ETC2 no alpha. */
+    64U,  /*!< ETC2 punch-through alpha. */
+    64U,  /*!< ETC2 independent alpha. */
+    64U,  /*!< R11 ETC2 single-channel. */
+    64U,  /*!< R11G11 ETC2 dual-channel. */
+    128U, /*!< ASTC. */
+    64U,  /*!< ETC1S. */
+    64U,  /*!< PVRTC. */
+    64U   /*!< PVRTC2. */
+};
+
+static uint32_t compBytes[] = {
+    8U,  /*!< BC1, aka DXT1, no alpha. */
+    8U,  /*!< BC1, aka DXT1, punch-through alpha. */
+    16U, /*!< BC2, aka DXT2 and DXT3. */
+    16U, /*!< BC3, aka DXT4 and DXT5. */
+    8U,  /*!< BC4. */
+    16U, /*!< BC5. */
+    16U, /*!< BC6h HDR format. */
+    16U, /*!< BC7. */
+    8U,  /*!< ETC2 no alpha. */
+    8U,  /*!< ETC2 punch-through alpha. */
+    16U, /*!< ETC2 independent alpha. */
+    8U,  /*!< R11 ETC2 single-channel. */
+    16U, /*!< R11G11 ETC2 dual-channel. */
+    16U, /*!< ASTC. */
+    8U,  /*!< ETC1S. */
+    8U,  /*!< PVRTC. */
+    8U   /*!< PVRTC2. */
+};
+
+/**
+ * @~English
+ * @brief Create a Data Format Descriptor for a compressed format.
+ *
+ * @param compScheme Vulkan-style compression scheme enumeration.
+ * @param bwidth Block width in texel coordinates.
+ * @param bheight Block height in texel coordinates.
+ * @param bdepth Block depth in texel coordinates.
+ * @author Mark Callow, Edgewise Consulting.
+ * @param suffix Indicates the format suffix for the type.
+ *
+ * @return A data format descriptor in malloc'd data. The caller is responsible
+ *         for freeing the descriptor.
+ **/
+uint32_t *createDFDCompressed(enum VkCompScheme compScheme, int bwidth, int bheight, int bdepth,
+                              enum VkSuffix suffix)
+{
+    uint32_t *DFD = 0;
+    uint32_t numSamples = compSampleCount[compScheme];
+    uint32_t* BDFD;
+    uint32_t *sample;
+    uint32_t channel;
+    // Use union to avoid type-punning complaints from gcc optimizer
+    // with -Wall.
+    union {
+        uint32_t i;
+        float f;
+    } lower, upper;
+
+    DFD = (uint32_t *) malloc(sizeof(uint32_t) *
+                              (1 + KHR_DF_WORD_SAMPLESTART +
+                               numSamples * KHR_DF_WORD_SAMPLEWORDS));
+    BDFD = DFD+1;
+    DFD[0] = sizeof(uint32_t) *
+        (1 + KHR_DF_WORD_SAMPLESTART +
+         numSamples * KHR_DF_WORD_SAMPLEWORDS);
+    BDFD[KHR_DF_WORD_VENDORID] =
+        (KHR_DF_VENDORID_KHRONOS << KHR_DF_SHIFT_VENDORID) |
+        (KHR_DF_KHR_DESCRIPTORTYPE_BASICFORMAT << KHR_DF_SHIFT_DESCRIPTORTYPE);
+    BDFD[KHR_DF_WORD_VERSIONNUMBER] =
+        (KHR_DF_VERSIONNUMBER_LATEST << KHR_DF_SHIFT_VERSIONNUMBER) |
+        (((uint32_t)sizeof(uint32_t) *
+          (KHR_DF_WORD_SAMPLESTART +
+           numSamples * KHR_DF_WORD_SAMPLEWORDS)
+          << KHR_DF_SHIFT_DESCRIPTORBLOCKSIZE));
+    BDFD[KHR_DF_WORD_MODEL] =
+        ((compModelMapping[compScheme] << KHR_DF_SHIFT_MODEL) |
+         (KHR_DF_PRIMARIES_BT709 << KHR_DF_SHIFT_PRIMARIES) | /* Assumed */
+         (KHR_DF_FLAG_ALPHA_STRAIGHT << KHR_DF_SHIFT_FLAGS));
+
+    if (suffix == s_SRGB) {
+        BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_SRGB << KHR_DF_SHIFT_TRANSFER;
+    } else {
+        BDFD[KHR_DF_WORD_TRANSFER] |= KHR_DF_TRANSFER_LINEAR << KHR_DF_SHIFT_TRANSFER;
+    }
+    BDFD[KHR_DF_WORD_TEXELBLOCKDIMENSION0] =
+        (bwidth - 1) | ((bheight - 1) << KHR_DF_SHIFT_TEXELBLOCKDIMENSION1) | ((bdepth - 1) << KHR_DF_SHIFT_TEXELBLOCKDIMENSION2);
+    /* bytesPlane0 = bytes, bytesPlane3..1 = 0 */
+    BDFD[KHR_DF_WORD_BYTESPLANE0] = compBytes[compScheme];
+    BDFD[KHR_DF_WORD_BYTESPLANE4] = 0; /* bytesPlane7..5 = 0 */
+
+    sample = BDFD + KHR_DF_WORD_SAMPLESTART;
+    channel = compFirstChannel[compScheme];
+    channel = setChannelFlags(channel, suffix);
+
+    sample[KHR_DF_SAMPLEWORD_BITOFFSET] =
+        (0 << KHR_DF_SAMPLESHIFT_BITOFFSET) |
+        ((compChannelBits[compScheme] - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) |
+        (channel << KHR_DF_SAMPLESHIFT_CHANNELID);
+
+    sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0;
+    switch (suffix) {
+    case s_UNORM:
+    case s_SRGB:
+    default:
+        upper.i = 0xFFFFFFFFU;
+        lower.i = 0U;
+        break;
+    case s_SNORM:
+        upper.i = 0x7FFFFFFF;
+        lower.i = ~upper.i;
+        break;
+    case s_USCALED:
+    case s_UINT:
+        upper.i = 1U;
+        lower.i = 0U;
+        break;
+    case s_SSCALED:
+    case s_SINT:
+        upper.i = 1U;
+        lower.i = ~0U;
+        break;
+    case s_SFLOAT:
+        upper.f = 1.0f;
+        lower.f = -1.0f;
+        break;
+    case s_UFLOAT:
+        upper.f = 1.0f;
+        lower.f = 0.0f;
+        break;
+    }
+    sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i;
+    sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i;
+
+    if (compSampleCount[compScheme] > 1) {
+        sample += KHR_DF_WORD_SAMPLEWORDS;
+        channel = compSecondChannel[compScheme];
+        channel = setChannelFlags(channel, suffix);
+
+        sample[KHR_DF_SAMPLEWORD_BITOFFSET] =
+            (compSecondChannelOffset[compScheme] << KHR_DF_SAMPLESHIFT_BITOFFSET) |
+            ((compChannelBits[compScheme] - 1) << KHR_DF_SAMPLESHIFT_BITLENGTH) |
+            (channel << KHR_DF_SAMPLESHIFT_CHANNELID);
+
+        sample[KHR_DF_SAMPLEWORD_SAMPLEPOSITION_ALL] = 0;
+
+        sample[KHR_DF_SAMPLEWORD_SAMPLELOWER] = lower.i;
+        sample[KHR_DF_SAMPLEWORD_SAMPLEUPPER] = upper.i;
+    }
+    return DFD;
+}
+
+/**
+ * @~English
+ * @brief Create a Data Format Descriptor for a depth-stencil format.
+ *
+ * @param depthBits   The numeber of bits in the depth channel.
+ * @param stencilBits The numeber of bits in the stencil channel.
+ * @param sizeBytes   The total byte size of the texel.
+ *
+ * @return A data format descriptor in malloc'd data. The caller is responsible
+ *         for freeing the descriptor.
+ **/
+uint32_t *createDFDDepthStencil(int depthBits,
+                                int stencilBits,
+                                int sizeBytes)
+{
+    /* N.B. Little-endian is assumed. */
+    uint32_t *DFD = 0;
+    DFD = writeHeader((depthBits > 0) + (stencilBits > 0),
+                      sizeBytes, s_UNORM, i_NON_COLOR);
+    if (depthBits == 32) {
+        writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_DEPTH,
+                    32, 0,
+                    1, 1, s_SFLOAT);
+    } else if (depthBits > 0) {
+        writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_DEPTH,
+                    depthBits, 0,
+                    1, 1, s_UNORM);
+    }
+    if (stencilBits > 0) {
+        if (depthBits > 0) {
+            writeSample(DFD, 1, KHR_DF_CHANNEL_RGBSDA_STENCIL,
+                        stencilBits, depthBits,
+                        1, 1, s_UINT);
+        } else {
+            writeSample(DFD, 0, KHR_DF_CHANNEL_RGBSDA_STENCIL,
+                        stencilBits, 0,
+                        1, 1, s_UINT);
+        }
+    }
+    return DFD;
+}
diff --git a/thirdparty/libktx/lib/dfdutils/dfd.h b/thirdparty/libktx/lib/dfdutils/dfd.h
new file mode 100644
index 00000000000..3ba0249dbb2
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/dfd.h
@@ -0,0 +1,170 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Header file defining the data format descriptor utilities API.
+ */
+
+/*
+ * Author: Andrew Garrard
+ */
+
+#ifndef _DFD_H_
+#define _DFD_H_
+
+#include <KHR/khr_df.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Qualifier suffix to the format, in Vulkan terms. */
+enum VkSuffix {
+    s_UNORM,   /*!< Unsigned normalized format. */
+    s_SNORM,   /*!< Signed normalized format. */
+    s_USCALED, /*!< Unsigned scaled format. */
+    s_SSCALED, /*!< Signed scaled format. */
+    s_UINT,    /*!< Unsigned integer format. */
+    s_SINT,    /*!< Signed integer format. */
+    s_SFLOAT,  /*!< Signed float format. */
+    s_UFLOAT,  /*!< Unsigned float format. */
+    s_SRGB     /*!< sRGB normalized format. */
+};
+
+/** Compression scheme, in Vulkan terms. */
+enum VkCompScheme {
+    c_BC1_RGB,       /*!< BC1, aka DXT1, no alpha. */
+    c_BC1_RGBA,      /*!< BC1, aka DXT1, punch-through alpha. */
+    c_BC2,           /*!< BC2, aka DXT2 and DXT3. */
+    c_BC3,           /*!< BC3, aka DXT4 and DXT5. */
+    c_BC4,           /*!< BC4. */
+    c_BC5,           /*!< BC5. */
+    c_BC6H,          /*!< BC6h HDR format. */
+    c_BC7,           /*!< BC7. */
+    c_ETC2_R8G8B8,   /*!< ETC2 no alpha. */
+    c_ETC2_R8G8B8A1, /*!< ETC2 punch-through alpha. */
+    c_ETC2_R8G8B8A8, /*!< ETC2 independent alpha. */
+    c_EAC_R11,       /*!< R11 ETC2 single-channel. */
+    c_EAC_R11G11,    /*!< R11G11 ETC2 dual-channel. */
+    c_ASTC,          /*!< ASTC. */
+    c_ETC1S,         /*!< ETC1S. */
+    c_PVRTC,         /*!< PVRTC(1). */
+    c_PVRTC2         /*!< PVRTC2. */
+};
+
+typedef unsigned int uint32_t;
+
+#if !defined(LIBKTX)
+#include <vulkan/vulkan_core.h>
+#else
+#include "../vkformat_enum.h"
+#endif
+
+uint32_t* vk2dfd(enum VkFormat format);
+
+/* Create a Data Format Descriptor for an unpacked format. */
+uint32_t *createDFDUnpacked(int bigEndian, int numChannels, int bytes,
+                            int redBlueSwap, enum VkSuffix suffix);
+
+/* Create a Data Format Descriptor for a packed format. */
+uint32_t *createDFDPacked(int bigEndian, int numChannels,
+                          int bits[], int channels[],
+                          enum VkSuffix suffix);
+
+/* Create a Data Format Descriptor for a compressed format. */
+uint32_t *createDFDCompressed(enum VkCompScheme compScheme,
+                              int bwidth, int bheight, int bdepth,
+                              enum VkSuffix suffix);
+
+/* Create a Data Format Descriptor for a depth/stencil format. */
+uint32_t *createDFDDepthStencil(int depthBits,
+                                int stencilBits,
+                                int sizeBytes);
+
+/** @brief Result of interpreting the data format descriptor. */
+enum InterpretDFDResult {
+    i_LITTLE_ENDIAN_FORMAT_BIT = 0, /*!< Confirmed little-endian (default for 8bpc). */
+    i_BIG_ENDIAN_FORMAT_BIT = 1,    /*!< Confirmed big-endian. */
+    i_PACKED_FORMAT_BIT = 2,        /*!< Packed format. */
+    i_SRGB_FORMAT_BIT = 4,          /*!< sRGB transfer function. */
+    i_NORMALIZED_FORMAT_BIT = 8,    /*!< Normalized (UNORM or SNORM). */
+    i_SIGNED_FORMAT_BIT = 16,       /*!< Format is signed. */
+    i_FLOAT_FORMAT_BIT = 32,        /*!< Format is floating point. */
+    i_UNSUPPORTED_ERROR_BIT = 64,   /*!< Format not successfully interpreted. */
+    /** "NONTRIVIAL_ENDIANNESS" means not big-endian, not little-endian
+     * (a channel has bits that are not consecutive in either order). **/
+    i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS     = i_UNSUPPORTED_ERROR_BIT,
+    /** "MULTIPLE_SAMPLE_LOCATIONS" is an error because only single-sample
+     * texel blocks (with coordinates 0,0,0,0 for all samples) are supported. **/
+    i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS = i_UNSUPPORTED_ERROR_BIT + 1,
+    /** "MULTIPLE_PLANES" is an error because only contiguous data is supported. */
+    i_UNSUPPORTED_MULTIPLE_PLANES           = i_UNSUPPORTED_ERROR_BIT + 2,
+    /** Only channels R, G, B and A are supported. */
+    i_UNSUPPORTED_CHANNEL_TYPES             = i_UNSUPPORTED_ERROR_BIT + 3,
+    /** Only channels with the same flags are supported
+     * (e.g. we don't support float red with integer green). */
+    i_UNSUPPORTED_MIXED_CHANNELS            = i_UNSUPPORTED_ERROR_BIT + 4
+};
+
+/** @brief Interpretation of a channel from the data format descriptor. */
+typedef struct _InterpretedDFDChannel {
+    uint32_t offset; /*!< Offset in bits for packed, bytes for unpacked. */
+    uint32_t size;   /*!< Size in bits for packed, bytes for unpacked. */
+} InterpretedDFDChannel;
+
+/* Interpret a Data Format Descriptor. */
+enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
+                                     InterpretedDFDChannel *R,
+                                     InterpretedDFDChannel *G,
+                                     InterpretedDFDChannel *B,
+                                     InterpretedDFDChannel *A,
+                                     uint32_t *wordBytes);
+
+/* Print a human-readable interpretation of a data format descriptor. */
+void printDFD(uint32_t *DFD);
+
+/* Get the number of components & component size from a DFD for an
+ * unpacked format.
+ */
+void
+getDFDComponentInfoUnpacked(const uint32_t* DFD, uint32_t* numComponents,
+                            uint32_t* componentByteLength);
+
+/* Return the number of components described by a DFD. */
+uint32_t getDFDNumComponents(const uint32_t* DFD);
+
+/* Recreate and return the value of bytesPlane0 as it should be for the data
+ * post-inflation from variable-rate compression.
+ */
+void
+recreateBytesPlane0FromSampleInfo(const uint32_t* DFD, uint32_t* bytesPlane0);
+
+/** @brief Colourspace primaries information.
+ *
+ * Structure to store the 1931 CIE x,y chromaticities of the red, green, and blue
+ * display primaries and the reference white point of a colourspace.
+ */
+typedef struct _Primaries {
+    float Rx; /*!< Red x. */
+    float Ry; /*!< Red y. */
+    float Gx; /*!< Green x. */
+    float Gy; /*!< Green y. */
+    float Bx; /*!< Blue x. */
+    float By; /*!< Blue y. */
+    float Wx; /*!< White x. */
+    float Wy; /*!< White y. */
+} Primaries;
+
+khr_df_primaries_e findMapping(Primaries *p, float latitude);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _DFD_H_ */
diff --git a/thirdparty/libktx/lib/dfdutils/dfd2vk.inl b/thirdparty/libktx/lib/dfdutils/dfd2vk.inl
new file mode 100644
index 00000000000..cfed9140e9f
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/dfd2vk.inl
@@ -0,0 +1,599 @@
+/* Copyright 2019-2020 The Khronos Group Inc. */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/***************************** Do not edit.  *****************************
+             Automatically generated by makedfd2vk.pl.
+ *************************************************************************/
+if (KHR_DFDVAL(dfd + 1, MODEL) == KHR_DF_MODEL_RGBSDA) {
+  enum InterpretDFDResult r;
+  InterpretedDFDChannel R = {0,0};
+  InterpretedDFDChannel G = {0,0};
+  InterpretedDFDChannel B = {0,0};
+  InterpretedDFDChannel A = {0,0};
+  uint32_t wordBytes;
+
+  /* Special case exponent format */
+  if (KHR_DFDSAMPLECOUNT(dfd + 1) == 6 &&
+      ((KHR_DFDSVAL((dfd + 1), 1, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_EXPONENT) > 0)) {
+    /* The only format we expect to be encoded like this. */
+    return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+  }
+
+  /* Special case depth formats (assumed little-endian) */
+  if (KHR_DFDSVAL((dfd + 1), 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {
+    if (KHR_DFDSAMPLECOUNT((dfd + 1)) == 1) {
+      if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 16-1) return VK_FORMAT_D16_UNORM;
+      if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 24-1) return VK_FORMAT_X8_D24_UNORM_PACK32;
+      return VK_FORMAT_D32_SFLOAT;
+    } else {
+      if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 16-1) return VK_FORMAT_D16_UNORM_S8_UINT;
+      if (KHR_DFDSVAL((dfd + 1), 0, BITLENGTH) == 24-1) return VK_FORMAT_D24_UNORM_S8_UINT;
+      return VK_FORMAT_D32_SFLOAT_S8_UINT;
+    }
+  }
+  if (KHR_DFDSVAL((dfd + 1), 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {
+    return VK_FORMAT_S8_UINT;
+  }
+
+  r = interpretDFD(dfd, &R, &G, &B, &A, &wordBytes);
+
+  if (r & i_UNSUPPORTED_ERROR_BIT) return VK_FORMAT_UNDEFINED;
+
+  if (r & i_PACKED_FORMAT_BIT) {
+    if (wordBytes == 1) return VK_FORMAT_R4G4_UNORM_PACK8;
+    else if (wordBytes == 2) { /* PACK16 */
+      if (A.size == 4) {
+        if (R.offset == 12) return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+        else return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
+      } else if (A.size == 0) { /* Three channels */
+        if (B.offset == 0) return VK_FORMAT_R5G6B5_UNORM_PACK16;
+        else return VK_FORMAT_B5G6R5_UNORM_PACK16;
+      } else { /* Four channels, one-bit alpha */
+        if (B.offset == 0) return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
+        if (B.offset == 1) return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
+        return VK_FORMAT_B5G5R5A1_UNORM_PACK16;
+      }
+    } else if (wordBytes == 4) { /* PACK32 */
+      if (A.size == 8) {
+        if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SRGB_PACK32;
+        if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_UNORM_PACK32;
+        if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SNORM_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_UINT_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A8B8G8R8_SINT_PACK32;
+      } else if (A.size == 2 && B.offset == 0) {
+        if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+        if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_SNORM_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_UINT_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2R10G10B10_SINT_PACK32;
+      } else if (A.size == 2 && R.offset == 0) {
+        if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+        if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_UINT_PACK32;
+        if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_A2B10G10R10_SINT_PACK32;
+      } else if (R.size == 11) return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+    }
+  } else { /* Not a packed format */
+    if (wordBytes == 1) {
+      if (A.size > 0) { /* 4 channels */
+        if (R.offset == 0) { /* RGBA */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8A8_SINT;
+        } else { /* BGRA */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8A8_SINT;
+        }
+      } else if (B.size > 0) { /* 3 channels */
+        if (R.offset == 0) { /* RGB */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8B8_SINT;
+        } else { /* BGR */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_B8G8R8_SINT;
+        }
+      } else if (G.size > 0) { /* 2 channels */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8G8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8G8_SINT;
+      } else { /* 1 channel */
+          if ((r & i_SRGB_FORMAT_BIT)) return VK_FORMAT_R8_SRGB;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R8_SINT;
+      }
+    } else if (wordBytes == 2) {
+      if (A.size > 0) { /* 4 channels */
+        if (R.offset == 0) { /* RGBA */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SFLOAT;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16A16_SINT;
+        } else { /* BGRA */
+        }
+      } else if (B.size > 0) { /* 3 channels */
+        if (R.offset == 0) { /* RGB */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SFLOAT;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16B16_SINT;
+        } else { /* BGR */
+        }
+      } else if (G.size > 0) { /* 2 channels */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16G16_SFLOAT;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16G16_SINT;
+      } else { /* 1 channel */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R16_SFLOAT;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_UNORM;
+          if ((r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_SNORM;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R16_SINT;
+      }
+    } else if (wordBytes == 4) {
+      if (A.size > 0) { /* 4 channels */
+        if (R.offset == 0) { /* RGBA */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32A32_SINT;
+        } else { /* BGRA */
+        }
+      } else if (B.size > 0) { /* 3 channels */
+        if (R.offset == 0) { /* RGB */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32B32_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32B32_SINT;
+        } else { /* BGR */
+        }
+      } else if (G.size > 0) { /* 2 channels */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32G32_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32G32_SINT;
+      } else { /* 1 channel */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R32_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R32_SINT;
+      }
+    } else if (wordBytes == 8) {
+      if (A.size > 0) { /* 4 channels */
+        if (R.offset == 0) { /* RGBA */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64A64_SINT;
+        } else { /* BGRA */
+        }
+      } else if (B.size > 0) { /* 3 channels */
+        if (R.offset == 0) { /* RGB */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64B64_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64B64_SINT;
+        } else { /* BGR */
+        }
+      } else if (G.size > 0) { /* 2 channels */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64G64_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64G64_SINT;
+      } else { /* 1 channel */
+          if ((r & i_FLOAT_FORMAT_BIT)) return VK_FORMAT_R64_SFLOAT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && !(r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64_UINT;
+          if (!(r & i_NORMALIZED_FORMAT_BIT) && (r & i_SIGNED_FORMAT_BIT)) return VK_FORMAT_R64_SINT;
+      }
+    }
+  }
+} else if (KHR_DFDVAL((dfd + 1), MODEL) >= 128) {
+  const uint32_t *bdb = dfd + 1;
+  switch (KHR_DFDVAL(bdb, MODEL)) {
+  case KHR_DF_MODEL_BC1A:
+    if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_BC1A_COLOR) {
+      if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_BC1_RGB_UNORM_BLOCK;
+      } else {
+        return VK_FORMAT_BC1_RGB_SRGB_BLOCK;
+      }
+    } else {
+      if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;
+      } else {
+        return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;
+      }
+    }
+  case KHR_DF_MODEL_BC2:
+    if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+      return VK_FORMAT_BC2_UNORM_BLOCK;
+    } else {
+      return VK_FORMAT_BC2_SRGB_BLOCK;
+    }
+  case KHR_DF_MODEL_BC3:
+    if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+      return VK_FORMAT_BC3_UNORM_BLOCK;
+    } else {
+      return VK_FORMAT_BC3_SRGB_BLOCK;
+    }
+  case KHR_DF_MODEL_BC4:
+    if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) {
+      return VK_FORMAT_BC4_UNORM_BLOCK;
+    } else {
+      return VK_FORMAT_BC4_SNORM_BLOCK;
+    }
+  case KHR_DF_MODEL_BC5:
+    if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) {
+      return VK_FORMAT_BC5_UNORM_BLOCK;
+    } else {
+      return VK_FORMAT_BC5_SNORM_BLOCK;
+    }
+  case KHR_DF_MODEL_BC6H:
+    if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) {
+      return VK_FORMAT_BC6H_UFLOAT_BLOCK;
+    } else {
+      return VK_FORMAT_BC6H_SFLOAT_BLOCK;
+    }
+  case KHR_DF_MODEL_BC7:
+    if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+      return VK_FORMAT_BC7_UNORM_BLOCK;
+    } else {
+      return VK_FORMAT_BC7_SRGB_BLOCK;
+    }
+  case KHR_DF_MODEL_ETC2:
+    if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_ETC2_COLOR) {
+      if (KHR_DFDVAL(bdb, DESCRIPTORBLOCKSIZE) == 40) {
+        if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+          return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
+        } else {
+          return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;
+        }
+      } else {
+        if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+          return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;
+        } else {
+          return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;
+        }
+      }
+    } else if (KHR_DFDSVAL(bdb, 0, CHANNELID) == KHR_DF_CHANNEL_ETC2_ALPHA) {
+      if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;
+      } else {
+        return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;
+      }
+    } else if (KHR_DFDVAL(bdb, DESCRIPTORBLOCKSIZE) == 40) {
+      if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) {
+        return VK_FORMAT_EAC_R11_UNORM_BLOCK;
+      } else {
+        return VK_FORMAT_EAC_R11_SNORM_BLOCK;
+      }
+    } else {
+      if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_SIGNED)) {
+        return VK_FORMAT_EAC_R11G11_UNORM_BLOCK;
+      } else {
+        return VK_FORMAT_EAC_R11G11_SNORM_BLOCK;
+      }
+    }
+  case KHR_DF_MODEL_ASTC:
+    if (!(KHR_DFDSVAL(bdb, 0, QUALIFIERS) & KHR_DF_SAMPLE_DATATYPE_FLOAT)) {
+      if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 0) {
+        if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_5x4_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_5x5_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_6x5_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_6x5_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_6x6_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_6x6_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_8x5_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_8x5_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_8x6_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_8x6_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_8x8_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_8x8_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_10x5_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_10x5_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_10x6_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_10x6_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_10x8_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_10x8_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_10x10_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_10x10_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_12x10_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_12x10_SRGB_BLOCK;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 11)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_12x12_UNORM_BLOCK;
+          } else {
+            return VK_FORMAT_ASTC_12x12_SRGB_BLOCK;
+          }
+        }
+      } else {
+        if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 2) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT;
+          }
+        }
+        if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT;
+          }
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 5)) {
+          if (KHR_DFDVAL(bdb, TRANSFER) != KHR_DF_TRANSFER_SRGB) {
+            return VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT;
+          } else {
+            return VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT;
+          }
+        }
+      }
+    } else {
+      if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 0) {
+        if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) {
+          return VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3)) {
+          return VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          return VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          return VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          return VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          return VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          return VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 7) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) {
+          return VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4)) {
+          return VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5)) {
+          return VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 7)) {
+          return VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 9) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) {
+          return VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 9)) {
+          return VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 11) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 11)) {
+          return VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT;
+        }
+      } else {
+        if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 2) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) &&
+            (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          return VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          return VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 2) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          return VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 2)) {
+          return VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          return VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 3) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          return VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 3)) {
+          return VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          return VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 4) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          return VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 4)) {
+          return VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT;
+        } else if ((KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION1) == 5) &&
+                   (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION2) == 5)) {
+          return VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT;
+        }
+      }
+    }
+    break;
+  case KHR_DF_MODEL_PVRTC:
+    if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) {
+      if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;
+      } else {
+        return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;
+      }
+    } else {
+      if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;
+      } else {
+        return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;
+      }
+    }
+  case KHR_DF_MODEL_PVRTC2:
+    if (KHR_DFDVAL(bdb, TEXELBLOCKDIMENSION0) == 3) {
+      if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG;
+      } else {
+        return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;
+      }
+    } else {
+      if (KHR_DFDVAL(bdb, TRANSFER) == KHR_DF_TRANSFER_SRGB) {
+        return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG;
+      } else {
+        return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG;
+      }
+    }
+  default:
+    ;
+  }
+}
+return VK_FORMAT_UNDEFINED; /* Drop-through for unmatched formats. */
diff --git a/thirdparty/libktx/lib/dfdutils/interpretdfd.c b/thirdparty/libktx/lib/dfdutils/interpretdfd.c
new file mode 100644
index 00000000000..273410a18cc
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/interpretdfd.c
@@ -0,0 +1,345 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Utility for interpreting a data format descriptor.
+ * @author Andrew Garrard
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <KHR/khr_df.h>
+#include "dfd.h"
+
+/**
+ * @~English
+ * @brief Interpret a Data Format Descriptor for a simple format.
+ *
+ * @param DFD Pointer to a Data Format Descriptor to interpret,
+              described as 32-bit words in native endianness.
+              Note that this is the whole descriptor, not just
+              the basic descriptor block.
+ * @param R Information about the decoded red channel, if any.
+ * @param G Information about the decoded green channel, if any.
+ * @param B Information about the decoded blue channel, if any.
+ * @param A Information about the decoded alpha channel, if any.
+ * @param wordBytes Byte size of the channels (unpacked) or total size (packed).
+ *
+ * @return An enumerant describing the decoded value,
+ *         or an error code in case of failure.
+ **/
+enum InterpretDFDResult interpretDFD(const uint32_t *DFD,
+                                     InterpretedDFDChannel *R,
+                                     InterpretedDFDChannel *G,
+                                     InterpretedDFDChannel *B,
+                                     InterpretedDFDChannel *A,
+                                     uint32_t *wordBytes)
+{
+    /* We specifically handle "simple" cases that can be translated */
+    /* to things a GPU can access. For simplicity, we also ignore */
+    /* the compressed formats, which are generally a single sample */
+    /* (and I believe are all defined to be little-endian in their */
+    /* in-memory layout, even if some documentation confuses this). */
+    /* We also just worry about layout and ignore sRGB, since that's */
+    /* trivial to extract anyway. */
+
+    /* DFD points to the whole descriptor, not the basic descriptor block. */
+    /* Make everything else relative to the basic descriptor block. */
+    const uint32_t *BDFDB = DFD+1;
+
+    uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
+
+    uint32_t sampleCounter;
+    int determinedEndianness = 0;
+    int determinedNormalizedness = 0;
+    int determinedSignedness = 0;
+    int determinedFloatness = 0;
+    enum InterpretDFDResult result = 0; /* Build this up incrementally. */
+
+    /* Clear these so following code doesn't get confused. */
+    R->offset = R->size = 0;
+    G->offset = G->size = 0;
+    B->offset = B->size = 0;
+    A->offset = A->size = 0;
+
+    /* First rule out the multiple planes case (trivially) */
+    /* - that is, we check that only bytesPlane0 is non-zero. */
+    /* This means we don't handle YUV even if the API could. */
+    /* (We rely on KHR_DF_WORD_BYTESPLANE0..3 being the same and */
+    /* KHR_DF_WORD_BYTESPLANE4..7 being the same as a short cut.) */
+    if ((BDFDB[KHR_DF_WORD_BYTESPLANE0] & ~KHR_DF_MASK_BYTESPLANE0)
+        || BDFDB[KHR_DF_WORD_BYTESPLANE4]) return i_UNSUPPORTED_MULTIPLE_PLANES;
+
+    /* Only support the RGB color model. */
+    /* We could expand this to allow "UNSPECIFIED" as well. */
+    if (KHR_DFDVAL(BDFDB, MODEL) != KHR_DF_MODEL_RGBSDA) return i_UNSUPPORTED_CHANNEL_TYPES;
+
+    /* We only pay attention to sRGB. */
+    if (KHR_DFDVAL(BDFDB, TRANSFER) == KHR_DF_TRANSFER_SRGB) result |= i_SRGB_FORMAT_BIT;
+
+    /* We only support samples at coordinate 0,0,0,0. */
+    /* (We could confirm this from texel_block_dimensions in 1.2, but */
+    /* the interpretation might change in later versions.) */
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEPOSITION_ALL))
+            return i_UNSUPPORTED_MULTIPLE_SAMPLE_LOCATIONS;
+    }
+
+    /* Set flags and check for consistency. */
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        /* Note: We're ignoring 9995, which is weird and worth special-casing */
+        /* rather than trying to generalise to all float formats. */
+        if (!determinedFloatness) {
+            if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
+                 & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
+                result |= i_FLOAT_FORMAT_BIT;
+                determinedFloatness = 1;
+            }
+        } else {
+            /* Check whether we disagree with our predetermined floatness. */
+            /* Note that this could justifiably happen with (say) D24S8. */
+            if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
+                 & KHR_DF_SAMPLE_DATATYPE_FLOAT) {
+                if (!(result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
+            } else {
+                if ((result & i_FLOAT_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
+            }
+        }
+        if (!determinedSignedness) {
+            if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
+                 & KHR_DF_SAMPLE_DATATYPE_SIGNED) {
+                result |= i_SIGNED_FORMAT_BIT;
+                determinedSignedness = 1;
+            }
+        } else {
+            /* Check whether we disagree with our predetermined signedness. */
+            if (KHR_DFDSVAL(BDFDB, sampleCounter, QUALIFIERS)
+                 & KHR_DF_SAMPLE_DATATYPE_SIGNED) {
+                if (!(result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
+            } else {
+                if ((result & i_SIGNED_FORMAT_BIT)) return i_UNSUPPORTED_MIXED_CHANNELS;
+            }
+        }
+        /* We define "unnormalized" as "sample_upper = 1". */
+        /* We don't check whether any non-1 normalization value is correct */
+        /* (i.e. set to the maximum bit value, and check min value) on */
+        /* the assumption that we're looking at a format which *came* from */
+        /* an API we can support. */
+        if (!determinedNormalizedness) {
+            /* The ambiguity here is if the bottom bit is a single-bit value, */
+            /* as in RGBA 5:5:5:1, so we defer the decision if the channel only has one bit. */
+            if (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) > 0) {
+                if ((result & i_FLOAT_FORMAT_BIT)) {
+                    if (*(float *)(void *)&BDFDB[KHR_DF_WORD_SAMPLESTART +
+                                                 KHR_DF_WORD_SAMPLEWORDS * sampleCounter +
+                                                 KHR_DF_SAMPLEWORD_SAMPLEUPPER] != 1.0f) {
+                        result |= i_NORMALIZED_FORMAT_BIT;
+                    }
+                } else {
+                    if (KHR_DFDSVAL(BDFDB, sampleCounter, SAMPLEUPPER) != 1U) {
+                        result |= i_NORMALIZED_FORMAT_BIT;
+                    }
+                }
+                determinedNormalizedness = 1;
+            }
+        }
+        /* Note: We don't check for inconsistent normalization, because */
+        /* channels composed of multiple samples will have 0 in the */
+        /* lower/upper range. */
+        /* This heuristic should handle 64-bit integers, too. */
+    }
+
+    /* If this is a packed format, we work out our offsets differently. */
+    /* We assume a packed format has channels that aren't byte-aligned. */
+    /* If we have a format in which every channel is byte-aligned *and* packed, */
+    /* we have the RGBA/ABGR ambiguity; we *probably* don't want the packed */
+    /* version in this case, and if hardware has to pack it and swizzle, */
+    /* that's up to the hardware to special-case. */
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        if (KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) & 0x7U) {
+            result |= i_PACKED_FORMAT_BIT;
+            /* Once we're packed, we're packed, no need to keep checking. */
+            break;
+        }
+    }
+
+    /* Remember: the canonical ordering of samples is to start with */
+    /* the lowest bit of the channel/location which touches bit 0 of */
+    /* the data, when the latter is concatenated in little-endian order, */
+    /* and then progress until all the bits of that channel/location */
+    /* have been processed. Multiple channels sharing the same source */
+    /* bits are processed in channel ID order. (I should clarify this */
+    /* for partially-shared data, but it doesn't really matter so long */
+    /* as everything is consecutive, except to make things canonical.) */
+    /* Note: For standard formats we could determine big/little-endianness */
+    /* simply from whether the first sample starts in bit 0; technically */
+    /* it's possible to have a format with unaligned channels wherein the */
+    /* first channel starts at bit 0 and is one byte, yet other channels */
+    /* take more bytes or aren't aligned (e.g. D24S8), but this should be */
+    /* irrelevant for the formats that we support. */
+    if ((result & i_PACKED_FORMAT_BIT)) {
+        /* A packed format. */
+        uint32_t currentChannel = ~0U; /* Don't start matched. */
+        uint32_t currentBitOffset = 0;
+        uint32_t currentByteOffset = 0;
+        uint32_t currentBitLength = 0;
+        *wordBytes = (BDFDB[KHR_DF_WORD_BYTESPLANE0] & 0xFFU);
+        for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+            uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
+            uint32_t sampleByteOffset = sampleBitOffset >> 3U;
+            /* The sample bitLength field stores the bit length - 1. */
+            uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
+            uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
+            InterpretedDFDChannel *sampleChannelPtr;
+            switch (sampleChannel) {
+            case KHR_DF_CHANNEL_RGBSDA_RED:
+                sampleChannelPtr = R;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_GREEN:
+                sampleChannelPtr = G;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_BLUE:
+                sampleChannelPtr = B;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_ALPHA:
+                sampleChannelPtr = A;
+                break;
+            default:
+                return i_UNSUPPORTED_CHANNEL_TYPES;
+            }
+            if (sampleChannel == currentChannel) {
+                /* Continuation of the same channel. */
+                /* Since a big (>32-bit) channel isn't "packed", */
+                /* this should only happen in big-endian, or if */
+                /* we have a wacky format that we won't support. */
+                if (sampleByteOffset == currentByteOffset - 1U && /* One byte earlier */
+                    ((currentBitOffset + currentBitLength) & 7U) == 0 && /* Already at the end of a byte */
+                    (sampleBitOffset & 7U) == 0) { /* Start at the beginning of the byte */
+                    /* All is good, continue big-endian. */
+                    /* N.B. We shouldn't be here if we decided we were little-endian, */
+                    /* so we don't bother to check that disagreement. */
+                    result |= i_BIG_ENDIAN_FORMAT_BIT;
+                    determinedEndianness = 1;
+                } else {
+                    /* Oh dear. */
+                    /* We could be little-endian, but not with any standard format. */
+                    /* More likely we've got something weird that we can't support. */
+                    return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                }
+                /* Remember where we are. */
+                currentBitOffset = sampleBitOffset;
+                currentByteOffset = sampleByteOffset;
+                currentBitLength = sampleBitLength;
+                /* Accumulate the bit length. */
+                sampleChannelPtr->size += sampleBitLength;
+            } else {
+                /* Everything is new. Hopefully. */
+                currentChannel = sampleChannel;
+                currentBitOffset = sampleBitOffset;
+                currentByteOffset = sampleByteOffset;
+                currentBitLength = sampleBitLength;
+                if (sampleChannelPtr->size) {
+                    /* Uh-oh, we've seen this channel before. */
+                    return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                }
+                /* For now, record the bit offset in little-endian terms, */
+                /* because we may not know to reverse it yet. */
+                sampleChannelPtr->offset = sampleBitOffset;
+                sampleChannelPtr->size = sampleBitLength;
+            }
+        }
+        if ((result & i_BIG_ENDIAN_FORMAT_BIT)) {
+            /* Our bit offsets to bit 0 of each channel are in little-endian terms. */
+            /* We need to do a byte swap to work out where they should be. */
+            /* We assume, for sanity, that byte sizes are a power of two for this. */
+            uint32_t offsetMask = (*wordBytes - 1U) << 3U;
+            R->offset ^= offsetMask;
+            G->offset ^= offsetMask;
+            B->offset ^= offsetMask;
+            A->offset ^= offsetMask;
+        }
+    } else {
+        /* Not a packed format. */
+        /* Everything is byte-aligned. */
+        /* Question is whether there multiple samples per channel. */
+        uint32_t currentChannel = ~0U; /* Don't start matched. */
+        uint32_t currentByteOffset = 0;
+        uint32_t currentByteLength = 0;
+        for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+            uint32_t sampleByteOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET) >> 3U;
+            uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;
+            uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
+            InterpretedDFDChannel *sampleChannelPtr;
+            switch (sampleChannel) {
+            case KHR_DF_CHANNEL_RGBSDA_RED:
+                sampleChannelPtr = R;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_GREEN:
+                sampleChannelPtr = G;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_BLUE:
+                sampleChannelPtr = B;
+                break;
+            case KHR_DF_CHANNEL_RGBSDA_ALPHA:
+                sampleChannelPtr = A;
+                break;
+            default:
+                return i_UNSUPPORTED_CHANNEL_TYPES;
+            }
+            if (sampleChannel == currentChannel) {
+                /* Continuation of the same channel. */
+                /* Either big-endian, or little-endian with a very large channel. */
+                if (sampleByteOffset == currentByteOffset - 1) { /* One byte earlier */
+                    if (determinedEndianness && !(result & i_BIG_ENDIAN_FORMAT_BIT)) {
+                        return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                    }
+                    /* All is good, continue big-endian. */
+                    result |= i_BIG_ENDIAN_FORMAT_BIT;
+                    determinedEndianness = 1;
+                    /* Update the start */
+                    sampleChannelPtr->offset = sampleByteOffset;
+                } else if (sampleByteOffset == currentByteOffset + currentByteLength) {
+                    if (determinedEndianness && (result & i_BIG_ENDIAN_FORMAT_BIT)) {
+                        return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                    }
+                    /* All is good, continue little-endian. */
+                    determinedEndianness = 1;
+                } else {
+                    /* Oh dear. */
+                    /* We could be little-endian, but not with any standard format. */
+                    /* More likely we've got something weird that we can't support. */
+                    return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                }
+                /* Remember where we are. */
+                currentByteOffset = sampleByteOffset;
+                currentByteLength = sampleByteLength;
+                /* Accumulate the byte length. */
+                sampleChannelPtr->size += sampleByteLength;
+                /* Assume these are all the same. */
+                *wordBytes = sampleChannelPtr->size;
+            } else {
+                /* Everything is new. Hopefully. */
+                currentChannel = sampleChannel;
+                currentByteOffset = sampleByteOffset;
+                currentByteLength = sampleByteLength;
+                if (sampleChannelPtr->size) {
+                    /* Uh-oh, we've seen this channel before. */
+                    return i_UNSUPPORTED_NONTRIVIAL_ENDIANNESS;
+                }
+                /* For now, record the byte offset in little-endian terms, */
+                /* because we may not know to reverse it yet. */
+                sampleChannelPtr->offset = sampleByteOffset;
+                sampleChannelPtr->size = sampleByteLength;
+                /* Assume these are all the same. */
+                *wordBytes = sampleByteLength;
+            }
+        }
+    }
+    return result;
+}
diff --git a/thirdparty/libktx/lib/dfdutils/printdfd.c b/thirdparty/libktx/lib/dfdutils/printdfd.c
new file mode 100644
index 00000000000..36d3d2c50d4
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/printdfd.c
@@ -0,0 +1,97 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Utilities for printing data format descriptors.
+ */
+
+/*
+ * Author: Andrew Garrard
+ */
+
+#include <stdio.h>
+#include <KHR/khr_df.h>
+#include "dfd.h"
+
+/**
+ * @~English
+ * @brief Print a human-readable interpretation of a data format descriptor.
+ *
+ * @param DFD Pointer to a data format descriptor.
+ **/
+void printDFD(uint32_t *DFD)
+{
+    uint32_t *BDB = DFD+1;
+    int samples = (KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE) - 4 * KHR_DF_WORD_SAMPLESTART) / (4 * KHR_DF_WORD_SAMPLEWORDS);
+    int sample;
+    int model = KHR_DFDVAL(BDB, MODEL);
+    printf("DFD total bytes: %d\n", DFD[0]);
+    printf("BDB descriptor type 0x%04x vendor id = 0x%05x\n",
+           KHR_DFDVAL(BDB, DESCRIPTORTYPE),
+           KHR_DFDVAL(BDB, VENDORID));
+    printf("Descriptor block size %d (%d samples) versionNumber = 0x%04x\n",
+           KHR_DFDVAL(BDB, DESCRIPTORBLOCKSIZE),
+           samples,
+           KHR_DFDVAL(BDB, VERSIONNUMBER));
+    printf("Flags 0x%02x Xfer %02d Primaries %02d Model %03d\n",
+           KHR_DFDVAL(BDB, FLAGS),
+           KHR_DFDVAL(BDB, TRANSFER),
+           KHR_DFDVAL(BDB, PRIMARIES),
+           model);
+    printf("Dimensions: %d,%d,%d,%d\n",
+           KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION0) + 1,
+           KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION1) + 1,
+           KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION2) + 1,
+           KHR_DFDVAL(BDB, TEXELBLOCKDIMENSION3) + 1);
+    printf("Plane bytes: %d,%d,%d,%d,%d,%d,%d,%d\n",
+           KHR_DFDVAL(BDB, BYTESPLANE0),
+           KHR_DFDVAL(BDB, BYTESPLANE1),
+           KHR_DFDVAL(BDB, BYTESPLANE2),
+           KHR_DFDVAL(BDB, BYTESPLANE3),
+           KHR_DFDVAL(BDB, BYTESPLANE4),
+           KHR_DFDVAL(BDB, BYTESPLANE5),
+           KHR_DFDVAL(BDB, BYTESPLANE6),
+           KHR_DFDVAL(BDB, BYTESPLANE7));
+    for (sample = 0; sample < samples; ++sample) {
+        int channelId = KHR_DFDSVAL(BDB, sample, CHANNELID);
+        printf("    Sample %d\n", sample);
+        printf("Qualifiers %x", KHR_DFDSVAL(BDB, sample, QUALIFIERS) >> 4);
+        printf(" Channel 0x%x", channelId);
+        if (model == KHR_DF_MODEL_UASTC) {
+            printf(" (%s)",
+                   channelId == KHR_DF_CHANNEL_UASTC_RRRG ? "RRRG"
+                 : channelId == KHR_DF_CHANNEL_UASTC_RGBA ? "RGBA"
+                 : channelId == KHR_DF_CHANNEL_UASTC_RRR ? "RRR"
+                 : channelId == KHR_DF_CHANNEL_UASTC_RGB ? "RGB"
+                 : channelId == KHR_DF_CHANNEL_UASTC_RG ? "RG"
+                 : "unknown");
+        } else if (model == KHR_DF_MODEL_ETC1S) {
+            printf(" (%s)",
+                   channelId == KHR_DF_CHANNEL_ETC1S_AAA ? "AAA"
+                 : channelId == KHR_DF_CHANNEL_ETC1S_GGG ? "GGG"
+                 : channelId == KHR_DF_CHANNEL_ETC1S_RRR ? "RRR"
+                 : channelId == KHR_DF_CHANNEL_ETC1S_RGB ? "RGB"
+                 : "unknown");
+        } else {
+            printf(" (%c)",
+               "RGB3456789abcdeA"[channelId]);
+        }
+        printf(" Length %d bits Offset %d\n",
+               KHR_DFDSVAL(BDB, sample, BITLENGTH) + 1,
+               KHR_DFDSVAL(BDB, sample, BITOFFSET));
+        printf("Position: %d,%d,%d,%d\n",
+               KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION0),
+               KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION1),
+               KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION2),
+               KHR_DFDSVAL(BDB, sample, SAMPLEPOSITION3));
+        printf("Lower 0x%08x\nUpper 0x%08x\n",
+               KHR_DFDSVAL(BDB, sample, SAMPLELOWER),
+               KHR_DFDSVAL(BDB, sample, SAMPLEUPPER));
+    }
+}
diff --git a/thirdparty/libktx/lib/dfdutils/queries.c b/thirdparty/libktx/lib/dfdutils/queries.c
new file mode 100644
index 00000000000..19488f9e33d
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/queries.c
@@ -0,0 +1,146 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Utilities for querying info from a data format descriptor.
+ * @author Mark Callow
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <KHR/khr_df.h>
+#include "dfd.h"
+
+/**
+ * @~English
+ * @brief Get the number and size of the image components from a DFD.
+ *
+ * This simplified function is for use only with the DFDs for unpacked
+ * formats which means all components have the same size.
+ *
+ * @param DFD Pointer to a Data Format Descriptor to interpret,
+              described as 32-bit words in native endianness.
+              Note that this is the whole descriptor, not just
+              the basic descriptor block.
+ * @param numComponents pointer to a 32-bit word in which the number of
+                        components will be written.
+ * @param componentByteLength pointer to a 32-bit word in which the size of
+                              a component in bytes will be written.
+ */
+void
+getDFDComponentInfoUnpacked(const uint32_t* DFD, uint32_t* numComponents,
+                            uint32_t* componentByteLength)
+{
+    const uint32_t *BDFDB = DFD+1;
+    uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
+    uint32_t sampleCounter;
+    uint32_t currentChannel = ~0U; /* Don't start matched. */
+
+    /* This is specifically for unpacked formats which means the size of */
+    /* each component is the same. */
+    *numComponents = 0;
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        uint32_t sampleByteLength = (KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1) >> 3U;
+        uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
+
+        if (sampleChannel == currentChannel) {
+            /* Continuation of the same channel. */
+            /* Accumulate the byte length. */
+            *componentByteLength += sampleByteLength;
+        } else {
+            /* Everything is new. Hopefully. */
+            currentChannel = sampleChannel;
+            (*numComponents)++;
+            *componentByteLength = sampleByteLength;
+        }
+    }
+}
+
+/**
+ * @~English
+ * @brief Return the number of "components" in the data.
+ *
+ * Calculates the number of uniques samples in the DFD by combining
+ * multiple samples for the same channel. For uncompressed colorModels
+ * this is the same as the number of components in the image data. For
+ * block-compressed color models this is the number of samples in
+ * the color model, typically 1 and in a few cases 2.
+ *
+ * @param DFD Pointer to a Data Format Descriptor for which,
+ *            described as 32-bit words in native endianness.
+ *            Note that this is the whole descriptor, not just
+ *            the basic descriptor block.
+ */
+uint32_t getDFDNumComponents(const uint32_t* DFD)
+{
+    const uint32_t *BDFDB = DFD+1;
+    uint32_t currentChannel = ~0U; /* Don't start matched. */
+    uint32_t numComponents = 0;
+    uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
+    uint32_t sampleCounter;
+
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        uint32_t sampleChannel = KHR_DFDSVAL(BDFDB, sampleCounter, CHANNELID);
+        if (sampleChannel != currentChannel) {
+            numComponents++;
+            currentChannel = sampleChannel;
+        }
+    }
+    return numComponents;
+}
+
+/**
+ * @~English
+ * @brief Recreate the value of bytesPlane0 from sample info.
+ *
+ * This can be use to recreate the value of bytesPlane0 for data that
+ * has been variable-rate compressed so has bytesPlane0 = 0.  For DFDs
+ * that are valid for KTX files. Little-endian data only and no multi-plane
+ * formats.
+ *
+ * @param DFD Pointer to a Data Format Descriptor for which,
+ *            described as 32-bit words in native endianness.
+ *            Note that this is the whole descriptor, not just
+ *            the basic descriptor block.
+ * @param bytesPlane0  pointer to a 32-bit word in which the recreated
+ *                    value of bytesPlane0 will be written.
+ */
+void
+recreateBytesPlane0FromSampleInfo(const uint32_t* DFD, uint32_t* bytesPlane0)
+{
+    const uint32_t *BDFDB = DFD+1;
+    uint32_t numSamples = KHR_DFDSAMPLECOUNT(BDFDB);
+    uint32_t sampleCounter;
+
+    uint32_t bitsPlane0 = 0;
+    uint32_t* bitOffsets = malloc(sizeof(uint32_t) * numSamples);
+    memset(bitOffsets, -1, sizeof(uint32_t) * numSamples);
+    for (sampleCounter = 0; sampleCounter < numSamples; ++sampleCounter) {
+        uint32_t sampleBitOffset = KHR_DFDSVAL(BDFDB, sampleCounter, BITOFFSET);
+        /* The sample bitLength field stores the bit length - 1. */
+        uint32_t sampleBitLength = KHR_DFDSVAL(BDFDB, sampleCounter, BITLENGTH) + 1;
+        uint32_t i;
+        for (i = 0; i < numSamples; i++) {
+            if (sampleBitOffset == bitOffsets[i]) {
+                // This sample is being repeated as in e.g. RGB9E5.
+                break;
+            }
+        }
+        if (i == numSamples) {
+            // Previously unseen bitOffset. Bump size.
+            bitsPlane0 += sampleBitLength;
+            bitOffsets[sampleCounter] = sampleBitOffset;
+        }
+    }
+    free(bitOffsets);
+    *bytesPlane0 = bitsPlane0 >> 3U;
+}
+
diff --git a/thirdparty/libktx/lib/dfdutils/vk2dfd.c b/thirdparty/libktx/lib/dfdutils/vk2dfd.c
new file mode 100644
index 00000000000..d476ced1be5
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/vk2dfd.c
@@ -0,0 +1,34 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2020 Mark Callow
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ * @brief Create a DFD for a VkFormat.
+ */
+
+#include "dfd.h"
+
+/**
+ * @~English
+ * @brief Create a DFD matching a VkFormat.
+ *
+ * @param[in] format    VkFormat for which to create a DFD.
+ *
+ * @return      pointer to the created DFD or 0 if format not supported or
+ *              unrecognized. Caller is responsible for freeing the created
+ *              DFD.
+ */
+uint32_t*
+vk2dfd(enum VkFormat format)
+ {
+     switch (format) {
+#include "vk2dfd.inl"
+         default: return 0;
+     }
+ }
+
diff --git a/thirdparty/libktx/lib/dfdutils/vk2dfd.inl b/thirdparty/libktx/lib/dfdutils/vk2dfd.inl
new file mode 100644
index 00000000000..4ae1e6d5e1d
--- /dev/null
+++ b/thirdparty/libktx/lib/dfdutils/vk2dfd.inl
@@ -0,0 +1,340 @@
+/* Copyright 2019-2020 The Khronos Group Inc. */
+/* SPDX-License-Identifier: Apache-2.0 */
+
+/***************************** Do not edit.  *****************************
+             Automatically generated by makevk2dfd.pl.
+ *************************************************************************/
+
+/* Vulkan combined depth & stencil formats are not included here
+ * because they do not exist outside a Vulkan device.
+ */
+case VK_FORMAT_R4G4_UNORM_PACK8: {
+    int channels[] = {1,0}; int bits[] = {4,4};
+    return createDFDPacked(0, 2, bits, channels, s_UNORM);
+}
+case VK_FORMAT_R4G4B4A4_UNORM_PACK16: {
+    int channels[] = {3,2,1,0}; int bits[] = {4,4,4,4};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_B4G4R4A4_UNORM_PACK16: {
+    int channels[] = {3,0,1,2}; int bits[] = {4,4,4,4};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_R5G6B5_UNORM_PACK16: {
+    int channels[] = {2,1,0}; int bits[] = {5,6,5};
+    return createDFDPacked(0, 3, bits, channels, s_UNORM);
+}
+case VK_FORMAT_B5G6R5_UNORM_PACK16: {
+    int channels[] = {0,1,2}; int bits[] = {5,6,5};
+    return createDFDPacked(0, 3, bits, channels, s_UNORM);
+}
+case VK_FORMAT_R5G5B5A1_UNORM_PACK16: {
+    int channels[] = {3,2,1,0}; int bits[] = {1,5,5,5};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_B5G5R5A1_UNORM_PACK16: {
+    int channels[] = {3,0,1,2}; int bits[] = {1,5,5,5};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_A1R5G5B5_UNORM_PACK16: {
+    int channels[] = {2,1,0,3}; int bits[] = {5,5,5,1};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_R8_UNORM: return createDFDUnpacked(0, 1, 1, 0, s_UNORM);
+case VK_FORMAT_R8_SNORM: return createDFDUnpacked(0, 1, 1, 0, s_SNORM);
+case VK_FORMAT_R8_USCALED: return createDFDUnpacked(0, 1, 1, 0, s_USCALED);
+case VK_FORMAT_R8_SSCALED: return createDFDUnpacked(0, 1, 1, 0, s_SSCALED);
+case VK_FORMAT_R8_UINT: return createDFDUnpacked(0, 1, 1, 0, s_UINT);
+case VK_FORMAT_R8_SINT: return createDFDUnpacked(0, 1, 1, 0, s_SINT);
+case VK_FORMAT_R8_SRGB: return createDFDUnpacked(0, 1, 1, 0, s_SRGB);
+case VK_FORMAT_R8G8_UNORM: return createDFDUnpacked(0, 2, 1, 0, s_UNORM);
+case VK_FORMAT_R8G8_SNORM: return createDFDUnpacked(0, 2, 1, 0, s_SNORM);
+case VK_FORMAT_R8G8_USCALED: return createDFDUnpacked(0, 2, 1, 0, s_USCALED);
+case VK_FORMAT_R8G8_SSCALED: return createDFDUnpacked(0, 2, 1, 0, s_SSCALED);
+case VK_FORMAT_R8G8_UINT: return createDFDUnpacked(0, 2, 1, 0, s_UINT);
+case VK_FORMAT_R8G8_SINT: return createDFDUnpacked(0, 2, 1, 0, s_SINT);
+case VK_FORMAT_R8G8_SRGB: return createDFDUnpacked(0, 2, 1, 0, s_SRGB);
+case VK_FORMAT_R8G8B8_UNORM: return createDFDUnpacked(0, 3, 1, 0, s_UNORM);
+case VK_FORMAT_R8G8B8_SNORM: return createDFDUnpacked(0, 3, 1, 0, s_SNORM);
+case VK_FORMAT_R8G8B8_USCALED: return createDFDUnpacked(0, 3, 1, 0, s_USCALED);
+case VK_FORMAT_R8G8B8_SSCALED: return createDFDUnpacked(0, 3, 1, 0, s_SSCALED);
+case VK_FORMAT_R8G8B8_UINT: return createDFDUnpacked(0, 3, 1, 0, s_UINT);
+case VK_FORMAT_R8G8B8_SINT: return createDFDUnpacked(0, 3, 1, 0, s_SINT);
+case VK_FORMAT_R8G8B8_SRGB: return createDFDUnpacked(0, 3, 1, 0, s_SRGB);
+case VK_FORMAT_B8G8R8_UNORM: return createDFDUnpacked(0, 3, 1, 1, s_UNORM);
+case VK_FORMAT_B8G8R8_SNORM: return createDFDUnpacked(0, 3, 1, 1, s_SNORM);
+case VK_FORMAT_B8G8R8_USCALED: return createDFDUnpacked(0, 3, 1, 1, s_USCALED);
+case VK_FORMAT_B8G8R8_SSCALED: return createDFDUnpacked(0, 3, 1, 1, s_SSCALED);
+case VK_FORMAT_B8G8R8_UINT: return createDFDUnpacked(0, 3, 1, 1, s_UINT);
+case VK_FORMAT_B8G8R8_SINT: return createDFDUnpacked(0, 3, 1, 1, s_SINT);
+case VK_FORMAT_B8G8R8_SRGB: return createDFDUnpacked(0, 3, 1, 1, s_SRGB);
+case VK_FORMAT_R8G8B8A8_UNORM: return createDFDUnpacked(0, 4, 1, 0, s_UNORM);
+case VK_FORMAT_R8G8B8A8_SNORM: return createDFDUnpacked(0, 4, 1, 0, s_SNORM);
+case VK_FORMAT_R8G8B8A8_USCALED: return createDFDUnpacked(0, 4, 1, 0, s_USCALED);
+case VK_FORMAT_R8G8B8A8_SSCALED: return createDFDUnpacked(0, 4, 1, 0, s_SSCALED);
+case VK_FORMAT_R8G8B8A8_UINT: return createDFDUnpacked(0, 4, 1, 0, s_UINT);
+case VK_FORMAT_R8G8B8A8_SINT: return createDFDUnpacked(0, 4, 1, 0, s_SINT);
+case VK_FORMAT_R8G8B8A8_SRGB: return createDFDUnpacked(0, 4, 1, 0, s_SRGB);
+case VK_FORMAT_B8G8R8A8_UNORM: return createDFDUnpacked(0, 4, 1, 1, s_UNORM);
+case VK_FORMAT_B8G8R8A8_SNORM: return createDFDUnpacked(0, 4, 1, 1, s_SNORM);
+case VK_FORMAT_B8G8R8A8_USCALED: return createDFDUnpacked(0, 4, 1, 1, s_USCALED);
+case VK_FORMAT_B8G8R8A8_SSCALED: return createDFDUnpacked(0, 4, 1, 1, s_SSCALED);
+case VK_FORMAT_B8G8R8A8_UINT: return createDFDUnpacked(0, 4, 1, 1, s_UINT);
+case VK_FORMAT_B8G8R8A8_SINT: return createDFDUnpacked(0, 4, 1, 1, s_SINT);
+case VK_FORMAT_B8G8R8A8_SRGB: return createDFDUnpacked(0, 4, 1, 1, s_SRGB);
+case VK_FORMAT_A8B8G8R8_UNORM_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_A8B8G8R8_SNORM_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_SNORM);
+}
+case VK_FORMAT_A8B8G8R8_USCALED_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_USCALED);
+}
+case VK_FORMAT_A8B8G8R8_SSCALED_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_SSCALED);
+}
+case VK_FORMAT_A8B8G8R8_UINT_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_UINT);
+}
+case VK_FORMAT_A8B8G8R8_SINT_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_SINT);
+}
+case VK_FORMAT_A8B8G8R8_SRGB_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {8,8,8,8};
+    return createDFDPacked(0, 4, bits, channels, s_SRGB);
+}
+case VK_FORMAT_A2R10G10B10_UNORM_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_A2R10G10B10_SNORM_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SNORM);
+}
+case VK_FORMAT_A2R10G10B10_USCALED_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_USCALED);
+}
+case VK_FORMAT_A2R10G10B10_SSCALED_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SSCALED);
+}
+case VK_FORMAT_A2R10G10B10_UINT_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_UINT);
+}
+case VK_FORMAT_A2R10G10B10_SINT_PACK32: {
+    int channels[] = {2,1,0,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SINT);
+}
+case VK_FORMAT_A2B10G10R10_UNORM_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_A2B10G10R10_SNORM_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SNORM);
+}
+case VK_FORMAT_A2B10G10R10_USCALED_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_USCALED);
+}
+case VK_FORMAT_A2B10G10R10_SSCALED_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SSCALED);
+}
+case VK_FORMAT_A2B10G10R10_UINT_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_UINT);
+}
+case VK_FORMAT_A2B10G10R10_SINT_PACK32: {
+    int channels[] = {0,1,2,3}; int bits[] = {10,10,10,2};
+    return createDFDPacked(0, 4, bits, channels, s_SINT);
+}
+case VK_FORMAT_R16_UNORM: return createDFDUnpacked(0, 1, 2, 0, s_UNORM);
+case VK_FORMAT_R16_SNORM: return createDFDUnpacked(0, 1, 2, 0, s_SNORM);
+case VK_FORMAT_R16_USCALED: return createDFDUnpacked(0, 1, 2, 0, s_USCALED);
+case VK_FORMAT_R16_SSCALED: return createDFDUnpacked(0, 1, 2, 0, s_SSCALED);
+case VK_FORMAT_R16_UINT: return createDFDUnpacked(0, 1, 2, 0, s_UINT);
+case VK_FORMAT_R16_SINT: return createDFDUnpacked(0, 1, 2, 0, s_SINT);
+case VK_FORMAT_R16_SFLOAT: return createDFDUnpacked(0, 1, 2, 0, s_SFLOAT);
+case VK_FORMAT_R16G16_UNORM: return createDFDUnpacked(0, 2, 2, 0, s_UNORM);
+case VK_FORMAT_R16G16_SNORM: return createDFDUnpacked(0, 2, 2, 0, s_SNORM);
+case VK_FORMAT_R16G16_USCALED: return createDFDUnpacked(0, 2, 2, 0, s_USCALED);
+case VK_FORMAT_R16G16_SSCALED: return createDFDUnpacked(0, 2, 2, 0, s_SSCALED);
+case VK_FORMAT_R16G16_UINT: return createDFDUnpacked(0, 2, 2, 0, s_UINT);
+case VK_FORMAT_R16G16_SINT: return createDFDUnpacked(0, 2, 2, 0, s_SINT);
+case VK_FORMAT_R16G16_SFLOAT: return createDFDUnpacked(0, 2, 2, 0, s_SFLOAT);
+case VK_FORMAT_R16G16B16_UNORM: return createDFDUnpacked(0, 3, 2, 0, s_UNORM);
+case VK_FORMAT_R16G16B16_SNORM: return createDFDUnpacked(0, 3, 2, 0, s_SNORM);
+case VK_FORMAT_R16G16B16_USCALED: return createDFDUnpacked(0, 3, 2, 0, s_USCALED);
+case VK_FORMAT_R16G16B16_SSCALED: return createDFDUnpacked(0, 3, 2, 0, s_SSCALED);
+case VK_FORMAT_R16G16B16_UINT: return createDFDUnpacked(0, 3, 2, 0, s_UINT);
+case VK_FORMAT_R16G16B16_SINT: return createDFDUnpacked(0, 3, 2, 0, s_SINT);
+case VK_FORMAT_R16G16B16_SFLOAT: return createDFDUnpacked(0, 3, 2, 0, s_SFLOAT);
+case VK_FORMAT_R16G16B16A16_UNORM: return createDFDUnpacked(0, 4, 2, 0, s_UNORM);
+case VK_FORMAT_R16G16B16A16_SNORM: return createDFDUnpacked(0, 4, 2, 0, s_SNORM);
+case VK_FORMAT_R16G16B16A16_USCALED: return createDFDUnpacked(0, 4, 2, 0, s_USCALED);
+case VK_FORMAT_R16G16B16A16_SSCALED: return createDFDUnpacked(0, 4, 2, 0, s_SSCALED);
+case VK_FORMAT_R16G16B16A16_UINT: return createDFDUnpacked(0, 4, 2, 0, s_UINT);
+case VK_FORMAT_R16G16B16A16_SINT: return createDFDUnpacked(0, 4, 2, 0, s_SINT);
+case VK_FORMAT_R16G16B16A16_SFLOAT: return createDFDUnpacked(0, 4, 2, 0, s_SFLOAT);
+case VK_FORMAT_R32_UINT: return createDFDUnpacked(0, 1, 4, 0, s_UINT);
+case VK_FORMAT_R32_SINT: return createDFDUnpacked(0, 1, 4, 0, s_SINT);
+case VK_FORMAT_R32_SFLOAT: return createDFDUnpacked(0, 1, 4, 0, s_SFLOAT);
+case VK_FORMAT_R32G32_UINT: return createDFDUnpacked(0, 2, 4, 0, s_UINT);
+case VK_FORMAT_R32G32_SINT: return createDFDUnpacked(0, 2, 4, 0, s_SINT);
+case VK_FORMAT_R32G32_SFLOAT: return createDFDUnpacked(0, 2, 4, 0, s_SFLOAT);
+case VK_FORMAT_R32G32B32_UINT: return createDFDUnpacked(0, 3, 4, 0, s_UINT);
+case VK_FORMAT_R32G32B32_SINT: return createDFDUnpacked(0, 3, 4, 0, s_SINT);
+case VK_FORMAT_R32G32B32_SFLOAT: return createDFDUnpacked(0, 3, 4, 0, s_SFLOAT);
+case VK_FORMAT_R32G32B32A32_UINT: return createDFDUnpacked(0, 4, 4, 0, s_UINT);
+case VK_FORMAT_R32G32B32A32_SINT: return createDFDUnpacked(0, 4, 4, 0, s_SINT);
+case VK_FORMAT_R32G32B32A32_SFLOAT: return createDFDUnpacked(0, 4, 4, 0, s_SFLOAT);
+case VK_FORMAT_R64_UINT: return createDFDUnpacked(0, 1, 8, 0, s_UINT);
+case VK_FORMAT_R64_SINT: return createDFDUnpacked(0, 1, 8, 0, s_SINT);
+case VK_FORMAT_R64_SFLOAT: return createDFDUnpacked(0, 1, 8, 0, s_SFLOAT);
+case VK_FORMAT_R64G64_UINT: return createDFDUnpacked(0, 2, 8, 0, s_UINT);
+case VK_FORMAT_R64G64_SINT: return createDFDUnpacked(0, 2, 8, 0, s_SINT);
+case VK_FORMAT_R64G64_SFLOAT: return createDFDUnpacked(0, 2, 8, 0, s_SFLOAT);
+case VK_FORMAT_R64G64B64_UINT: return createDFDUnpacked(0, 3, 8, 0, s_UINT);
+case VK_FORMAT_R64G64B64_SINT: return createDFDUnpacked(0, 3, 8, 0, s_SINT);
+case VK_FORMAT_R64G64B64_SFLOAT: return createDFDUnpacked(0, 3, 8, 0, s_SFLOAT);
+case VK_FORMAT_R64G64B64A64_UINT: return createDFDUnpacked(0, 4, 8, 0, s_UINT);
+case VK_FORMAT_R64G64B64A64_SINT: return createDFDUnpacked(0, 4, 8, 0, s_SINT);
+case VK_FORMAT_R64G64B64A64_SFLOAT: return createDFDUnpacked(0, 4, 8, 0, s_SFLOAT);
+case VK_FORMAT_B10G11R11_UFLOAT_PACK32: {
+    int channels[] = {0,1,2}; int bits[] = {11,11,10};
+    return createDFDPacked(0, 3, bits, channels, s_UFLOAT);
+}
+case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32: {
+    int bits[] = {0}; int channels[] = {0};
+    return createDFDPacked(0, 6, bits, channels, s_UFLOAT);
+}
+case VK_FORMAT_D16_UNORM: return createDFDDepthStencil(16,0,2);
+case VK_FORMAT_X8_D24_UNORM_PACK32: return createDFDDepthStencil(24,0,4);
+case VK_FORMAT_D32_SFLOAT: return createDFDDepthStencil(32,0,4);
+case VK_FORMAT_S8_UINT: return createDFDDepthStencil(0,8,1);
+case VK_FORMAT_BC1_RGB_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC1_RGB_SRGB_BLOCK: return createDFDCompressed(c_BC1_RGB, 4, 4, 1, s_SRGB);
+case VK_FORMAT_BC1_RGBA_UNORM_BLOCK: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC1_RGBA_SRGB_BLOCK: return createDFDCompressed(c_BC1_RGBA, 4, 4, 1, s_SRGB);
+case VK_FORMAT_BC2_UNORM_BLOCK: return createDFDCompressed(c_BC2, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC2_SRGB_BLOCK: return createDFDCompressed(c_BC2, 4, 4, 1, s_SRGB);
+case VK_FORMAT_BC3_UNORM_BLOCK: return createDFDCompressed(c_BC3, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC3_SRGB_BLOCK: return createDFDCompressed(c_BC3, 4, 4, 1, s_SRGB);
+case VK_FORMAT_BC4_UNORM_BLOCK: return createDFDCompressed(c_BC4, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC4_SNORM_BLOCK: return createDFDCompressed(c_BC4, 4, 4, 1, s_SNORM);
+case VK_FORMAT_BC5_UNORM_BLOCK: return createDFDCompressed(c_BC5, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC5_SNORM_BLOCK: return createDFDCompressed(c_BC5, 4, 4, 1, s_SNORM);
+case VK_FORMAT_BC6H_UFLOAT_BLOCK: return createDFDCompressed(c_BC6H, 4, 4, 1, s_UFLOAT);
+case VK_FORMAT_BC6H_SFLOAT_BLOCK: return createDFDCompressed(c_BC6H, 4, 4, 1, s_SFLOAT);
+case VK_FORMAT_BC7_UNORM_BLOCK: return createDFDCompressed(c_BC7, 4, 4, 1, s_UNORM);
+case VK_FORMAT_BC7_SRGB_BLOCK: return createDFDCompressed(c_BC7, 4, 4, 1, s_SRGB);
+case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_UNORM);
+case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8, 4, 4, 1, s_SRGB);
+case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A1, 4, 4, 1, s_UNORM);
+case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A1, 4, 4, 1, s_SRGB);
+case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_UNORM);
+case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK: return createDFDCompressed(c_ETC2_R8G8B8A8, 4, 4, 1, s_SRGB);
+case VK_FORMAT_EAC_R11_UNORM_BLOCK: return createDFDCompressed(c_EAC_R11, 4, 4, 1, s_UNORM);
+case VK_FORMAT_EAC_R11_SNORM_BLOCK: return createDFDCompressed(c_EAC_R11, 4, 4, 1, s_SNORM);
+case VK_FORMAT_EAC_R11G11_UNORM_BLOCK: return createDFDCompressed(c_EAC_R11G11, 4, 4, 1, s_UNORM);
+case VK_FORMAT_EAC_R11G11_SNORM_BLOCK: return createDFDCompressed(c_EAC_R11G11, 4, 4, 1, s_SNORM);
+case VK_FORMAT_ASTC_4x4_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 4, 4, 1, s_UNORM);
+case VK_FORMAT_ASTC_4x4_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 4, 4, 1, s_SRGB);
+case VK_FORMAT_ASTC_5x4_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 5, 4, 1, s_UNORM);
+case VK_FORMAT_ASTC_5x4_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 5, 4, 1, s_SRGB);
+case VK_FORMAT_ASTC_5x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 5, 5, 1, s_UNORM);
+case VK_FORMAT_ASTC_5x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 5, 5, 1, s_SRGB);
+case VK_FORMAT_ASTC_6x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 6, 5, 1, s_UNORM);
+case VK_FORMAT_ASTC_6x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 6, 5, 1, s_SRGB);
+case VK_FORMAT_ASTC_6x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 6, 6, 1, s_UNORM);
+case VK_FORMAT_ASTC_6x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 6, 6, 1, s_SRGB);
+case VK_FORMAT_ASTC_8x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 5, 1, s_UNORM);
+case VK_FORMAT_ASTC_8x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 5, 1, s_SRGB);
+case VK_FORMAT_ASTC_8x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 6, 1, s_UNORM);
+case VK_FORMAT_ASTC_8x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 6, 1, s_SRGB);
+case VK_FORMAT_ASTC_8x8_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 8, 8, 1, s_UNORM);
+case VK_FORMAT_ASTC_8x8_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 8, 8, 1, s_SRGB);
+case VK_FORMAT_ASTC_10x5_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 5, 1, s_UNORM);
+case VK_FORMAT_ASTC_10x5_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 5, 1, s_SRGB);
+case VK_FORMAT_ASTC_10x6_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 6, 1, s_UNORM);
+case VK_FORMAT_ASTC_10x6_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 6, 1, s_SRGB);
+case VK_FORMAT_ASTC_10x8_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 8, 1, s_UNORM);
+case VK_FORMAT_ASTC_10x8_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 8, 1, s_SRGB);
+case VK_FORMAT_ASTC_10x10_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 10, 10, 1, s_UNORM);
+case VK_FORMAT_ASTC_10x10_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SRGB);
+case VK_FORMAT_ASTC_12x10_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 10, 1, s_UNORM);
+case VK_FORMAT_ASTC_12x10_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SRGB);
+case VK_FORMAT_ASTC_12x12_UNORM_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_UNORM);
+case VK_FORMAT_ASTC_12x12_SRGB_BLOCK: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SRGB);
+case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_UNORM);
+case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_UNORM);
+case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_UNORM);
+case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 4, 4, 1, s_UNORM);
+case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 8, 4, 1, s_SRGB);
+case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC, 4, 4, 1, s_SRGB);
+case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 8, 4, 1, s_SRGB);
+case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG: return createDFDCompressed(c_PVRTC2, 4, 4, 1, s_SRGB);
+case VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 5, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 6, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 8, 8, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 5, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 6, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 8, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 10, 10, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 10, 1, s_SFLOAT);
+case VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 12, 12, 1, s_SFLOAT);
+#if 0
+case VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_UNORM);
+case VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SRGB);
+case VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 3, 3, 3, s_SFLOAT);
+case VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_UNORM);
+case VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_SRGB);
+case VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 3, 3, s_SFLOAT);
+case VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_UNORM);
+case VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_SRGB);
+case VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 3, s_SFLOAT);
+case VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_UNORM);
+case VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_SRGB);
+case VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 4, 4, 4, s_SFLOAT);
+case VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_UNORM);
+case VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_SRGB);
+case VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 4, 4, s_SFLOAT);
+case VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_UNORM);
+case VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_SRGB);
+case VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 4, s_SFLOAT);
+case VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_UNORM);
+case VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_SRGB);
+case VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 5, 5, 5, s_SFLOAT);
+case VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_UNORM);
+case VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_SRGB);
+case VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 5, 5, s_SFLOAT);
+case VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_UNORM);
+case VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_SRGB);
+case VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 5, s_SFLOAT);
+case VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_UNORM);
+case VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SRGB);
+case VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT: return createDFDCompressed(c_ASTC, 6, 6, 6, s_SFLOAT);
+#endif
+case VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT: {
+    int channels[] = {2,1,0,3}; int bits[] = {4,4,4,4};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
+case VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT: {
+    int channels[] = {0,1,2,3}; int bits[] = {4,4,4,4};
+    return createDFDPacked(0, 4, bits, channels, s_UNORM);
+}
diff --git a/thirdparty/libktx/lib/filestream.c b/thirdparty/libktx/lib/filestream.c
new file mode 100644
index 00000000000..b1e0eba7c6f
--- /dev/null
+++ b/thirdparty/libktx/lib/filestream.c
@@ -0,0 +1,393 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ *
+ * @brief Implementation of ktxStream for FILE.
+ *
+ * @author Maksim Kolesin, Under Development
+ * @author Georg Kolling, Imagination Technology
+ * @author Mark Callow, HI Corporation
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <string.h>
+/* I need these on Linux. Why? */
+#define __USE_LARGEFILE 1  // For declaration of ftello, etc.
+#define __USE_POSIX 1      // For declaration of fileno.
+#define _POSIX_SOURCE 1    // For both the above in Emscripten.
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>     // For stat.h on Windows
+#define __USE_MISC 1       // For declaration of S_IF...
+#include <sys/stat.h>
+
+#include "ktx.h"
+#include "ktxint.h"
+#include "filestream.h"
+
+// Gotta love Windows :-(
+#if defined(_MSC_VER)
+  #if defined(_WIN64)
+    #define ftello _ftelli64
+    #define fseeko _fseeki64
+  #else
+    #define ftello ftell
+    #define fseeko fseek
+  #endif
+  #define fileno _fileno
+  #define fstat _fstat
+  #define stat _stat
+  #define S_IFIFO _S_IFIFO
+  #define S_IFSOCK 0xC000
+  typedef unsigned short mode_t;
+#endif
+
+#if defined(__MINGW32__)
+    #define S_IFSOCK 0xC000
+#endif
+
+#define KTX_FILE_STREAM_MAX (1 << (sizeof(ktx_off_t) - 1) - 1)
+
+/**
+ * @~English
+ * @brief Read bytes from a ktxFileStream.
+ *
+ * @param [in]  str     pointer to the ktxStream from which to read.
+ * @param [out] dst     pointer to a block of memory with a size
+ *                      of at least @p size bytes, converted to a void*.
+ * @param [in,out] count   pointer to total count of bytes to be read.
+ *                         On completion set to number of bytes read.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p dst is @c NULL or @p src is @c NULL.
+ * @exception KTX_FILE_READ_ERROR  an error occurred while reading the file.
+ * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
+ */
+static
+KTX_error_code ktxFileStream_read(ktxStream* str, void* dst, const ktx_size_t count)
+{
+    ktx_size_t nread;
+
+    if (!str || !dst)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+    if ((nread = fread(dst, 1, count, str->data.file)) != count) {
+        if (feof(str->data.file)) {
+            return KTX_FILE_UNEXPECTED_EOF;
+        } else {
+            return KTX_FILE_READ_ERROR;
+        }
+    }
+    str->readpos += count;
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Skip bytes in a ktxFileStream.
+ *
+ * @param [in] str           pointer to a ktxStream object.
+ * @param [in] count         number of bytes to be skipped.
+ *
+ * In order to support applications reading from stdin, read characters
+ * rather than using seek functions.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str is @c NULL or @p count is less than zero.
+ * @exception KTX_INVALID_OPERATION skipping @p count bytes would go beyond EOF.
+ * @exception KTX_FILE_READ_ERROR  an error occurred while reading the file.
+ * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
+ *                                    @p count is set to the number of bytes
+ *                                    skipped.
+ */
+static
+KTX_error_code ktxFileStream_skip(ktxStream* str, const ktx_size_t count)
+{
+    if (!str)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+    for (ktx_uint32_t i = 0; i < count; i++) {
+        int ret = getc(str->data.file);
+        if (ret == EOF) {
+            if (feof(str->data.file)) {
+                return KTX_FILE_UNEXPECTED_EOF;
+            } else {
+                return KTX_FILE_READ_ERROR;
+            }
+        }
+    }
+    str->readpos += count;
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Write bytes to a ktxFileStream.
+ *
+ * @param [in] str      pointer to the ktxStream that is the destination of the
+ *                      write.
+ * @param [in] src      pointer to the array of elements to be written,
+ *                      converted to a const void*.
+ * @param [in] size     size in bytes of each element to be written.
+ * @param [in] count    number of elements, each one with a @p size of size
+ *                      bytes.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str is @c NULL or @p src is @c NULL.
+ * @exception KTX_FILE_OVERFLOW the requested write would caused the file to
+ *                              exceed the maximum supported file size.
+ * @exception KTX_FILE_WRITE_ERROR a system error occurred while writing the
+ *                                 file.
+ */
+static
+KTX_error_code ktxFileStream_write(ktxStream* str, const void *src,
+                                   const ktx_size_t size,
+                                   const ktx_size_t count)
+{
+    if (!str || !src)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+    if (fwrite(src, size, count, str->data.file) != count) {
+        if (errno == EFBIG || errno == EOVERFLOW)
+            return KTX_FILE_OVERFLOW;
+        else
+            return KTX_FILE_WRITE_ERROR;
+    }
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Get the current read/write position in a ktxFileStream.
+ *
+ * @param [in] str      pointer to the ktxStream to query.
+ * @param [in,out] off  pointer to variable to receive the offset value.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_ISPIPE file descriptor underlying stream is associated
+ *                            with a pipe or FIFO so does not have a
+ *                            file-position indicator.
+ * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL.
+ */
+static
+KTX_error_code ktxFileStream_getpos(ktxStream* str, ktx_off_t* pos)
+{
+    ktx_off_t ftellval;
+
+    if (!str || !pos)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+    if (str->data.file == stdin) {
+        *pos = str->readpos;
+    } else {
+        /* The cast quiets an Xcode warning when building for "Generic iOS Device".
+         * I'm not sure why.
+         */
+        ftellval = (ktx_off_t)ftello(str->data.file);
+        if (ftellval < 0) {
+            switch (errno) {
+              case ESPIPE: return KTX_FILE_ISPIPE;
+              case EOVERFLOW: return KTX_FILE_OVERFLOW;
+            }
+        }
+
+        *pos = ftellval;
+    }
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Set the current read/write position in a ktxFileStream.
+ *
+ * Offset of 0 is the start of the file. This function operates
+ * like Linux > 3.1's @c lseek() when it is passed a @c whence
+ * of @c SEEK_DATA as it returns an error if the seek would
+ * go beyond the end of the file.
+ *
+ * @param [in] str    pointer to the ktxStream whose r/w position is to be set.
+ * @param [in] off    pointer to the offset value to set.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * Throws the same exceptions as ktxFileStream_getsize() for the reasons given
+ * there plus the following:
+ *
+ * @exception KTX_INVALID_VALUE @p str is @c NULL.
+ * @exception KTX_INVALID_OPERATION @p pos is > the size of the file or an
+ *                                  fseek error occurred.
+ */
+static
+KTX_error_code ktxFileStream_setpos(ktxStream* str, ktx_off_t pos)
+{
+    ktx_size_t fileSize;
+    KTX_error_code result;
+
+    if (!str)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+    if (str->data.file == stdin) {
+        if (pos > str->readpos)
+            return str->skip(str, pos - str->readpos);
+        else
+            return KTX_FILE_ISPIPE;
+    }
+
+    result = str->getsize(str, &fileSize);
+
+    if (result != KTX_SUCCESS) {
+        // Device is likely not seekable.
+        return result;
+    }
+
+    if (pos > (ktx_off_t)fileSize)
+        return KTX_INVALID_OPERATION;
+
+    if (fseeko(str->data.file, pos, SEEK_SET) < 0)
+            return KTX_FILE_SEEK_ERROR;
+    else
+            return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Get the size of a ktxFileStream in bytes.
+ *
+ * @param [in] str       pointer to the ktxStream whose size is to be queried.
+ * @param [in,out] size  pointer to a variable in which size will be written.
+ *
+ * @return    KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_OVERFLOW size is too large to be returned in a
+ *                              @c ktx_size_t.
+ * @exception KTX_FILE_ISPIPE file descriptor underlying stream is associated
+ *                            with a pipe or FIFO so does not have a
+ *                            file-position indicator.
+ * @exception KTX_FILE_READ_ERROR a system error occurred while getting the
+ *                                size.
+ * @exception KTX_INVALID_VALUE @p str or @p size is @c NULL.
+ * @exception KTX_INVALID_OPERATION stream is a tty.
+ */
+static
+KTX_error_code ktxFileStream_getsize(ktxStream* str, ktx_size_t* size)
+{
+    struct stat statbuf;
+    int statret;
+
+    if (!str || !size)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeFile);
+
+  // Need to flush so that fstat will return the current size.
+  // Can ignore return value. The only error that can happen is to tell you
+  // it was a NOP because the file is read only.
+#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(__MINGW64__) && !defined(_UCRT)
+  // Bug in VS2013 msvcrt. fflush on FILE open for READ changes file offset
+  // to 4096.
+  if (str->data.file->_flag & _IOWRT)
+#endif
+    (void)fflush(str->data.file);
+    statret = fstat(fileno(str->data.file), &statbuf);
+    if (statret < 0) {
+        switch (errno) {
+          case EOVERFLOW: return KTX_FILE_OVERFLOW;
+          case EIO:
+          default:
+            return KTX_FILE_READ_ERROR;
+        }
+    }
+
+    mode_t ftype = statbuf.st_mode & S_IFMT;
+    if (ftype == S_IFIFO || ftype == S_IFSOCK)
+        return KTX_FILE_ISPIPE;
+
+    if (statbuf.st_mode & S_IFCHR)
+        return KTX_INVALID_OPERATION;
+
+    *size = (ktx_size_t)statbuf.st_size; /* See _getpos for why this cast. */
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Initialize a ktxFileStream.
+ *
+ * @param [in] str      pointer to the ktxStream to initialize.
+ * @param [in] file     pointer to the underlying FILE object.
+ * @param [in] closeFileOnDestruct if not false, stdio file pointer will be closed when ktxStream
+ *             is destructed.
+ *
+ * @return      KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
+ *
+ * @exception KTX_INVALID_VALUE @p stream is @c NULL or @p file is @c NULL.
+ */
+KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file,
+                                       ktx_bool_t closeFileOnDestruct)
+{
+    if (!str || !file)
+        return KTX_INVALID_VALUE;
+
+    str->data.file = file;
+    str->readpos = 0;
+    str->type = eStreamTypeFile;
+    str->read = ktxFileStream_read;
+    str->skip = ktxFileStream_skip;
+    str->write = ktxFileStream_write;
+    str->getpos = ktxFileStream_getpos;
+    str->setpos = ktxFileStream_setpos;
+    str->getsize = ktxFileStream_getsize;
+    str->destruct = ktxFileStream_destruct;
+    str->closeOnDestruct = closeFileOnDestruct;
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Destruct the stream, potentially closing the underlying FILE.
+ *
+ * This only closes the underyling FILE if the @c closeOnDestruct parameter to
+ * ktxFileStream_construct() was not @c KTX_FALSE.
+ *
+ * @param [in] str pointer to the ktxStream whose FILE is to potentially
+ *             be closed.
+ */
+void
+ktxFileStream_destruct(ktxStream* str)
+{
+    assert(str && str->type == eStreamTypeFile);
+
+    if (str->closeOnDestruct)
+        fclose(str->data.file);
+    str->data.file = 0;
+}
diff --git a/thirdparty/libktx/lib/filestream.h b/thirdparty/libktx/lib/filestream.h
new file mode 100644
index 00000000000..5c0ea7d2dd1
--- /dev/null
+++ b/thirdparty/libktx/lib/filestream.h
@@ -0,0 +1,27 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/*
+ * Author: Maksim Kolesin from original code
+ * by Mark Callow and Georg Kolling
+ */
+
+#ifndef FILESTREAM_H
+#define FILESTREAM_H
+
+#include "ktx.h"
+
+/*
+ * ktxFileInit: Initialize a ktxStream to a ktxFileStream with a FILE object
+ */
+KTX_error_code ktxFileStream_construct(ktxStream* str, FILE* file,
+                                       ktx_bool_t closeFileOnDestruct);
+
+void ktxFileStream_destruct(ktxStream* str);
+
+#endif /* FILESTREAM_H */
diff --git a/thirdparty/libktx/lib/formatsize.h b/thirdparty/libktx/lib/formatsize.h
new file mode 100644
index 00000000000..7112a3a90d7
--- /dev/null
+++ b/thirdparty/libktx/lib/formatsize.h
@@ -0,0 +1,58 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file
+ * @~English
+ *
+ * @brief Struct for returning size information about an image format.
+ *
+ * @author Mark Callow, www.edgewise-consulting.com
+ */
+
+#ifndef _FORMATSIZE_H_
+#define _FORMATSIZE_H_
+
+#include "ktx.h"
+
+typedef enum ktxFormatSizeFlagBits {
+    KTX_FORMAT_SIZE_PACKED_BIT                = 0x00000001,
+    KTX_FORMAT_SIZE_COMPRESSED_BIT            = 0x00000002,
+    KTX_FORMAT_SIZE_PALETTIZED_BIT            = 0x00000004,
+    KTX_FORMAT_SIZE_DEPTH_BIT                 = 0x00000008,
+    KTX_FORMAT_SIZE_STENCIL_BIT               = 0x00000010,
+} ktxFormatSizeFlagBits;
+
+typedef ktx_uint32_t ktxFormatSizeFlags;
+
+/**
+ * @brief Structure for holding size information for a texture format.
+ */
+typedef struct ktxFormatSize {
+    ktxFormatSizeFlags  flags;
+    unsigned int        paletteSizeInBits;  // For KTX1.
+    unsigned int        blockSizeInBits;
+    unsigned int        blockWidth;         // in texels
+    unsigned int        blockHeight;        // in texels
+    unsigned int        blockDepth;         // in texels
+    unsigned int        minBlocksX;         // Minimum required number of blocks
+    unsigned int        minBlocksY;
+} ktxFormatSize;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+bool ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif /* _FORMATSIZE_H_ */
diff --git a/thirdparty/libktx/lib/gl_format.h b/thirdparty/libktx/lib/gl_format.h
new file mode 100644
index 00000000000..2381505a68c
--- /dev/null
+++ b/thirdparty/libktx/lib/gl_format.h
@@ -0,0 +1,2654 @@
+/*
+================================================================================================
+
+Description	:	OpenGL formats/types and properties.
+Author		:	J.M.P. van Waveren
+Date		:	07/17/2016
+Language	:	C99
+Format		:	Real tabs with the tab size equal to 4 spaces.
+Copyright	:	Copyright (c) 2016 Oculus VR, LLC. All Rights reserved.
+
+
+LICENSE
+=======
+
+Copyright 2016 Oculus VR, LLC.
+SPDX-License-Identifier: Apache-2.0
+
+
+DESCRIPTION
+===========
+
+This header stores the OpenGL formats/types and two simple routines
+to derive the format/type from an internal format. These routines
+are useful to verify the data in a KTX container files. The OpenGL
+constants are generally useful to convert files like KTX and glTF
+to different graphics APIs.
+
+This header stores the OpenGL formats/types that are used as parameters
+to the following OpenGL functions:
+
+void glTexImage2D( GLenum target, GLint level, GLint internalFormat,
+	GLsizei width, GLsizei height, GLint border,
+	GLenum format, GLenum type, const GLvoid * data );
+void glTexImage3D( GLenum target, GLint level, GLint internalFormat,
+	GLsizei width, GLsizei height, GLsizei depth, GLint border,
+	GLenum format, GLenum type, const GLvoid * data );
+void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat,
+	GLsizei width, GLsizei height, GLint border,
+	GLsizei imageSize, const GLvoid * data );
+void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat,
+	GLsizei width, GLsizei height, GLsizei depth, GLint border,
+	GLsizei imageSize, const GLvoid * data );
+void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat,
+	GLsizei width, GLsizei height );
+void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat,
+	GLsizei width, GLsizei height, GLsizei depth );
+void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized,
+	GLsizei stride, const GLvoid * pointer);
+
+
+IMPLEMENTATION
+==============
+
+This file does not include OpenGL / OpenGL ES headers because:
+
+  1. Including OpenGL / OpenGL ES headers is platform dependent and
+     may require a separate installation of an OpenGL SDK.
+  2. The OpenGL format/type constants are the same between extensions and core.
+  3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES.
+  4. The OpenGL constants in this header are also used to derive Vulkan formats
+     from the OpenGL formats/types stored in files like KTX and glTF. These file
+     formats may use OpenGL formats/types that are not supported by the OpenGL
+     implementation on the platform but are supported by the Vulkan implementation.
+
+
+ENTRY POINTS
+============
+
+static inline GLenum glGetFormatFromInternalFormat( const GLenum internalFormat );
+static inline GLenum glGetTypeFromInternalFormat( const GLenum internalFormat );
+static inline void glGetFormatSize( const GLenum internalFormat, GlFormatSize * pFormatSize );
+static inline unsigned int glGetTypeSizeFromType( const GLenum type );
+static inline GLenum glGetInternalFormatFromVkFormat ( VkFormat format );
+
+MODIFICATIONS for use in libktx
+===============================
+
+2018.3.23 Added glGetTypeSizeFromType. Mark Callow, Edgewise Consulting.
+2019.3.09 #if 0 around GL type declarations.            〃
+2019.5.30 Use common ktxFormatSize to return results.         〃
+2019.5.30 Return blockSizeInBits 0 for default case of glGetFormatSize. 〃
+2019.5.30 Added glGetInternalFormatFromVkFormat.            〃
+
+================================================================================================
+*/
+
+#if !defined( GL_FORMAT_H )
+#define GL_FORMAT_H
+
+#include <assert.h>
+#include "formatsize.h"
+#include "vkformat_enum.h"
+
+#if defined(_WIN32) && !defined(__MINGW32__)
+#ifndef NOMINMAX
+#define NOMINMAX
+#endif
+#ifndef __cplusplus
+#undef inline
+#define inline __inline
+#endif // __cplusplus
+#endif
+
+/*
+===========================================================================
+Avoid warnings or even errors when using strict C99. "Redefinition of
+(type) is a C11 feature." All includers in libktx also include ktx.h where
+they are also defined.
+===========================================================================
+*/
+#if 0
+typedef unsigned int GLenum;
+typedef unsigned char GLboolean;
+typedef unsigned int GLuint;
+#endif
+
+#if !defined( GL_INVALID_VALUE )
+#define GL_INVALID_VALUE								0x0501
+#endif
+
+/*
+================================================================================================================================
+
+Format to glTexImage2D and glTexImage3D.
+
+================================================================================================================================
+*/
+
+#if !defined( GL_RED )
+#define GL_RED											0x1903	// same as GL_RED_EXT
+#endif
+#if !defined( GL_GREEN )
+#define GL_GREEN										0x1904	// deprecated
+#endif
+#if !defined( GL_BLUE )
+#define GL_BLUE											0x1905	// deprecated
+#endif
+#if !defined( GL_ALPHA )
+#define GL_ALPHA										0x1906	// deprecated
+#endif
+#if !defined( GL_LUMINANCE )
+#define GL_LUMINANCE									0x1909	// deprecated
+#endif
+#if !defined( GL_SLUMINANCE )
+#define GL_SLUMINANCE									0x8C46	// deprecated, same as GL_SLUMINANCE_EXT
+#endif
+#if !defined( GL_LUMINANCE_ALPHA )
+#define GL_LUMINANCE_ALPHA								0x190A	// deprecated
+#endif
+#if !defined( GL_SLUMINANCE_ALPHA )
+#define GL_SLUMINANCE_ALPHA								0x8C44	// deprecated, same as GL_SLUMINANCE_ALPHA_EXT
+#endif
+#if !defined( GL_INTENSITY )
+#define GL_INTENSITY									0x8049	// deprecated, same as GL_INTENSITY_EXT
+#endif
+#if !defined( GL_RG )
+#define GL_RG											0x8227	// same as GL_RG_EXT
+#endif
+#if !defined( GL_RGB )
+#define GL_RGB											0x1907
+#endif
+#if !defined( GL_BGR )
+#define GL_BGR											0x80E0	// same as GL_BGR_EXT
+#endif
+#if !defined( GL_RGBA )
+#define GL_RGBA											0x1908
+#endif
+#if !defined( GL_BGRA )
+#define GL_BGRA											0x80E1	// same as GL_BGRA_EXT
+#endif
+#if !defined( GL_RED_INTEGER )
+#define GL_RED_INTEGER									0x8D94	// same as GL_RED_INTEGER_EXT
+#endif
+#if !defined( GL_GREEN_INTEGER )
+#define GL_GREEN_INTEGER								0x8D95	// deprecated, same as GL_GREEN_INTEGER_EXT
+#endif
+#if !defined( GL_BLUE_INTEGER )
+#define GL_BLUE_INTEGER									0x8D96	// deprecated, same as GL_BLUE_INTEGER_EXT
+#endif
+#if !defined( GL_ALPHA_INTEGER )
+#define GL_ALPHA_INTEGER								0x8D97	// deprecated, same as GL_ALPHA_INTEGER_EXT
+#endif
+#if !defined( GL_LUMINANCE_INTEGER )
+#define GL_LUMINANCE_INTEGER							0x8D9C	// deprecated, same as GL_LUMINANCE_INTEGER_EXT
+#endif
+#if !defined( GL_LUMINANCE_ALPHA_INTEGER )
+#define GL_LUMINANCE_ALPHA_INTEGER						0x8D9D	// deprecated, same as GL_LUMINANCE_ALPHA_INTEGER_EXT
+#endif
+#if !defined( GL_RG_INTEGER )
+#define GL_RG_INTEGER									0x8228	// same as GL_RG_INTEGER_EXT
+#endif
+#if !defined( GL_RGB_INTEGER )
+#define GL_RGB_INTEGER									0x8D98	// same as GL_RGB_INTEGER_EXT
+#endif
+#if !defined( GL_BGR_INTEGER )
+#define GL_BGR_INTEGER									0x8D9A	// same as GL_BGR_INTEGER_EXT
+#endif
+#if !defined( GL_RGBA_INTEGER )
+#define GL_RGBA_INTEGER									0x8D99	// same as GL_RGBA_INTEGER_EXT
+#endif
+#if !defined( GL_BGRA_INTEGER )
+#define GL_BGRA_INTEGER									0x8D9B	// same as GL_BGRA_INTEGER_EXT
+#endif
+#if !defined( GL_COLOR_INDEX )
+#define GL_COLOR_INDEX									0x1900	// deprecated
+#endif
+#if !defined( GL_STENCIL_INDEX )
+#define GL_STENCIL_INDEX								0x1901
+#endif
+#if !defined( GL_DEPTH_COMPONENT )
+#define GL_DEPTH_COMPONENT								0x1902
+#endif
+#if !defined( GL_DEPTH_STENCIL )
+#define GL_DEPTH_STENCIL								0x84F9	// same as GL_DEPTH_STENCIL_NV and GL_DEPTH_STENCIL_EXT and GL_DEPTH_STENCIL_OES
+#endif
+
+/*
+================================================================================================================================
+
+Type to glTexImage2D, glTexImage3D and glVertexAttribPointer.
+
+================================================================================================================================
+*/
+
+#if !defined( GL_BYTE )
+#define GL_BYTE											0x1400
+#endif
+#if !defined( GL_UNSIGNED_BYTE )
+#define GL_UNSIGNED_BYTE								0x1401
+#endif
+#if !defined( GL_SHORT )
+#define GL_SHORT										0x1402
+#endif
+#if !defined( GL_UNSIGNED_SHORT )
+#define GL_UNSIGNED_SHORT								0x1403
+#endif
+#if !defined( GL_INT )
+#define GL_INT											0x1404
+#endif
+#if !defined( GL_UNSIGNED_INT )
+#define GL_UNSIGNED_INT									0x1405
+#endif
+#if !defined( GL_INT64 )
+#define GL_INT64										0x140E	// same as GL_INT64_NV and GL_INT64_ARB
+#endif
+#if !defined( GL_UNSIGNED_INT64 )
+#define GL_UNSIGNED_INT64								0x140F	// same as GL_UNSIGNED_INT64_NV and GL_UNSIGNED_INT64_ARB
+#endif
+#if !defined( GL_HALF_FLOAT )
+#define GL_HALF_FLOAT									0x140B	// same as GL_HALF_FLOAT_NV and GL_HALF_FLOAT_ARB
+#endif
+#if !defined( GL_HALF_FLOAT_OES )
+#define GL_HALF_FLOAT_OES								0x8D61	// Note that this different from GL_HALF_FLOAT.
+#endif
+#if !defined( GL_FLOAT )
+#define GL_FLOAT										0x1406
+#endif
+#if !defined( GL_DOUBLE )
+#define GL_DOUBLE										0x140A	// same as GL_DOUBLE_EXT
+#endif
+#if !defined( GL_UNSIGNED_BYTE_3_3_2 )
+#define GL_UNSIGNED_BYTE_3_3_2							0x8032	// same as GL_UNSIGNED_BYTE_3_3_2_EXT
+#endif
+#if !defined( GL_UNSIGNED_BYTE_2_3_3_REV )
+#define GL_UNSIGNED_BYTE_2_3_3_REV						0x8362	// same as GL_UNSIGNED_BYTE_2_3_3_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_5_6_5 )
+#define GL_UNSIGNED_SHORT_5_6_5							0x8363	// same as GL_UNSIGNED_SHORT_5_6_5_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_5_6_5_REV )
+#define GL_UNSIGNED_SHORT_5_6_5_REV						0x8364	// same as GL_UNSIGNED_SHORT_5_6_5_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_4_4_4_4 )
+#define GL_UNSIGNED_SHORT_4_4_4_4						0x8033	// same as GL_UNSIGNED_SHORT_4_4_4_4_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_4_4_4_4_REV )
+#define GL_UNSIGNED_SHORT_4_4_4_4_REV					0x8365	// same as GL_UNSIGNED_SHORT_4_4_4_4_REV_IMG and GL_UNSIGNED_SHORT_4_4_4_4_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_5_5_5_1 )
+#define GL_UNSIGNED_SHORT_5_5_5_1						0x8034	// same as GL_UNSIGNED_SHORT_5_5_5_1_EXT
+#endif
+#if !defined( GL_UNSIGNED_SHORT_1_5_5_5_REV )
+#define GL_UNSIGNED_SHORT_1_5_5_5_REV					0x8366	// same as GL_UNSIGNED_SHORT_1_5_5_5_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_8_8_8_8 )
+#define GL_UNSIGNED_INT_8_8_8_8							0x8035	// same as GL_UNSIGNED_INT_8_8_8_8_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_8_8_8_8_REV )
+#define GL_UNSIGNED_INT_8_8_8_8_REV						0x8367	// same as GL_UNSIGNED_INT_8_8_8_8_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_10_10_10_2 )
+#define GL_UNSIGNED_INT_10_10_10_2						0x8036	// same as GL_UNSIGNED_INT_10_10_10_2_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_2_10_10_10_REV )
+#define GL_UNSIGNED_INT_2_10_10_10_REV					0x8368	// same as GL_UNSIGNED_INT_2_10_10_10_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_10F_11F_11F_REV )
+#define GL_UNSIGNED_INT_10F_11F_11F_REV					0x8C3B	// same as GL_UNSIGNED_INT_10F_11F_11F_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_5_9_9_9_REV )
+#define GL_UNSIGNED_INT_5_9_9_9_REV						0x8C3E	// same as GL_UNSIGNED_INT_5_9_9_9_REV_EXT
+#endif
+#if !defined( GL_UNSIGNED_INT_24_8 )
+#define GL_UNSIGNED_INT_24_8							0x84FA	// same as GL_UNSIGNED_INT_24_8_NV and GL_UNSIGNED_INT_24_8_EXT and GL_UNSIGNED_INT_24_8_OES
+#endif
+#if !defined( GL_FLOAT_32_UNSIGNED_INT_24_8_REV )
+#define GL_FLOAT_32_UNSIGNED_INT_24_8_REV				0x8DAD	// same as GL_FLOAT_32_UNSIGNED_INT_24_8_REV_NV and GL_FLOAT_32_UNSIGNED_INT_24_8_REV_ARB
+#endif
+
+/*
+================================================================================================================================
+
+Internal format to glTexImage2D, glTexImage3D, glCompressedTexImage2D, glCompressedTexImage3D, glTexStorage2D, glTexStorage3D
+
+================================================================================================================================
+*/
+
+//
+// 8 bits per component
+//
+
+#if !defined( GL_R8 )
+#define GL_R8											0x8229	// same as GL_R8_EXT
+#endif
+#if !defined( GL_RG8 )
+#define GL_RG8											0x822B	// same as GL_RG8_EXT
+#endif
+#if !defined( GL_RGB8 )
+#define GL_RGB8											0x8051	// same as GL_RGB8_EXT and GL_RGB8_OES
+#endif
+#if !defined( GL_RGBA8 )
+#define GL_RGBA8										0x8058	// same as GL_RGBA8_EXT and GL_RGBA8_OES
+#endif
+
+#if !defined( GL_R8_SNORM )
+#define GL_R8_SNORM										0x8F94
+#endif
+#if !defined( GL_RG8_SNORM )
+#define GL_RG8_SNORM									0x8F95
+#endif
+#if !defined( GL_RGB8_SNORM )
+#define GL_RGB8_SNORM									0x8F96
+#endif
+#if !defined( GL_RGBA8_SNORM )
+#define GL_RGBA8_SNORM									0x8F97
+#endif
+
+#if !defined( GL_R8UI )
+#define GL_R8UI											0x8232
+#endif
+#if !defined( GL_RG8UI )
+#define GL_RG8UI										0x8238
+#endif
+#if !defined( GL_RGB8UI )
+#define GL_RGB8UI										0x8D7D	// same as GL_RGB8UI_EXT
+#endif
+#if !defined( GL_RGBA8UI )
+#define GL_RGBA8UI										0x8D7C	// same as GL_RGBA8UI_EXT
+#endif
+
+#if !defined( GL_R8I )
+#define GL_R8I											0x8231
+#endif
+#if !defined( GL_RG8I )
+#define GL_RG8I											0x8237
+#endif
+#if !defined( GL_RGB8I )
+#define GL_RGB8I										0x8D8F	// same as GL_RGB8I_EXT
+#endif
+#if !defined( GL_RGBA8I )
+#define GL_RGBA8I										0x8D8E	// same as GL_RGBA8I_EXT
+#endif
+
+#if !defined( GL_SR8 )
+#define GL_SR8											0x8FBD	// same as GL_SR8_EXT
+#endif
+#if !defined( GL_SRG8 )
+#define GL_SRG8											0x8FBE	// same as GL_SRG8_EXT
+#endif
+#if !defined( GL_SRGB8 )
+#define GL_SRGB8										0x8C41	// same as GL_SRGB8_EXT
+#endif
+#if !defined( GL_SRGB8_ALPHA8 )
+#define GL_SRGB8_ALPHA8									0x8C43	// same as GL_SRGB8_ALPHA8_EXT
+#endif
+
+//
+// 16 bits per component
+//
+
+#if !defined( GL_R16 )
+#define GL_R16											0x822A	// same as GL_R16_EXT
+#endif
+#if !defined( GL_RG16 )
+#define GL_RG16											0x822C	// same as GL_RG16_EXT
+#endif
+#if !defined( GL_RGB16 )
+#define GL_RGB16										0x8054	// same as GL_RGB16_EXT
+#endif
+#if !defined( GL_RGBA16 )
+#define GL_RGBA16										0x805B	// same as GL_RGBA16_EXT
+#endif
+
+#if !defined( GL_R16_SNORM )
+#define GL_R16_SNORM									0x8F98	// same as GL_R16_SNORM_EXT
+#endif
+#if !defined( GL_RG16_SNORM )
+#define GL_RG16_SNORM									0x8F99	// same as GL_RG16_SNORM_EXT
+#endif
+#if !defined( GL_RGB16_SNORM )
+#define GL_RGB16_SNORM									0x8F9A	// same as GL_RGB16_SNORM_EXT
+#endif
+#if !defined( GL_RGBA16_SNORM )
+#define GL_RGBA16_SNORM									0x8F9B	// same as GL_RGBA16_SNORM_EXT
+#endif
+
+#if !defined( GL_R16UI )
+#define GL_R16UI										0x8234
+#endif
+#if !defined( GL_RG16UI )
+#define GL_RG16UI										0x823A
+#endif
+#if !defined( GL_RGB16UI )
+#define GL_RGB16UI										0x8D77	// same as GL_RGB16UI_EXT
+#endif
+#if !defined( GL_RGBA16UI )
+#define GL_RGBA16UI										0x8D76	// same as GL_RGBA16UI_EXT
+#endif
+
+#if !defined( GL_R16I )
+#define GL_R16I											0x8233
+#endif
+#if !defined( GL_RG16I )
+#define GL_RG16I										0x8239
+#endif
+#if !defined( GL_RGB16I )
+#define GL_RGB16I										0x8D89	// same as GL_RGB16I_EXT
+#endif
+#if !defined( GL_RGBA16I )
+#define GL_RGBA16I										0x8D88	// same as GL_RGBA16I_EXT
+#endif
+
+#if !defined( GL_R16F )
+#define GL_R16F											0x822D	// same as GL_R16F_EXT
+#endif
+#if !defined( GL_RG16F )
+#define GL_RG16F										0x822F	// same as GL_RG16F_EXT
+#endif
+#if !defined( GL_RGB16F )
+#define GL_RGB16F										0x881B	// same as GL_RGB16F_EXT and GL_RGB16F_ARB
+#endif
+#if !defined( GL_RGBA16F )
+#define GL_RGBA16F										0x881A	// sama as GL_RGBA16F_EXT and GL_RGBA16F_ARB
+#endif
+
+//
+// 32 bits per component
+//
+
+#if !defined( GL_R32UI )
+#define GL_R32UI										0x8236
+#endif
+#if !defined( GL_RG32UI )
+#define GL_RG32UI										0x823C
+#endif
+#if !defined( GL_RGB32UI )
+#define GL_RGB32UI										0x8D71	// same as GL_RGB32UI_EXT
+#endif
+#if !defined( GL_RGBA32UI )
+#define GL_RGBA32UI										0x8D70	// same as GL_RGBA32UI_EXT
+#endif
+
+#if !defined( GL_R32I )
+#define GL_R32I											0x8235
+#endif
+#if !defined( GL_RG32I )
+#define GL_RG32I										0x823B
+#endif
+#if !defined( GL_RGB32I )
+#define GL_RGB32I										0x8D83	// same as GL_RGB32I_EXT
+#endif
+#if !defined( GL_RGBA32I )
+#define GL_RGBA32I										0x8D82	// same as GL_RGBA32I_EXT
+#endif
+
+#if !defined( GL_R32F )
+#define GL_R32F											0x822E	// same as GL_R32F_EXT
+#endif
+#if !defined( GL_RG32F )
+#define GL_RG32F										0x8230	// same as GL_RG32F_EXT
+#endif
+#if !defined( GL_RGB32F )
+#define GL_RGB32F										0x8815	// same as GL_RGB32F_EXT and GL_RGB32F_ARB
+#endif
+#if !defined( GL_RGBA32F )
+#define GL_RGBA32F										0x8814	// same as GL_RGBA32F_EXT and GL_RGBA32F_ARB
+#endif
+
+//
+// Packed
+//
+
+#if !defined( GL_R3_G3_B2 )
+#define GL_R3_G3_B2										0x2A10
+#endif
+#if !defined( GL_RGB4 )
+#define GL_RGB4											0x804F	// same as GL_RGB4_EXT
+#endif
+#if !defined( GL_RGB5 )
+#define GL_RGB5											0x8050	// same as GL_RGB5_EXT
+#endif
+#if !defined( GL_RGB565 )
+#define GL_RGB565										0x8D62	// same as GL_RGB565_EXT and GL_RGB565_OES
+#endif
+#if !defined( GL_RGB10 )
+#define GL_RGB10										0x8052	// same as GL_RGB10_EXT
+#endif
+#if !defined( GL_RGB12 )
+#define GL_RGB12										0x8053	// same as GL_RGB12_EXT
+#endif
+#if !defined( GL_RGBA2 )
+#define GL_RGBA2										0x8055	// same as GL_RGBA2_EXT
+#endif
+#if !defined( GL_RGBA4 )
+#define GL_RGBA4										0x8056	// same as GL_RGBA4_EXT and GL_RGBA4_OES
+#endif
+#if !defined( GL_RGBA12 )
+#define GL_RGBA12										0x805A	// same as GL_RGBA12_EXT
+#endif
+#if !defined( GL_RGB5_A1 )
+#define GL_RGB5_A1										0x8057	// same as GL_RGB5_A1_EXT and GL_RGB5_A1_OES
+#endif
+#if !defined( GL_RGB10_A2 )
+#define GL_RGB10_A2										0x8059	// same as GL_RGB10_A2_EXT
+#endif
+#if !defined( GL_RGB10_A2UI )
+#define GL_RGB10_A2UI									0x906F
+#endif
+#if !defined( GL_R11F_G11F_B10F )
+#define GL_R11F_G11F_B10F								0x8C3A	// same as GL_R11F_G11F_B10F_APPLE and GL_R11F_G11F_B10F_EXT
+#endif
+#if !defined( GL_RGB9_E5 )
+#define GL_RGB9_E5										0x8C3D	// same as GL_RGB9_E5_APPLE and GL_RGB9_E5_EXT
+#endif
+
+//
+// Alpha
+//
+
+#if !defined( GL_ALPHA4 )
+#define GL_ALPHA4										0x803B	// deprecated, same as GL_ALPHA4_EXT
+#endif
+#if !defined( GL_ALPHA8 )
+#define GL_ALPHA8										0x803C	// deprecated, same as GL_ALPHA8_EXT
+#endif
+#if !defined( GL_ALPHA8_SNORM )
+#define GL_ALPHA8_SNORM									0x9014	// deprecated
+#endif
+#if !defined( GL_ALPHA8UI_EXT )
+#define GL_ALPHA8UI_EXT									0x8D7E	// deprecated
+#endif
+#if !defined( GL_ALPHA8I_EXT )
+#define GL_ALPHA8I_EXT									0x8D90	// deprecated
+#endif
+#if !defined( GL_ALPHA12 )
+#define GL_ALPHA12										0x803D	// deprecated, same as GL_ALPHA12_EXT
+#endif
+#if !defined( GL_ALPHA16 )
+#define GL_ALPHA16										0x803E	// deprecated, same as GL_ALPHA16_EXT
+#endif
+#if !defined( GL_ALPHA16_SNORM )
+#define GL_ALPHA16_SNORM								0x9018	// deprecated
+#endif
+#if !defined( GL_ALPHA16UI_EXT )
+#define GL_ALPHA16UI_EXT								0x8D78	// deprecated
+#endif
+#if !defined( GL_ALPHA16I_EXT )
+#define GL_ALPHA16I_EXT									0x8D8A	// deprecated
+#endif
+#if !defined( GL_ALPHA16F_ARB )
+#define GL_ALPHA16F_ARB									0x881C	// deprecated, same as GL_ALPHA_FLOAT16_APPLE and GL_ALPHA_FLOAT16_ATI
+#endif
+#if !defined( GL_ALPHA32UI_EXT )
+#define GL_ALPHA32UI_EXT								0x8D72	// deprecated
+#endif
+#if !defined( GL_ALPHA32I_EXT )
+#define GL_ALPHA32I_EXT									0x8D84	// deprecated
+#endif
+#if !defined( GL_ALPHA32F_ARB )
+#define GL_ALPHA32F_ARB									0x8816	// deprecated, same as GL_ALPHA_FLOAT32_APPLE and GL_ALPHA_FLOAT32_ATI
+#endif
+
+//
+// Luminance
+//
+
+#if !defined( GL_LUMINANCE4 )
+#define GL_LUMINANCE4									0x803F	// deprecated, same as GL_LUMINANCE4_EXT
+#endif
+#if !defined( GL_LUMINANCE8 )
+#define GL_LUMINANCE8									0x8040	// deprecated, same as GL_LUMINANCE8_EXT
+#endif
+#if !defined( GL_LUMINANCE8_SNORM )
+#define GL_LUMINANCE8_SNORM								0x9015	// deprecated
+#endif
+#if !defined( GL_SLUMINANCE8 )
+#define GL_SLUMINANCE8									0x8C47	// deprecated, same as GL_SLUMINANCE8_EXT
+#endif
+#if !defined( GL_LUMINANCE8UI_EXT )
+#define GL_LUMINANCE8UI_EXT								0x8D80	// deprecated
+#endif
+#if !defined( GL_LUMINANCE8I_EXT )
+#define GL_LUMINANCE8I_EXT								0x8D92	// deprecated
+#endif
+#if !defined( GL_LUMINANCE12 )
+#define GL_LUMINANCE12									0x8041	// deprecated, same as GL_LUMINANCE12_EXT
+#endif
+#if !defined( GL_LUMINANCE16 )
+#define GL_LUMINANCE16									0x8042	// deprecated, same as GL_LUMINANCE16_EXT
+#endif
+#if !defined( GL_LUMINANCE16_SNORM )
+#define GL_LUMINANCE16_SNORM							0x9019	// deprecated
+#endif
+#if !defined( GL_LUMINANCE16UI_EXT )
+#define GL_LUMINANCE16UI_EXT							0x8D7A	// deprecated
+#endif
+#if !defined( GL_LUMINANCE16I_EXT )
+#define GL_LUMINANCE16I_EXT								0x8D8C	// deprecated
+#endif
+#if !defined( GL_LUMINANCE16F_ARB )
+#define GL_LUMINANCE16F_ARB								0x881E	// deprecated, same as GL_LUMINANCE_FLOAT16_APPLE and GL_LUMINANCE_FLOAT16_ATI
+#endif
+#if !defined( GL_LUMINANCE32UI_EXT )
+#define GL_LUMINANCE32UI_EXT							0x8D74	// deprecated
+#endif
+#if !defined( GL_LUMINANCE32I_EXT )
+#define GL_LUMINANCE32I_EXT								0x8D86	// deprecated
+#endif
+#if !defined( GL_LUMINANCE32F_ARB )
+#define GL_LUMINANCE32F_ARB								0x8818	// deprecated, same as GL_LUMINANCE_FLOAT32_APPLE and GL_LUMINANCE_FLOAT32_ATI
+#endif
+
+//
+// Luminance/Alpha
+//
+
+#if !defined( GL_LUMINANCE4_ALPHA4 )
+#define GL_LUMINANCE4_ALPHA4							0x8043	// deprecated, same as GL_LUMINANCE4_ALPHA4_EXT
+#endif
+#if !defined( GL_LUMINANCE6_ALPHA2 )
+#define GL_LUMINANCE6_ALPHA2							0x8044	// deprecated, same as GL_LUMINANCE6_ALPHA2_EXT
+#endif
+#if !defined( GL_LUMINANCE8_ALPHA8 )
+#define GL_LUMINANCE8_ALPHA8							0x8045	// deprecated, same as GL_LUMINANCE8_ALPHA8_EXT
+#endif
+#if !defined( GL_LUMINANCE8_ALPHA8_SNORM )
+#define GL_LUMINANCE8_ALPHA8_SNORM						0x9016	// deprecated
+#endif
+#if !defined( GL_SLUMINANCE8_ALPHA8 )
+#define GL_SLUMINANCE8_ALPHA8							0x8C45	// deprecated, same as GL_SLUMINANCE8_ALPHA8_EXT
+#endif
+#if !defined( GL_LUMINANCE_ALPHA8UI_EXT )
+#define GL_LUMINANCE_ALPHA8UI_EXT						0x8D81	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA8I_EXT )
+#define GL_LUMINANCE_ALPHA8I_EXT						0x8D93	// deprecated
+#endif
+#if !defined( GL_LUMINANCE12_ALPHA4 )
+#define GL_LUMINANCE12_ALPHA4							0x8046	// deprecated, same as GL_LUMINANCE12_ALPHA4_EXT
+#endif
+#if !defined( GL_LUMINANCE12_ALPHA12 )
+#define GL_LUMINANCE12_ALPHA12							0x8047	// deprecated, same as GL_LUMINANCE12_ALPHA12_EXT
+#endif
+#if !defined( GL_LUMINANCE16_ALPHA16 )
+#define GL_LUMINANCE16_ALPHA16							0x8048	// deprecated, same as GL_LUMINANCE16_ALPHA16_EXT
+#endif
+#if !defined( GL_LUMINANCE16_ALPHA16_SNORM )
+#define GL_LUMINANCE16_ALPHA16_SNORM					0x901A	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA16UI_EXT )
+#define GL_LUMINANCE_ALPHA16UI_EXT						0x8D7B	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA16I_EXT )
+#define GL_LUMINANCE_ALPHA16I_EXT						0x8D8D	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA16F_ARB )
+#define GL_LUMINANCE_ALPHA16F_ARB						0x881F	// deprecated, same as GL_LUMINANCE_ALPHA_FLOAT16_APPLE and GL_LUMINANCE_ALPHA_FLOAT16_ATI
+#endif
+#if !defined( GL_LUMINANCE_ALPHA32UI_EXT )
+#define GL_LUMINANCE_ALPHA32UI_EXT						0x8D75	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA32I_EXT )
+#define GL_LUMINANCE_ALPHA32I_EXT						0x8D87	// deprecated
+#endif
+#if !defined( GL_LUMINANCE_ALPHA32F_ARB )
+#define GL_LUMINANCE_ALPHA32F_ARB						0x8819	// deprecated, same as GL_LUMINANCE_ALPHA_FLOAT32_APPLE and GL_LUMINANCE_ALPHA_FLOAT32_ATI
+#endif
+
+//
+// Intensity
+//
+
+#if !defined( GL_INTENSITY4 )
+#define GL_INTENSITY4									0x804A	// deprecated, same as GL_INTENSITY4_EXT
+#endif
+#if !defined( GL_INTENSITY8 )
+#define GL_INTENSITY8									0x804B	// deprecated, same as GL_INTENSITY8_EXT
+#endif
+#if !defined( GL_INTENSITY8_SNORM )
+#define GL_INTENSITY8_SNORM								0x9017	// deprecated
+#endif
+#if !defined( GL_INTENSITY8UI_EXT )
+#define GL_INTENSITY8UI_EXT								0x8D7F	// deprecated
+#endif
+#if !defined( GL_INTENSITY8I_EXT )
+#define GL_INTENSITY8I_EXT								0x8D91	// deprecated
+#endif
+#if !defined( GL_INTENSITY12 )
+#define GL_INTENSITY12									0x804C	// deprecated, same as GL_INTENSITY12_EXT
+#endif
+#if !defined( GL_INTENSITY16 )
+#define GL_INTENSITY16									0x804D	// deprecated, same as GL_INTENSITY16_EXT
+#endif
+#if !defined( GL_INTENSITY16_SNORM )
+#define GL_INTENSITY16_SNORM							0x901B	// deprecated
+#endif
+#if !defined( GL_INTENSITY16UI_EXT )
+#define GL_INTENSITY16UI_EXT							0x8D79	// deprecated
+#endif
+#if !defined( GL_INTENSITY16I_EXT )
+#define GL_INTENSITY16I_EXT								0x8D8B	// deprecated
+#endif
+#if !defined( GL_INTENSITY16F_ARB )
+#define GL_INTENSITY16F_ARB								0x881D	// deprecated, same as GL_INTENSITY_FLOAT16_APPLE and GL_INTENSITY_FLOAT16_ATI
+#endif
+#if !defined( GL_INTENSITY32UI_EXT )
+#define GL_INTENSITY32UI_EXT							0x8D73	// deprecated
+#endif
+#if !defined( GL_INTENSITY32I_EXT )
+#define GL_INTENSITY32I_EXT								0x8D85	// deprecated
+#endif
+#if !defined( GL_INTENSITY32F_ARB )
+#define GL_INTENSITY32F_ARB								0x8817	// deprecated, same as GL_INTENSITY_FLOAT32_APPLE and GL_INTENSITY_FLOAT32_ATI
+#endif
+
+//
+// Generic compression
+//
+
+#if !defined( GL_COMPRESSED_RED )
+#define GL_COMPRESSED_RED								0x8225
+#endif
+#if !defined( GL_COMPRESSED_ALPHA )
+#define GL_COMPRESSED_ALPHA								0x84E9	// deprecated, same as GL_COMPRESSED_ALPHA_ARB
+#endif
+#if !defined( GL_COMPRESSED_LUMINANCE )
+#define GL_COMPRESSED_LUMINANCE							0x84EA	// deprecated, same as GL_COMPRESSED_LUMINANCE_ARB
+#endif
+#if !defined( GL_COMPRESSED_SLUMINANCE )
+#define GL_COMPRESSED_SLUMINANCE						0x8C4A	// deprecated, same as GL_COMPRESSED_SLUMINANCE_EXT
+#endif
+#if !defined( GL_COMPRESSED_LUMINANCE_ALPHA )
+#define GL_COMPRESSED_LUMINANCE_ALPHA					0x84EB	// deprecated, same as GL_COMPRESSED_LUMINANCE_ALPHA_ARB
+#endif
+#if !defined( GL_COMPRESSED_SLUMINANCE_ALPHA )
+#define GL_COMPRESSED_SLUMINANCE_ALPHA					0x8C4B	// deprecated, same as GL_COMPRESSED_SLUMINANCE_ALPHA_EXT
+#endif
+#if !defined( GL_COMPRESSED_INTENSITY )
+#define GL_COMPRESSED_INTENSITY							0x84EC	// deprecated, same as GL_COMPRESSED_INTENSITY_ARB
+#endif
+#if !defined( GL_COMPRESSED_RG )
+#define GL_COMPRESSED_RG								0x8226
+#endif
+#if !defined( GL_COMPRESSED_RGB )
+#define GL_COMPRESSED_RGB								0x84ED	// same as GL_COMPRESSED_RGB_ARB
+#endif
+#if !defined( GL_COMPRESSED_RGBA )
+#define GL_COMPRESSED_RGBA								0x84EE	// same as GL_COMPRESSED_RGBA_ARB
+#endif
+#if !defined( GL_COMPRESSED_SRGB )
+#define GL_COMPRESSED_SRGB								0x8C48	// same as GL_COMPRESSED_SRGB_EXT
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA )
+#define GL_COMPRESSED_SRGB_ALPHA						0x8C49	// same as GL_COMPRESSED_SRGB_ALPHA_EXT
+#endif
+
+//
+// FXT1
+//
+
+#if !defined( GL_COMPRESSED_RGB_FXT1_3DFX )
+#define GL_COMPRESSED_RGB_FXT1_3DFX						0x86B0	// deprecated
+#endif
+#if !defined( GL_COMPRESSED_RGBA_FXT1_3DFX )
+#define GL_COMPRESSED_RGBA_FXT1_3DFX					0x86B1	// deprecated
+#endif
+
+//
+// S3TC/DXT/BC
+//
+
+#if !defined( GL_COMPRESSED_RGB_S3TC_DXT1_EXT )
+#define GL_COMPRESSED_RGB_S3TC_DXT1_EXT					0x83F0
+#endif
+#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT1_EXT )
+#define GL_COMPRESSED_RGBA_S3TC_DXT1_EXT				0x83F1
+#endif
+#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT3_EXT )
+#define GL_COMPRESSED_RGBA_S3TC_DXT3_EXT				0x83F2
+#endif
+#if !defined( GL_COMPRESSED_RGBA_S3TC_DXT5_EXT )
+#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT				0x83F3
+#endif
+
+#if !defined( GL_COMPRESSED_SRGB_S3TC_DXT1_EXT )
+#define GL_COMPRESSED_SRGB_S3TC_DXT1_EXT				0x8C4C
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT )
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT			0x8C4D
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT )
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT			0x8C4E
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT )
+#define GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT			0x8C4F
+#endif
+
+#if !defined( GL_COMPRESSED_LUMINANCE_LATC1_EXT )
+#define GL_COMPRESSED_LUMINANCE_LATC1_EXT				0x8C70
+#endif
+#if !defined( GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT )
+#define GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT			0x8C72
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT )
+#define GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT		0x8C71
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT )
+#define GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT	0x8C73
+#endif
+
+#if !defined( GL_COMPRESSED_RED_RGTC1 )
+#define GL_COMPRESSED_RED_RGTC1							0x8DBB	// same as GL_COMPRESSED_RED_RGTC1_EXT
+#endif
+#if !defined( GL_COMPRESSED_RG_RGTC2 )
+#define GL_COMPRESSED_RG_RGTC2							0x8DBD	// same as GL_COMPRESSED_RG_RGTC2_EXT
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_RED_RGTC1 )
+#define GL_COMPRESSED_SIGNED_RED_RGTC1					0x8DBC	// same as GL_COMPRESSED_SIGNED_RED_RGTC1_EXT
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_RG_RGTC2 )
+#define GL_COMPRESSED_SIGNED_RG_RGTC2					0x8DBE	// same as GL_COMPRESSED_SIGNED_RG_RGTC2_EXT
+#endif
+
+#if !defined( GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT )
+#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT				0x8E8E	// same as GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT_ARB
+#endif
+#if !defined( GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT )
+#define GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT			0x8E8F	// same as GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT_ARB
+#endif
+#if !defined( GL_COMPRESSED_RGBA_BPTC_UNORM )
+#define GL_COMPRESSED_RGBA_BPTC_UNORM					0x8E8C	// same as GL_COMPRESSED_RGBA_BPTC_UNORM_ARB	
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM )
+#define GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM				0x8E8D	// same as GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM_ARB
+#endif
+
+//
+// ETC
+//
+
+#if !defined( GL_ETC1_RGB8_OES )
+#define GL_ETC1_RGB8_OES								0x8D64
+#endif
+
+#if !defined( GL_COMPRESSED_RGB8_ETC2 )
+#define GL_COMPRESSED_RGB8_ETC2							0x9274
+#endif
+#if !defined( GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 )
+#define GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2		0x9276
+#endif
+#if !defined( GL_COMPRESSED_RGBA8_ETC2_EAC )
+#define GL_COMPRESSED_RGBA8_ETC2_EAC					0x9278
+#endif
+
+#if !defined( GL_COMPRESSED_SRGB8_ETC2 )
+#define GL_COMPRESSED_SRGB8_ETC2						0x9275
+#endif
+#if !defined( GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 )
+#define GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2	0x9277
+#endif
+#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC )
+#define GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC				0x9279
+#endif
+
+#if !defined( GL_COMPRESSED_R11_EAC )
+#define GL_COMPRESSED_R11_EAC							0x9270
+#endif
+#if !defined( GL_COMPRESSED_RG11_EAC )
+#define GL_COMPRESSED_RG11_EAC							0x9272
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_R11_EAC )
+#define GL_COMPRESSED_SIGNED_R11_EAC					0x9271
+#endif
+#if !defined( GL_COMPRESSED_SIGNED_RG11_EAC )
+#define GL_COMPRESSED_SIGNED_RG11_EAC					0x9273
+#endif
+
+//
+// PVRTC
+//
+
+#if !defined( GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG )
+#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG				0x8C01
+#define GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG				0x8C00
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG				0x8C03
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG				0x8C02
+#endif
+#if !defined( GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG )
+#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG				0x9137
+#define GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG				0x9138
+#endif
+#if !defined( GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT )
+#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT				0x8A54
+#define GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT				0x8A55
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT		0x8A56
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT		0x8A57
+#endif
+#if !defined( GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG )
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG		0x93F0
+#define GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG		0x93F1
+#endif
+
+//
+// ASTC
+//
+
+#if !defined( GL_COMPRESSED_RGBA_ASTC_4x4_KHR )
+#define GL_COMPRESSED_RGBA_ASTC_4x4_KHR					0x93B0
+#define GL_COMPRESSED_RGBA_ASTC_5x4_KHR					0x93B1
+#define GL_COMPRESSED_RGBA_ASTC_5x5_KHR					0x93B2
+#define GL_COMPRESSED_RGBA_ASTC_6x5_KHR					0x93B3
+#define GL_COMPRESSED_RGBA_ASTC_6x6_KHR					0x93B4
+#define GL_COMPRESSED_RGBA_ASTC_8x5_KHR					0x93B5
+#define GL_COMPRESSED_RGBA_ASTC_8x6_KHR					0x93B6
+#define GL_COMPRESSED_RGBA_ASTC_8x8_KHR					0x93B7
+#define GL_COMPRESSED_RGBA_ASTC_10x5_KHR				0x93B8
+#define GL_COMPRESSED_RGBA_ASTC_10x6_KHR				0x93B9
+#define GL_COMPRESSED_RGBA_ASTC_10x8_KHR				0x93BA
+#define GL_COMPRESSED_RGBA_ASTC_10x10_KHR				0x93BB
+#define GL_COMPRESSED_RGBA_ASTC_12x10_KHR				0x93BC
+#define GL_COMPRESSED_RGBA_ASTC_12x12_KHR				0x93BD
+#endif
+
+#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR )
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR			0x93D0
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR			0x93D1
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR			0x93D2
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR			0x93D3
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR			0x93D4
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR			0x93D5
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR			0x93D6
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR			0x93D7
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR		0x93D8
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR		0x93D9
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR		0x93DA
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR		0x93DB
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR		0x93DC
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR		0x93DD
+#endif
+
+#if !defined( GL_COMPRESSED_RGBA_ASTC_3x3x3_OES )
+#define GL_COMPRESSED_RGBA_ASTC_3x3x3_OES				0x93C0
+#define GL_COMPRESSED_RGBA_ASTC_4x3x3_OES				0x93C1
+#define GL_COMPRESSED_RGBA_ASTC_4x4x3_OES				0x93C2
+#define GL_COMPRESSED_RGBA_ASTC_4x4x4_OES				0x93C3
+#define GL_COMPRESSED_RGBA_ASTC_5x4x4_OES				0x93C4
+#define GL_COMPRESSED_RGBA_ASTC_5x5x4_OES				0x93C5
+#define GL_COMPRESSED_RGBA_ASTC_5x5x5_OES				0x93C6
+#define GL_COMPRESSED_RGBA_ASTC_6x5x5_OES				0x93C7
+#define GL_COMPRESSED_RGBA_ASTC_6x6x5_OES				0x93C8
+#define GL_COMPRESSED_RGBA_ASTC_6x6x6_OES				0x93C9
+#endif
+
+#if !defined( GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES )
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES		0x93E0
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES		0x93E1
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES		0x93E2
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES		0x93E3
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES		0x93E4
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES		0x93E5
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES		0x93E6
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES		0x93E7
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES		0x93E8
+#define GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES		0x93E9
+#endif
+
+//
+// ATC
+//
+
+#if !defined( GL_ATC_RGB_AMD )
+#define GL_ATC_RGB_AMD									0x8C92
+#define GL_ATC_RGBA_EXPLICIT_ALPHA_AMD					0x8C93
+#define GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD				0x87EE
+#endif
+
+//
+// Palletized (combined palette)
+//
+
+#if !defined( GL_PALETTE4_RGB8_OES )
+#define GL_PALETTE4_RGB8_OES							0x8B90
+#define GL_PALETTE4_RGBA8_OES							0x8B91
+#define GL_PALETTE4_R5_G6_B5_OES						0x8B92
+#define GL_PALETTE4_RGBA4_OES							0x8B93
+#define GL_PALETTE4_RGB5_A1_OES							0x8B94
+#define GL_PALETTE8_RGB8_OES							0x8B95
+#define GL_PALETTE8_RGBA8_OES							0x8B96
+#define GL_PALETTE8_R5_G6_B5_OES						0x8B97
+#define GL_PALETTE8_RGBA4_OES							0x8B98
+#define GL_PALETTE8_RGB5_A1_OES							0x8B99
+#endif
+
+//
+// Palletized (separate palette)
+//
+
+#if !defined( GL_COLOR_INDEX1_EXT )
+#define GL_COLOR_INDEX1_EXT								0x80E2	// deprecated
+#define GL_COLOR_INDEX2_EXT								0x80E3	// deprecated
+#define GL_COLOR_INDEX4_EXT								0x80E4	// deprecated
+#define GL_COLOR_INDEX8_EXT								0x80E5	// deprecated
+#define GL_COLOR_INDEX12_EXT							0x80E6	// deprecated
+#define GL_COLOR_INDEX16_EXT							0x80E7	// deprecated
+#endif
+
+//
+// Depth/stencil
+//
+
+#if !defined( GL_DEPTH_COMPONENT16 )
+#define GL_DEPTH_COMPONENT16							0x81A5	// same as GL_DEPTH_COMPONENT16_SGIX and GL_DEPTH_COMPONENT16_ARB
+#endif
+#if !defined( GL_DEPTH_COMPONENT24 )
+#define GL_DEPTH_COMPONENT24							0x81A6	// same as GL_DEPTH_COMPONENT24_SGIX and GL_DEPTH_COMPONENT24_ARB
+#endif
+#if !defined( GL_DEPTH_COMPONENT32 )
+#define GL_DEPTH_COMPONENT32							0x81A7	// same as GL_DEPTH_COMPONENT32_SGIX and GL_DEPTH_COMPONENT32_ARB and GL_DEPTH_COMPONENT32_OES
+#endif
+#if !defined( GL_DEPTH_COMPONENT32F )
+#define GL_DEPTH_COMPONENT32F							0x8CAC	// same as GL_DEPTH_COMPONENT32F_ARB
+#endif
+#if !defined( GL_DEPTH_COMPONENT32F_NV )
+#define GL_DEPTH_COMPONENT32F_NV						0x8DAB	// note that this is different from GL_DEPTH_COMPONENT32F
+#endif
+#if !defined( GL_STENCIL_INDEX1 )
+#define GL_STENCIL_INDEX1								0x8D46	// same as GL_STENCIL_INDEX1_EXT
+#endif
+#if !defined( GL_STENCIL_INDEX4 )
+#define GL_STENCIL_INDEX4								0x8D47	// same as GL_STENCIL_INDEX4_EXT
+#endif
+#if !defined( GL_STENCIL_INDEX8 )
+#define GL_STENCIL_INDEX8								0x8D48	// same as GL_STENCIL_INDEX8_EXT
+#endif
+#if !defined( GL_STENCIL_INDEX16 )
+#define GL_STENCIL_INDEX16								0x8D49	// same as GL_STENCIL_INDEX16_EXT
+#endif
+#if !defined( GL_DEPTH24_STENCIL8 )
+#define GL_DEPTH24_STENCIL8								0x88F0	// same as GL_DEPTH24_STENCIL8_EXT and GL_DEPTH24_STENCIL8_OES
+#endif
+#if !defined( GL_DEPTH32F_STENCIL8 )
+#define GL_DEPTH32F_STENCIL8							0x8CAD	// same as GL_DEPTH32F_STENCIL8_ARB
+#endif
+#if !defined( GL_DEPTH32F_STENCIL8_NV )
+#define GL_DEPTH32F_STENCIL8_NV							0x8DAC	// note that this is different from GL_DEPTH32F_STENCIL8
+#endif
+
+static inline GLenum glGetFormatFromInternalFormat( const GLenum internalFormat )
+{
+	switch ( internalFormat )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_R8:												return GL_RED;		// 1-component, 8-bit unsigned normalized
+		case GL_RG8:											return GL_RG;		// 2-component, 8-bit unsigned normalized
+		case GL_RGB8:											return GL_RGB;		// 3-component, 8-bit unsigned normalized
+		case GL_RGBA8:											return GL_RGBA;		// 4-component, 8-bit unsigned normalized
+
+		case GL_R8_SNORM:										return GL_RED;		// 1-component, 8-bit signed normalized
+		case GL_RG8_SNORM:										return GL_RG;		// 2-component, 8-bit signed normalized
+		case GL_RGB8_SNORM:										return GL_RGB;		// 3-component, 8-bit signed normalized
+		case GL_RGBA8_SNORM:									return GL_RGBA;		// 4-component, 8-bit signed normalized
+
+		case GL_R8UI:											return GL_RED;		// 1-component, 8-bit unsigned integer
+		case GL_RG8UI:											return GL_RG;		// 2-component, 8-bit unsigned integer
+		case GL_RGB8UI:											return GL_RGB;		// 3-component, 8-bit unsigned integer
+		case GL_RGBA8UI:										return GL_RGBA;		// 4-component, 8-bit unsigned integer
+
+		case GL_R8I:											return GL_RED;		// 1-component, 8-bit signed integer
+		case GL_RG8I:											return GL_RG;		// 2-component, 8-bit signed integer
+		case GL_RGB8I:											return GL_RGB;		// 3-component, 8-bit signed integer
+		case GL_RGBA8I:											return GL_RGBA;		// 4-component, 8-bit signed integer
+
+		case GL_SR8:											return GL_RED;		// 1-component, 8-bit sRGB
+		case GL_SRG8:											return GL_RG;		// 2-component, 8-bit sRGB
+		case GL_SRGB8:											return GL_RGB;		// 3-component, 8-bit sRGB
+		case GL_SRGB8_ALPHA8:									return GL_RGBA;		// 4-component, 8-bit sRGB
+
+		//
+		// 16 bits per component
+		//
+		case GL_R16:											return GL_RED;		// 1-component, 16-bit unsigned normalized
+		case GL_RG16:											return GL_RG;		// 2-component, 16-bit unsigned normalized
+		case GL_RGB16:											return GL_RGB;		// 3-component, 16-bit unsigned normalized
+		case GL_RGBA16:											return GL_RGBA;		// 4-component, 16-bit unsigned normalized
+
+		case GL_R16_SNORM:										return GL_RED;		// 1-component, 16-bit signed normalized
+		case GL_RG16_SNORM:										return GL_RG;		// 2-component, 16-bit signed normalized
+		case GL_RGB16_SNORM:									return GL_RGB;		// 3-component, 16-bit signed normalized
+		case GL_RGBA16_SNORM:									return GL_RGBA;		// 4-component, 16-bit signed normalized
+
+		case GL_R16UI:											return GL_RED;		// 1-component, 16-bit unsigned integer
+		case GL_RG16UI:											return GL_RG;		// 2-component, 16-bit unsigned integer
+		case GL_RGB16UI:										return GL_RGB;		// 3-component, 16-bit unsigned integer
+		case GL_RGBA16UI:										return GL_RGBA;		// 4-component, 16-bit unsigned integer
+
+		case GL_R16I:											return GL_RED;		// 1-component, 16-bit signed integer
+		case GL_RG16I:											return GL_RG;		// 2-component, 16-bit signed integer
+		case GL_RGB16I:											return GL_RGB;		// 3-component, 16-bit signed integer
+		case GL_RGBA16I:										return GL_RGBA;		// 4-component, 16-bit signed integer
+
+		case GL_R16F:											return GL_RED;		// 1-component, 16-bit floating-point
+		case GL_RG16F:											return GL_RG;		// 2-component, 16-bit floating-point
+		case GL_RGB16F:											return GL_RGB;		// 3-component, 16-bit floating-point
+		case GL_RGBA16F:										return GL_RGBA;		// 4-component, 16-bit floating-point
+
+		//
+		// 32 bits per component
+		//
+		case GL_R32UI:											return GL_RED;		// 1-component, 32-bit unsigned integer
+		case GL_RG32UI:											return GL_RG;		// 2-component, 32-bit unsigned integer
+		case GL_RGB32UI:										return GL_RGB;		// 3-component, 32-bit unsigned integer
+		case GL_RGBA32UI:										return GL_RGBA;		// 4-component, 32-bit unsigned integer
+
+		case GL_R32I:											return GL_RED;		// 1-component, 32-bit signed integer
+		case GL_RG32I:											return GL_RG;		// 2-component, 32-bit signed integer
+		case GL_RGB32I:											return GL_RGB;		// 3-component, 32-bit signed integer
+		case GL_RGBA32I:										return GL_RGBA;		// 4-component, 32-bit signed integer
+
+		case GL_R32F:											return GL_RED;		// 1-component, 32-bit floating-point
+		case GL_RG32F:											return GL_RG;		// 2-component, 32-bit floating-point
+		case GL_RGB32F:											return GL_RGB;		// 3-component, 32-bit floating-point
+		case GL_RGBA32F:										return GL_RGBA;		// 4-component, 32-bit floating-point
+
+		//
+		// Packed
+		//
+		case GL_R3_G3_B2:										return GL_RGB;		// 3-component 3:3:2,       unsigned normalized
+		case GL_RGB4:											return GL_RGB;		// 3-component 4:4:4,       unsigned normalized
+		case GL_RGB5:											return GL_RGB;		// 3-component 5:5:5,       unsigned normalized
+		case GL_RGB565:											return GL_RGB;		// 3-component 5:6:5,       unsigned normalized
+		case GL_RGB10:											return GL_RGB;		// 3-component 10:10:10,    unsigned normalized
+		case GL_RGB12:											return GL_RGB;		// 3-component 12:12:12,    unsigned normalized
+		case GL_RGBA2:											return GL_RGBA;		// 4-component 2:2:2:2,     unsigned normalized
+		case GL_RGBA4:											return GL_RGBA;		// 4-component 4:4:4:4,     unsigned normalized
+		case GL_RGBA12:											return GL_RGBA;		// 4-component 12:12:12:12, unsigned normalized
+		case GL_RGB5_A1:										return GL_RGBA;		// 4-component 5:5:5:1,     unsigned normalized
+		case GL_RGB10_A2:										return GL_RGBA;		// 4-component 10:10:10:2,  unsigned normalized
+		case GL_RGB10_A2UI:										return GL_RGBA;		// 4-component 10:10:10:2,  unsigned integer
+		case GL_R11F_G11F_B10F:									return GL_RGB;		// 3-component 11:11:10,    floating-point
+		case GL_RGB9_E5:										return GL_RGB;		// 3-component/exp 9:9:9/5, floating-point
+
+		//
+		// S3TC/DXT/BC
+		//
+
+		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:					return GL_RGB;		// line through 3D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:					return GL_RGBA;		// line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:					return GL_RGBA;		// line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:					return GL_RGBA;		// line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:					return GL_RGB;		// line through 3D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:			return GL_RGBA;		// line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:			return GL_RGBA;		// line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:			return GL_RGBA;		// line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_LUMINANCE_LATC1_EXT:					return GL_RED;		// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:			return GL_RG;		// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:			return GL_RED;		// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:	return GL_RG;		// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RED_RGTC1:							return GL_RED;		// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG_RGTC2:							return GL_RG;		// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RED_RGTC1:					return GL_RED;		// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG_RGTC2:						return GL_RG;		// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:				return GL_RGB;		// 3-component, 4x4 blocks, unsigned floating-point
+		case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:				return GL_RGB;		// 3-component, 4x4 blocks, signed floating-point
+		case GL_COMPRESSED_RGBA_BPTC_UNORM:						return GL_RGBA;		// 4-component, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:				return GL_RGBA;		// 4-component, 4x4 blocks, sRGB
+
+		//
+		// ETC
+		//
+		case GL_ETC1_RGB8_OES:									return GL_RGB;		// 3-component ETC1, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_RGB8_ETC2:							return GL_RGB;		// 3-component ETC2, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return GL_RGBA;		// 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA8_ETC2_EAC:						return GL_RGBA;		// 4-component ETC2, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ETC2:							return GL_RGB;		// 3-component ETC2, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return GL_RGBA;		// 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:				return GL_RGBA;		// 4-component ETC2, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_R11_EAC:								return GL_RED;		// 1-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG11_EAC:							return GL_RG;		// 2-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_R11_EAC:						return GL_RED;		// 1-component ETC, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG11_EAC:						return GL_RG;		// 2-component ETC, 4x4 blocks, signed normalized
+
+		//
+		// PVRTC
+		//
+		case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:				return GL_RGB;		// 3-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:				return GL_RGB;		// 3-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:				return GL_RGBA;		// 4-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:				return GL_RGBA;		// 4-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG:				return GL_RGBA;		// 4-component PVRTC,  8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG:				return GL_RGBA;		// 4-component PVRTC,  4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:				return GL_RGB;		// 3-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:				return GL_RGB;		// 3-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:			return GL_RGBA;		// 4-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:			return GL_RGBA;		// 4-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG:			return GL_RGBA;		// 4-component PVRTC,  8x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG:			return GL_RGBA;		// 4-component PVRTC,  4x4 blocks, sRGB
+
+		//
+		// ASTC
+		//
+		case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:					return GL_RGBA;		// 4-component ASTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:					return GL_RGBA;		// 4-component ASTC, 5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:					return GL_RGBA;		// 4-component ASTC, 5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:					return GL_RGBA;		// 4-component ASTC, 6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:					return GL_RGBA;		// 4-component ASTC, 6x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:					return GL_RGBA;		// 4-component ASTC, 8x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:					return GL_RGBA;		// 4-component ASTC, 8x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:					return GL_RGBA;		// 4-component ASTC, 8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:					return GL_RGBA;		// 4-component ASTC, 10x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:					return GL_RGBA;		// 4-component ASTC, 10x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:					return GL_RGBA;		// 4-component ASTC, 10x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:					return GL_RGBA;		// 4-component ASTC, 10x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:					return GL_RGBA;		// 4-component ASTC, 12x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:					return GL_RGBA;		// 4-component ASTC, 12x12 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:			return GL_RGBA;		// 4-component ASTC, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:			return GL_RGBA;		// 4-component ASTC, 5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:			return GL_RGBA;		// 4-component ASTC, 5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:			return GL_RGBA;		// 4-component ASTC, 6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:			return GL_RGBA;		// 4-component ASTC, 6x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:			return GL_RGBA;		// 4-component ASTC, 8x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:			return GL_RGBA;		// 4-component ASTC, 8x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:			return GL_RGBA;		// 4-component ASTC, 8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:			return GL_RGBA;		// 4-component ASTC, 10x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:			return GL_RGBA;		// 4-component ASTC, 10x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:			return GL_RGBA;		// 4-component ASTC, 10x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:			return GL_RGBA;		// 4-component ASTC, 10x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:			return GL_RGBA;		// 4-component ASTC, 12x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:			return GL_RGBA;		// 4-component ASTC, 12x12 blocks, sRGB
+
+		case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES:					return GL_RGBA;		// 4-component ASTC, 3x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES:					return GL_RGBA;		// 4-component ASTC, 4x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES:					return GL_RGBA;		// 4-component ASTC, 4x4x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES:					return GL_RGBA;		// 4-component ASTC, 4x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES:					return GL_RGBA;		// 4-component ASTC, 5x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES:					return GL_RGBA;		// 4-component ASTC, 5x5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES:					return GL_RGBA;		// 4-component ASTC, 5x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES:					return GL_RGBA;		// 4-component ASTC, 6x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES:					return GL_RGBA;		// 4-component ASTC, 6x6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES:					return GL_RGBA;		// 4-component ASTC, 6x6x6 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES:			return GL_RGBA;		// 4-component ASTC, 3x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES:			return GL_RGBA;		// 4-component ASTC, 4x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES:			return GL_RGBA;		// 4-component ASTC, 4x4x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES:			return GL_RGBA;		// 4-component ASTC, 4x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES:			return GL_RGBA;		// 4-component ASTC, 5x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES:			return GL_RGBA;		// 4-component ASTC, 5x5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES:			return GL_RGBA;		// 4-component ASTC, 5x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES:			return GL_RGBA;		// 4-component ASTC, 6x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES:			return GL_RGBA;		// 4-component ASTC, 6x6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES:			return GL_RGBA;		// 4-component ASTC, 6x6x6 blocks, sRGB
+
+		//
+		// ATC
+		//
+		case GL_ATC_RGB_AMD:									return GL_RGB;		// 3-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:					return GL_RGBA;		// 4-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:				return GL_RGBA;		// 4-component, 4x4 blocks, unsigned normalized
+
+		//
+		// Palletized
+		//
+		case GL_PALETTE4_RGB8_OES:								return GL_RGB;		// 3-component 8:8:8,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA8_OES:								return GL_RGBA;		// 4-component 8:8:8:8, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_R5_G6_B5_OES:							return GL_RGB;		// 3-component 5:6:5,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA4_OES:								return GL_RGBA;		// 4-component 4:4:4:4, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGB5_A1_OES:							return GL_RGBA;		// 4-component 5:5:5:1, 4-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB8_OES:								return GL_RGB;		// 3-component 8:8:8,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA8_OES:								return GL_RGBA;		// 4-component 8:8:8:8, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_R5_G6_B5_OES:							return GL_RGB;		// 3-component 5:6:5,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA4_OES:								return GL_RGBA;		// 4-component 4:4:4:4, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB5_A1_OES:							return GL_RGBA;		// 4-component 5:5:5:1, 8-bit palette, unsigned normalized
+
+		//
+		// Depth/stencil
+		//
+		case GL_DEPTH_COMPONENT16:								return GL_DEPTH_COMPONENT;
+		case GL_DEPTH_COMPONENT24:								return GL_DEPTH_COMPONENT;
+		case GL_DEPTH_COMPONENT32:								return GL_DEPTH_COMPONENT;
+		case GL_DEPTH_COMPONENT32F:								return GL_DEPTH_COMPONENT;
+		case GL_DEPTH_COMPONENT32F_NV:							return GL_DEPTH_COMPONENT;
+		case GL_STENCIL_INDEX1:									return GL_STENCIL_INDEX;
+		case GL_STENCIL_INDEX4:									return GL_STENCIL_INDEX;
+		case GL_STENCIL_INDEX8:									return GL_STENCIL_INDEX;
+		case GL_STENCIL_INDEX16:								return GL_STENCIL_INDEX;
+		case GL_DEPTH24_STENCIL8:								return GL_DEPTH_STENCIL;
+		case GL_DEPTH32F_STENCIL8:								return GL_DEPTH_STENCIL;
+		case GL_DEPTH32F_STENCIL8_NV:							return GL_DEPTH_STENCIL;
+
+		default:												return GL_INVALID_VALUE;
+	}
+}
+
+static inline GLenum glGetTypeFromInternalFormat( const GLenum internalFormat )
+{
+	switch ( internalFormat )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_R8:												return GL_UNSIGNED_BYTE;				// 1-component, 8-bit unsigned normalized
+		case GL_RG8:											return GL_UNSIGNED_BYTE;				// 2-component, 8-bit unsigned normalized
+		case GL_RGB8:											return GL_UNSIGNED_BYTE;				// 3-component, 8-bit unsigned normalized
+		case GL_RGBA8:											return GL_UNSIGNED_BYTE;				// 4-component, 8-bit unsigned normalized
+
+		case GL_R8_SNORM:										return GL_BYTE;							// 1-component, 8-bit signed normalized
+		case GL_RG8_SNORM:										return GL_BYTE;							// 2-component, 8-bit signed normalized
+		case GL_RGB8_SNORM:										return GL_BYTE;							// 3-component, 8-bit signed normalized
+		case GL_RGBA8_SNORM:									return GL_BYTE;							// 4-component, 8-bit signed normalized
+
+		case GL_R8UI:											return GL_UNSIGNED_BYTE;				// 1-component, 8-bit unsigned integer
+		case GL_RG8UI:											return GL_UNSIGNED_BYTE;				// 2-component, 8-bit unsigned integer
+		case GL_RGB8UI:											return GL_UNSIGNED_BYTE;				// 3-component, 8-bit unsigned integer
+		case GL_RGBA8UI:										return GL_UNSIGNED_BYTE;				// 4-component, 8-bit unsigned integer
+
+		case GL_R8I:											return GL_BYTE;							// 1-component, 8-bit signed integer
+		case GL_RG8I:											return GL_BYTE;							// 2-component, 8-bit signed integer
+		case GL_RGB8I:											return GL_BYTE;							// 3-component, 8-bit signed integer
+		case GL_RGBA8I:											return GL_BYTE;							// 4-component, 8-bit signed integer
+
+		case GL_SR8:											return GL_UNSIGNED_BYTE;				// 1-component, 8-bit sRGB
+		case GL_SRG8:											return GL_UNSIGNED_BYTE;				// 2-component, 8-bit sRGB
+		case GL_SRGB8:											return GL_UNSIGNED_BYTE;				// 3-component, 8-bit sRGB
+		case GL_SRGB8_ALPHA8:									return GL_UNSIGNED_BYTE;				// 4-component, 8-bit sRGB
+
+		//
+		// 16 bits per component
+		//
+		case GL_R16:											return GL_UNSIGNED_SHORT;				// 1-component, 16-bit unsigned normalized
+		case GL_RG16:											return GL_UNSIGNED_SHORT;				// 2-component, 16-bit unsigned normalized
+		case GL_RGB16:											return GL_UNSIGNED_SHORT;				// 3-component, 16-bit unsigned normalized
+		case GL_RGBA16:											return GL_UNSIGNED_SHORT;				// 4-component, 16-bit unsigned normalized
+
+		case GL_R16_SNORM:										return GL_SHORT;						// 1-component, 16-bit signed normalized
+		case GL_RG16_SNORM:										return GL_SHORT;						// 2-component, 16-bit signed normalized
+		case GL_RGB16_SNORM:									return GL_SHORT;						// 3-component, 16-bit signed normalized
+		case GL_RGBA16_SNORM:									return GL_SHORT;						// 4-component, 16-bit signed normalized
+
+		case GL_R16UI:											return GL_UNSIGNED_SHORT;				// 1-component, 16-bit unsigned integer
+		case GL_RG16UI:											return GL_UNSIGNED_SHORT;				// 2-component, 16-bit unsigned integer
+		case GL_RGB16UI:										return GL_UNSIGNED_SHORT;				// 3-component, 16-bit unsigned integer
+		case GL_RGBA16UI:										return GL_UNSIGNED_SHORT;				// 4-component, 16-bit unsigned integer
+
+		case GL_R16I:											return GL_SHORT;						// 1-component, 16-bit signed integer
+		case GL_RG16I:											return GL_SHORT;						// 2-component, 16-bit signed integer
+		case GL_RGB16I:											return GL_SHORT;						// 3-component, 16-bit signed integer
+		case GL_RGBA16I:										return GL_SHORT;						// 4-component, 16-bit signed integer
+
+		case GL_R16F:											return GL_HALF_FLOAT;					// 1-component, 16-bit floating-point
+		case GL_RG16F:											return GL_HALF_FLOAT;					// 2-component, 16-bit floating-point
+		case GL_RGB16F:											return GL_HALF_FLOAT;					// 3-component, 16-bit floating-point
+		case GL_RGBA16F:										return GL_HALF_FLOAT;					// 4-component, 16-bit floating-point
+
+		//
+		// 32 bits per component
+		//
+		case GL_R32UI:											return GL_UNSIGNED_INT;					// 1-component, 32-bit unsigned integer
+		case GL_RG32UI:											return GL_UNSIGNED_INT;					// 2-component, 32-bit unsigned integer
+		case GL_RGB32UI:										return GL_UNSIGNED_INT;					// 3-component, 32-bit unsigned integer
+		case GL_RGBA32UI:										return GL_UNSIGNED_INT;					// 4-component, 32-bit unsigned integer
+
+		case GL_R32I:											return GL_INT;							// 1-component, 32-bit signed integer
+		case GL_RG32I:											return GL_INT;							// 2-component, 32-bit signed integer
+		case GL_RGB32I:											return GL_INT;							// 3-component, 32-bit signed integer
+		case GL_RGBA32I:										return GL_INT;							// 4-component, 32-bit signed integer
+
+		case GL_R32F:											return GL_FLOAT;						// 1-component, 32-bit floating-point
+		case GL_RG32F:											return GL_FLOAT;						// 2-component, 32-bit floating-point
+		case GL_RGB32F:											return GL_FLOAT;						// 3-component, 32-bit floating-point
+		case GL_RGBA32F:										return GL_FLOAT;						// 4-component, 32-bit floating-point
+
+		//
+		// Packed
+		//
+		case GL_R3_G3_B2:										return GL_UNSIGNED_BYTE_2_3_3_REV;		// 3-component 3:3:2,       unsigned normalized
+		case GL_RGB4:											return GL_UNSIGNED_SHORT_4_4_4_4;		// 3-component 4:4:4,       unsigned normalized
+		case GL_RGB5:											return GL_UNSIGNED_SHORT_5_5_5_1;		// 3-component 5:5:5,       unsigned normalized
+		case GL_RGB565:											return GL_UNSIGNED_SHORT_5_6_5;			// 3-component 5:6:5,       unsigned normalized
+		case GL_RGB10:											return GL_UNSIGNED_INT_10_10_10_2;		// 3-component 10:10:10,    unsigned normalized
+		case GL_RGB12:											return GL_UNSIGNED_SHORT;				// 3-component 12:12:12,    unsigned normalized
+		case GL_RGBA2:											return GL_UNSIGNED_BYTE;				// 4-component 2:2:2:2,     unsigned normalized
+		case GL_RGBA4:											return GL_UNSIGNED_SHORT_4_4_4_4;		// 4-component 4:4:4:4,     unsigned normalized
+		case GL_RGBA12:											return GL_UNSIGNED_SHORT;				// 4-component 12:12:12:12, unsigned normalized
+		case GL_RGB5_A1:										return GL_UNSIGNED_SHORT_5_5_5_1;		// 4-component 5:5:5:1,     unsigned normalized
+		case GL_RGB10_A2:										return GL_UNSIGNED_INT_2_10_10_10_REV;	// 4-component 10:10:10:2,  unsigned normalized
+		case GL_RGB10_A2UI:										return GL_UNSIGNED_INT_2_10_10_10_REV;	// 4-component 10:10:10:2,  unsigned integer
+		case GL_R11F_G11F_B10F:									return GL_UNSIGNED_INT_10F_11F_11F_REV;	// 3-component 11:11:10,    floating-point
+		case GL_RGB9_E5:										return GL_UNSIGNED_INT_5_9_9_9_REV;		// 3-component/exp 9:9:9/5, floating-point
+
+		//
+		// S3TC/DXT/BC
+		//
+
+		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:					return GL_UNSIGNED_BYTE;				// line through 3D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:					return GL_UNSIGNED_BYTE;				// line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:					return GL_UNSIGNED_BYTE;				// line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:					return GL_UNSIGNED_BYTE;				// line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:					return GL_UNSIGNED_BYTE;				// line through 3D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:			return GL_UNSIGNED_BYTE;				// line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:			return GL_UNSIGNED_BYTE;				// line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:			return GL_UNSIGNED_BYTE;				// line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_LUMINANCE_LATC1_EXT:					return GL_UNSIGNED_BYTE;				// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:			return GL_UNSIGNED_BYTE;				// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:			return GL_UNSIGNED_BYTE;				// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:	return GL_UNSIGNED_BYTE;				// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RED_RGTC1:							return GL_UNSIGNED_BYTE;				// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG_RGTC2:							return GL_UNSIGNED_BYTE;				// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RED_RGTC1:					return GL_UNSIGNED_BYTE;				// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG_RGTC2:						return GL_UNSIGNED_BYTE;				// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:				return GL_FLOAT;						// 3-component, 4x4 blocks, unsigned floating-point
+		case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:				return GL_FLOAT;						// 3-component, 4x4 blocks, signed floating-point
+		case GL_COMPRESSED_RGBA_BPTC_UNORM:						return GL_UNSIGNED_BYTE;				// 4-component, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:				return GL_UNSIGNED_BYTE;				// 4-component, 4x4 blocks, sRGB
+
+		//
+		// ETC
+		//
+		case GL_ETC1_RGB8_OES:									return GL_UNSIGNED_BYTE;				// 3-component ETC1, 4x4 blocks, unsigned normalized" ),
+
+		case GL_COMPRESSED_RGB8_ETC2:							return GL_UNSIGNED_BYTE;				// 3-component ETC2, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return GL_UNSIGNED_BYTE;				// 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA8_ETC2_EAC:						return GL_UNSIGNED_BYTE;				// 4-component ETC2, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ETC2:							return GL_UNSIGNED_BYTE;				// 3-component ETC2, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return GL_UNSIGNED_BYTE;				// 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:				return GL_UNSIGNED_BYTE;				// 4-component ETC2, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_R11_EAC:								return GL_UNSIGNED_BYTE;				// 1-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG11_EAC:							return GL_UNSIGNED_BYTE;				// 2-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_R11_EAC:						return GL_UNSIGNED_BYTE;				// 1-component ETC, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG11_EAC:						return GL_UNSIGNED_BYTE;				// 2-component ETC, 4x4 blocks, signed normalized
+
+		//
+		// PVRTC
+		//
+		case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:				return GL_UNSIGNED_BYTE;				// 3-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:				return GL_UNSIGNED_BYTE;				// 3-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:				return GL_UNSIGNED_BYTE;				// 4-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:				return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG:				return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG:				return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:				return GL_UNSIGNED_BYTE;				// 3-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:				return GL_UNSIGNED_BYTE;				// 3-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:			return GL_UNSIGNED_BYTE;				// 4-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:			return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG:			return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  8x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG:			return GL_UNSIGNED_BYTE;				// 4-component PVRTC,  4x4 blocks, sRGB
+
+		//
+		// ASTC
+		//
+		case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 12x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 12x12 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 10x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 12x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 12x12 blocks, sRGB
+
+		case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 3x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES:					return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6x6 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 3x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 4x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 5x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES:			return GL_UNSIGNED_BYTE;				// 4-component ASTC, 6x6x6 blocks, sRGB
+
+		//
+		// ATC
+		//
+		case GL_ATC_RGB_AMD:									return GL_UNSIGNED_BYTE;				// 3-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:					return GL_UNSIGNED_BYTE;				// 4-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:				return GL_UNSIGNED_BYTE;				// 4-component, 4x4 blocks, unsigned normalized
+
+		//
+		// Palletized
+		//
+		case GL_PALETTE4_RGB8_OES:								return GL_UNSIGNED_BYTE;				// 3-component 8:8:8,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA8_OES:								return GL_UNSIGNED_BYTE;				// 4-component 8:8:8:8, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_R5_G6_B5_OES:							return GL_UNSIGNED_SHORT_5_6_5;			// 3-component 5:6:5,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA4_OES:								return GL_UNSIGNED_SHORT_4_4_4_4;		// 4-component 4:4:4:4, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGB5_A1_OES:							return GL_UNSIGNED_SHORT_5_5_5_1;		// 4-component 5:5:5:1, 4-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB8_OES:								return GL_UNSIGNED_BYTE;				// 3-component 8:8:8,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA8_OES:								return GL_UNSIGNED_BYTE;				// 4-component 8:8:8:8, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_R5_G6_B5_OES:							return GL_UNSIGNED_SHORT_5_6_5;			// 3-component 5:6:5,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA4_OES:								return GL_UNSIGNED_SHORT_4_4_4_4;		// 4-component 4:4:4:4, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB5_A1_OES:							return GL_UNSIGNED_SHORT_5_5_5_1;		// 4-component 5:5:5:1, 8-bit palette, unsigned normalized
+
+		//
+		// Depth/stencil
+		//
+		case GL_DEPTH_COMPONENT16:								return GL_UNSIGNED_SHORT;
+		case GL_DEPTH_COMPONENT24:								return GL_UNSIGNED_INT_24_8;
+		case GL_DEPTH_COMPONENT32:								return GL_UNSIGNED_INT;
+		case GL_DEPTH_COMPONENT32F:								return GL_FLOAT;
+		case GL_DEPTH_COMPONENT32F_NV:							return GL_FLOAT;
+		case GL_STENCIL_INDEX1:									return GL_UNSIGNED_BYTE;
+		case GL_STENCIL_INDEX4:									return GL_UNSIGNED_BYTE;
+		case GL_STENCIL_INDEX8:									return GL_UNSIGNED_BYTE;
+		case GL_STENCIL_INDEX16:								return GL_UNSIGNED_SHORT;
+		case GL_DEPTH24_STENCIL8:								return GL_UNSIGNED_INT_24_8;
+		case GL_DEPTH32F_STENCIL8:								return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
+		case GL_DEPTH32F_STENCIL8_NV:							return GL_FLOAT_32_UNSIGNED_INT_24_8_REV;
+
+		default:												return GL_INVALID_VALUE;
+	}
+}
+
+static inline unsigned int glGetTypeSizeFromType(GLenum type)
+{
+    switch (type) {
+        case GL_BYTE:
+        case GL_UNSIGNED_BYTE:
+        case GL_UNSIGNED_BYTE_3_3_2:
+        case GL_UNSIGNED_BYTE_2_3_3_REV:
+            return 1;
+
+        case GL_SHORT:
+        case GL_UNSIGNED_SHORT:
+        case GL_UNSIGNED_SHORT_5_6_5:
+        case GL_UNSIGNED_SHORT_4_4_4_4:
+        case GL_UNSIGNED_SHORT_5_5_5_1:
+        case GL_UNSIGNED_SHORT_5_6_5_REV:
+        case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+        case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+        case GL_HALF_FLOAT:
+            return 2;
+
+        case GL_INT:
+        case GL_UNSIGNED_INT:
+        case GL_UNSIGNED_INT_8_8_8_8:
+        case GL_UNSIGNED_INT_8_8_8_8_REV:
+        case GL_UNSIGNED_INT_10_10_10_2:
+        case GL_UNSIGNED_INT_2_10_10_10_REV:
+        case GL_UNSIGNED_INT_24_8:
+        case GL_UNSIGNED_INT_10F_11F_11F_REV:
+        case GL_UNSIGNED_INT_5_9_9_9_REV:
+        case GL_FLOAT:
+        case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+            return 4;
+
+        default:
+            return GL_INVALID_VALUE;
+    }
+}
+
+static inline void glGetFormatSize( const GLenum internalFormat, ktxFormatSize * pFormatSize )
+{
+	pFormatSize->minBlocksX = pFormatSize->minBlocksY = 1;
+	switch ( internalFormat )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_R8:												// 1-component, 8-bit unsigned normalized
+		case GL_R8_SNORM:										// 1-component, 8-bit signed normalized
+		case GL_R8UI:											// 1-component, 8-bit unsigned integer
+		case GL_R8I:											// 1-component, 8-bit signed integer
+		case GL_SR8:											// 1-component, 8-bit sRGB
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 1 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RG8:											// 2-component, 8-bit unsigned normalized
+		case GL_RG8_SNORM:										// 2-component, 8-bit signed normalized
+		case GL_RG8UI:											// 2-component, 8-bit unsigned integer
+		case GL_RG8I:											// 2-component, 8-bit signed integer
+		case GL_SRG8:											// 2-component, 8-bit sRGB
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB8:											// 3-component, 8-bit unsigned normalized
+		case GL_RGB8_SNORM:										// 3-component, 8-bit signed normalized
+		case GL_RGB8UI:											// 3-component, 8-bit unsigned integer
+		case GL_RGB8I:											// 3-component, 8-bit signed integer
+		case GL_SRGB8:											// 3-component, 8-bit sRGB
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 3 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA8:											// 4-component, 8-bit unsigned normalized
+		case GL_RGBA8_SNORM:									// 4-component, 8-bit signed normalized
+		case GL_RGBA8UI:										// 4-component, 8-bit unsigned integer
+		case GL_RGBA8I:											// 4-component, 8-bit signed integer
+		case GL_SRGB8_ALPHA8:									// 4-component, 8-bit sRGB
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// 16 bits per component
+		//
+		case GL_R16:											// 1-component, 16-bit unsigned normalized
+		case GL_R16_SNORM:										// 1-component, 16-bit signed normalized
+		case GL_R16UI:											// 1-component, 16-bit unsigned integer
+		case GL_R16I:											// 1-component, 16-bit signed integer
+		case GL_R16F:											// 1-component, 16-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RG16:											// 2-component, 16-bit unsigned normalized
+		case GL_RG16_SNORM:										// 2-component, 16-bit signed normalized
+		case GL_RG16UI:											// 2-component, 16-bit unsigned integer
+		case GL_RG16I:											// 2-component, 16-bit signed integer
+		case GL_RG16F:											// 2-component, 16-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB16:											// 3-component, 16-bit unsigned normalized
+		case GL_RGB16_SNORM:									// 3-component, 16-bit signed normalized
+		case GL_RGB16UI:										// 3-component, 16-bit unsigned integer
+		case GL_RGB16I:											// 3-component, 16-bit signed integer
+		case GL_RGB16F:											// 3-component, 16-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 6 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA16:											// 4-component, 16-bit unsigned normalized
+		case GL_RGBA16_SNORM:									// 4-component, 16-bit signed normalized
+		case GL_RGBA16UI:										// 4-component, 16-bit unsigned integer
+		case GL_RGBA16I:										// 4-component, 16-bit signed integer
+		case GL_RGBA16F:										// 4-component, 16-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// 32 bits per component
+		//
+		case GL_R32UI:											// 1-component, 32-bit unsigned integer
+		case GL_R32I:											// 1-component, 32-bit signed integer
+		case GL_R32F:											// 1-component, 32-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RG32UI:											// 2-component, 32-bit unsigned integer
+		case GL_RG32I:											// 2-component, 32-bit signed integer
+		case GL_RG32F:											// 2-component, 32-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB32UI:										// 3-component, 32-bit unsigned integer
+		case GL_RGB32I:											// 3-component, 32-bit signed integer
+		case GL_RGB32F:											// 3-component, 32-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 12 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA32UI:										// 4-component, 32-bit unsigned integer
+		case GL_RGBA32I:										// 4-component, 32-bit signed integer
+		case GL_RGBA32F:										// 4-component, 32-bit floating-point
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// Packed
+		//
+		case GL_R3_G3_B2:										// 3-component 3:3:2, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB4:											// 3-component 4:4:4, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 12;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB5:											// 3-component 5:5:5, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB565:											// 3-component 5:6:5, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB10:											// 3-component 10:10:10, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB12:											// 3-component 12:12:12, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 36;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA2:											// 4-component 2:2:2:2, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA4:											// 4-component 4:4:4:4, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGBA12:											// 4-component 12:12:12:12, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 48;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB5_A1:										// 4-component 5:5:5:1, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB10_A2:										// 4-component 10:10:10:2, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_RGB10_A2UI:										// 4-component 10:10:10:2, unsigned integer
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_R11F_G11F_B10F:									// 3-component 11:11:10, floating-point
+		case GL_RGB9_E5:										// 3-component/exp 9:9:9/5, floating-point
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// S3TC/DXT/BC
+		//
+		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:					// line through 3D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:					// line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:					// line through 3D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:			// line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:					// line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:					// line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:			// line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:			// line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		case GL_COMPRESSED_LUMINANCE_LATC1_EXT:					// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:			// line through 1D space, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:			// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:	// two lines through 1D space, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		case GL_COMPRESSED_RED_RGTC1:							// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RED_RGTC1:					// line through 1D space, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RG_RGTC2:							// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RG_RGTC2:						// two lines through 1D space, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:				// 3-component, 4x4 blocks, unsigned floating-point
+		case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:				// 3-component, 4x4 blocks, signed floating-point
+		case GL_COMPRESSED_RGBA_BPTC_UNORM:						// 4-component, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:				// 4-component, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// ETC
+		//
+		case GL_ETC1_RGB8_OES:									// 3-component ETC1, 4x4 blocks, unsigned normalized" ),
+		case GL_COMPRESSED_RGB8_ETC2:							// 3-component ETC2, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ETC2:							// 3-component ETC2, 4x4 blocks, sRGB
+		case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:		// 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:		// 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA8_ETC2_EAC:						// 4-component ETC2, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:				// 4-component ETC2, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		case GL_COMPRESSED_R11_EAC:								// 1-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_R11_EAC:						// 1-component ETC, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RG11_EAC:							// 2-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RG11_EAC:						// 2-component ETC, 4x4 blocks, signed normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// PVRTC
+		//
+		case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:				// 3-component PVRTC, 8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:				// 3-component PVRTC, 8x4 blocks, sRGB
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:				// 4-component PVRTC, 8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:			// 4-component PVRTC, 8x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			pFormatSize->minBlocksX = 2;
+			pFormatSize->minBlocksY = 2;
+			break;
+		case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:				// 3-component PVRTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:				// 3-component PVRTC, 4x4 blocks, sRGB
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:				// 4-component PVRTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:			// 4-component PVRTC, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			pFormatSize->minBlocksX = 2;
+			pFormatSize->minBlocksY = 2;
+			break;
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG:				// 4-component PVRTC, 8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG:			// 4-component PVRTC, 8x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG:				// 4-component PVRTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG:			// 4-component PVRTC, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// ASTC
+		//
+		case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:					// 4-component ASTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:			// 4-component ASTC, 4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:					// 4-component ASTC, 5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:			// 4-component ASTC, 5x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:					// 4-component ASTC, 5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:			// 4-component ASTC, 5x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:					// 4-component ASTC, 6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:			// 4-component ASTC, 6x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:					// 4-component ASTC, 6x6 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:			// 4-component ASTC, 6x6 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:					// 4-component ASTC, 8x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:			// 4-component ASTC, 8x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:					// 4-component ASTC, 8x6 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:			// 4-component ASTC, 8x6 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:					// 4-component ASTC, 8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:			// 4-component ASTC, 8x8 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 8;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:					// 4-component ASTC, 10x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:			// 4-component ASTC, 10x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:					// 4-component ASTC, 10x6 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:			// 4-component ASTC, 10x6 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:					// 4-component ASTC, 10x8 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:			// 4-component ASTC, 10x8 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 8;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:					// 4-component ASTC, 10x10 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:			// 4-component ASTC, 10x10 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 10;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:					// 4-component ASTC, 12x10 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:			// 4-component ASTC, 12x10 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 12;
+			pFormatSize->blockHeight = 10;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:					// 4-component ASTC, 12x12 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:			// 4-component ASTC, 12x12 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 12;
+			pFormatSize->blockHeight = 12;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES:					// 4-component ASTC, 3x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES:			// 4-component ASTC, 3x3x3 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 3;
+			pFormatSize->blockHeight = 3;
+			pFormatSize->blockDepth = 3;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES:					// 4-component ASTC, 4x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES:			// 4-component ASTC, 4x3x3 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 3;
+			pFormatSize->blockDepth = 3;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES:					// 4-component ASTC, 4x4x3 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES:			// 4-component ASTC, 4x4x3 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 3;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES:					// 4-component ASTC, 4x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES:			// 4-component ASTC, 4x4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 4;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES:					// 4-component ASTC, 5x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES:			// 4-component ASTC, 5x4x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 4;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES:					// 4-component ASTC, 5x5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES:			// 4-component ASTC, 5x5x4 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 4;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES:					// 4-component ASTC, 5x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES:			// 4-component ASTC, 5x5x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 5;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES:					// 4-component ASTC, 6x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES:			// 4-component ASTC, 6x5x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 5;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES:					// 4-component ASTC, 6x6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES:			// 4-component ASTC, 6x6x5 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 5;
+			break;
+		case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES:					// 4-component ASTC, 6x6x6 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES:			// 4-component ASTC, 6x6x6 blocks, sRGB
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 6;
+			break;
+
+		//
+		// ATC
+		//
+		case GL_ATC_RGB_AMD:									// 3-component, 4x4 blocks, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:					// 4-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:				// 4-component, 4x4 blocks, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 128;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// Palletized
+		//
+		case GL_PALETTE4_RGB8_OES:								// 3-component 8:8:8,   4-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 16 * 24;
+			pFormatSize->blockSizeInBits = 4;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_PALETTE4_RGBA8_OES:								// 4-component 8:8:8:8, 4-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 16 * 32;
+			pFormatSize->blockSizeInBits = 4;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_PALETTE4_R5_G6_B5_OES:							// 3-component 5:6:5,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA4_OES:								// 4-component 4:4:4:4, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGB5_A1_OES:							// 4-component 5:5:5:1, 4-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 16 * 16;
+			pFormatSize->blockSizeInBits = 4;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_PALETTE8_RGB8_OES:								// 3-component 8:8:8,   8-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 256 * 24;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_PALETTE8_RGBA8_OES:								// 4-component 8:8:8:8, 8-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 256 * 32;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_PALETTE8_R5_G6_B5_OES:							// 3-component 5:6:5,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA4_OES:								// 4-component 4:4:4:4, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB5_A1_OES:							// 4-component 5:5:5:1, 8-bit palette, unsigned normalized
+			pFormatSize->flags = KTX_FORMAT_SIZE_PALETTIZED_BIT;
+			pFormatSize->paletteSizeInBits = 256 * 16;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		//
+		// Depth/stencil
+		//
+		case GL_DEPTH_COMPONENT16:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_DEPTH_COMPONENT24:
+		case GL_DEPTH_COMPONENT32:
+		case GL_DEPTH_COMPONENT32F:
+		case GL_DEPTH_COMPONENT32F_NV:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_STENCIL_INDEX1:
+			pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 1;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_STENCIL_INDEX4:
+			pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_STENCIL_INDEX8:
+			pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_STENCIL_INDEX16:
+			pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_DEPTH24_STENCIL8:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case GL_DEPTH32F_STENCIL8:
+		case GL_DEPTH32F_STENCIL8_NV:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 64;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+
+		default:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 0 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+	}
+}
+
+static inline GLint glGetInternalFormatFromVkFormat( VkFormat vkFormat )
+{
+    switch ( vkFormat )
+    {
+        //
+        // 8 bits per component
+        //
+        case VK_FORMAT_R8_UNORM:                   return GL_R8;                // 1-component, 8-bit unsigned normalized
+        case VK_FORMAT_R8G8_UNORM:                 return GL_RG8;               // 2-component, 8-bit unsigned normalized
+        case VK_FORMAT_R8G8B8_UNORM:               return GL_RGB8;              // 3-component, 8-bit unsigned normalized
+        case VK_FORMAT_R8G8B8A8_UNORM:             return GL_RGBA8;             // 4-component, 8-bit unsigned normalized
+
+        case VK_FORMAT_R8_SNORM:                   return GL_R8_SNORM;          // 1-component, 8-bit signed normalized
+        case VK_FORMAT_R8G8_SNORM:                 return GL_RG8_SNORM;         // 2-component, 8-bit signed normalized
+        case VK_FORMAT_R8G8B8_SNORM:               return GL_RGB8_SNORM;        // 3-component, 8-bit signed normalized
+        case VK_FORMAT_R8G8B8A8_SNORM:             return GL_RGBA8_SNORM;       // 4-component, 8-bit signed normalized
+
+        case VK_FORMAT_R8_UINT:                    return GL_R8UI;              // 1-component, 8-bit unsigned integer
+        case VK_FORMAT_R8G8_UINT:                  return GL_RG8UI;             // 2-component, 8-bit unsigned integer
+        case VK_FORMAT_R8G8B8_UINT:                return GL_RGB8UI;            // 3-component, 8-bit unsigned integer
+        case VK_FORMAT_R8G8B8A8_UINT:              return GL_RGBA8UI;           // 4-component, 8-bit unsigned integer
+
+        case VK_FORMAT_R8_SINT:                    return GL_R8I;               // 1-component, 8-bit signed integer
+        case VK_FORMAT_R8G8_SINT:                  return GL_RG8I;              // 2-component, 8-bit signed integer
+        case VK_FORMAT_R8G8B8_SINT:                return GL_RGB8I;             // 3-component, 8-bit signed integer
+        case VK_FORMAT_R8G8B8A8_SINT:              return GL_RGBA8I;            // 4-component, 8-bit signed integer
+
+        case VK_FORMAT_R8_SRGB:                    return GL_SR8;               // 1-component, 8-bit sRGB
+        case VK_FORMAT_R8G8_SRGB:                  return GL_SRG8;              // 2-component, 8-bit sRGB
+        case VK_FORMAT_R8G8B8_SRGB:                return GL_SRGB8;             // 3-component, 8-bit sRGB
+        case VK_FORMAT_R8G8B8A8_SRGB:              return GL_SRGB8_ALPHA8;      // 4-component, 8-bit sRGB
+
+        //
+        // 16 bits per component
+        //
+        case VK_FORMAT_R16_UNORM:                  return GL_R16;               // 1-component, 16-bit unsigned normalized
+        case VK_FORMAT_R16G16_UNORM:               return GL_RG16;              // 2-component, 16-bit unsigned normalized
+        case VK_FORMAT_R16G16B16_UNORM:            return GL_RGB16;             // 3-component, 16-bit unsigned normalized
+        case VK_FORMAT_R16G16B16A16_UNORM:         return GL_RGBA16;            // 4-component, 16-bit unsigned normalized
+
+        case VK_FORMAT_R16_SNORM:                  return GL_R16_SNORM;         // 1-component, 16-bit signed normalized
+        case VK_FORMAT_R16G16_SNORM:               return GL_RG16_SNORM;        // 2-component, 16-bit signed normalized
+        case VK_FORMAT_R16G16B16_SNORM:            return GL_RGB16_SNORM;       // 3-component, 16-bit signed normalized
+        case VK_FORMAT_R16G16B16A16_SNORM:         return GL_RGBA16_SNORM;      // 4-component, 16-bit signed normalized
+
+        case VK_FORMAT_R16_UINT:                   return GL_R16UI;             // 1-component, 16-bit unsigned integer
+        case VK_FORMAT_R16G16_UINT:                return GL_RG16UI;            // 2-component, 16-bit unsigned integer
+        case VK_FORMAT_R16G16B16_UINT:             return GL_RGB16UI;           // 3-component, 16-bit unsigned integer
+        case VK_FORMAT_R16G16B16A16_UINT:          return GL_RGBA16UI;          // 4-component, 16-bit unsigned integer
+
+        case VK_FORMAT_R16_SINT:                   return GL_R16I;              // 1-component, 16-bit signed integer
+        case VK_FORMAT_R16G16_SINT:                return GL_RG16I;             // 2-component, 16-bit signed integer
+        case VK_FORMAT_R16G16B16_SINT:             return GL_RGB16I;            // 3-component, 16-bit signed integer
+        case VK_FORMAT_R16G16B16A16_SINT:          return GL_RGBA16I;           // 4-component, 16-bit signed integer
+
+        case VK_FORMAT_R16_SFLOAT:                 return GL_R16F;              // 1-component, 16-bit floating-point
+        case VK_FORMAT_R16G16_SFLOAT:              return GL_RG16F;             // 2-component, 16-bit floating-point
+        case VK_FORMAT_R16G16B16_SFLOAT:           return GL_RGB16F;            // 3-component, 16-bit floating-point
+        case VK_FORMAT_R16G16B16A16_SFLOAT:        return GL_RGBA16F;           // 4-component, 16-bit floating-point
+
+        //
+        // 32 bits per component
+        //
+        case VK_FORMAT_R32_UINT:                   return GL_R32UI;             // 1-component, 32-bit unsigned integer
+        case VK_FORMAT_R32G32_UINT:                return GL_RG32UI;            // 2-component, 32-bit unsigned integer
+        case VK_FORMAT_R32G32B32_UINT:             return GL_RGB32UI;           // 3-component, 32-bit unsigned integer
+        case VK_FORMAT_R32G32B32A32_UINT:          return GL_RGBA32UI;          // 4-component, 32-bit unsigned integer
+
+        case VK_FORMAT_R32_SINT:                   return GL_R32I;              // 1-component, 32-bit signed integer
+        case VK_FORMAT_R32G32_SINT:                return GL_RG32I;             // 2-component, 32-bit signed integer
+        case VK_FORMAT_R32G32B32_SINT:             return GL_RGB32I;            // 3-component, 32-bit signed integer
+        case VK_FORMAT_R32G32B32A32_SINT:          return GL_RGBA32I;           // 4-component, 32-bit signed integer
+
+        case VK_FORMAT_R32_SFLOAT:                 return GL_R32F;              // 1-component, 32-bit floating-point
+        case VK_FORMAT_R32G32_SFLOAT:              return GL_RG32F;             // 2-component, 32-bit floating-point
+        case VK_FORMAT_R32G32B32_SFLOAT:           return GL_RGB32F;            // 3-component, 32-bit floating-point
+        case VK_FORMAT_R32G32B32A32_SFLOAT:        return GL_RGBA32F;           // 4-component, 32-bit floating-point
+
+        //
+        // Packed
+        //
+        case VK_FORMAT_R5G5B5A1_UNORM_PACK16:      return GL_RGB5;              // 3-component 5:5:5,       unsigned normalized
+        case VK_FORMAT_R5G6B5_UNORM_PACK16:        return GL_RGB565;            // 3-component 5:6:5,       unsigned normalized
+        case VK_FORMAT_R4G4B4A4_UNORM_PACK16:      return GL_RGBA4;             // 4-component 4:4:4:4,     unsigned normalized
+        case VK_FORMAT_A1R5G5B5_UNORM_PACK16:      return GL_RGB5_A1;           // 4-component 5:5:5:1,     unsigned normalized
+        case VK_FORMAT_A2R10G10B10_UNORM_PACK32:   return GL_RGB10_A2;          // 4-component 10:10:10:2,  unsigned normalized
+        case VK_FORMAT_A2R10G10B10_UINT_PACK32:    return GL_RGB10_A2UI;        // 4-component 10:10:10:2,  unsigned integer
+        case VK_FORMAT_B10G11R11_UFLOAT_PACK32:    return GL_R11F_G11F_B10F;    // 3-component 11:11:10,    floating-point
+        case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:     return GL_RGB9_E5;           // 3-component/exp 9:9:9/5, floating-point
+
+        //
+        // S3TC/DXT/BC
+        //
+
+        case VK_FORMAT_BC1_RGB_UNORM_BLOCK:        return GL_COMPRESSED_RGB_S3TC_DXT1_EXT;                  // line through 3D space, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:       return GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;                 // line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC2_UNORM_BLOCK:            return GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;                 // line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC3_UNORM_BLOCK:            return GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;                 // line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+
+        case VK_FORMAT_BC1_RGB_SRGB_BLOCK:         return GL_COMPRESSED_SRGB_S3TC_DXT1_EXT;                 // line through 3D space, 4x4 blocks, sRGB
+        case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:        return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT;           // line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+        case VK_FORMAT_BC2_SRGB_BLOCK:             return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT;           // line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+        case VK_FORMAT_BC3_SRGB_BLOCK:             return GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT;           // line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+
+        case VK_FORMAT_BC4_UNORM_BLOCK:            return GL_COMPRESSED_RED_RGTC1;                          // line through 1D space, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC5_UNORM_BLOCK:            return GL_COMPRESSED_RG_RGTC2;                           // two lines through 1D space, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC4_SNORM_BLOCK:            return GL_COMPRESSED_SIGNED_RED_RGTC1;                   // line through 1D space, 4x4 blocks, signed normalized
+        case VK_FORMAT_BC5_SNORM_BLOCK:            return GL_COMPRESSED_SIGNED_RG_RGTC2;                    // two lines through 1D space, 4x4 blocks, signed normalized
+
+        case VK_FORMAT_BC6H_UFLOAT_BLOCK:          return GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT;            // 3-component, 4x4 blocks, unsigned floating-point
+        case VK_FORMAT_BC6H_SFLOAT_BLOCK:          return GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT;              // 3-component, 4x4 blocks, signed floating-point
+        case VK_FORMAT_BC7_UNORM_BLOCK:            return GL_COMPRESSED_RGBA_BPTC_UNORM;                    // 4-component, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_BC7_SRGB_BLOCK:             return GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM;              // 4-component, 4x4 blocks, sRGB
+
+        //
+        // ETC
+        //
+        case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:    return GL_COMPRESSED_RGB8_ETC2;                          // 3-component ETC2, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:  return GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2;      // 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:  return GL_COMPRESSED_RGBA8_ETC2_EAC;                     // 4-component ETC2, 4x4 blocks, unsigned normalized
+
+        case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:     return GL_COMPRESSED_SRGB8_ETC2;                         // 3-component ETC2, 4x4 blocks, sRGB
+        case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:   return GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2;     // 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+        case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:   return GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC;              // 4-component ETC2, 4x4 blocks, sRGB
+
+        case VK_FORMAT_EAC_R11_UNORM_BLOCK:        return GL_COMPRESSED_R11_EAC;                            // 1-component ETC, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:     return GL_COMPRESSED_RG11_EAC;                           // 2-component ETC, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_EAC_R11_SNORM_BLOCK:        return GL_COMPRESSED_SIGNED_R11_EAC;                     // 1-component ETC, 4x4 blocks, signed normalized
+        case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:     return GL_COMPRESSED_SIGNED_RG11_EAC;                    // 2-component ETC, 4x4 blocks, signed normalized
+
+        //
+        // PVRTC
+        //
+        case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:    return GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG;           // 3- or 4-component PVRTC, 16x8 blocks, unsigned normalized
+        case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:    return GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG;           // 3- or 4-component PVRTC,  8x8 blocks, unsigned normalized
+        case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:    return GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG;           // 3- or 4-component PVRTC, 16x8 blocks, unsigned normalized
+        case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:    return GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG;           // 3- or 4-component PVRTC,  4x4 blocks, unsigned normalized
+
+        case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:     return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT;     // 4-component PVRTC, 16x8 blocks, sRGB
+        case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:     return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT;     // 4-component PVRTC,  8x8 blocks, sRGB
+        case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:     return GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG;     // 4-component PVRTC,  8x4 blocks, sRGB
+        case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:     return GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG;     // 4-component PVRTC,  4x4 blocks, sRGB
+
+        //
+        // ASTC
+        //
+        case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_4x4_KHR;                // 4-component ASTC, 4x4 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_5x4_KHR;                // 4-component ASTC, 5x4 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_5x5_KHR;                // 4-component ASTC, 5x5 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_6x5_KHR;                // 4-component ASTC, 6x5 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_6x6_KHR;                // 4-component ASTC, 6x6 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_8x5_KHR;                // 4-component ASTC, 8x5 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_8x6_KHR;                // 4-component ASTC, 8x6 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:           return GL_COMPRESSED_RGBA_ASTC_8x8_KHR;                // 4-component ASTC, 8x8 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:          return GL_COMPRESSED_RGBA_ASTC_10x5_KHR;               // 4-component ASTC, 10x5 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:          return GL_COMPRESSED_RGBA_ASTC_10x6_KHR;               // 4-component ASTC, 10x6 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:          return GL_COMPRESSED_RGBA_ASTC_10x8_KHR;               // 4-component ASTC, 10x8 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:         return GL_COMPRESSED_RGBA_ASTC_10x10_KHR;              // 4-component ASTC, 10x10 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:         return GL_COMPRESSED_RGBA_ASTC_12x10_KHR;              // 4-component ASTC, 12x10 blocks, unsigned normalized
+        case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:         return GL_COMPRESSED_RGBA_ASTC_12x12_KHR;              // 4-component ASTC, 12x12 blocks, unsigned normalized
+
+        case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR;        // 4-component ASTC, 4x4 blocks, sRGB
+        case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR;        // 4-component ASTC, 5x4 blocks, sRGB
+        case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR;        // 4-component ASTC, 5x5 blocks, sRGB
+        case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR;        // 4-component ASTC, 6x5 blocks, sRGB
+        case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR;        // 4-component ASTC, 6x6 blocks, sRGB
+        case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR;        // 4-component ASTC, 8x5 blocks, sRGB
+        case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR;        // 4-component ASTC, 8x6 blocks, sRGB
+        case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:            return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR;        // 4-component ASTC, 8x8 blocks, sRGB
+        case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:           return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR;       // 4-component ASTC, 10x5 blocks, sRGB
+        case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:           return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR;       // 4-component ASTC, 10x6 blocks, sRGB
+        case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:           return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR;       // 4-component ASTC, 10x8 blocks, sRGB
+        case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:          return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR;      // 4-component ASTC, 10x10 blocks, sRGB
+        case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:          return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR;      // 4-component ASTC, 12x10 blocks, sRGB
+        case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:          return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR;      // 4-component ASTC, 12x12 blocks, sRGB
+
+        // XXX FIXME Update once Vulkan ASTC HDR & 3D extensions are released.
+#if 0
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_3x3x3_OES;               // 4-component ASTC, 3x3x3 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_4x3x3_OES;               // 4-component ASTC, 4x3x3 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_4x4x3_OES;               // 4-component ASTC, 4x4x3 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_4x4x4_OES;               // 4-component ASTC, 4x4x4 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_5x4x4_OES;               // 4-component ASTC, 5x4x4 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_5x5x4_OES;               // 4-component ASTC, 5x5x4 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_5x5x5_OES;               // 4-component ASTC, 5x5x5 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_6x5x5_OES;               // 4-component ASTC, 6x5x5 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_6x6x5_OES;               // 4-component ASTC, 6x6x5 blocks, unsigned normalized
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_RGBA_ASTC_6x6x6_OES;               // 4-component ASTC, 6x6x6 blocks, unsigned normalized
+
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES;       // 4-component ASTC, 3x3x3 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES;       // 4-component ASTC, 4x3x3 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES;       // 4-component ASTC, 4x4x3 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES;       // 4-component ASTC, 4x4x4 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES;       // 4-component ASTC, 5x4x4 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES;       // 4-component ASTC, 5x5x4 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES;       // 4-component ASTC, 5x5x5 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES;       // 4-component ASTC, 6x5x5 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES;       // 4-component ASTC, 6x6x5 blocks, sRGB
+        case VK_FORMAT_UNDEFINED:                      return GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES;       // 4-component ASTC, 6x6x6 blocks, sRGB
+#endif
+
+        //
+        // Depth/stencil
+        //
+        case VK_FORMAT_D16_UNORM:                      return GL_DEPTH_COMPONENT16;
+        case VK_FORMAT_X8_D24_UNORM_PACK32:            return GL_DEPTH_COMPONENT24;
+        case VK_FORMAT_D32_SFLOAT:                     return GL_DEPTH_COMPONENT32F;
+        case VK_FORMAT_S8_UINT:                        return GL_STENCIL_INDEX8;
+        case VK_FORMAT_D24_UNORM_S8_UINT:              return GL_DEPTH24_STENCIL8;
+        case VK_FORMAT_D32_SFLOAT_S8_UINT:             return GL_DEPTH32F_STENCIL8;
+
+        default:                                       return GL_INVALID_VALUE;
+    }
+}
+
+#endif // !GL_FORMAT_H
diff --git a/thirdparty/libktx/lib/hashlist.c b/thirdparty/libktx/lib/hashlist.c
new file mode 100644
index 00000000000..0ca89fc561b
--- /dev/null
+++ b/thirdparty/libktx/lib/hashlist.c
@@ -0,0 +1,604 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file hashlist.c
+ * @~English
+ *
+ * @brief Functions for creating and using a hash list of key-value
+ *        pairs.
+ *
+ * @author Mark Callow, HI Corporation
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// This is to avoid compile warnings. strlen is defined as returning
+// size_t and is used by the uthash macros. This avoids having to
+// make changes to uthash and a bunch of casts in this file. The
+// casts would be required because the key and value lengths in KTX
+// are specified as 4 byte quantities so we can't change _keyAndValue
+// below to use size_t.
+#define strlen(x) ((unsigned int)strlen(x))
+
+#include "uthash.h"
+
+#include "ktx.h"
+#include "ktxint.h"
+
+
+/**
+ * @internal
+ * @struct ktxKVListEntry
+ * @brief Hash list entry structure
+ */
+typedef struct ktxKVListEntry {
+    unsigned int keyLen;    /*!< Length of the key */
+    char* key;              /*!< Pointer to key string */
+    unsigned int valueLen;  /*!< Length of the value */
+    void* value;            /*!< Pointer to the value */
+    UT_hash_handle hh;      /*!< handle used by UT hash */
+} ktxKVListEntry;
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Construct an empty hash list for storing key-value pairs.
+ *
+ * @param [in] pHead pointer to the location to write the list head.
+ */
+void
+ktxHashList_Construct(ktxHashList* pHead)
+{
+    *pHead = NULL;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Construct a hash list by copying another.
+ *
+ * @param [in] pHead pointer to head of the list.
+ * @param [in] orig  head of the original hash list.
+ */
+void
+ktxHashList_ConstructCopy(ktxHashList* pHead, ktxHashList orig)
+{
+    ktxHashListEntry* entry = orig;
+    *pHead = NULL;
+    for (; entry != NULL; entry = ktxHashList_Next(entry)) {
+        (void)ktxHashList_AddKVPair(pHead,
+                                    entry->key, entry->valueLen, entry->value);
+    }
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Destruct a hash list.
+ *
+ * All memory associated with the hash list's keys and values
+ * is freed.
+ *
+ * @param [in] pHead pointer to the hash list to be destroyed.
+ */
+void
+ktxHashList_Destruct(ktxHashList* pHead)
+{
+    ktxKVListEntry* kv;
+    ktxKVListEntry* head = *pHead;
+
+    for(kv = head; kv != NULL;) {
+        ktxKVListEntry* tmp = (ktxKVListEntry*)kv->hh.next;
+        HASH_DELETE(hh, head, kv);
+        free(kv);
+        kv = tmp;
+    }
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Create an empty hash list for storing key-value pairs.
+ *
+ * @param [in,out] ppHl address of a variable in which to set a pointer to
+ *                 the newly created hash list.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ * @exception KTX_OUT_OF_MEMORY if not enough memory.
+ */
+KTX_error_code
+ktxHashList_Create(ktxHashList** ppHl)
+{
+    ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*));
+    if (hl == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    ktxHashList_Construct(hl);
+    *ppHl = hl;
+    return KTX_SUCCESS;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Create a copy of a hash list.
+ *
+ * @param [in,out] ppHl address of a variable in which to set a pointer to
+ *                      the newly created hash list.
+ * @param [in]     orig head of the ktxHashList to copy.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ * @exception KTX_OUT_OF_MEMORY if not enough memory.
+ */
+KTX_error_code
+ktxHashList_CreateCopy(ktxHashList** ppHl, ktxHashList orig)
+{
+    ktxHashList* hl = (ktxHashList*)malloc(sizeof (ktxKVListEntry*));
+    if (hl == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    ktxHashList_ConstructCopy(hl, orig);
+    *ppHl = hl;
+    return KTX_SUCCESS;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Destroy a hash list.
+ *
+ * All memory associated with the hash list's keys and values
+ * is freed. The hash list is also freed.
+ *
+ * @param [in] pHead pointer to the hash list to be destroyed.
+ */
+void
+ktxHashList_Destroy(ktxHashList* pHead)
+{
+    ktxHashList_Destruct(pHead);
+    free(pHead);
+}
+
+#if !__clang__ && __GNUC__ // Grumble clang grumble
+// These are in uthash.h macros. I don't want to change that file.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wimplicit-fallthrough"
+#endif
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Add a key value pair to a hash list.
+ *
+ * The value can be empty, i.e, its length can be 0.
+ *
+ * @param [in] pHead    pointer to the head of the target hash list.
+ * @param [in] key      pointer to the UTF8 NUL-terminated string to be used as the key.
+ * @param [in] valueLen the number of bytes of data in @p value.
+ * @param [in] value    pointer to the bytes of data constituting the value.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ * @exception KTX_INVALID_VALUE if @p pHead, @p key or @p value are NULL, @p key is an
+ *            empty string or @p valueLen == 0.
+ */
+KTX_error_code
+ktxHashList_AddKVPair(ktxHashList* pHead, const char* key, unsigned int valueLen, const void* value)
+{
+    if (pHead && key && (valueLen == 0 || value)) {
+        unsigned int keyLen = (unsigned int)strlen(key) + 1;
+        ktxKVListEntry* kv;
+
+        if (keyLen == 1)
+            return KTX_INVALID_VALUE;   /* Empty string */
+
+        /* Allocate all the memory as a block */
+        kv = (ktxKVListEntry*)malloc(sizeof(ktxKVListEntry) + keyLen + valueLen);
+        /* Put key first */
+        kv->key = (char *)kv + sizeof(ktxKVListEntry);
+        kv->keyLen = keyLen;
+        memcpy(kv->key, key, keyLen);
+        /* then value */
+        kv->valueLen = valueLen;
+        if (valueLen > 0) {
+            kv->value = kv->key + keyLen;
+            memcpy(kv->value, value, valueLen);
+        } else {
+            kv->value = 0;
+        }
+
+        HASH_ADD_KEYPTR( hh, *pHead, kv->key, kv->keyLen-1, kv);
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Delete a key value pair in a hash list.
+ *
+ * Is a nop if the key is not in the hash.
+ *
+ * @param [in] pHead    pointer to the head of the target hash list.
+ * @param [in] key      pointer to the UTF8 NUL-terminated string to be used as the key.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ * @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty
+ *            string.
+ */
+KTX_error_code
+ktxHashList_DeleteKVPair(ktxHashList* pHead, const char* key)
+{
+    if (pHead && key) {
+        ktxKVListEntry* kv;
+
+        HASH_FIND_STR( *pHead, key, kv );  /* kv: pointer to target entry. */
+        if (kv != NULL)
+            HASH_DEL(*pHead, kv);
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Delete an entry from a hash list.
+ *
+ * @param [in] pHead    pointer to the head of the target hash list.
+ * @param [in] pEntry   pointer to the ktxHashListEntry to delete.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ * @exception KTX_INVALID_VALUE if @p pHead is NULL or @p key is an empty
+ *            string.
+ */
+KTX_error_code
+ktxHashList_DeleteEntry(ktxHashList* pHead, ktxHashListEntry* pEntry)
+{
+    if (pHead && pEntry) {
+        HASH_DEL(*pHead, pEntry);
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Looks up a key in a hash list and returns the entry.
+ *
+ * @param [in]     pHead        pointer to the head of the target hash list.
+ * @param [in]     key          pointer to a UTF8 NUL-terminated string to find.
+ * @param [in,out] ppEntry      @p *ppEntry is set to the point at the
+ *                              ktxHashListEntry.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue
+ *                              is NULL.
+ * @exception KTX_NOT_FOUND     an entry matching @p key was not found.
+ */
+KTX_error_code
+ktxHashList_FindEntry(ktxHashList* pHead, const char* key,
+                      ktxHashListEntry** ppEntry)
+{
+    if (pHead && key) {
+        ktxKVListEntry* kv;
+
+        HASH_FIND_STR( *pHead, key, kv );  /* kv: output pointer */
+
+        if (kv) {
+            *ppEntry = kv;
+            return KTX_SUCCESS;
+        } else
+            return KTX_NOT_FOUND;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Looks up a key in a hash list and returns the value.
+ *
+ * @param [in]     pHead        pointer to the head of the target hash list.
+ * @param [in]     key          pointer to a UTF8 NUL-terminated string to find.
+ * @param [in,out] pValueLen    @p *pValueLen is set to the number of bytes of
+ *                              data in the returned value.
+ * @param [in,out] ppValue      @p *ppValue is set to the point to the value for
+ *                              @p key.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p This, @p key or @p pValueLen or @p ppValue
+ *                              is NULL.
+ * @exception KTX_NOT_FOUND     an entry matching @p key was not found.
+ */
+KTX_error_code
+ktxHashList_FindValue(ktxHashList *pHead, const char* key, unsigned int* pValueLen, void** ppValue)
+{
+    if (pValueLen && ppValue) {
+        ktxHashListEntry* pEntry;
+        KTX_error_code result;
+
+        result = ktxHashList_FindEntry(pHead, key, &pEntry);
+        if (result == KTX_SUCCESS) {
+            ktxHashListEntry_GetValue(pEntry, pValueLen, ppValue);
+            return KTX_SUCCESS;
+        } else
+            return result;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+#if !__clang__ && __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Returns the next entry in a ktxHashList.
+ *
+ * Use for iterating through the list:
+ * @code
+ *    ktxHashListEntry* entry;
+ *    for (entry = listHead; entry != NULL; entry = ktxHashList_Next(entry)) {
+ *       ...
+ *    };
+ * @endcode
+ *
+ * Note
+ *
+ * @param [in]  entry   pointer to a hash list entry. Note that a ktxHashList*,
+ *                      i.e. the list head, is also a pointer to an entry so
+ *                      can be passed to this function.
+ *
+ * @return a pointer to the next entry or NULL.
+ *
+ */
+ktxHashListEntry*
+ktxHashList_Next(ktxHashListEntry* entry)
+{
+    if (entry) {
+        return ((ktxKVListEntry*)entry)->hh.next;
+    } else
+        return NULL;
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Serialize a hash list to a block of data suitable for writing
+ *        to a file.
+ *
+ * The caller is responsible for freeing the data block returned by this
+ * function.
+ *
+ * @param [in]     pHead        pointer to the head of the target hash list.
+ * @param [in,out] pKvdLen      @p *pKvdLen is set to the number of bytes of
+ *                              data in the returned data block.
+ * @param [in,out] ppKvd        @p *ppKvd is set to the point to the block of
+ *                              memory containing the serialized data or
+ *                              NULL. if the hash list is empty.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p This, @p pKvdLen or @p ppKvd is NULL.
+ * @exception KTX_OUT_OF_MEMORY there was not enough memory to serialize the
+ *                              data.
+ */
+KTX_error_code
+ktxHashList_Serialize(ktxHashList* pHead,
+                      unsigned int* pKvdLen, unsigned char** ppKvd)
+{
+
+    if (pHead && pKvdLen && ppKvd) {
+        ktxKVListEntry* kv;
+        unsigned int bytesOfKeyValueData = 0;
+        unsigned int keyValueLen;
+        unsigned char* sd;
+        char padding[4] = {0, 0, 0, 0};
+
+        for (kv = *pHead; kv != NULL; kv = kv->hh.next) {
+            /* sizeof(sd) is to make space to write keyAndValueByteSize */
+            keyValueLen = kv->keyLen + kv->valueLen + sizeof(ktx_uint32_t);
+            /* Add valuePadding */
+            keyValueLen = _KTX_PAD4(keyValueLen);
+            bytesOfKeyValueData += keyValueLen;
+        }
+
+        if (bytesOfKeyValueData == 0) {
+            *pKvdLen = 0;
+            *ppKvd = NULL;
+        } else {
+            sd = malloc(bytesOfKeyValueData);
+            if (!sd)
+                return KTX_OUT_OF_MEMORY;
+
+            *pKvdLen = bytesOfKeyValueData;
+            *ppKvd = sd;
+
+            for (kv = *pHead; kv != NULL; kv = kv->hh.next) {
+                int padLen;
+
+                keyValueLen = kv->keyLen + kv->valueLen;
+                *(ktx_uint32_t*)sd = keyValueLen;
+                sd += sizeof(ktx_uint32_t);
+                memcpy(sd, kv->key, kv->keyLen);
+                sd += kv->keyLen;
+                if (kv->valueLen > 0)
+                    memcpy(sd, kv->value, kv->valueLen);
+                sd += kv->valueLen;
+                padLen = _KTX_PAD4_LEN(keyValueLen);
+                memcpy(sd, padding, padLen);
+                sd += padLen;
+            }
+        }
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+int sort_by_key_codepoint(ktxKVListEntry* a, ktxKVListEntry* b) {
+  return strcmp(a->key, b->key);
+}
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Sort a hash list in order of the UTF8 codepoints.
+ *
+ * @param [in]     pHead        pointer to the head of the target hash list.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p This is NULL.
+ */
+KTX_error_code
+ktxHashList_Sort(ktxHashList* pHead)
+{
+    if (pHead) {
+        //ktxKVListEntry* kv = (ktxKVListEntry*)pHead;
+
+        HASH_SORT(*pHead, sort_by_key_codepoint);
+        return KTX_SUCCESS;
+    } else {
+        return KTX_INVALID_VALUE;
+    }
+}
+
+
+/**
+ * @memberof ktxHashList @public
+ * @~English
+ * @brief Construct a hash list from a block of serialized key-value
+ *        data read from a file.
+ * @note The bytes of the 32-bit key-value lengths within the serialized data
+ *       are expected to be in native endianness.
+ *
+ * @param [in]      pHead       pointer to the head of the target hash list.
+ * @param [in]      kvdLen      the length of the serialized key-value data.
+ * @param [in]      pKvd        pointer to the serialized key-value data.
+ *                              table.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_OPERATION if @p pHead does not point to an empty list.
+ * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
+ * @exception KTX_OUT_OF_MEMORY there was not enough memory to create the hash
+ *                              table.
+ */
+KTX_error_code
+ktxHashList_Deserialize(ktxHashList* pHead, unsigned int kvdLen, void* pKvd)
+{
+    char* src = pKvd;
+    KTX_error_code result;
+
+    if (kvdLen == 0 || pKvd == NULL || pHead == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (*pHead != NULL)
+        return KTX_INVALID_OPERATION;
+
+    result = KTX_SUCCESS;
+    while (result == KTX_SUCCESS && src < (char *)pKvd + kvdLen) {
+        char* key;
+        unsigned int keyLen, valueLen;
+        void* value;
+        ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);
+
+        src += sizeof(keyAndValueByteSize);
+        key = src;
+        keyLen = (unsigned int)strlen(key) + 1;
+        value = key + keyLen;
+
+        valueLen = keyAndValueByteSize - keyLen;
+        result = ktxHashList_AddKVPair(pHead, key, valueLen,
+                                       valueLen > 0 ? value : NULL);
+        if (result == KTX_SUCCESS) {
+            src += _KTX_PAD4(keyAndValueByteSize);
+        }
+    }
+    return result;
+}
+
+
+/**
+ * @memberof ktxHashListEntry @public
+ * @~English
+ * @brief Return the key of a ktxHashListEntry
+ *
+ * @param [in]     This       The target hash list entry.
+ * @param [in,out] pKeyLen    @p *pKeyLen is set to the byte length of
+ *                            the returned key.
+ * @param [in,out] ppKey      @p *ppKey is set to the point to the value of
+ *                            @p the key.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
+ */
+KTX_error_code
+ktxHashListEntry_GetKey(ktxHashListEntry* This,
+                        unsigned int* pKeyLen, char** ppKey)
+{
+    if (pKeyLen && ppKey) {
+        ktxKVListEntry* kv = (ktxKVListEntry*)This;
+        *pKeyLen = kv->keyLen;
+        *ppKey = kv->key;
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
+
+
+/**
+ * @memberof ktxHashListEntry @public
+ * @~English
+ * @brief Return the value from a ktxHashListEntry
+ *
+ * @param [in]     This         The target hash list entry.
+ * @param [in,out] pValueLen    @p *pValueLen is set to the number of bytes of
+ *                              data in the returned value.
+ * @param [in,out] ppValue      @p *ppValue is set to point to the value of
+ *                              of the target entry.
+ *
+ * @return KTX_SUCCESS or one of the following error codes.
+ *
+ * @exception KTX_INVALID_VALUE if @p pKvd or @p pHt is NULL or kvdLen == 0.
+ */
+KTX_error_code
+ktxHashListEntry_GetValue(ktxHashListEntry* This,
+                          unsigned int* pValueLen, void** ppValue)
+{
+    if (pValueLen && ppValue) {
+        ktxKVListEntry* kv = (ktxKVListEntry*)This;
+        *pValueLen = kv->valueLen;
+        *ppValue = kv->valueLen > 0 ? kv->value : NULL;
+        return KTX_SUCCESS;
+    } else
+        return KTX_INVALID_VALUE;
+}
diff --git a/thirdparty/libktx/lib/ktxint.h b/thirdparty/libktx/lib/ktxint.h
new file mode 100644
index 00000000000..03c9945ce78
--- /dev/null
+++ b/thirdparty/libktx/lib/ktxint.h
@@ -0,0 +1,266 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* $Id: e36ad79b5eac8ea237d6a05602c71aadab575519 $ */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+
+/*
+ * Author: Mark Callow from original code by Georg Kolling
+ */
+
+#ifndef KTXINT_H
+#define KTXINT_H
+
+#include <math.h>
+
+/* Define this to include the ETC unpack software in the library. */
+#ifndef SUPPORT_SOFTWARE_ETC_UNPACK
+  /* Include for all GL versions because have seen OpenGL ES 3
+   * implementaions that do not support ETC1 (ARM Mali emulator v1.0)!
+   */
+  #define SUPPORT_SOFTWARE_ETC_UNPACK 1
+#endif
+
+#ifndef MAX
+#define MAX(x, y) (((x) > (y)) ? (x) : (y))
+#endif
+
+#define QUOTE(x) #x
+#define STR(x) QUOTE(x)
+
+#define KTX2_IDENTIFIER_REF  { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A }
+#define KTX2_HEADER_SIZE     (80)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @internal
+ * @brief used to pass GL context capabilites to subroutines.
+ */
+#define _KTX_NO_R16_FORMATS     0x0
+#define _KTX_R16_FORMATS_NORM   0x1
+#define _KTX_R16_FORMATS_SNORM  0x2
+#define _KTX_ALL_R16_FORMATS (_KTX_R16_FORMATS_NORM | _KTX_R16_FORMATS_SNORM)
+extern GLint _ktxR16Formats;
+extern GLboolean _ktxSupportsSRGB;
+
+/**
+ * @internal
+ * @~English
+ * @brief KTX file header.
+ *
+ * See the KTX specification for descriptions.
+ */
+typedef struct KTX_header {
+    ktx_uint8_t  identifier[12];
+    ktx_uint32_t endianness;
+    ktx_uint32_t glType;
+    ktx_uint32_t glTypeSize;
+    ktx_uint32_t glFormat;
+    ktx_uint32_t glInternalformat;
+    ktx_uint32_t glBaseInternalformat;
+    ktx_uint32_t pixelWidth;
+    ktx_uint32_t pixelHeight;
+    ktx_uint32_t pixelDepth;
+    ktx_uint32_t numberOfArrayElements;
+    ktx_uint32_t numberOfFaces;
+    ktx_uint32_t numberOfMipLevels;
+    ktx_uint32_t bytesOfKeyValueData;
+} KTX_header;
+
+/* This will cause compilation to fail if the struct size doesn't match */
+typedef int KTX_header_SIZE_ASSERT [sizeof(KTX_header) == KTX_HEADER_SIZE];
+
+/**
+ * @internal
+ * @~English
+ * @brief 32-bit KTX 2 index entry.
+ */
+typedef struct ktxIndexEntry32 {
+    ktx_uint32_t byteOffset; /*!< Offset of item from start of file. */
+    ktx_uint32_t byteLength; /*!< Number of bytes of data in the item. */
+} ktxIndexEntry32;
+/**
+ * @internal
+ * @~English
+ * @brief 64-bit KTX 2 index entry.
+ */
+typedef struct ktxIndexEntry64 {
+    ktx_uint64_t byteOffset; /*!< Offset of item from start of file. */
+    ktx_uint64_t byteLength; /*!< Number of bytes of data in the item. */
+} ktxIndexEntry64;
+
+/**
+ * @internal
+ * @~English
+ * @brief KTX 2 file header.
+ *
+ * See the KTX 2 specification for descriptions.
+ */
+typedef struct KTX_header2 {
+    ktx_uint8_t  identifier[12];
+    ktx_uint32_t vkFormat;
+    ktx_uint32_t typeSize;
+    ktx_uint32_t pixelWidth;
+    ktx_uint32_t pixelHeight;
+    ktx_uint32_t pixelDepth;
+    ktx_uint32_t layerCount;
+    ktx_uint32_t faceCount;
+    ktx_uint32_t levelCount;
+    ktx_uint32_t supercompressionScheme;
+    ktxIndexEntry32 dataFormatDescriptor;
+    ktxIndexEntry32 keyValueData;
+    ktxIndexEntry64 supercompressionGlobalData;
+} KTX_header2;
+
+/* This will cause compilation to fail if the struct size doesn't match */
+typedef int KTX_header2_SIZE_ASSERT [sizeof(KTX_header2) == KTX2_HEADER_SIZE];
+
+/**
+ * @internal
+ * @~English
+ * @brief KTX 2 level index entry.
+ */
+typedef struct ktxLevelIndexEntry {
+    ktx_uint64_t byteOffset; /*!< Offset of level from start of file. */
+    ktx_uint64_t byteLength;
+                /*!< Number of bytes of compressed image data in the level. */
+    ktx_uint64_t uncompressedByteLength;
+                /*!< Number of bytes of uncompressed image data in the level. */
+} ktxLevelIndexEntry;
+
+/**
+ * @internal
+ * @~English
+ * @brief Structure for supplemental information about the texture.
+ *
+ * _ktxCheckHeader returns supplemental information about the texture in this
+ * structure that is derived during checking of the file header.
+ */
+typedef struct KTX_supplemental_info
+{
+    ktx_uint8_t compressed;
+    ktx_uint8_t generateMipmaps;
+    ktx_uint16_t textureDimension;
+} KTX_supplemental_info;
+/**
+ * @internal
+ * @var ktx_uint8_t KTX_supplemental_info::compressed
+ * @~English
+ * @brief KTX_TRUE, if this a compressed texture, KTX_FALSE otherwise?
+ */
+/**
+ * @internal
+ * @var ktx_uint8_t KTX_supplemental_info::generateMipmaps
+ * @~English
+ * @brief KTX_TRUE, if mipmap generation is required, KTX_FALSE otherwise.
+ */
+/**
+ * @internal
+ * @var ktx_uint16_t KTX_supplemental_info::textureDimension
+ * @~English
+ * @brief The number of dimensions, 1, 2 or 3, of data in the texture image.
+ */
+
+/*
+ * @internal
+ * CheckHeader1
+ *
+ * Reads the KTX file header and performs some sanity checking on the values
+ */
+KTX_error_code ktxCheckHeader1_(KTX_header* pHeader,
+                                KTX_supplemental_info* pSuppInfo);
+
+/*
+ * @internal
+ * CheckHeader2
+ *
+ * Reads the KTX 2 file header and performs some sanity checking on the values
+ */
+KTX_error_code ktxCheckHeader2_(KTX_header2* pHeader,
+                                KTX_supplemental_info* pSuppInfo);
+
+/*
+ * SwapEndian16: Swaps endianness in an array of 16-bit values
+ */
+void _ktxSwapEndian16(ktx_uint16_t* pData16, ktx_size_t count);
+
+/*
+ * SwapEndian32: Swaps endianness in an array of 32-bit values
+ */
+void _ktxSwapEndian32(ktx_uint32_t* pData32, ktx_size_t count);
+
+/*
+ * SwapEndian32: Swaps endianness in an array of 64-bit values
+ */
+void _ktxSwapEndian64(ktx_uint64_t* pData64, ktx_size_t count);
+
+/*
+ * UnpackETC: uncompresses an ETC compressed texture image
+ */
+KTX_error_code _ktxUnpackETC(const GLubyte* srcETC, const GLenum srcFormat,
+                             ktx_uint32_t active_width, ktx_uint32_t active_height,
+                             GLubyte** dstImage,
+                             GLenum* format, GLenum* internalFormat, GLenum* type,
+                             GLint R16Formats, GLboolean supportsSRGB);
+
+/*
+ * Pad nbytes to next multiple of n
+ */
+#define _KTX_PADN(n, nbytes) (ktx_uint32_t)(n * ceilf((float)(nbytes) / n))
+/*
+ * Calculate bytes of of padding needed to reach next multiple of n.
+ */
+/* Equivalent to (n * ceil(nbytes / n)) - nbytes */
+#define _KTX_PADN_LEN(n, nbytes) \
+    (ktx_uint32_t)((n * ceilf((float)(nbytes) / n)) - (nbytes))
+
+/*
+ * Pad nbytes to next multiple of 4
+ */
+#define _KTX_PAD4(nbytes) _KTX_PADN(4, nbytes)
+/*
+ * Calculate bytes of of padding needed to reach next multiple of 4.
+ */
+#define _KTX_PAD4_LEN(nbytes) _KTX_PADN_LEN(4, nbytes)
+
+/*
+ * Pad nbytes to next multiple of 8
+ */
+#define _KTX_PAD8(nbytes) _KTX_PADN(8, nbytes)
+/*
+ * Calculate bytes of of padding needed to reach next multiple of 8.
+ */
+#define _KTX_PAD8_LEN(nbytes) _KTX_PADN_LEN(8, nbytes)
+
+/*
+ * Pad nbytes to KTX_GL_UNPACK_ALIGNMENT
+ */
+#define _KTX_PAD_UNPACK_ALIGN(nbytes)  \
+        _KTX_PADN(KTX_GL_UNPACK_ALIGNMENT, nbytes)
+/*
+ * Calculate bytes of of padding needed to reach KTX_GL_UNPACK_ALIGNMENT.
+ */
+#define _KTX_PAD_UNPACK_ALIGN_LEN(nbytes)  \
+        _KTX_PADN_LEN(KTX_GL_UNPACK_ALIGNMENT, nbytes)
+
+/*
+ ======================================
+     Internal utility functions
+ ======================================
+*/
+
+void printKTX2Info2(ktxStream* src, KTX_header2* header);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* KTXINT_H */
diff --git a/thirdparty/libktx/lib/memstream.c b/thirdparty/libktx/lib/memstream.c
new file mode 100644
index 00000000000..b963fa70cad
--- /dev/null
+++ b/thirdparty/libktx/lib/memstream.c
@@ -0,0 +1,561 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @file
+ * @~English
+ *
+ * @brief Implementation of ktxStream for memory.
+ *
+ * @author Maksim Kolesin, Under Development
+ * @author Georg Kolling, Imagination Technology
+ * @author Mark Callow, HI Corporation
+ */
+
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "ktx.h"
+#include "ktxint.h"
+#include "memstream.h"
+
+/**
+* @brief Default allocation size for a ktxMemStream.
+*/
+#define KTX_MEM_DEFAULT_ALLOCATED_SIZE 256
+
+/**
+ * @brief Structure to store information about data allocated for ktxMemStream.
+ */
+struct ktxMem
+{
+    const ktx_uint8_t* robytes;/*!< pointer to read-only data */
+    ktx_uint8_t* bytes;        /*!< pointer to rw data. */
+    ktx_size_t alloc_size;       /*!< allocated size of the memory block. */
+    ktx_size_t used_size;        /*!< bytes used. Effectively the write position. */
+    ktx_off_t pos;               /*!< read/write position. */
+};
+
+static KTX_error_code ktxMem_expand(ktxMem* pMem, const ktx_size_t size);
+
+/**
+ * @brief Initialize a ktxMem struct for read-write.
+ *
+ * Memory for the stream data is allocated internally but the
+ * caller is responsible for freeing the memory. A pointer to
+ * the memory can be obtained with ktxMem_getdata().
+ *
+ * @sa ktxMem_getdata.
+ *
+ * @param [in] pMem pointer to the @c ktxMem to initialize.
+ */
+static KTX_error_code
+ktxMem_construct(ktxMem* pMem)
+{
+    pMem->pos = 0;
+    pMem->alloc_size = 0;
+    pMem->robytes = 0;
+    pMem->bytes = 0;
+    pMem->used_size = 0;
+    return ktxMem_expand(pMem, KTX_MEM_DEFAULT_ALLOCATED_SIZE);
+}
+
+/**
+ * @brief Create & initialize a ktxMem struct for read-write.
+ *
+ * @sa ktxMem_construct.
+ *
+ * @param [in,out] ppMem pointer to the location in which to return
+ *                       a pointer to the newly created @c ktxMem.
+ *
+ * @return     KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
+ *
+ * @exception  KTX_OUT_OF_MEMORY    System failed to allocate sufficient pMemory.
+ */
+static KTX_error_code
+ktxMem_create(ktxMem** ppMem)
+{
+    ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
+    if (pNewMem) {
+        KTX_error_code result = ktxMem_construct(pNewMem);
+        if (result == KTX_SUCCESS)
+            *ppMem = pNewMem;
+        return result;
+    }
+    else {
+        return KTX_OUT_OF_MEMORY;
+    }
+}
+
+/**
+ * @brief Initialize a ktxMem struct for read-only.
+ *
+ * @param [in] pMem     pointer to the @c ktxMem to initialize.
+ * @param [in] bytes    pointer to the data to be read.
+ * @param [in] numBytes number of bytes of data.
+ */
+static void
+ktxMem_construct_ro(ktxMem* pMem, const void* bytes, ktx_size_t numBytes)
+{
+    pMem->pos = 0;
+    pMem->robytes = bytes;
+    pMem->bytes = 0;
+    pMem->used_size = numBytes;
+    pMem->alloc_size = numBytes;
+}
+
+/**
+ * @brief Create & initialize a ktxMem struct for read-only.
+ *
+ * @sa ktxMem_construct.
+ *
+ * @param [in,out] ppMem    pointer to the location in which to return
+ *                          a pointer to the newly created @c ktxMem.
+ * @param [in]     bytes    pointer to the data to be read.
+ * @param [in]     numBytes number of bytes of data.
+ *
+ * @return     KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
+ *
+ * @exception  KTX_OUT_OF_MEMORY    System failed to allocate sufficient pMemory.
+ */
+static KTX_error_code
+ktxMem_create_ro(ktxMem** ppMem, const void* bytes, ktx_size_t numBytes)
+{
+    ktxMem* pNewMem = (ktxMem*)malloc(sizeof(ktxMem));
+    if (pNewMem) {
+        ktxMem_construct_ro(pNewMem, bytes, numBytes);
+        *ppMem = pNewMem;
+        return KTX_SUCCESS;
+    }
+    else {
+        return KTX_OUT_OF_MEMORY;
+    }
+}
+
+/*
+ * ktxMem_destruct not needed as ktxMem_construct caller is reponsible
+ * for freeing the data written.
+ */
+
+/**
+ * @brief Free the memory of a struct ktxMem.
+ *
+ * @param pMem pointer to ktxMem to free.
+ */
+static void
+ktxMem_destroy(ktxMem* pMem, ktx_bool_t freeData)
+{
+    assert(pMem != NULL);
+    if (freeData) {
+        free(pMem->bytes);
+    }
+    free(pMem);
+}
+
+#ifdef KTXMEM_CLEAR_USED
+/**
+ * @brief Clear the data of a memory stream.
+ *
+ * @param pMem pointer to ktxMem to clear.
+ */
+static void
+ktxMem_clear(ktxMem* pMem)
+{
+    assert(pMem != NULL);
+    memset(pMem, 0, sizeof(ktxMem));
+}
+#endif
+
+/**
+ * @~English
+ * @brief Expand a ktxMem to fit to a new size.
+ *
+ * @param [in] pMem          pointer to ktxMem struct to expand.
+ * @param [in] newsize       minimum new size required.
+ *
+ * @return     KTX_SUCCESS on success, KTX_OUT_OF_MEMORY on error.
+ *
+ * @exception  KTX_OUT_OF_MEMORY    System failed to allocate sufficient pMemory.
+ */
+static KTX_error_code
+ktxMem_expand(ktxMem *pMem, const ktx_size_t newsize)
+{
+    ktx_size_t new_alloc_size;
+
+    assert(pMem != NULL && newsize != 0);
+
+    new_alloc_size = pMem->alloc_size == 0 ?
+                     KTX_MEM_DEFAULT_ALLOCATED_SIZE : pMem->alloc_size;
+    while (new_alloc_size < newsize) {
+        ktx_size_t alloc_size = new_alloc_size;
+        new_alloc_size <<= 1;
+        if (new_alloc_size < alloc_size) {
+            /* Overflow. Set to maximum size. newsize can't be larger. */
+            new_alloc_size = (ktx_size_t)-1L;
+        }
+    }
+
+    if (new_alloc_size == pMem->alloc_size)
+        return KTX_SUCCESS;
+
+    if (!pMem->bytes)
+        pMem->bytes = (ktx_uint8_t*)malloc(new_alloc_size);
+    else
+        pMem->bytes = (ktx_uint8_t*)realloc(pMem->bytes, new_alloc_size);
+
+    if (!pMem->bytes)
+    {
+        pMem->alloc_size = 0;
+        pMem->used_size = 0;
+        return KTX_OUT_OF_MEMORY;
+    }
+
+    pMem->alloc_size = new_alloc_size;
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Read bytes from a ktxMemStream.
+ *
+ * @param [in]     str      pointer to ktxMem struct, converted to a void*, that
+ *                          specifies an input stream.
+ * @param [in,out] dst      pointer to memory where to copy read bytes.
+ * @param [in,out] count    pointer to number of bytes to read.
+ *
+ * @return      KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
+ *
+ * @exception KTX_INVALID_VALUE     @p str or @p dst is @c NULL or @p str->data is
+ *                                  @c NULL.
+ * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
+ */
+static
+KTX_error_code ktxMemStream_read(ktxStream* str, void* dst, const ktx_size_t count)
+{
+    ktxMem* mem;
+    ktx_off_t newpos;
+    const ktx_uint8_t* bytes;
+
+
+    if (!str || (mem = str->data.mem)== 0)
+        return KTX_INVALID_VALUE;
+
+    newpos = mem->pos + count;
+    /* The first clause checks for overflow. */
+    if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
+        return KTX_FILE_UNEXPECTED_EOF;
+
+    bytes = mem->robytes ? mem->robytes : mem->bytes;
+    memcpy(dst, bytes + mem->pos, count);
+    mem->pos = newpos;
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Skip bytes in a ktxMemStream.
+ *
+ * @param [in] str      pointer to the ktxStream on which to operate.
+ * @param [in] count    number of bytes to skip.
+ *
+ * @return      KTX_SUCCESS on success, KTX_INVALID_VALUE on error.
+ *
+ * @exception KTX_INVALID_VALUE     @p str or @p mem is @c NULL or sufficient
+ *                                  data is not available in ktxMem.
+ * @exception KTX_FILE_UNEXPECTED_EOF not enough data to satisfy the request.
+ */
+static
+KTX_error_code ktxMemStream_skip(ktxStream* str, const ktx_size_t count)
+{
+    ktxMem* mem;
+    ktx_off_t newpos;
+
+    if (!str || (mem = str->data.mem) == 0)
+        return KTX_INVALID_VALUE;
+
+    newpos = mem->pos + count;
+    /* The first clause checks for overflow. */
+    if (newpos < mem->pos || (ktx_uint32_t)newpos > mem->used_size)
+        return KTX_FILE_UNEXPECTED_EOF;
+
+    mem->pos = newpos;
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Write bytes to a ktxMemStream.
+ *
+ * @param [out] str    pointer to the ktxStream that specifies the destination.
+ * @param [in] src     pointer to the array of elements to be written,
+ *                     converted to a const void*.
+ * @param [in] size    size in bytes of each element to be written.
+ * @param [in] count   number of elements, each one with a @p size of size
+ *                     bytes.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_OVERFLOW        write would result in file exceeding the
+ *                                     maximum permissible size.
+ * @exception KTX_INVALID_OPERATION    @p str is a read-only stream.
+ * @exception KTX_INVALID_VALUE        @p dst is @c NULL or @p mem is @c NULL.
+ * @exception KTX_OUT_OF_MEMORY        See ktxMem_expand() for causes.
+ */
+static
+KTX_error_code ktxMemStream_write(ktxStream* str, const void* src,
+                                  const ktx_size_t size, const ktx_size_t count)
+{
+    ktxMem* mem;
+    KTX_error_code rc = KTX_SUCCESS;
+    ktx_size_t new_size;
+
+    if (!str || (mem = str->data.mem) == 0)
+        return KTX_INVALID_VALUE;
+
+    if (mem->robytes)
+        return KTX_INVALID_OPERATION; /* read-only */
+
+    new_size = mem->pos + (size*count);
+    //if (new_size < mem->used_size)
+    if ((ktx_off_t)new_size < mem->pos)
+        return KTX_FILE_OVERFLOW;
+
+    if (mem->alloc_size < new_size) {
+        rc = ktxMem_expand(mem, new_size);
+        if (rc != KTX_SUCCESS)
+            return rc;
+    }
+
+    memcpy(mem->bytes + mem->pos, src, size*count);
+    mem->pos += size*count;
+    if (mem->pos > (ktx_off_t)mem->used_size)
+        mem->used_size = mem->pos;
+
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Get the current read/write position in a ktxMemStream.
+ *
+ * @param [in] str      pointer to the ktxStream to query.
+ * @param [in,out] off  pointer to variable to receive the offset value.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str or @p pos is @c NULL.
+ */
+static
+KTX_error_code ktxMemStream_getpos(ktxStream* str, ktx_off_t* const pos)
+{
+    if (!str || !pos)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeMemory);
+
+    *pos = str->data.mem->pos;
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Set the current read/write position in a ktxMemStream.
+ *
+ * Offset of 0 is the start of the file.
+ *
+ * @param [in] str    pointer to the ktxStream whose r/w position is to be set.
+ * @param [in] off    pointer to the offset value to set.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str is @c NULL.
+ * @exception KTX_INVALID_OPERATION @p pos > size of the allocated memory.
+ */
+static
+KTX_error_code ktxMemStream_setpos(ktxStream* str, ktx_off_t pos)
+{
+    if (!str)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeMemory);
+
+    if (pos > (ktx_off_t)str->data.mem->alloc_size)
+        return KTX_INVALID_OPERATION;
+
+    str->data.mem->pos = pos;
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Get a pointer to a ktxMemStream's data.
+ *
+ * Gets a pointer to data that has been written to the stream. Returned
+ * pointer will be 0 if stream is read-only.
+ *
+ * @param [in] str       pointer to the ktxStream whose data pointer is to
+ *                       be queried.
+ * @param [in,out] ppBytes  pointer to a variable in which the data pointer
+ *                       will be written.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str or @p ppBytes is @c NULL.
+ */
+KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes)
+{
+    if (!str || !ppBytes)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeMemory);
+
+    *ppBytes = str->data.mem->bytes;
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Get the size of a ktxMemStream in bytes.
+ *
+ * @param [in] str       pointer to the ktxStream whose size is to be queried.
+ * @param [in,out] size  pointer to a variable in which size will be written.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p str or @p pSize is @c NULL.
+ */
+static
+KTX_error_code ktxMemStream_getsize(ktxStream* str, ktx_size_t* pSize)
+{
+    if (!str || !pSize)
+        return KTX_INVALID_VALUE;
+
+    assert(str->type == eStreamTypeMemory);
+
+    *pSize = str->data.mem->used_size;
+    return KTX_SUCCESS;
+}
+
+/**
+ * @~English
+ * @brief Setup ktxMemStream function pointers.
+ */
+void
+ktxMemStream_setup(ktxStream* str)
+{
+    str->type = eStreamTypeMemory;
+    str->read = ktxMemStream_read;
+    str->skip = ktxMemStream_skip;
+    str->write = ktxMemStream_write;
+    str->getpos = ktxMemStream_getpos;
+    str->setpos = ktxMemStream_setpos;
+    str->getsize = ktxMemStream_getsize;
+    str->destruct = ktxMemStream_destruct;
+}
+
+/**
+ * @~English
+ * @brief Initialize a read-write ktxMemStream.
+ *
+ * Memory is allocated as data is written. The caller of this is
+ * responsible for freeing this memory unless @a freeOnDestruct
+ * is not KTX_FALSE.
+ *
+ * @param [in] str             pointer to a ktxStream struct to initialize.
+ * @param [in] freeOnDestruct  If not KTX_FALSE memory holding the data will
+ *                             be freed by the destructor.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE     @p str is @c NULL.
+ * @exception KTX_OUT_OF_MEMORY     system failed to allocate sufficient memory.
+ */
+KTX_error_code ktxMemStream_construct(ktxStream* str,
+                                      ktx_bool_t freeOnDestruct)
+{
+    ktxMem* mem;
+    KTX_error_code result = KTX_SUCCESS;
+
+    if (!str)
+        return KTX_INVALID_VALUE;
+
+    result = ktxMem_create(&mem);
+
+    if (KTX_SUCCESS == result) {
+        str->data.mem = mem;
+        ktxMemStream_setup(str);
+        str->closeOnDestruct = freeOnDestruct;
+    }
+
+    return result;
+}
+
+/**
+ * @~English
+ * @brief Initialize a read-only ktxMemStream.
+ *
+ * @param [in] str      pointer to a ktxStream struct to initialize.
+ * @param [in] bytes    pointer to an array of bytes containing the data.
+ * @param [in] numBytes     size of array of data for ktxMemStream.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE     @p str or @p mem is @c NULL or @p numBytes
+ *                                  is 0.
+ *                                  or @p size is less than 0.
+ * @exception KTX_OUT_OF_MEMORY     system failed to allocate sufficient memory.
+ */
+KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
+                                         const ktx_uint8_t* bytes,
+                                         const ktx_size_t numBytes)
+{
+    ktxMem* mem;
+    KTX_error_code result = KTX_SUCCESS;
+
+    if (!str || !bytes || numBytes == 0)
+        return KTX_INVALID_VALUE;
+
+    result = ktxMem_create_ro(&mem, bytes, numBytes);
+
+    if (KTX_SUCCESS == result) {
+        str->data.mem = mem;
+        ktxMemStream_setup(str);
+        str->closeOnDestruct = KTX_FALSE;
+    }
+
+    return result;
+}
+
+/**
+ * @~English
+ * @brief Free the memory used by a ktxMemStream.
+ *
+ * This only frees the memory used to store the data written to the stream,
+ * if the @c freeOnDestruct parameter to ktxMemStream_construct() was not
+ * @c KTX_FALSE. Otherwise it is the responsibility of the caller of
+ * ktxMemStream_construct() and a pointer to this memory should be retrieved
+ * using ktxMemStream_getdata() before calling this function.
+ *
+ * @sa ktxMemStream_construct, ktxMemStream_getdata.
+ *
+ * @param [in] str pointer to the ktxStream whose memory is
+ *                 to be freed.
+ */
+void
+ktxMemStream_destruct(ktxStream* str)
+{
+    assert(str && str->type == eStreamTypeMemory);
+
+    ktxMem_destroy(str->data.mem, str->closeOnDestruct);
+    str->data.mem = NULL;
+}
+
diff --git a/thirdparty/libktx/lib/memstream.h b/thirdparty/libktx/lib/memstream.h
new file mode 100644
index 00000000000..4ef8d59cd2e
--- /dev/null
+++ b/thirdparty/libktx/lib/memstream.h
@@ -0,0 +1,43 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file
+ * @~English
+ *
+ * @brief Interface of ktxStream for memory.
+ *
+ * @author Maksim Kolesin
+ * @author Georg Kolling, Imagination Technology
+ * @author Mark Callow, HI Corporation
+ */
+
+#ifndef MEMSTREAM_H
+#define MEMSTREAM_H
+
+#include "ktx.h"
+
+/*
+ * Initialize a ktxStream to a ktxMemStream with internally
+ * allocated memory. Can be read or written.
+ */
+KTX_error_code ktxMemStream_construct(ktxStream* str,
+                                      ktx_bool_t freeOnDestruct);
+/*
+ * Initialize a ktxStream to a read-only ktxMemStream reading
+ * from an array of bytes.
+ */
+KTX_error_code ktxMemStream_construct_ro(ktxStream* str,
+                                         const ktx_uint8_t* pBytes,
+                                         const ktx_size_t size);
+void ktxMemStream_destruct(ktxStream* str);
+
+KTX_error_code ktxMemStream_getdata(ktxStream* str, ktx_uint8_t** ppBytes);
+
+#endif /* MEMSTREAM_H */
diff --git a/thirdparty/libktx/lib/swap.c b/thirdparty/libktx/lib/swap.c
new file mode 100644
index 00000000000..3fdeb4f3a46
--- /dev/null
+++ b/thirdparty/libktx/lib/swap.c
@@ -0,0 +1,57 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* $Id: 02ea6de2d8db512ca3af08f48b98ab5f6c35e7e5 $ */
+
+/*
+ * Copyright 2010-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+#include <KHR/khrplatform.h>
+#include "ktx.h"
+
+/*
+ * SwapEndian16: Swaps endianness in an array of 16-bit values
+ */
+void
+_ktxSwapEndian16(khronos_uint16_t* pData16, ktx_size_t count)
+{
+    for (ktx_size_t i = 0; i < count; ++i)
+    {
+        khronos_uint16_t x = *pData16;
+        *pData16++ = (x << 8) | (x >> 8);
+    }
+}
+
+/*
+ * SwapEndian32: Swaps endianness in an array of 32-bit values
+ */
+void
+_ktxSwapEndian32(khronos_uint32_t* pData32, ktx_size_t count)
+{
+    for (ktx_size_t i = 0; i < count; ++i)
+    {
+        khronos_uint32_t x = *pData32;
+        *pData32++ = (x << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | (x >> 24);
+    }
+}
+
+/*
+ * SwapEndian364: Swaps endianness in an array of 32-bit values
+ */
+void
+_ktxSwapEndian64(khronos_uint64_t* pData64, ktx_size_t count)
+{
+    for (ktx_size_t i = 0; i < count; ++i)
+    {
+        khronos_uint64_t x = *pData64;
+        *pData64++ = (x << 56) | ((x & 0xFF00) << 40) | ((x & 0xFF0000) << 24)
+                     | ((x & 0xFF000000) << 8 ) | ((x & 0xFF00000000) >> 8)
+                     | ((x & 0xFF0000000000) >> 24)
+                     | ((x & 0xFF000000000000) << 40) | (x >> 56);
+    }
+}
+
+
+
diff --git a/thirdparty/libktx/lib/texture.c b/thirdparty/libktx/lib/texture.c
new file mode 100644
index 00000000000..35619a3e73f
--- /dev/null
+++ b/thirdparty/libktx/lib/texture.c
@@ -0,0 +1,911 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2018-2020 Mark Callow.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file writer.c
+ * @~English
+ *
+ * @brief ktxTexture implementation.
+ *
+ * @author Mark Callow, www.edgewise-consulting.com
+ */
+
+#if defined(_WIN32)
+  #define _CRT_SECURE_NO_WARNINGS
+  #ifndef __cplusplus
+    #undef inline
+    #define inline __inline
+  #endif // __cplusplus
+#endif
+
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "ktx.h"
+#include "ktxint.h"
+#include "formatsize.h"
+#include "filestream.h"
+#include "memstream.h"
+#include "texture1.h"
+#include "texture2.h"
+#include "unused.h"
+
+ktx_size_t ktxTexture_GetDataSize(ktxTexture* This);
+
+static ktx_uint32_t padRow(ktx_uint32_t* rowBytes);
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Construct (initialize) a ktxTexture base class instance.
+ *
+ * @param[in] This pointer to a ktxTexture-sized block of memory to
+ *                 initialize.
+ * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
+ *                       information describing the texture.
+ * @param[in] formatSize pointer to a ktxFormatSize giving size information
+ *                       about the texture's elements.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
+ *                              valid OpenGL internal format value.
+ * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
+ *                              or 3.
+ * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
+ *                              @p createInfo is 0.
+ * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
+ * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
+ * @exception KTX_INVALID_OPERATION
+ *                              The <tt>base{Width,Height,Depth}</tt> specified
+ *                              in @p createInfo are inconsistent with
+ *                              @c numDimensions.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a 3D array or
+ *                              3D cubemap texture.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a cubemap with
+ *                              non-square or non-2D images.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting more mip levels
+ *                              than needed for the specified
+ *                              <tt>base{Width,Height,Depth}</tt>.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture.
+ */
+KTX_error_code
+ktxTexture_construct(ktxTexture* This, ktxTextureCreateInfo* createInfo,
+                     ktxFormatSize* formatSize)
+{
+    DECLARE_PROTECTED(ktxTexture);
+
+    memset(This, 0, sizeof(*This));
+    This->_protected = (struct ktxTexture_protected*)malloc(sizeof(*prtctd));
+    if (!This->_protected)
+        return KTX_OUT_OF_MEMORY;
+    prtctd = This->_protected;
+    memset(prtctd, 0, sizeof(*prtctd));
+    memcpy(&prtctd->_formatSize, formatSize, sizeof(prtctd->_formatSize));
+
+    This->isCompressed = (formatSize->flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
+
+    This->orientation.x = KTX_ORIENT_X_RIGHT;
+    This->orientation.y = KTX_ORIENT_Y_DOWN;
+    This->orientation.z = KTX_ORIENT_Z_OUT;
+
+    /* Check texture dimensions. KTX files can store 8 types of textures:
+     * 1D, 2D, 3D, cube, and array variants of these.
+     */
+    if (createInfo->numDimensions < 1 || createInfo->numDimensions > 3)
+        return KTX_INVALID_VALUE;
+
+    if (createInfo->baseWidth == 0 || createInfo->baseHeight == 0
+        || createInfo->baseDepth == 0)
+        return KTX_INVALID_VALUE;
+
+    switch (createInfo->numDimensions) {
+      case 1:
+        if (createInfo->baseHeight > 1 || createInfo->baseDepth > 1)
+            return KTX_INVALID_OPERATION;
+        break;
+
+      case 2:
+        if (createInfo->baseDepth > 1)
+            return KTX_INVALID_OPERATION;
+        break;
+
+      case 3:
+        /* 3D array textures and 3D cubemaps are not supported by either
+         * OpenGL or Vulkan.
+         */
+        if (createInfo->isArray || createInfo->numFaces != 1
+            || createInfo->numLayers != 1)
+            return KTX_INVALID_OPERATION;
+        break;
+    }
+    This->numDimensions = createInfo->numDimensions;
+    This->baseWidth = createInfo->baseWidth;
+    This->baseDepth = createInfo->baseDepth;
+    This->baseHeight = createInfo->baseHeight;
+
+    if (createInfo->numLayers == 0)
+        return KTX_INVALID_VALUE;
+    This->numLayers = createInfo->numLayers;
+    This->isArray = createInfo->isArray;
+
+    if (createInfo->numFaces == 6) {
+        if (This->numDimensions != 2) {
+            /* cube map needs 2D faces */
+            return KTX_INVALID_OPERATION;
+        }
+        if (createInfo->baseWidth != createInfo->baseHeight) {
+            /* cube maps require square images */
+            return KTX_INVALID_OPERATION;
+        }
+        This->isCubemap = KTX_TRUE;
+    } else if (createInfo->numFaces != 1) {
+        /* numFaces must be either 1 or 6 */
+        return KTX_INVALID_VALUE;
+    }
+    This->numFaces = createInfo->numFaces;
+
+    /* Check number of mipmap levels */
+    if (createInfo->numLevels == 0)
+        return KTX_INVALID_VALUE;
+    This->numLevels = createInfo->numLevels;
+    This->generateMipmaps = createInfo->generateMipmaps;
+
+    if (createInfo->numLevels > 1) {
+        GLuint max_dim = MAX(MAX(createInfo->baseWidth, createInfo->baseHeight),
+                             createInfo->baseDepth);
+        if (max_dim < ((GLuint)1 << (This->numLevels - 1)))
+        {
+            /* Can't have more mip levels than 1 + log2(max(width, height, depth)) */
+            return KTX_INVALID_OPERATION;
+        }
+    }
+
+    ktxHashList_Construct(&This->kvDataHead);
+    return KTX_SUCCESS;
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Construct (initialize) the part of a ktxTexture base class that is
+ *        not related to the stream contents.
+ *
+ * @param[in] This pointer to a ktxTexture-sized block of memory to
+ *                 initialize.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ */
+KTX_error_code
+ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream,
+                               ktxTextureCreateFlags createFlags)
+{
+    ktxStream* stream;
+    UNUSED(createFlags); // Reference to keep compiler happy.
+
+    assert(This != NULL);
+    assert(pStream->data.mem != NULL);
+    assert(pStream->type == eStreamTypeFile
+           || pStream->type == eStreamTypeMemory
+           || pStream->type == eStreamTypeCustom);
+
+    This->_protected = (struct ktxTexture_protected *)
+                                malloc(sizeof(struct ktxTexture_protected));
+    stream = ktxTexture_getStream(This);
+    // Copy stream info into struct for later use.
+    *stream = *pStream;
+
+    This->orientation.x = KTX_ORIENT_X_RIGHT;
+    This->orientation.y = KTX_ORIENT_Y_DOWN;
+    This->orientation.z = KTX_ORIENT_Z_OUT;
+
+    return KTX_SUCCESS;
+}
+
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Free the memory associated with the texture contents
+ *
+ * @param[in] This pointer to the ktxTextureInt whose texture contents are
+ *                 to be freed.
+ */
+void
+ktxTexture_destruct(ktxTexture* This)
+{
+    ktxStream stream = *(ktxTexture_getStream(This));
+
+    if (stream.data.file != NULL)
+        stream.destruct(&stream);
+    if (This->kvDataHead != NULL)
+        ktxHashList_Destruct(&This->kvDataHead);
+    if (This->kvData != NULL)
+        free(This->kvData);
+    if (This->pData != NULL)
+        free(This->pData);
+    free(This->_protected);
+}
+
+
+/**
+ * @defgroup reader Reader
+ * @brief Read KTX-formatted data.
+ * @{
+ */
+
+typedef enum { KTX1, KTX2 } ktxFileType_;
+typedef union {
+    KTX_header ktx;
+    KTX_header2 ktx2;
+} ktxHeaderUnion_;
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Determine if stream data is KTX1 or KTX2.
+ *
+ * @param pStream   pointer to the ktxStream to examine.
+ * @param pFileType pointer to a ktxFileType enum where the type of the data
+ *                  will be written.
+ * @param pHeader   pointer to a ktxHeaderUnion where the header info. will be
+ *                  written.
+ */
+static KTX_error_code
+ktxDetermineFileType_(ktxStream* pStream, ktxFileType_* pFileType,
+                      ktxHeaderUnion_* pHeader)
+{
+    ktx_uint8_t ktx_ident_ref[12] = KTX_IDENTIFIER_REF;
+    ktx_uint8_t ktx2_ident_ref[12] = KTX2_IDENTIFIER_REF;
+    KTX_error_code result;
+
+    assert(pStream != NULL && pFileType != NULL);
+    assert(pStream->data.mem != NULL);
+    assert(pStream->type == eStreamTypeFile
+           || pStream->type == eStreamTypeMemory
+           || pStream->type == eStreamTypeCustom);
+
+    result = pStream->read(pStream, pHeader, sizeof(ktx2_ident_ref));
+    if (result == KTX_SUCCESS) {
+#if BIG_ENDIAN
+        // byte swap the heaader fields
+#endif
+        // Compare identifier, is this a KTX  or KTX2 file?
+        if (!memcmp(pHeader->ktx.identifier, ktx_ident_ref, 12)) {
+                *pFileType = KTX1;
+        } else if (!memcmp(pHeader->ktx2.identifier, ktx2_ident_ref, 12)) {
+                *pFileType = KTX2;
+        } else {
+                return KTX_UNKNOWN_FILE_FORMAT;
+        }
+        // Read rest of header.
+        if (*pFileType == KTX1) {
+            // Read rest of header.
+            result = pStream->read(pStream, &pHeader->ktx.endianness,
+                                  KTX_HEADER_SIZE - sizeof(ktx_ident_ref));
+        } else {
+           result = pStream->read(pStream, &pHeader->ktx2.vkFormat,
+                                 KTX2_HEADER_SIZE - sizeof(ktx2_ident_ref));
+        }
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Construct (initialize) a ktx1 or ktx2 texture according to the stream
+ *        data.
+ *
+ * @copydetails ktxTexture_CreateFromStdioStream
+ */
+KTX_error_code
+ktxTexture_CreateFromStream(ktxStream* pStream,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture** newTex)
+{
+    ktxHeaderUnion_ header;
+    ktxFileType_ fileType;
+    KTX_error_code result;
+    ktxTexture* tex;
+
+    result = ktxDetermineFileType_(pStream, &fileType, &header);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    if (fileType == KTX1) {
+        ktxTexture1* tex1 = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+        if (tex1 == NULL)
+            return KTX_OUT_OF_MEMORY;
+        memset(tex1, 0, sizeof(ktxTexture1));
+        result = ktxTexture1_constructFromStreamAndHeader(tex1, pStream,
+                                                          &header.ktx,
+                                                          createFlags);
+        tex = ktxTexture(tex1);
+    } else {
+        ktxTexture2* tex2 = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+        if (tex2 == NULL)
+            return KTX_OUT_OF_MEMORY;
+        memset(tex2, 0, sizeof(ktxTexture2));
+        result = ktxTexture2_constructFromStreamAndHeader(tex2, pStream,
+                                                          &header.ktx2,
+                                                          createFlags);
+        tex = ktxTexture(tex2);
+    }
+
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Create a ktxTexture1 or ktxTexture2 from a stdio stream according
+ *        to the stream data.
+ *
+ * @copydetails ktxTexture1_CreateFromStdioStream()
+ */
+KTX_error_code
+ktxTexture_CreateFromStdioStream(FILE* stdioStream,
+                                 ktxTextureCreateFlags createFlags,
+                                 ktxTexture** newTex)
+{
+    ktxStream stream;
+    KTX_error_code result;
+
+    if (stdioStream == NULL || newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
+    if (result == KTX_SUCCESS) {
+        result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Create a ktxTexture1 or ktxTexture2 from a named KTX file according
+ *        to the file contents.
+ *
+ * The address of a newly created ktxTexture reflecting the contents of the
+ * file is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] filename    pointer to a char array containing the file name.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+
+ * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
+ * @exception KTX_INVALID_VALUE @p filename is @c NULL.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture_CreateFromNamedFile(const char* const filename,
+                               ktxTextureCreateFlags createFlags,
+                               ktxTexture** newTex)
+{
+    KTX_error_code result;
+    ktxStream stream;
+    FILE* file;
+
+    if (filename == NULL || newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    file = fopen(filename, "rb");
+    if (!file)
+       return KTX_FILE_OPEN_FAILED;
+
+    result = ktxFileStream_construct(&stream, file, KTX_TRUE);
+    if (result == KTX_SUCCESS) {
+        result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Create a ktxTexture1 or ktxTexture2 from KTX-formatted data in memory
+ *        according to the data contents.
+ *
+ * The address of a newly created ktxTexture reflecting the contents of the
+ * serialized KTX data is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] bytes pointer to the memory containing the serialized KTX data.
+ * @param[in] size  length of the KTX data in bytes.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                            ktxTextureCreateFlags createFlags,
+                            ktxTexture** newTex)
+{
+    KTX_error_code result;
+    ktxStream stream;
+
+    if (bytes == NULL || newTex == NULL || size == 0)
+        return KTX_INVALID_VALUE;
+
+    result = ktxMemStream_construct_ro(&stream, bytes, size);
+    if (result == KTX_SUCCESS) {
+        result = ktxTexture_CreateFromStream(&stream, createFlags, newTex);
+    }
+    return result;}
+
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Return a pointer to the texture image data.
+ *
+ * @param[in] This pointer to the ktxTexture object of interest.
+ */
+ktx_uint8_t*
+ktxTexture_GetData(ktxTexture* This)
+{
+    return This->pData;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Return the total size of the texture image data in bytes.
+ *
+ * For a ktxTexture2 with supercompressionScheme != KTX_SS_NONE this will
+ * return the deflated size of the data.
+ *
+ * @param[in] This pointer to the ktxTexture object of interest.
+ */
+ktx_size_t
+ktxTexture_GetDataSize(ktxTexture* This)
+{
+    assert(This != NULL);
+    return This->dataSize;
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Return the size in bytes of an elements of a texture's
+ *        images.
+ *
+ * For uncompressed textures an element is one texel. For compressed
+ * textures it is one block.
+ *
+ * @param[in]     This     pointer to the ktxTexture object of interest.
+ */
+ktx_uint32_t
+ktxTexture_GetElementSize(ktxTexture* This)
+{
+    assert (This != NULL);
+
+    return (This->_protected->_formatSize.blockSizeInBits / 8);
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Calculate & return the size in bytes of an image at the specified
+ *        mip level.
+ *
+ * For arrays, this is the size of layer, for cubemaps, the size of a face
+ * and for 3D textures, the size of a depth slice.
+ *
+ * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
+ *
+ * @param[in]     This     pointer to the ktxTexture object of interest.
+ * @param[in]     level    level of interest.
+ * @param[in]     fv       enum specifying format version for which to calculate
+ *                         image size.
+ */
+ktx_size_t
+ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,
+                         ktxFormatVersionEnum fv)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    struct blockCount {
+        ktx_uint32_t x, y;
+    } blockCount;
+    ktx_uint32_t blockSizeInBytes;
+    ktx_uint32_t rowBytes;
+
+    assert (This != NULL);
+
+    float levelWidth  = (float)(This->baseWidth >> level);
+    float levelHeight = (float)(This->baseHeight >> level);
+    // Round up to next whole block. We can't use KTX_PADN because some of
+    // the block sizes are not powers of 2.
+    blockCount.x
+        = (ktx_uint32_t)ceilf(levelWidth / prtctd->_formatSize.blockWidth);
+    blockCount.y
+        = (ktx_uint32_t)ceilf(levelHeight / prtctd->_formatSize.blockHeight);
+    blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
+    blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);
+
+    blockSizeInBytes = prtctd->_formatSize.blockSizeInBits / 8;
+
+    if (prtctd->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT) {
+        assert(This->isCompressed);
+        return blockCount.x * blockCount.y * blockSizeInBytes;
+    } else {
+        assert(prtctd->_formatSize.blockWidth == 1U
+               && prtctd->_formatSize.blockHeight == 1U
+               && prtctd->_formatSize.blockDepth == 1U);
+        rowBytes = blockCount.x * blockSizeInBytes;
+        if (fv == KTX_FORMAT_VERSION_ONE)
+            (void)padRow(&rowBytes);
+        return rowBytes * blockCount.y;
+    }
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Iterate over the levels or faces in a ktxTexture object.
+ *
+ * Blocks of image data are passed to an application-supplied callback
+ * function. This is not a strict per-image iteration. Rather it reflects how
+ * OpenGL needs the images. For most textures the block of data includes all
+ * images of a mip level which implies all layers of an array. However, for
+ * non-array cube map textures the block is a single face of the mip level,
+ * i.e the callback is called once for each face.
+ *
+ * This function works even if @p This->pData == 0 so it can be used to
+ * obtain offsets and sizes for each level by callers who have loaded the data
+ * externally.
+ *
+ * @param[in]     This      pointer to the ktxTexture object of interest.
+ * @param[in,out] iterCb    the address of a callback function which is called
+ *                          with the data for each image block.
+ * @param[in,out] userdata  the address of application-specific data which is
+ *                          passed to the callback along with the image data.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The
+ *          following are returned directly by this function. @p iterCb may
+ *          return these for other causes or may return additional errors.
+ *
+ * @exception KTX_FILE_DATA_ERROR   Mip level sizes are increasing not
+ *                                  decreasing
+ * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL.
+ *
+ */
+KTX_error_code
+ktxTexture_IterateLevelFaces(ktxTexture* This, PFNKTXITERCB iterCb,
+                             void* userdata)
+{
+    ktx_uint32_t    miplevel;
+    KTX_error_code  result = KTX_SUCCESS;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (iterCb == NULL)
+        return KTX_INVALID_VALUE;
+
+    for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
+    {
+        ktx_uint32_t faceLodSize;
+        ktx_uint32_t face;
+        ktx_uint32_t innerIterations;
+        GLsizei      width, height, depth;
+
+        /* Array textures have the same number of layers at each mip level. */
+        width = MAX(1, This->baseWidth  >> miplevel);
+        height = MAX(1, This->baseHeight >> miplevel);
+        depth = MAX(1, This->baseDepth  >> miplevel);
+
+        faceLodSize = (ktx_uint32_t)ktxTexture_calcFaceLodSize(
+                                                    This, miplevel);
+
+        /* All array layers are passed in a group because that is how
+         * GL & Vulkan need them. Hence no
+         *    for (layer = 0; layer < This->numLayers)
+         */
+        if (This->isCubemap && !This->isArray)
+            innerIterations = This->numFaces;
+        else
+            innerIterations = 1;
+        for (face = 0; face < innerIterations; ++face)
+        {
+            /* And all z_slices are also passed as a group hence no
+             *    for (slice = 0; slice < This->depth)
+             */
+            ktx_size_t offset;
+
+            ktxTexture_GetImageOffset(This, miplevel, 0, face, &offset);
+            result = iterCb(miplevel, face,
+                            width, height, depth,
+                            faceLodSize, This->pData + offset, userdata);
+
+            if (result != KTX_SUCCESS)
+                break;
+        }
+    }
+
+    return result;
+}
+
+/**
+ * @internal
+ * @brief  Calculate and apply the padding needed to comply with
+ *         KTX_GL_UNPACK_ALIGNMENT.
+ *
+ * For uncompressed textures, KTX format specifies KTX_GL_UNPACK_ALIGNMENT = 4.
+ *
+ * @param[in,out] rowBytes    pointer to variable containing the packed no. of
+ *                            bytes in a row. The no. of bytes after padding
+ *                            is written into this location.
+ * @return the no. of bytes of padding.
+ */
+static ktx_uint32_t
+padRow(ktx_uint32_t* rowBytes)
+{
+    ktx_uint32_t rowPadding;
+
+    assert (rowBytes != NULL);
+
+    rowPadding = _KTX_PAD_UNPACK_ALIGN_LEN(*rowBytes);
+    *rowBytes += rowPadding;
+    return rowPadding;
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Calculate the size of an array layer at the specified mip level.
+ *
+ * The size of a layer is the size of an image * either the number of faces
+ * or the number of depth slices. This is the size of a layer as needed to
+ * find the offset within the array of images of a level and layer so the size
+ * reflects any @c cubePadding.
+ *
+ * @param[in]  This     pointer to the ktxTexture object of interest.
+ * @param[in] level     level whose layer size to return.
+ *
+ * @return the layer size in bytes.
+ */
+ktx_size_t
+ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,
+                    ktxFormatVersionEnum fv)
+{
+    /*
+     * As there are no 3D cubemaps, the image's z block count will always be
+     * 1 for cubemaps and numFaces will always be 1 for 3D textures so the
+     * multiply is safe. 3D cubemaps, if they existed, would require
+     * imageSize * (blockCount.z + This->numFaces);
+     */
+    DECLARE_PROTECTED(ktxTexture);
+    ktx_uint32_t blockCountZ;
+    ktx_size_t imageSize, layerSize;
+
+    assert (This != NULL);
+
+    blockCountZ = MAX(1, (This->baseDepth / prtctd->_formatSize.blockDepth)  >> level);
+    imageSize = ktxTexture_calcImageSize(This, level, fv);
+    layerSize = imageSize * blockCountZ;
+    if (fv == KTX_FORMAT_VERSION_ONE && KTX_GL_UNPACK_ALIGNMENT != 4) {
+        if (This->isCubemap && !This->isArray) {
+            /* cubePadding. NOTE: this adds padding after the last face too. */
+            layerSize += _KTX_PAD4(layerSize);
+        }
+    }
+    return layerSize * This->numFaces;
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Calculate the size of the specified mip level.
+ *
+ * The size of a level is the size of a layer * the number of layers.
+ *
+ * @param[in]  This     pointer to the ktxTexture object of interest.
+ * @param[in] level     level whose layer size to return.
+ *
+ * @return the level size in bytes.
+ */
+ktx_size_t
+ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level,
+                         ktxFormatVersionEnum fv)
+{
+    assert (This != NULL);
+    assert (level < This->numLevels);
+    return ktxTexture_layerSize(This, level, fv) * This->numLayers;
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Calculate the faceLodSize of the specified mip level.
+ *
+ * The faceLodSize of a level for most textures is the size of a level. For
+ * non-array cube map textures is the size of a face. This is the size that
+ * must be provided to OpenGL when uploading textures. Faces get uploaded 1
+ * at a time while all layers of an array or all slices of a 3D texture are
+ * uploaded together.
+ *
+ * @param[in]  This     pointer to the ktxTexture object of interest.
+ * @param[in] level     level whose layer size to return.
+ *
+ * @return the faceLodSize size in bytes.
+ */
+ktx_size_t
+ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level,
+                             ktxFormatVersionEnum fv)
+{
+    /*
+     * For non-array cubemaps this is the size of a face. For everything
+     * else it is the size of the level.
+     */
+    if (This->isCubemap && !This->isArray)
+        return ktxTexture_calcImageSize(This, level, fv);
+    else
+        return ktxTexture_calcLevelSize(This, level, fv);
+}
+
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Return the number of bytes needed to store all the image data for
+ *        a ktxTexture.
+ *
+ * The caclulated size does not include space for storing the @c imageSize
+ * fields of each mip level.
+ *
+ * @param[in]     This  pointer to the ktxTexture object of interest.
+ * @param[in]     fv    enum specifying format version for which to calculate
+ *                      image size.
+ *
+ * @return the data size in bytes.
+ */
+ktx_size_t
+ktxTexture_calcDataSizeTexture(ktxTexture* This)
+{
+    assert (This != NULL);
+    return ktxTexture_calcDataSizeLevels(This, This->numLevels);
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Get information about rows of an uncompresssed texture image at a
+ *        specified level.
+ *
+ * For an image at @p level of a ktxTexture provide the number of rows, the
+ * packed (unpadded) number of bytes in a row and the padding necessary to
+ * comply with KTX_GL_UNPACK_ALIGNMENT.
+ *
+ * @param[in]     This     pointer to the ktxTexture object of interest.
+ * @param[in]     level    level of interest.
+ * @param[in,out] numRows  pointer to location to store the number of rows.
+ * @param[in,out] pRowLengthBytes pointer to location to store number of bytes
+ *                                in a row.
+ * @param[in.out] pRowPadding pointer to location to store the number of bytes
+ *                            of padding.
+ */
+void
+ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level,
+                   ktx_uint32_t* numRows, ktx_uint32_t* pRowLengthBytes,
+                   ktx_uint32_t* pRowPadding)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    struct blockCount {
+        ktx_uint32_t x;
+    } blockCount;
+
+    assert (This != NULL);
+
+    assert(!This->isCompressed);
+    assert(prtctd->_formatSize.blockWidth == 1U
+           && prtctd->_formatSize.blockHeight == 1U
+           && prtctd->_formatSize.blockDepth == 1U);
+
+    blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth)  >> level);
+    *numRows = MAX(1, (This->baseHeight / prtctd->_formatSize.blockHeight)  >> level);
+
+    *pRowLengthBytes = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;
+    *pRowPadding = padRow(pRowLengthBytes);
+}
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ * @brief Return pitch betweeb rows of a texture image level in bytes.
+ *
+ * For uncompressed textures the pitch is the number of bytes between
+ * rows of texels. For compressed textures it is the number of bytes
+ * between rows of blocks. The value is padded to GL_UNPACK_ALIGNMENT,
+ * if necessary. For all currently known compressed formats padding
+ * will not be necessary.
+ *
+ * @param[in]     This     pointer to the ktxTexture object of interest.
+ * @param[in]     level    level of interest.
+ *
+ * @return  the row pitch in bytes.
+ */
+ ktx_uint32_t
+ ktxTexture_GetRowPitch(ktxTexture* This, ktx_uint32_t level)
+ {
+    DECLARE_PROTECTED(ktxTexture)
+    struct blockCount {
+        ktx_uint32_t x;
+    } blockCount;
+    ktx_uint32_t pitch;
+
+    blockCount.x = MAX(1, (This->baseWidth / prtctd->_formatSize.blockWidth)  >> level);
+    pitch = blockCount.x * prtctd->_formatSize.blockSizeInBits / 8;
+    (void)padRow(&pitch);
+
+    return pitch;
+ }
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Query if a ktxTexture has an active stream.
+ *
+ * Tests if a ktxTexture has unread image data. The internal stream is closed
+ * once all the images have been read.
+ *
+ * @param[in]     This     pointer to the ktxTexture object of interest.
+ *
+ * @return KTX_TRUE if there is an active stream, KTX_FALSE otherwise.
+ */
+ktx_bool_t
+ktxTexture_isActiveStream(ktxTexture* This)
+{
+    assert(This != NULL);
+    ktxStream* stream = ktxTexture_getStream(This);
+    return stream->data.file != NULL;
+}
+
+/** @} */
+
diff --git a/thirdparty/libktx/lib/texture.h b/thirdparty/libktx/lib/texture.h
new file mode 100644
index 00000000000..e415a09d6a4
--- /dev/null
+++ b/thirdparty/libktx/lib/texture.h
@@ -0,0 +1,107 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab textwidth=70: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture.h
+ * @~English
+ *
+ * @brief Declare internal ktxTexture functions for sharing between
+ *        compilation units.
+ *
+ * These functions are private and should not be used outside the library.
+ */
+
+#ifndef _TEXTURE_H_
+#define _TEXTURE_H_
+
+#include "ktx.h"
+#include "formatsize.h"
+
+#define DECLARE_PRIVATE(class) class ## _private* private = This->_private
+#define DECLARE_PROTECTED(class) class ## _protected* prtctd = This->_protected;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+    KTX_FORMAT_VERSION_ONE = 1,
+    KTX_FORMAT_VERSION_TWO = 2
+} ktxFormatVersionEnum;
+
+typedef ktx_size_t (* PFNCALCDATASIZELEVELS)(ktxTexture* This,
+                                             ktx_uint32_t levels);
+typedef ktx_size_t (* PFNCALCFACELODSIZE)(ktxTexture* This, ktx_uint32_t level);
+typedef ktx_size_t (* PFNCALCLEVELOFFSET)(ktxTexture* This, ktx_uint32_t level);
+typedef struct ktxTexture_vtblInt {
+    PFNCALCDATASIZELEVELS calcDataSizeLevels;
+    PFNCALCFACELODSIZE calcFaceLodSize;
+    PFNCALCLEVELOFFSET calcLevelOffset;
+} ktxTexture_vtblInt;
+
+#define ktxTexture_calcDataSizeLevels(This, levels) \
+            This->_protected->_vtbl.calcDataSizeLevels(This, levels);
+#define ktxTexture_calcFaceLodSize(This, level) \
+            This->_protected->_vtbl.calcFaceLodSize(This, level);
+#define ktxTexture_calcLevelOffset(This, level) \
+            This->_protected->_vtbl.calcLevelOffset(This, level);
+
+/**
+ * @memberof ktxTexture
+ * @~English
+ *
+ * @brief protected members of ktxTexture.
+ */
+typedef struct ktxTexture_protected {
+    ktxTexture_vtblInt _vtbl;
+    ktxFormatSize _formatSize;
+    ktx_uint32_t _typeSize;
+    ktxStream _stream;
+} ktxTexture_protected;
+
+#define ktxTexture_getStream(t) ((ktxStream*)(&(t)->_protected->_stream))
+#define ktxTexture1_getStream(t1) ktxTexture_getStream((ktxTexture*)t1)
+#define ktxTexture2_getStream(t2) ktxTexture_getStream((ktxTexture*)t2)
+
+KTX_error_code
+ktxTexture_iterateLoadedImages(ktxTexture* This, PFNKTXITERCB iterCb,
+                               void* userdata);
+KTX_error_code
+ktxTexture_iterateSourceImages(ktxTexture* This, PFNKTXITERCB iterCb,
+                               void* userdata);
+
+ktx_size_t ktxTexture_calcDataSizeTexture(ktxTexture* This);
+ktx_size_t ktxTexture_calcImageSize(ktxTexture* This, ktx_uint32_t level,
+                                    ktxFormatVersionEnum fv);
+ktx_bool_t ktxTexture_isActiveStream(ktxTexture* This);
+ktx_size_t ktxTexture_calcLevelSize(ktxTexture* This, ktx_uint32_t level,
+                                    ktxFormatVersionEnum fv);
+ktx_size_t ktxTexture_doCalcFaceLodSize(ktxTexture* This, ktx_uint32_t level,
+                                        ktxFormatVersionEnum fv);
+ktx_size_t ktxTexture_layerSize(ktxTexture* This, ktx_uint32_t level,
+                                ktxFormatVersionEnum fv);
+void ktxTexture_rowInfo(ktxTexture* This, ktx_uint32_t level,
+                        ktx_uint32_t* numRows, ktx_uint32_t* rowBytes,
+                        ktx_uint32_t* rowPadding);
+KTX_error_code
+ktxTexture_construct(ktxTexture* This, ktxTextureCreateInfo* createInfo,
+                     ktxFormatSize* formatSize);
+
+KTX_error_code
+ktxTexture_constructFromStream(ktxTexture* This, ktxStream* pStream,
+                               ktxTextureCreateFlags createFlags);
+
+void
+ktxTexture_destruct(ktxTexture* This);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TEXTURE_H_ */
diff --git a/thirdparty/libktx/lib/texture1.c b/thirdparty/libktx/lib/texture1.c
new file mode 100644
index 00000000000..8420f402b29
--- /dev/null
+++ b/thirdparty/libktx/lib/texture1.c
@@ -0,0 +1,1459 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture2.c
+ * @~English
+ *
+ * @brief ktxTexture1 implementation. Support for KTX format.
+ *
+ * @author Mark Callow, www.edgewise-consulting.com
+ */
+
+#if defined(_WIN32)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dfdutils/dfd.h"
+#include "ktx.h"
+#include "ktxint.h"
+#include "filestream.h"
+#include "memstream.h"
+#include "texture1.h"
+#include "unused.h"
+#include "gl_format.h"
+
+typedef struct ktxTexture1_private {
+   ktx_bool_t   _needSwap;
+} ktxTexture1_private;
+
+struct ktxTexture_vtbl ktxTexture1_vtbl;
+struct ktxTexture_vtblInt ktxTexture1_vtblInt;
+
+static KTX_error_code
+ktxTexture1_constructCommon(ktxTexture1* This)
+{
+    assert(This != NULL);
+
+    This->classId = ktxTexture1_c;
+    This->vtbl = &ktxTexture1_vtbl;
+    This->_protected->_vtbl = ktxTexture1_vtblInt;
+    This->_private = (ktxTexture1_private*)malloc(sizeof(ktxTexture1_private));
+    if (This->_private == NULL) {
+        return KTX_OUT_OF_MEMORY;
+    }
+	memset(This->_private, 0, sizeof(*This->_private));
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @copydoc ktxTexture2_construct
+ */
+static KTX_error_code
+ktxTexture1_construct(ktxTexture1* This, ktxTextureCreateInfo* createInfo,
+                      ktxTextureCreateStorageEnum storageAllocation)
+{
+    ktxTexture_protected* prtctd;
+    ktxFormatSize formatSize;
+    GLuint typeSize;
+    GLenum glFormat;
+    KTX_error_code result;
+
+	memset(This, 0, sizeof(*This));
+
+    This->glInternalformat = createInfo->glInternalformat;
+    glGetFormatSize(This->glInternalformat, &formatSize);
+    if (formatSize.blockSizeInBits == 0) {
+        // Most likely a deprecated legacy format.
+        return KTX_UNSUPPORTED_TEXTURE_TYPE;
+    }
+    glFormat= glGetFormatFromInternalFormat(createInfo->glInternalformat);
+    if (glFormat == GL_INVALID_VALUE) {
+            return KTX_INVALID_VALUE;
+    }
+    result =  ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    result = ktxTexture1_constructCommon(This);
+    if (result != KTX_SUCCESS)
+        return result;
+    prtctd = This->_protected;
+
+    This->isCompressed
+                    = (formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
+    if (This->isCompressed) {
+        This->glFormat = 0;
+        This->glBaseInternalformat = glFormat;
+        This->glType = 0;
+        prtctd->_typeSize = 1;
+    } else {
+        This->glBaseInternalformat = This->glFormat = glFormat;
+        This->glType
+                = glGetTypeFromInternalFormat(createInfo->glInternalformat);
+        if (This->glType == GL_INVALID_VALUE) {
+            result = KTX_INVALID_VALUE;
+            goto cleanup;
+        }
+        typeSize = glGetTypeSizeFromType(This->glType);
+        assert(typeSize != GL_INVALID_VALUE);
+
+        /* Do some sanity checking */
+        if (typeSize != 1 &&
+            typeSize != 2 &&
+            typeSize != 4)
+        {
+            /* Only 8, 16, and 32-bit types are supported for byte-swapping.
+             * See UNPACK_SWAP_BYTES & table 8.4 in the OpenGL 4.4 spec.
+             */
+            result = KTX_INVALID_VALUE;
+            goto cleanup;
+        }
+        prtctd->_typeSize = typeSize;
+    }
+
+    if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {
+        This->dataSize
+                    = ktxTexture_calcDataSizeTexture(ktxTexture(This));
+        This->pData = malloc(This->dataSize);
+        if (This->pData == NULL) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+    }
+    return result;
+
+cleanup:
+    ktxTexture1_destruct(This);
+    ktxTexture_destruct(ktxTexture(This));
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.
+ *
+ * The KTX header, that must have been read prior to calling this, is passed
+ * to the function.
+ *
+ * The stream object is copied into the constructed ktxTexture1.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] This pointer to a ktxTexture1-sized block of memory to
+ *                 initialize.
+ * @param[in] pStream pointer to the stream to read.
+ * @param[in] pHeader pointer to a KTX header that has already been read from
+ *            the stream.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Source data is inconsistent with the KTX
+ *                              specification.
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ * @exception KTX_FILE_UNEXPECTED_EOF
+ *                              Not enough data in the source.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or
+ *                              the key-value data.
+ * @exception KTX_UNKNOWN_FILE_FORMAT
+ *                              The source is not in KTX format.
+ * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
+ *                              The source describes a texture type not
+ *                              supported by OpenGL or Vulkan, e.g, a 3D array.
+ */
+KTX_error_code
+ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream,
+                                          KTX_header* pHeader,
+                                          ktxTextureCreateFlags createFlags)
+{
+    ktxTexture1_private* private;
+    KTX_error_code result;
+    KTX_supplemental_info suppInfo;
+    ktxStream* stream;
+    ktx_off_t pos;
+    ktx_size_t size;
+    ktxFormatSize formatSize;
+
+    assert(pHeader != NULL && pStream != NULL);
+
+	memset(This, 0, sizeof(*This));
+    result = ktxTexture_constructFromStream(ktxTexture(This), pStream, createFlags);
+    if (result != KTX_SUCCESS)
+        return result;
+    result = ktxTexture1_constructCommon(This);
+    if (result != KTX_SUCCESS) {
+        ktxTexture_destruct(ktxTexture(This));
+        return result;
+    }
+
+    private = This->_private;
+    stream = ktxTexture1_getStream(This);
+
+    result = ktxCheckHeader1_(pHeader, &suppInfo);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+
+    /*
+     * Initialize from pHeader info.
+     */
+    This->glFormat = pHeader->glFormat;
+    This->glInternalformat = pHeader->glInternalformat;
+    This->glType = pHeader->glType;
+    glGetFormatSize(This->glInternalformat, &formatSize);
+    if (formatSize.blockSizeInBits == 0) {
+        // Most likely a deprecated legacy format.
+        result = KTX_UNSUPPORTED_TEXTURE_TYPE;
+        goto cleanup;
+    }
+    This->_protected->_formatSize = formatSize;
+    This->glBaseInternalformat = pHeader->glBaseInternalformat;
+    // Can these be done by a ktxTexture_constructFromStream?
+    This->numDimensions = suppInfo.textureDimension;
+    This->baseWidth = pHeader->pixelWidth;
+    assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);
+    switch (suppInfo.textureDimension) {
+      case 1:
+        This->baseHeight = This->baseDepth = 1;
+        break;
+      case 2:
+        This->baseHeight = pHeader->pixelHeight;
+        This->baseDepth = 1;
+        break;
+      case 3:
+        This->baseHeight = pHeader->pixelHeight;
+        This->baseDepth = pHeader->pixelDepth;
+        break;
+    }
+    if (pHeader->numberOfArrayElements > 0) {
+        This->numLayers = pHeader->numberOfArrayElements;
+        This->isArray = KTX_TRUE;
+    } else {
+        This->numLayers = 1;
+        This->isArray = KTX_FALSE;
+    }
+    This->numFaces = pHeader->numberOfFaces;
+    if (pHeader->numberOfFaces == 6)
+        This->isCubemap = KTX_TRUE;
+    else
+        This->isCubemap = KTX_FALSE;
+    This->numLevels = pHeader->numberOfMipLevels;
+    This->isCompressed = suppInfo.compressed;
+    This->generateMipmaps = suppInfo.generateMipmaps;
+    if (pHeader->endianness == KTX_ENDIAN_REF_REV)
+        private->_needSwap = KTX_TRUE;
+    This->_protected->_typeSize = pHeader->glTypeSize;
+
+    /*
+     * Make an empty hash list.
+     */
+    ktxHashList_Construct(&This->kvDataHead);
+    /*
+     * Load KVData.
+     */
+    if (pHeader->bytesOfKeyValueData > 0) {
+        if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
+            ktx_uint32_t kvdLen = pHeader->bytesOfKeyValueData;
+            ktx_uint8_t* pKvd;
+
+            pKvd = malloc(kvdLen);
+            if (pKvd == NULL) {
+                result = KTX_OUT_OF_MEMORY;
+                goto cleanup;
+            }
+
+            result = stream->read(stream, pKvd, kvdLen);
+            if (result != KTX_SUCCESS)
+                goto cleanup;
+
+            if (private->_needSwap) {
+                /* Swap the counts inside the key & value data. */
+                ktx_uint8_t* src = pKvd;
+                ktx_uint8_t* end = pKvd + kvdLen;
+                while (src < end) {
+                    ktx_uint32_t* pKeyAndValueByteSize = (ktx_uint32_t*)src;
+                    _ktxSwapEndian32(pKeyAndValueByteSize, 1);
+                    src += _KTX_PAD4(*pKeyAndValueByteSize);
+                }
+            }
+
+            if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {
+                char* orientation;
+                ktx_uint32_t orientationLen;
+
+                result = ktxHashList_Deserialize(&This->kvDataHead,
+                                                 kvdLen, pKvd);
+                free(pKvd);
+                if (result != KTX_SUCCESS) {
+                    goto cleanup;
+                }
+
+                result = ktxHashList_FindValue(&This->kvDataHead,
+                                               KTX_ORIENTATION_KEY,
+                                               &orientationLen,
+                                               (void**)&orientation);
+                assert(result != KTX_INVALID_VALUE);
+                if (result == KTX_SUCCESS) {
+                    ktx_uint32_t count;
+                    char orient[4] = {0, 0, 0, 0};
+
+                    count = sscanf(orientation, KTX_ORIENTATION3_FMT,
+                                   &orient[0],
+                                   &orient[1],
+                                   &orient[2]);
+
+                    if (count > This->numDimensions) {
+                        // KTX 1 is less strict than KTX2 so there is a chance
+                        // of having more dimensions than needed.
+                        count = This->numDimensions;
+                    }
+                    switch (This->numDimensions) {
+                      case 3:
+                        This->orientation.z = orient[2];
+                        FALLTHROUGH;
+                      case 2:
+                        This->orientation.y = orient[1];
+                        FALLTHROUGH;
+                      case 1:
+                        This->orientation.x = orient[0];
+                    }
+                }
+            } else {
+                This->kvDataLen = kvdLen;
+                This->kvData = pKvd;
+            }
+        } else {
+            stream->skip(stream, pHeader->bytesOfKeyValueData);
+        }
+    }
+
+    /*
+     * Get the size of the image data.
+     */
+    result = stream->getsize(stream, &size);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+
+    result = stream->getpos(stream, &pos);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+
+                                /* Remove space for faceLodSize fields */
+    This->dataSize = size - pos - This->numLevels * sizeof(ktx_uint32_t);
+
+    /*
+     * Load the images, if requested.
+     */
+    if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {
+        result = ktxTexture1_LoadImageData(This, NULL, 0);
+    }
+    if (result == KTX_SUCCESS)
+        return result;
+
+cleanup:
+    ktxTexture1_destruct(This);
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @brief Construct a ktxTexture1 from a ktxStream reading from a KTX source.
+ *
+ * The stream object is copied into the constructed ktxTexture1.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] This pointer to a ktxTexture1-sized block of memory to
+ *            initialize.
+ * @param[in] pStream pointer to the stream to read.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return    KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ *
+ * For other exceptions see ktxTexture1_constructFromStreamAndHeader().
+ */
+static KTX_error_code
+ktxTexture1_constructFromStream(ktxTexture1* This, ktxStream* pStream,
+                                ktxTextureCreateFlags createFlags)
+{
+    KTX_header header;
+    KTX_error_code result;
+
+    // Read header.
+    result = pStream->read(pStream, &header, KTX_HEADER_SIZE);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    return ktxTexture1_constructFromStreamAndHeader(This, pStream,
+                                                    &header, createFlags);
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @brief Construct a ktxTexture1 from a stdio stream reading from a KTX source.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @note Do not close the stdio stream until you are finished with the texture
+ *       object.
+ *
+ * @param[in] This pointer to a ktxTextureInt-sized block of memory to
+ *                 initialize.
+ * @param[in] stdioStream a stdio FILE pointer opened on the source.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture1_constructFromStdioStream(ktxTexture1* This, FILE* stdioStream,
+                                     ktxTextureCreateFlags createFlags)
+{
+    ktxStream stream;
+    KTX_error_code result;
+
+    if (stdioStream == NULL || This == NULL)
+        return KTX_INVALID_VALUE;
+
+    result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture1_constructFromStream(This, &stream, createFlags);
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @brief Construct a ktxTexture1 from a named KTX file.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @param[in] This pointer to a ktxTextureInt-sized block of memory to
+ *                 initialize.
+ * @param[in] filename    pointer to a char array containing the file name.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
+ * @exception KTX_INVALID_VALUE @p filename is @c NULL.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture1_constructFromNamedFile(ktxTexture1* This,
+                                   const char* const filename,
+                                   ktxTextureCreateFlags createFlags)
+{
+    FILE* file;
+    ktxStream stream;
+    KTX_error_code result;
+
+    if (This == NULL || filename == NULL)
+        return KTX_INVALID_VALUE;
+
+    file = fopen(filename, "rb");
+    if (!file)
+       return KTX_FILE_OPEN_FAILED;
+
+    result = ktxFileStream_construct(&stream, file, KTX_TRUE);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture1_constructFromStream(This, &stream, createFlags);
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @brief Construct a ktxTexture1 from KTX-formatted data in memory.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @param[in] This  pointer to a ktxTextureInt-sized block of memory to
+ *                  initialize.
+ * @param[in] bytes pointer to the memory containing the serialized KTX data.
+ * @param[in] size  length of the KTX data in bytes.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture1_constructFromMemory(ktxTexture1* This,
+                                  const ktx_uint8_t* bytes, ktx_size_t size,
+                                  ktxTextureCreateFlags createFlags)
+{
+    ktxStream stream;
+    KTX_error_code result;
+
+    if (bytes == NULL || size == 0)
+        return KTX_INVALID_VALUE;
+
+    result = ktxMemStream_construct_ro(&stream, bytes, size);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture1_constructFromStream(This, &stream, createFlags);
+
+    return result;
+}
+
+void
+ktxTexture1_destruct(ktxTexture1* This)
+{
+    if (This->_private) free(This->_private);
+    ktxTexture_destruct(ktxTexture(This));
+}
+
+/**
+ * @defgroup reader Reader
+ * @brief Read KTX-formatted data.
+ * @{
+ */
+
+/**
+ * @memberof ktxTexture1
+ * @ingroup writer
+ * @brief Create a new empty ktxTexture1.
+ *
+ * The address of the newly created ktxTexture1 is written to the location
+ * pointed at by @p newTex.
+ *
+ * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
+ *                       information describing the texture.
+ * @param[in] storageAllocation
+ *                       enum indicating whether or not to allocate storage
+ *                       for the texture images.
+ * @param[in,out] newTex pointer to a location in which store the address of
+ *                       the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
+ *                              valid OpenGL internal format value.
+ * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
+ *                              or 3.
+ * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
+ *                              @p createInfo is 0.
+ * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
+ * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
+ * @exception KTX_INVALID_OPERATION
+ *                              The <tt>base{Width,Height,Depth}</tt> specified
+ *                              in @p createInfo are inconsistent with
+ *                              @c numDimensions.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a 3D array or
+ *                              3D cubemap texture.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a cubemap with
+ *                              non-square or non-2D images.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting more mip levels
+ *                              than needed for the specified
+ *                              <tt>base{Width,Height,Depth}</tt>.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.
+ */
+KTX_error_code
+ktxTexture1_Create(ktxTextureCreateInfo* createInfo,
+                  ktxTextureCreateStorageEnum storageAllocation,
+                  ktxTexture1** newTex)
+{
+    KTX_error_code result;
+
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture1_construct(tex, createInfo, storageAllocation);
+    if (result != KTX_SUCCESS) {
+        free(tex);
+    } else {
+        *newTex = tex;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Create a ktxTexture1 from a stdio stream reading from a KTX source.
+ *
+ * The address of a newly created ktxTexture1 reflecting the contents of the
+ * stdio stream is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] stdioStream stdio FILE pointer created from the desired file.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p newTex is @c NULL.
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Source data is inconsistent with the KTX
+ *                              specification.
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ * @exception KTX_FILE_UNEXPECTED_EOF
+ *                              Not enough data in the source.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,
+ *                              load the images or load the key-value data.
+ * @exception KTX_UNKNOWN_FILE_FORMAT
+ *                              The source is not in KTX format.
+ * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
+ *                              The source describes a texture type not
+ *                              supported by OpenGL or Vulkan, e.g, a 3D array.
+ */
+KTX_error_code
+ktxTexture1_CreateFromStdioStream(FILE* stdioStream,
+                                  ktxTextureCreateFlags createFlags,
+                                  ktxTexture1** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture1_constructFromStdioStream(tex, stdioStream,
+                                                  createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture1*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/*
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Create a ktxTexture1 from a named KTX file.
+ *
+ * The address of a newly created ktxTexture1 reflecting the contents of the
+ * file is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] filename    pointer to a char array containing the file name.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
+ * @exception KTX_INVALID_VALUE @p filename is @c NULL.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture1_CreateFromNamedFile(const char* const filename,
+                                ktxTextureCreateFlags createFlags,
+                                ktxTexture1** newTex)
+{
+    KTX_error_code result;
+
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture1_constructFromNamedFile(tex, filename, createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture1*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Create a ktxTexture1 from KTX-formatted data in memory.
+ *
+ * The address of a newly created ktxTexture1 reflecting the contents of the
+ * serialized KTX data is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] bytes pointer to the memory containing the serialized KTX data.
+ * @param[in] size  length of the KTX data in bytes.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture1_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture1** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture1_constructFromMemory(tex, bytes, size,
+                                             createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture1*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Create a ktxTexture1 from KTX-formatted data from a `ktxStream`.
+ *
+ * The address of a newly created ktxTexture1 reflecting the contents of the
+ * serialized KTX data is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture1 is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture1.
+ *
+ * @param[in] stream pointer to the stream to read KTX data from.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture1_CreateFromStream(ktxStream* stream,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture1** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture1* tex = (ktxTexture1*)malloc(sizeof(ktxTexture1));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture1_constructFromStream(tex, stream, createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture1*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Destroy a ktxTexture1 object.
+ *
+ * This frees the memory associated with the texture contents and the memory
+ * of the ktxTexture1 object. This does @e not delete any OpenGL or Vulkan
+ * texture objects created by ktxTexture1_GLUpload or ktxTexture1_VkUpload.
+ *
+ * @param[in] This pointer to the ktxTexture1 object to destroy
+ */
+void
+ktxTexture1_Destroy(ktxTexture1* This)
+{
+    ktxTexture1_destruct(This);
+    free(This);
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Calculate the size of the image data for the specified number
+ *        of levels.
+ *
+ * The data size is the sum of the sizes of each level up to the number
+ * specified and includes any @c mipPadding.
+ *
+ * @param[in] This     pointer to the ktxTexture object of interest.
+ * @param[in] levels   number of levels whose data size to return.
+ *
+ * @return the data size in bytes.
+ */
+ktx_size_t
+ktxTexture1_calcDataSizeLevels(ktxTexture1* This, ktx_uint32_t levels)
+{
+    ktx_uint32_t i;
+    ktx_size_t dataSize = 0;
+
+    assert(This != NULL);
+    assert(levels <= This->numLevels);
+    for (i = 0; i < levels; i++) {
+        ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
+                                                        KTX_FORMAT_VERSION_ONE);
+        /* mipPadding. NOTE: this adds padding after the last level too. */
+        #if KTX_GL_UNPACK_ALIGNMENT != 4
+            dataSize += _KTX_PAD4(levelSize);
+        #else
+            dataSize += levelSize;
+        #endif
+    }
+    return dataSize;
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @~English
+ *
+ * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize
+ */
+ktx_size_t
+ktxTexture1_calcFaceLodSize(ktxTexture1* This, ktx_uint32_t level)
+{
+    return ktxTexture_doCalcFaceLodSize(ktxTexture(This), level,
+                                        KTX_FORMAT_VERSION_ONE);
+}
+
+/**
+ * @memberof ktxTexture @private
+ * @~English
+ * @brief Return the offset of a level in bytes from the start of the image
+ *        data in a ktxTexture.
+ *
+ * The caclulated size does not include space for storing the @c imageSize
+ * fields of each mip level.
+ *
+ * @param[in]     This  pointer to the ktxTexture object of interest.
+ * @param[in]     level level whose offset to return.
+ * @param[in]     fv    enum specifying format version for which to calculate
+ *                      image size.
+ *
+ * @return the data size in bytes.
+ */
+ktx_size_t
+ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level)
+{
+    assert (This != NULL);
+    assert (level < This->numLevels);
+    return ktxTexture1_calcDataSizeLevels(This, level);
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Find the offset of an image within a ktxTexture's image data.
+ *
+ * As there is no such thing as a 3D cubemap we make the 3rd location parameter
+ * do double duty.
+ *
+ * @param[in]     This      pointer to the ktxTexture object of interest.
+ * @param[in]     level     mip level of the image.
+ * @param[in]     layer     array layer of the image.
+ * @param[in]     faceSlice cube map face or depth slice of the image.
+ * @param[in,out] pOffset   pointer to location to store the offset.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_OPERATION
+ *                         @p level, @p layer or @p faceSlice exceed the
+ *                         dimensions of the texture.
+ * @exception KTX_INVALID_VALID @p This is NULL.
+ */
+KTX_error_code
+ktxTexture1_GetImageOffset(ktxTexture1* This, ktx_uint32_t level,
+                          ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                          ktx_size_t* pOffset)
+{
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (level >= This->numLevels || layer >= This->numLayers)
+        return KTX_INVALID_OPERATION;
+
+    if (This->isCubemap) {
+        if (faceSlice >= This->numFaces)
+            return KTX_INVALID_OPERATION;
+    } else {
+        ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);
+        if (faceSlice >= maxSlice)
+            return KTX_INVALID_OPERATION;
+    }
+
+    // Get the size of the data up to the start of the indexed level.
+    *pOffset = ktxTexture_calcDataSizeLevels(ktxTexture(This), level);
+
+    // All layers, faces & slices within a level are the same size.
+    if (layer != 0) {
+        ktx_size_t layerSize;
+        layerSize = ktxTexture_layerSize(ktxTexture(This), level,
+                                                    KTX_FORMAT_VERSION_ONE);
+        *pOffset += layer * layerSize;
+    }
+    if (faceSlice != 0) {
+        ktx_size_t imageSize;
+        imageSize = ktxTexture_GetImageSize(ktxTexture(This), level);
+#if (KTX_GL_UNPACK_ALIGNMENT != 4)
+        if (This->isCubemap)
+            _KTX_PAD4(imageSize); // Account for cubePadding.
+#endif
+        *pOffset += faceSlice * imageSize;
+    }
+
+    return KTX_SUCCESS;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Return the total size in bytes of the uncompressed data of a ktxTexture1.
+ *
+ * This always returns the value of @c This->dataSize. The function is provided for
+ * symmetry with ktxTexture2.
+ *
+ * @param[in]     This     pointer to the ktxTexture1 object of interest.
+ * @return    The size of the data in the texture.
+ */
+ktx_size_t
+ktxTexture1_GetDataSizeUncompressed(ktxTexture1* This)
+{
+    return This->dataSize;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Calculate & return the size in bytes of an image at the specified
+ *        mip level.
+ *
+ * For arrays, this is the size of layer, for cubemaps, the size of a face
+ * and for 3D textures, the size of a depth slice.
+ *
+ * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
+ *
+ * @param[in]     This     pointer to the ktxTexture1 object of interest.
+ * @param[in]     level    level of interest.
+ */
+ktx_size_t
+ktxTexture1_GetImageSize(ktxTexture1* This, ktx_uint32_t level)
+{
+    return ktxTexture_calcImageSize(ktxTexture(This), level,
+                                    KTX_FORMAT_VERSION_ONE);
+}
+
+/**
+ * @memberof ktxTexture1 @private
+ * @~English
+ * @brief Return the size of the primitive type of a single color component
+ *
+ * @param[in]     This       pointer to the ktxTexture1 object of interest.
+ *
+ * @return the type size in bytes.
+ */
+ktx_uint32_t
+ktxTexture1_glTypeSize(ktxTexture1* This)
+{
+    assert(This != NULL);
+    return This->_protected->_typeSize;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Iterate over the mip levels in a ktxTexture1 object.
+ *
+ * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is
+ * that the blocks of image data for non-array cube maps include all faces of
+ * a mip level.
+ *
+ * This function works even if @p This->pData == 0 so it can be used to
+ * obtain offsets and sizes for each level by callers who have loaded the data
+ * externally.
+ *
+ * @param[in]     This     handle of the 1 opened on the data.
+ * @param[in,out] iterCb   the address of a callback function which is called
+ *                         with the data for each image block.
+ * @param[in,out] userdata the address of application-specific data which is
+ *                         passed to the callback along with the image data.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The
+ *          following are returned directly by this function. @p iterCb may
+ *          return these for other causes or may return additional errors.
+ *
+ * @exception KTX_FILE_DATA_ERROR   Mip level sizes are increasing not
+ *                                  decreasing
+ * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL.
+ *
+ */
+KTX_error_code
+ktxTexture1_IterateLevels(ktxTexture1* This, PFNKTXITERCB iterCb, void* userdata)
+{
+    ktx_uint32_t    miplevel;
+    KTX_error_code  result = KTX_SUCCESS;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (iterCb == NULL)
+        return KTX_INVALID_VALUE;
+
+    for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
+    {
+        GLsizei width, height, depth;
+        ktx_uint32_t levelSize;
+        ktx_size_t offset;
+
+        /* Array textures have the same number of layers at each mip level. */
+        width = MAX(1, This->baseWidth  >> miplevel);
+        height = MAX(1, This->baseHeight >> miplevel);
+        depth = MAX(1, This->baseDepth  >> miplevel);
+
+        levelSize = (ktx_uint32_t)ktxTexture_calcLevelSize(ktxTexture(This),
+                                                       miplevel,
+                                                       KTX_FORMAT_VERSION_ONE);
+
+        /* All array layers are passed in a group because that is how
+         * GL & Vulkan need them. Hence no
+         *    for (layer = 0; layer < This->numLayers)
+         */
+        ktxTexture_GetImageOffset(ktxTexture(This), miplevel, 0, 0, &offset);
+        result = iterCb(miplevel, 0, width, height, depth,
+                         levelSize, This->pData + offset, userdata);
+        if (result != KTX_SUCCESS)
+            break;
+    }
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Iterate over the images in a ktxTexture1 object while loading the
+ *        image data.
+ *
+ * This operates similarly to ktxTexture_IterateLevelFaces() except that it
+ * loads the images from the ktxTexture1's source to a temporary buffer
+ * while iterating. The callback function must copy the image data if it
+ * wishes to preserve it as the temporary buffer is reused for each level and
+ * is freed when this function exits.
+ *
+ * This function is helpful for reducing memory usage when uploading the data
+ * to a graphics API.
+ *
+ * @param[in]     This     pointer to the ktxTexture1 object of interest.
+ * @param[in,out] iterCb   the address of a callback function which is called
+ *                         with the data for each image.
+ * @param[in,out] userdata the address of application-specific data which is
+ *                         passed to the callback along with the image data.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The
+ *          following are returned directly by this function. @p iterCb may
+ *          return these for other causes or may return additional errors.
+ *
+ * @exception KTX_FILE_DATA_ERROR   mip level sizes are increasing not
+ *                                  decreasing
+ * @exception KTX_INVALID_OPERATION the ktxTexture1 was not created from a
+ *                                  stream, i.e there is no data to load, or
+ *                                  this ktxTexture1's images have already
+ *                                  been loaded.
+ * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL.
+ * @exception KTX_OUT_OF_MEMORY     not enough memory to allocate a block to
+ *                                  hold the base level image.
+ */
+KTX_error_code
+ktxTexture1_IterateLoadLevelFaces(ktxTexture1* This, PFNKTXITERCB iterCb,
+                                  void* userdata)
+{
+    DECLARE_PRIVATE(ktxTexture1);
+    struct ktxTexture_protected* prtctd = This->_protected;
+    ktxStream* stream = (ktxStream *)&prtctd->_stream;
+    ktx_uint32_t    dataSize = 0;
+    ktx_uint32_t    miplevel;
+    KTX_error_code  result = KTX_SUCCESS;
+    void*           data = NULL;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (This->classId != ktxTexture1_c)
+        return KTX_INVALID_OPERATION;
+
+    if (iterCb == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (prtctd->_stream.data.file == NULL)
+        // This Texture not created from a stream or images are already loaded.
+        return KTX_INVALID_OPERATION;
+
+    for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
+    {
+        ktx_uint32_t faceLodSize;
+        ktx_uint32_t faceLodSizePadded;
+        ktx_uint32_t face;
+        ktx_uint32_t innerIterations;
+        GLsizei      width, height, depth;
+
+        /* Array textures have the same number of layers at each mip level. */
+        width = MAX(1, This->baseWidth  >> miplevel);
+        height = MAX(1, This->baseHeight >> miplevel);
+        depth = MAX(1, This->baseDepth  >> miplevel);
+
+        result = stream->read(stream, &faceLodSize, sizeof(ktx_uint32_t));
+        if (result != KTX_SUCCESS) {
+            goto cleanup;
+        }
+        if (private->_needSwap) {
+            _ktxSwapEndian32(&faceLodSize, 1);
+        }
+#if (KTX_GL_UNPACK_ALIGNMENT != 4)
+        faceLodSizePadded = _KTX_PAD4(faceLodSize);
+#else
+        faceLodSizePadded = faceLodSize;
+#endif
+        if (!data) {
+            /* allocate memory sufficient for the base miplevel */
+            data = malloc(faceLodSizePadded);
+            if (!data) {
+                result = KTX_OUT_OF_MEMORY;
+                goto cleanup;
+            }
+            dataSize = faceLodSizePadded;
+        }
+        else if (dataSize < faceLodSizePadded) {
+            /* subsequent miplevels cannot be larger than the base miplevel */
+            result = KTX_FILE_DATA_ERROR;
+            goto cleanup;
+        }
+
+        /* All array layers are passed in a group because that is how
+         * GL & Vulkan need them. Hence no
+         *    for (layer = 0; layer < This->numLayers)
+         */
+        if (This->isCubemap && !This->isArray)
+            innerIterations = This->numFaces;
+        else
+            innerIterations = 1;
+        for (face = 0; face < innerIterations; ++face)
+        {
+            /* And all z_slices are also passed as a group hence no
+             *    for (z_slice = 0; z_slice < This->depth)
+             */
+            result = stream->read(stream, data, faceLodSizePadded);
+            if (result != KTX_SUCCESS) {
+                goto cleanup;
+            }
+
+            /* Perform endianness conversion on texture data */
+            if (private->_needSwap) {
+                if (prtctd->_typeSize == 2)
+                    _ktxSwapEndian16((ktx_uint16_t*)data, faceLodSize / 2);
+                else if (prtctd->_typeSize == 4)
+                    _ktxSwapEndian32((ktx_uint32_t*)data, faceLodSize / 4);
+            }
+
+            result = iterCb(miplevel, face,
+                             width, height, depth,
+                             faceLodSize, data, userdata);
+        }
+    }
+
+cleanup:
+    free(data);
+    // No further need for this.
+    stream->destruct(stream);
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture1
+ * @~English
+ * @brief Load all the image data from the ktxTexture1's source.
+ *
+ * The data is loaded into the provided buffer or to an internally allocated
+ * buffer, if @p pBuffer is @c NULL.
+ *
+ * @param[in] This pointer to the ktxTexture object of interest.
+ * @param[in] pBuffer pointer to the buffer in which to load the image data.
+ * @param[in] bufSize size of the buffer pointed at by @p pBuffer.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p This is NULL.
+ * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.
+ * @exception KTX_INVALID_OPERATION
+ *                              The data has already been loaded or the
+ *                              ktxTexture was not created from a KTX source.
+ * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.
+ */
+KTX_error_code
+ktxTexture1_LoadImageData(ktxTexture1* This,
+                          ktx_uint8_t* pBuffer, ktx_size_t bufSize)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    DECLARE_PRIVATE(ktxTexture1);
+    ktx_uint32_t    miplevel;
+    ktx_uint8_t*    pDest;
+    KTX_error_code  result = KTX_SUCCESS;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (prtctd->_stream.data.file == NULL)
+        // This Texture not created from a stream or images already loaded;
+        return KTX_INVALID_OPERATION;
+
+    if (pBuffer == NULL) {
+        This->pData = malloc(This->dataSize);
+        if (This->pData == NULL)
+            return KTX_OUT_OF_MEMORY;
+        pDest = This->pData;
+    } else if (bufSize < This->dataSize) {
+        return KTX_INVALID_VALUE;
+    } else {
+        pDest = pBuffer;
+    }
+
+    // Need to loop through for correct byte swapping
+    for (miplevel = 0; miplevel < This->numLevels; ++miplevel)
+    {
+        ktx_uint32_t faceLodSize;
+        ktx_uint32_t faceLodSizePadded;
+        ktx_uint32_t face;
+        ktx_uint32_t innerIterations;
+
+        result = prtctd->_stream.read(&prtctd->_stream, &faceLodSize,
+                                      sizeof(ktx_uint32_t));
+        if (result != KTX_SUCCESS) {
+            goto cleanup;
+        }
+        if (private->_needSwap) {
+            _ktxSwapEndian32(&faceLodSize, 1);
+        }
+#if (KTX_GL_UNPACK_ALIGNMENT != 4)
+        faceLodSizePadded = _KTX_PAD4(faceLodSize);
+#else
+        faceLodSizePadded = faceLodSize;
+#endif
+
+        if (This->isCubemap && !This->isArray)
+            innerIterations = This->numFaces;
+        else
+            innerIterations = 1;
+        for (face = 0; face < innerIterations; ++face)
+        {
+            result = prtctd->_stream.read(&prtctd->_stream, pDest,
+                                          faceLodSizePadded);
+            if (result != KTX_SUCCESS) {
+                goto cleanup;
+            }
+
+            /* Perform endianness conversion on texture data */
+            if (private->_needSwap) {
+                if (prtctd->_typeSize == 2)
+                    _ktxSwapEndian16((ktx_uint16_t*)pDest, faceLodSize / 2);
+                else if (prtctd->_typeSize == 4)
+                    _ktxSwapEndian32((ktx_uint32_t*)pDest, faceLodSize / 4);
+            }
+
+            pDest += faceLodSizePadded;
+        }
+    }
+
+cleanup:
+    // No further need for This->
+    prtctd->_stream.destruct(&prtctd->_stream);
+    return result;
+}
+
+ktx_bool_t
+ktxTexture1_NeedsTranscoding(ktxTexture1* This)
+{
+    UNUSED(This);
+    return KTX_FALSE;
+}
+
+#if !KTX_FEATURE_WRITE
+
+/*
+ * Stubs for writer functions that return a proper error code
+ */
+
+KTX_error_code
+ktxTexture1_SetImageFromMemory(ktxTexture1* This, ktx_uint32_t level,
+                               ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                               const ktx_uint8_t* src, ktx_size_t srcSize)
+{
+    UNUSED(This);
+    UNUSED(level);
+    UNUSED(layer);
+    UNUSED(faceSlice);
+    UNUSED(src);
+    UNUSED(srcSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture1_SetImageFromStdioStream(ktxTexture1* This, ktx_uint32_t level,
+                                    ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                                    FILE* src, ktx_size_t srcSize)
+{
+    UNUSED(This);
+    UNUSED(level);
+    UNUSED(layer);
+    UNUSED(faceSlice);
+    UNUSED(src);
+    UNUSED(srcSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture1_WriteToStdioStream(ktxTexture1* This, FILE* dstsstr)
+{
+    UNUSED(This);
+    UNUSED(dstsstr);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture1_WriteToNamedFile(ktxTexture1* This, const char* const dstname)
+{
+    UNUSED(This);
+    UNUSED(dstname);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture1_WriteToMemory(ktxTexture1* This,
+                          ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
+{
+    UNUSED(This);
+    UNUSED(ppDstBytes);
+    UNUSED(pSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture1_WriteToStream(ktxTexture1* This,
+                          ktxStream* dststr)
+{
+    UNUSED(This);
+    UNUSED(dststr);
+    return KTX_INVALID_OPERATION;
+}
+
+#endif
+
+/*
+ * Initialized here at the end to avoid the need for multiple declarations of
+ * these functions.
+ */
+
+struct ktxTexture_vtblInt ktxTexture1_vtblInt = {
+    (PFNCALCDATASIZELEVELS)ktxTexture1_calcDataSizeLevels,
+    (PFNCALCFACELODSIZE)ktxTexture1_calcFaceLodSize,
+    (PFNCALCLEVELOFFSET)ktxTexture1_calcLevelOffset
+};
+
+struct ktxTexture_vtbl ktxTexture1_vtbl = {
+    (PFNKTEXDESTROY)ktxTexture1_Destroy,
+    (PFNKTEXGETIMAGEOFFSET)ktxTexture1_GetImageOffset,
+    (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture1_GetDataSizeUncompressed,
+    (PFNKTEXGETIMAGESIZE)ktxTexture1_GetImageSize,
+    (PFNKTEXITERATELEVELS)ktxTexture1_IterateLevels,
+    (PFNKTEXITERATELOADLEVELFACES)ktxTexture1_IterateLoadLevelFaces,
+    (PFNKTEXNEEDSTRANSCODING)ktxTexture1_NeedsTranscoding,
+    (PFNKTEXLOADIMAGEDATA)ktxTexture1_LoadImageData,
+    (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture1_SetImageFromMemory,
+    (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture1_SetImageFromStdioStream,
+    (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture1_WriteToStdioStream,
+    (PFNKTEXWRITETONAMEDFILE)ktxTexture1_WriteToNamedFile,
+    (PFNKTEXWRITETOMEMORY)ktxTexture1_WriteToMemory,
+    (PFNKTEXWRITETOSTREAM)ktxTexture1_WriteToStream,
+};
+
+/** @} */
+
diff --git a/thirdparty/libktx/lib/texture1.h b/thirdparty/libktx/lib/texture1.h
new file mode 100644
index 00000000000..95734cc5f74
--- /dev/null
+++ b/thirdparty/libktx/lib/texture1.h
@@ -0,0 +1,46 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab textwidth=70: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture1.h
+ * @~English
+ *
+ * @brief Declare internal ktxTexture1 functions for sharing between
+ *        compilation units.
+ *
+ * These functions are private and should not be used outside the library.
+ */
+
+#ifndef _TEXTURE1_H_
+#define _TEXTURE1_H_
+
+#include "texture.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CLASS ktxTexture1
+#include "texture_funcs.inl"
+#undef CLASS
+
+KTX_error_code
+ktxTexture1_constructFromStreamAndHeader(ktxTexture1* This, ktxStream* pStream,
+                                         KTX_header* pHeader,
+                                         ktxTextureCreateFlags createFlags);
+
+ktx_uint64_t ktxTexture1_calcDataSizeTexture(ktxTexture1* This);
+ktx_size_t ktxTexture1_calcLevelOffset(ktxTexture1* This, ktx_uint32_t level);
+ktx_uint32_t ktxTexture1_glTypeSize(ktxTexture1* This);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TEXTURE1_H_ */
diff --git a/thirdparty/libktx/lib/texture2.c b/thirdparty/libktx/lib/texture2.c
new file mode 100644
index 00000000000..afbe7dcbfee
--- /dev/null
+++ b/thirdparty/libktx/lib/texture2.c
@@ -0,0 +1,2524 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture2.c
+ * @~English
+ *
+ * @brief ktxTexture2 implementation. Support for KTX2 format.
+ *
+ * @author Mark Callow, www.edgewise-consulting.com
+ */
+
+#if defined(_WIN32)
+#define _CRT_SECURE_NO_WARNINGS
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <zstd.h>
+#include <zstd_errors.h>
+#include <KHR/khr_df.h>
+
+#include "dfdutils/dfd.h"
+#include "ktx.h"
+#include "ktxint.h"
+#include "filestream.h"
+#include "memstream.h"
+#include "texture2.h"
+#include "unused.h"
+#include "vk_format.h"
+
+// FIXME: Test this #define and put it in a header somewhere.
+//#define IS_BIG_ENDIAN (1 == *(unsigned char *)&(const int){0x01000000ul})
+#define IS_BIG_ENDIAN 0
+
+struct ktxTexture_vtbl ktxTexture2_vtbl;
+struct ktxTexture_vtblInt ktxTexture2_vtblInt;
+
+#if !defined(BITFIELD_ORDER_FROM_MSB)
+// Most compilers, including all those tested so far, including clang, gcc
+// and msvc, order bitfields from the lsb so these struct declarations work.
+// Could this be because I've only tested on little-endian machines?
+// These are preferred as they are much easier to manually initialize
+// and verify.
+struct sampleType {
+    uint32_t bitOffset: 16;
+    uint32_t bitLength: 8;
+    uint32_t channelType: 8; // Includes qualifiers
+    uint32_t samplePosition0: 8;
+    uint32_t samplePosition1: 8;
+    uint32_t samplePosition2: 8;
+    uint32_t samplePosition3: 8;
+    uint32_t lower;
+    uint32_t upper;
+};
+
+struct BDFD {
+    uint32_t vendorId: 17;
+    uint32_t descriptorType: 15;
+    uint32_t versionNumber: 16;
+    uint32_t descriptorBlockSize: 16;
+    uint32_t model: 8;
+    uint32_t primaries: 8;
+    uint32_t transfer: 8;
+    uint32_t flags: 8;
+    uint32_t texelBlockDimension0: 8;
+    uint32_t texelBlockDimension1: 8;
+    uint32_t texelBlockDimension2: 8;
+    uint32_t texelBlockDimension3: 8;
+    uint32_t bytesPlane0: 8;
+    uint32_t bytesPlane1: 8;
+    uint32_t bytesPlane2: 8;
+    uint32_t bytesPlane3: 8;
+    uint32_t bytesPlane4: 8;
+    uint32_t bytesPlane5: 8;
+    uint32_t bytesPlane6: 8;
+    uint32_t bytesPlane7: 8;
+    struct sampleType samples[6];
+};
+
+struct BDFD e5b9g9r9_ufloat_comparator = {
+    .vendorId = 0,
+    .descriptorType = 0,
+    .versionNumber = 2,
+    .descriptorBlockSize = sizeof(struct BDFD),
+    .model = KHR_DF_MODEL_RGBSDA,
+    .primaries = KHR_DF_PRIMARIES_BT709,
+    .transfer = KHR_DF_TRANSFER_LINEAR,
+    .flags = KHR_DF_FLAG_ALPHA_STRAIGHT,
+    .texelBlockDimension0 = 0,
+    .texelBlockDimension1 = 0,
+    .texelBlockDimension2 = 0,
+    .texelBlockDimension3 = 0,
+    .bytesPlane0 = 4,
+    .bytesPlane1 = 0,
+    .bytesPlane2 = 0,
+    .bytesPlane3 = 0,
+    .bytesPlane4 = 0,
+    .bytesPlane5 = 0,
+    .bytesPlane6 = 0,
+    .bytesPlane7 = 0,
+    // gcc likes this way. It does not like, e.g.,
+    // .samples[0].bitOffset = 0, etc. which is accepted by both clang & msvc.
+    // I find the standards docs impenetrable so I don't know which is correct.
+    .samples[0] = {
+        .bitOffset = 0,
+        .bitLength = 8,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_RED,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 0,
+        .upper = 8448,
+    },
+    .samples[1] = {
+        .bitOffset = 27,
+        .bitLength = 4,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 15,
+        .upper = 31,
+    },
+    .samples[2] = {
+        .bitOffset = 9,
+        .bitLength = 8,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 0,
+        .upper = 8448,
+    },
+    .samples[3] = {
+        .bitOffset = 27,
+        .bitLength = 4,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 15,
+        .upper = 31,
+    },
+    .samples[4] = {
+        .bitOffset = 18,
+        .bitLength = 8,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 0,
+        .upper = 8448,
+    },
+    .samples[5] = {
+        .bitOffset = 27,
+        .bitLength = 4,
+        .channelType = KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT,
+        .samplePosition0 = 0,
+        .samplePosition1 = 0,
+        .samplePosition2 = 0,
+        .samplePosition3 = 0,
+        .lower = 15,
+        .upper = 31,
+    }
+};
+#else
+// For compilers which order bitfields from the msb rather than lsb.
+#define shift(x,val) ((val) << KHR_DF_SHIFT_ ## x)
+#define sampleshift(x,val) ((val) << KHR_DF_SAMPLESHIFT_ ## x)
+#define e5b9g9r9_bdbwordcount KHR_DFDSIZEWORDS(6)
+ktx_uint32_t e5b9g9r9_ufloat_comparator[e5b9g9r9_bdbwordcount] = {
+    0,    // descriptorType & vendorId
+    shift(DESCRIPTORBLOCKSIZE, e5b9g9r9_bdbwordcount * sizeof(ktx_uint32_t)) | shift(VERSIONNUMBER, 2),
+    // N.B. Allow various values of primaries, transfer & flags
+    shift(FLAGS, KHR_DF_FLAG_ALPHA_STRAIGHT) | shift(TRANSFER, KHR_DF_TRANSFER_LINEAR) | shift(PRIMARIES, KHR_DF_PRIMARIES_BT709) | shift(MODEL, KHR_DF_MODEL_RGBSDA),
+    0,    // texelBlockDimension3~0
+    shift(BYTESPLANE0, 4),  // All other bytesPlane fields are 0.
+    0,    // bytesPlane7~4
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 0),
+    0,    // samplePosition3~0
+    0,    // sampleLower
+    8448, // sampleUpper
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_RED | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
+    0,    // samplePosition3~0
+    15,   // sampleLower
+    31,   // sampleUpper
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 9),
+    0,    // samplePosition3~0
+    0,    // sampleLower
+    8448, // sampleUpper
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_GREEN | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
+    0,    // samplePosition3~0
+    15,   // sampleLower
+    31,   // sampleUpper
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE) | sampleshift(BITLENGTH, 8) | sampleshift(BITOFFSET, 18),
+    0,    // samplePosition3~0
+    0,    // sampleLower
+    8448, // sampleUpper
+    sampleshift(CHANNELID, KHR_DF_CHANNEL_RGBSDA_BLUE | KHR_DF_SAMPLE_DATATYPE_EXPONENT) | sampleshift(BITLENGTH, 4) | sampleshift(BITOFFSET, 27),
+    0,    // samplePosition3~0
+    15,   // sampleLower
+    31,   // sampleUpper
+};
+#endif
+
+/**
+* @private
+* @~English
+* @brief Initialize a ktxFormatSize object from the info in a DFD.
+*
+* This is used instead of referring to the DFD directly so code dealing
+* with format info can be common to KTX 1 & 2.
+*
+* @param[in] This   pointer the ktxTexture2 whose DFD to use.
+* @param[in] fi       pointer to the ktxFormatSize object to initialize.
+*
+* @return    KTX_TRUE on success, otherwise KTX_FALSE.
+*/
+bool
+ktxFormatSize_initFromDfd(ktxFormatSize* This, ktx_uint32_t* pDfd)
+{
+    uint32_t* pBdb = pDfd + 1;
+
+    // Check the DFD is of the expected type and version.
+    if (*pBdb != 0) {
+        // Either decriptorType or vendorId is not 0
+        return false;
+    }
+    if (KHR_DFDVAL(pBdb, VERSIONNUMBER) != KHR_DF_VERSIONNUMBER_1_3) {
+        return false;
+    }
+
+    // DFD has supported type and version. Process it.
+    This->blockWidth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION0) + 1;
+    This->blockHeight = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION1) + 1;
+    This->blockDepth = KHR_DFDVAL(pBdb, TEXELBLOCKDIMENSION2) + 1;
+    This->blockSizeInBits = KHR_DFDVAL(pBdb, BYTESPLANE0) * 8;
+    This->paletteSizeInBits = 0; // No paletted formats in ktx v2.
+    This->flags = 0;
+    This->minBlocksX = This->minBlocksY = 1;
+    if (KHR_DFDVAL(pBdb, MODEL) >= KHR_DF_MODEL_DXT1A) {
+        // A block compressed format. Entire block is a single sample.
+        This->flags |= KTX_FORMAT_SIZE_COMPRESSED_BIT;
+        if (KHR_DFDVAL(pBdb, MODEL) == KHR_DF_MODEL_PVRTC) {
+            This->minBlocksX = This->minBlocksY = 2;
+        }
+    } else {
+        // An uncompressed format.
+
+        // Special case depth & depth stencil formats
+        if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_DEPTH) {
+            if (KHR_DFDSAMPLECOUNT(pBdb) == 1) {
+                This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
+            } else if (KHR_DFDSAMPLECOUNT(pBdb) == 2) {
+                This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
+                This->flags |= KTX_FORMAT_SIZE_DEPTH_BIT;
+                This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
+            } else {
+                return false;
+            }
+        } else if (KHR_DFDSVAL(pBdb, 0, CHANNELID) == KHR_DF_CHANNEL_RGBSDA_STENCIL) {
+            This->flags |= KTX_FORMAT_SIZE_STENCIL_BIT;
+        } else if (KHR_DFDSAMPLECOUNT(pBdb) == 6
+#if !defined(BITFIELD_ORDER_FROM_MSB)
+                   && !memcmp(((uint32_t*)&e5b9g9r9_ufloat_comparator) + KHR_DF_WORD_TEXELBLOCKDIMENSION0, &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {
+#else
+                   && !memcmp(&e5b9g9r9_ufloat_comparator[KHR_DF_WORD_TEXELBLOCKDIMENSION0], &pBdb[KHR_DF_WORD_TEXELBLOCKDIMENSION0], sizeof(e5b9g9r9_ufloat_comparator)-(KHR_DF_WORD_TEXELBLOCKDIMENSION0)*sizeof(uint32_t))) {
+#endif
+            // Special case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 as  interpretDFD
+            // only handles "simple formats", i.e. where channels are described
+            // in contiguous bits.
+            This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
+        } else {
+            InterpretedDFDChannel rgba[4];
+            uint32_t wordBytes;
+            enum InterpretDFDResult result;
+
+            result = interpretDFD(pDfd, &rgba[0], &rgba[1], &rgba[2], &rgba[3],
+                                  &wordBytes);
+            if (result >= i_UNSUPPORTED_ERROR_BIT)
+                return false;
+            if (result & i_PACKED_FORMAT_BIT)
+                This->flags |= KTX_FORMAT_SIZE_PACKED_BIT;
+        }
+    }
+    if (This->blockSizeInBits == 0) {
+        // The DFD shows a supercompressed texture. Complete the ktxFormatSize
+        // struct by figuring out the post inflation value for bytesPlane0.
+        // Setting it here simplifies stuff later in this file. Setting the
+        // post inflation block size here will not cause any problems for
+        // the following reasons. (1) in v2 files levelIndex is always used to
+        // calculate data size and, of course, for the level offsets. (2) Finer
+        // grain access to supercompressed data than levels is not possible.
+        uint32_t blockByteLength;
+        recreateBytesPlane0FromSampleInfo(pDfd, &blockByteLength);
+        This->blockSizeInBits = blockByteLength * 8;
+    }
+    return true;
+}
+
+/**
+ * @private
+ * @~English
+ * @brief Create a DFD for a VkFormat.
+ *
+ * This KTX-specific function adds support for combined depth stencil formats
+ * which are not supported by @e dfdutils' @c vk2dfd function because they
+ * are not seen outside a Vulkan device. KTX has its own definitions for
+ * these that enable uploading, with some effort.
+ *
+ * @param[in] vkFormat   the format for which to create a DFD.
+ */
+static uint32_t*
+ktxVk2dfd(ktx_uint32_t vkFormat)
+{
+    switch(vkFormat) {
+      case VK_FORMAT_D16_UNORM_S8_UINT:
+        // 2 16-bit words. D16 in the first. S8 in the 8 LSBs of the second.
+        return createDFDDepthStencil(16, 8, 4);
+      case VK_FORMAT_D24_UNORM_S8_UINT:
+        // 1 32-bit word. D24 in the MSBs. S8 in the LSBs.
+        return createDFDDepthStencil(24, 8, 4);
+      case VK_FORMAT_D32_SFLOAT_S8_UINT:
+        // 2 32-bit words. D32 float in the first word. S8 in LSBs of the
+        // second.
+        return createDFDDepthStencil(32, 8, 8);
+      default:
+        return vk2dfd(vkFormat);
+    }
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Do the part of ktxTexture2 construction that is common to
+ *        new textures and those constructed from a stream.
+ *
+ * @param[in] This      pointer to a ktxTexture2-sized block of memory to
+ *                      initialize.
+ * @param[in] numLevels the number of levels the texture must have.
+ *
+ * @return    KTX_SUCCESS on success, other KTX_* enum values on error.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
+ */
+static KTX_error_code
+ktxTexture2_constructCommon(ktxTexture2* This, ktx_uint32_t numLevels)
+{
+    assert(This != NULL);
+    ktx_size_t privateSize;
+
+    This->classId = ktxTexture2_c;
+    This->vtbl = &ktxTexture2_vtbl;
+    This->_protected->_vtbl = ktxTexture2_vtblInt;
+    privateSize = sizeof(ktxTexture2_private)
+                + sizeof(ktxLevelIndexEntry) * (numLevels - 1);
+    This->_private = (ktxTexture2_private*)malloc(privateSize);
+    if (This->_private == NULL) {
+        return KTX_OUT_OF_MEMORY;
+    }
+    memset(This->_private, 0, privateSize);
+    return KTX_SUCCESS;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a new, empty, ktxTexture2.
+ *
+ * @param[in] This       pointer to a ktxTexture2-sized block of memory to
+ *                       initialize.
+ * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
+ *                       information describing the texture.
+ * @param[in] storageAllocation
+ *                       enum indicating whether or not to allocate storage
+ *                       for the texture images.
+ * @return    KTX_SUCCESS on success, other KTX_* enum values on error.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture or image data.
+ * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
+ *                              The request VkFormat is one of the
+ *                              prohibited formats.
+ */
+static KTX_error_code
+ktxTexture2_construct(ktxTexture2* This, ktxTextureCreateInfo* createInfo,
+                      ktxTextureCreateStorageEnum storageAllocation)
+{
+    ktxFormatSize formatSize;
+    KTX_error_code result;
+
+    memset(This, 0, sizeof(*This));
+
+    if (createInfo->vkFormat != VK_FORMAT_UNDEFINED) {
+        This->pDfd = ktxVk2dfd(createInfo->vkFormat);
+        if (!This->pDfd)
+            return KTX_INVALID_VALUE;  // Format is unknown or unsupported.
+
+#ifdef _DEBUG
+        // If this fires, an unsupported format or incorrect DFD
+        // has crept into vk2dfd.
+        assert(ktxFormatSize_initFromDfd(&formatSize, This->pDfd));
+#else
+        (void)ktxFormatSize_initFromDfd(&formatSize, This->pDfd);
+#endif
+
+    } else {
+        // TODO: Validate createInfo->pDfd.
+        This->pDfd = (ktx_uint32_t*)malloc(*createInfo->pDfd);
+        if (!This->pDfd)
+            return KTX_OUT_OF_MEMORY;
+        memcpy(This->pDfd, createInfo->pDfd, *createInfo->pDfd);
+        if (ktxFormatSize_initFromDfd(&formatSize, This->pDfd)) {
+            result = KTX_UNSUPPORTED_TEXTURE_TYPE;
+            goto cleanup;
+        }
+    }
+
+    result =  ktxTexture_construct(ktxTexture(This), createInfo, &formatSize);
+
+    if (result != KTX_SUCCESS)
+        return result;
+    result = ktxTexture2_constructCommon(This, createInfo->numLevels);
+    if (result != KTX_SUCCESS)
+        goto cleanup;;
+
+    This->vkFormat = createInfo->vkFormat;
+
+    // Ideally we'd set all these things in ktxFormatSize_initFromDfd
+    // but This->_protected is not allocated until ktxTexture_construct;
+    if (This->isCompressed)
+        This->_protected->_typeSize = 1;
+    else if (formatSize.flags & KTX_FORMAT_SIZE_PACKED_BIT)
+        This->_protected->_typeSize = formatSize.blockSizeInBits / 8;
+    else if (formatSize.flags & (KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT)) {
+        if (createInfo->vkFormat == VK_FORMAT_D16_UNORM_S8_UINT)
+            This->_protected->_typeSize = 2;
+        else
+            This->_protected->_typeSize = 4;
+    } else {
+        // Unpacked and uncompressed
+        uint32_t numComponents;
+        getDFDComponentInfoUnpacked(This->pDfd, &numComponents,
+                                    &This->_protected->_typeSize);
+    }
+
+    This->supercompressionScheme = KTX_SS_NONE;
+
+    This->_private->_requiredLevelAlignment
+                        = ktxTexture2_calcRequiredLevelAlignment(This);
+
+    // Create levelIndex. Offsets are from start of the KTX2 stream.
+    ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
+
+    This->_private->_firstLevelFileOffset = 0;
+
+    for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
+        levelIndex[level].uncompressedByteLength =
+            ktxTexture_calcLevelSize(ktxTexture(This), level,
+                                     KTX_FORMAT_VERSION_TWO);
+        levelIndex[level].byteLength =
+            levelIndex[level].uncompressedByteLength;
+        levelIndex[level].byteOffset =
+            ktxTexture_calcLevelOffset(ktxTexture(This), level);
+    }
+
+    // Allocate storage, if requested.
+    if (storageAllocation == KTX_TEXTURE_CREATE_ALLOC_STORAGE) {
+        This->dataSize
+                = ktxTexture_calcDataSizeTexture(ktxTexture(This));
+        This->pData = malloc(This->dataSize);
+        if (This->pData == NULL) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+    }
+    return result;
+
+cleanup:
+    ktxTexture2_destruct(This);
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture by copying a source ktxTexture.
+ *
+ * @param[in] This pointer to a ktxTexture2-sized block of memory to
+ *                 initialize.
+ * @param[in] orig pointer to the source texture to copy.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
+ */
+static KTX_error_code
+ktxTexture2_constructCopy(ktxTexture2* This, ktxTexture2* orig)
+{
+    KTX_error_code result;
+
+    memcpy(This, orig, sizeof(ktxTexture2));
+    // Zero all the pointers to make error handling easier
+    This->_protected = NULL;
+    This->_private = NULL;
+    This->pDfd = NULL;
+    This->kvData = NULL;
+    This->kvDataHead = NULL;
+    This->pData = NULL;
+
+    This->_protected =
+                    (ktxTexture_protected*)malloc(sizeof(ktxTexture_protected));
+    if (!This->_protected)
+        return KTX_OUT_OF_MEMORY;
+    // Must come before memcpy of _protected so as to close an active stream.
+    if (!orig->pData && ktxTexture_isActiveStream((ktxTexture*)orig))
+        ktxTexture2_LoadImageData(orig, NULL, 0);
+    memcpy(This->_protected, orig->_protected, sizeof(ktxTexture_protected));
+
+    ktx_size_t privateSize = sizeof(ktxTexture2_private)
+                           + sizeof(ktxLevelIndexEntry) * (orig->numLevels - 1);
+    This->_private = (ktxTexture2_private*)malloc(privateSize);
+    if (This->_private == NULL) {
+        result = KTX_OUT_OF_MEMORY;
+        goto cleanup;
+    }
+    memcpy(This->_private, orig->_private, privateSize);
+    if (orig->_private->_sgdByteLength > 0) {
+        This->_private->_supercompressionGlobalData
+                        = (ktx_uint8_t*)malloc(orig->_private->_sgdByteLength);
+        if (!This->_private->_supercompressionGlobalData) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+        memcpy(This->_private->_supercompressionGlobalData,
+               orig->_private->_supercompressionGlobalData,
+               orig->_private->_sgdByteLength);
+    }
+
+    This->pDfd = (ktx_uint32_t*)malloc(*orig->pDfd);
+    if (!This->pDfd) {
+        result = KTX_OUT_OF_MEMORY;
+        goto cleanup;
+    }
+    memcpy(This->pDfd, orig->pDfd, *orig->pDfd);
+
+    if (orig->kvDataHead) {
+        ktxHashList_ConstructCopy(&This->kvDataHead, orig->kvDataHead);
+    } else if (orig->kvData) {
+        This->kvData = (ktx_uint8_t*)malloc(orig->kvDataLen);
+        if (!This->kvData) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+        memcpy(This->kvData, orig->kvData, orig->kvDataLen);
+    }
+
+    // Can't share the image data as the data pointer is exposed in the
+    // ktxTexture2 structure. Changing it to a ref-counted pointer would
+    // break code. Maybe that's okay as we're still pre-release. But,
+    // since this constructor will be mostly be used when transcoding
+    // supercompressed images, it is probably not too big a deal to make
+    // a copy of the data.
+    This->pData = (ktx_uint8_t*)malloc(This->dataSize);
+    if (This->pData == NULL) {
+        result = KTX_OUT_OF_MEMORY;
+        goto cleanup;
+    }
+    memcpy(This->pData, orig->pData, orig->dataSize);
+    return KTX_SUCCESS;
+
+cleanup:
+    if (This->_protected) free(This->_protected);
+    if (This->_private) {
+        if (This->_private->_supercompressionGlobalData)
+            free(This->_private->_supercompressionGlobalData);
+        free(This->_private);
+    }
+    if (This->pDfd) free (This->pDfd);
+    if (This->kvDataHead) ktxHashList_Destruct(&This->kvDataHead);
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture from a ktxStream reading from a KTX source.
+ *
+ * The KTX header, which must have been read prior to calling this, is passed
+ * to the function.
+ *
+ * The stream object is copied into the constructed ktxTexture2.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * If either KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT or
+ * KTX_TEXTURE_CREATE_RAW_KVDATA_BIT is set then the ktxTexture's orientation
+ * fields will be set to defaults even if the KTX source contains
+ * KTXorientation metadata.
+ *
+ * @param[in] This pointer to a ktxTexture2-sized block of memory to
+ *                 initialize.
+ * @param[in] pStream pointer to the stream to read.
+ * @param[in] pHeader pointer to a KTX header that has already been read from
+ *            the stream.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Source data is inconsistent with the KTX
+ *                              specification.
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ * @exception KTX_FILE_UNEXPECTED_EOF
+ *                              Not enough data in the source.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to load either the images or
+ *                              the key-value data.
+ * @exception KTX_UNKNOWN_FILE_FORMAT
+ *                              The source is not in KTX format.
+ * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
+ *                              The source describes a texture type not
+ *                              supported by OpenGL or Vulkan, e.g, a 3D array.
+ */
+KTX_error_code
+ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
+                                        KTX_header2* pHeader,
+                                        ktxTextureCreateFlags createFlags)
+{
+    ktxTexture2_private* private;
+    KTX_error_code result;
+    KTX_supplemental_info suppInfo;
+    ktxStream* stream;
+    ktx_size_t levelIndexSize;
+
+    assert(pHeader != NULL && pStream != NULL);
+
+    memset(This, 0, sizeof(*This));
+    result = ktxTexture_constructFromStream(ktxTexture(This), pStream,
+                                            createFlags);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    result = ktxCheckHeader2_(pHeader, &suppInfo);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+    // ktxCheckHeader2_ has done the max(1, levelCount) on pHeader->levelCount.
+    result = ktxTexture2_constructCommon(This, pHeader->levelCount);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+    private = This->_private;
+
+    stream = ktxTexture2_getStream(This);
+
+    /*
+     * Initialize from pHeader->info.
+     */
+    This->vkFormat = pHeader->vkFormat;
+    This->supercompressionScheme = pHeader->supercompressionScheme;
+
+    This->_protected->_typeSize = pHeader->typeSize;
+    // Can these be done by a ktxTexture_constructFromStream?
+    This->numDimensions = suppInfo.textureDimension;
+    This->baseWidth = pHeader->pixelWidth;
+    assert(suppInfo.textureDimension > 0 && suppInfo.textureDimension < 4);
+    switch (suppInfo.textureDimension) {
+      case 1:
+        This->baseHeight = This->baseDepth = 1;
+        break;
+      case 2:
+        This->baseHeight = pHeader->pixelHeight;
+        This->baseDepth = 1;
+        break;
+      case 3:
+        This->baseHeight = pHeader->pixelHeight;
+        This->baseDepth = pHeader->pixelDepth;
+        break;
+    }
+    if (pHeader->layerCount > 0) {
+        This->numLayers = pHeader->layerCount;
+        This->isArray = KTX_TRUE;
+    } else {
+        This->numLayers = 1;
+        This->isArray = KTX_FALSE;
+    }
+    This->numFaces = pHeader->faceCount;
+    if (pHeader->faceCount == 6)
+        This->isCubemap = KTX_TRUE;
+    else
+        This->isCubemap = KTX_FALSE;
+    // ktxCheckHeader2_ does the max(1, levelCount) and sets
+    // suppInfo.generateMipmaps when it was originally 0.
+    This->numLevels = pHeader->levelCount;
+    This->generateMipmaps = suppInfo.generateMipmaps;
+
+    // Read level index
+    levelIndexSize = sizeof(ktxLevelIndexEntry) * This->numLevels;
+    result = stream->read(stream, &private->_levelIndex, levelIndexSize);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+    // Rebase index to start of data and save file offset.
+    private->_firstLevelFileOffset
+                    = private->_levelIndex[This->numLevels-1].byteOffset;
+    for (ktx_uint32_t level = 0; level < This->numLevels; level++) {
+        private->_levelIndex[level].byteOffset
+                                        -= private->_firstLevelFileOffset;
+    }
+
+    // Read DFD
+    This->pDfd =
+            (ktx_uint32_t*)malloc(pHeader->dataFormatDescriptor.byteLength);
+    if (!This->pDfd) {
+        result = KTX_OUT_OF_MEMORY;
+        goto cleanup;
+    }
+    result = stream->read(stream, This->pDfd,
+                          pHeader->dataFormatDescriptor.byteLength);
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+
+    if (!ktxFormatSize_initFromDfd(&This->_protected->_formatSize, This->pDfd)) {
+        result = KTX_UNSUPPORTED_TEXTURE_TYPE;
+        goto cleanup;
+    }
+    This->isCompressed = (This->_protected->_formatSize.flags & KTX_FORMAT_SIZE_COMPRESSED_BIT);
+
+    if (This->supercompressionScheme == KTX_SS_BASIS_LZ
+        && KHR_DFDVAL(This->pDfd + 1, MODEL) != KHR_DF_MODEL_ETC1S)
+    {
+        result = KTX_FILE_DATA_ERROR;
+        goto cleanup;
+    }
+
+    This->_private->_requiredLevelAlignment
+                          = ktxTexture2_calcRequiredLevelAlignment(This);
+
+    // Make an empty hash list.
+    ktxHashList_Construct(&This->kvDataHead);
+    // Load KVData.
+    if (pHeader->keyValueData.byteLength > 0) {
+        if (!(createFlags & KTX_TEXTURE_CREATE_SKIP_KVDATA_BIT)) {
+            ktx_uint32_t kvdLen = pHeader->keyValueData.byteLength;
+            ktx_uint8_t* pKvd;
+
+            pKvd = malloc(kvdLen);
+            if (pKvd == NULL) {
+                result = KTX_OUT_OF_MEMORY;
+                goto cleanup;
+            }
+
+            result = stream->read(stream, pKvd, kvdLen);
+            if (result != KTX_SUCCESS)
+                goto cleanup;
+
+            if (IS_BIG_ENDIAN) {
+                /* Swap the counts inside the key & value data. */
+                ktx_uint8_t* src = pKvd;
+                ktx_uint8_t* end = pKvd + kvdLen;
+                while (src < end) {
+                    ktx_uint32_t keyAndValueByteSize = *((ktx_uint32_t*)src);
+                    _ktxSwapEndian32(&keyAndValueByteSize, 1);
+                    src += _KTX_PAD4(keyAndValueByteSize);
+                }
+            }
+
+            if (!(createFlags & KTX_TEXTURE_CREATE_RAW_KVDATA_BIT)) {
+                char* orientationStr;
+                ktx_uint32_t orientationLen;
+                ktx_uint32_t animData[3];
+                ktx_uint32_t animDataLen;
+
+                result = ktxHashList_Deserialize(&This->kvDataHead,
+                                                 kvdLen, pKvd);
+                free(pKvd);
+                if (result != KTX_SUCCESS) {
+                    goto cleanup;
+                }
+
+                result = ktxHashList_FindValue(&This->kvDataHead,
+                                               KTX_ORIENTATION_KEY,
+                                               &orientationLen,
+                                               (void**)&orientationStr);
+                assert(result != KTX_INVALID_VALUE);
+                if (result == KTX_SUCCESS) {
+                    // Length includes the terminating NUL.
+                    if (orientationLen != This->numDimensions + 1) {
+                        // There needs to be an entry for each dimension of
+                        // the texture.
+                        result = KTX_FILE_DATA_ERROR;
+                        goto cleanup;
+                    } else {
+                        switch (This->numDimensions) {
+                          case 3:
+                            This->orientation.z = orientationStr[2];
+                            FALLTHROUGH;
+                          case 2:
+                            This->orientation.y = orientationStr[1];
+                            FALLTHROUGH;
+                          case 1:
+                            This->orientation.x = orientationStr[0];
+                        }
+                    }
+                } else {
+                    result = KTX_SUCCESS; // Not finding orientation is okay.
+                }
+                result = ktxHashList_FindValue(&This->kvDataHead,
+                                               KTX_ANIMDATA_KEY,
+                                               &animDataLen,
+                                               (void**)animData);
+                assert(result != KTX_INVALID_VALUE);
+                if (result == KTX_SUCCESS) {
+                    if (animDataLen != sizeof(animData)) {
+                        result = KTX_FILE_DATA_ERROR;
+                        goto cleanup;
+                    }
+                    if (This->isArray) {
+                        This->isVideo = KTX_TRUE;
+                        This->duration = animData[0];
+                        This->timescale = animData[1];
+                        This->loopcount = animData[2];
+                    } else {
+                        // animData is only valid for array textures.
+                        result = KTX_FILE_DATA_ERROR;
+                        goto cleanup;
+                    }
+                } else {
+                    result = KTX_SUCCESS; // Not finding video is okay.
+                }
+            } else {
+                This->kvDataLen = kvdLen;
+                This->kvData = pKvd;
+            }
+        } else {
+            stream->skip(stream, pHeader->keyValueData.byteLength);
+        }
+    }
+
+    if (pHeader->supercompressionGlobalData.byteLength > 0) {
+        // There could be padding here so seek to the next item.
+        (void)stream->setpos(stream,
+                             pHeader->supercompressionGlobalData.byteOffset);
+
+        // Read supercompressionGlobalData
+        private->_supercompressionGlobalData =
+          (ktx_uint8_t*)malloc(pHeader->supercompressionGlobalData.byteLength);
+        if (!private->_supercompressionGlobalData) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+        private->_sgdByteLength
+                            = pHeader->supercompressionGlobalData.byteLength;
+        result = stream->read(stream, private->_supercompressionGlobalData,
+                              private->_sgdByteLength);
+
+        if (result != KTX_SUCCESS)
+            goto cleanup;
+    }
+
+    // Calculate size of the image data. Level 0 is the last level in the data.
+    This->dataSize = private->_levelIndex[0].byteOffset
+                     + private->_levelIndex[0].byteLength;
+
+    /*
+     * Load the images, if requested.
+     */
+    if (createFlags & KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT) {
+        result = ktxTexture2_LoadImageData(This, NULL, 0);
+    }
+    if (result != KTX_SUCCESS)
+        goto cleanup;
+
+    return result;
+
+cleanup:
+    ktxTexture2_destruct(This);
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture from a ktxStream reading from a KTX source.
+ *
+ * The stream object is copied into the constructed ktxTexture2.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] This pointer to a ktxTexture2-sized block of memory to
+ *            initialize.
+ * @param[in] pStream pointer to the stream to read.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return    KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ *
+ * For other exceptions see ktxTexture2_constructFromStreamAndHeader().
+ */
+static KTX_error_code
+ktxTexture2_constructFromStream(ktxTexture2* This, ktxStream* pStream,
+                                ktxTextureCreateFlags createFlags)
+{
+    KTX_header2 header;
+    KTX_error_code result;
+
+    // Read header.
+    result = pStream->read(pStream, &header, KTX2_HEADER_SIZE);
+    if (result != KTX_SUCCESS)
+        return result;
+
+#if IS_BIG_ENDIAN
+    // byte swap the header
+#endif
+    return ktxTexture2_constructFromStreamAndHeader(This, pStream,
+                                                    &header, createFlags);
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture from a stdio stream reading from a KTX source.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @note Do not close the stdio stream until you are finished with the texture
+ *       object.
+ *
+ * @param[in] This pointer to a ktxTextureInt-sized block of memory to
+ *                 initialize.
+ * @param[in] stdioStream a stdio FILE pointer opened on the source.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p stdiostream or @p This is null.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture2_constructFromStdioStream(ktxTexture2* This, FILE* stdioStream,
+                                     ktxTextureCreateFlags createFlags)
+{
+    KTX_error_code result;
+    ktxStream stream;
+
+    if (stdioStream == NULL || This == NULL)
+        return KTX_INVALID_VALUE;
+
+    result = ktxFileStream_construct(&stream, stdioStream, KTX_FALSE);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture2_constructFromStream(This, &stream, createFlags);
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture from a named KTX file.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @param[in] This pointer to a ktxTextureInt-sized block of memory to
+ *                 initialize.
+ * @param[in] filename    pointer to a char array containing the file name.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
+ * @exception KTX_INVALID_VALUE @p filename is @c NULL.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture2_constructFromNamedFile(ktxTexture2* This,
+                                   const char* const filename,
+                                   ktxTextureCreateFlags createFlags)
+{
+    KTX_error_code result;
+    ktxStream stream;
+    FILE* file;
+
+    if (This == NULL || filename == NULL)
+        return KTX_INVALID_VALUE;
+
+    file = fopen(filename, "rb");
+    if (!file)
+       return KTX_FILE_OPEN_FAILED;
+
+    result = ktxFileStream_construct(&stream, file, KTX_TRUE);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture2_constructFromStream(This, &stream, createFlags);
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Construct a ktxTexture from KTX-formatted data in memory.
+ *
+ * See ktxTextureInt_constructFromStream for details.
+ *
+ * @param[in] This  pointer to a ktxTextureInt-sized block of memory to
+ *                  initialize.
+ * @param[in] bytes pointer to the memory containing the serialized KTX data.
+ * @param[in] size  length of the KTX data in bytes.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_constructFromStream().
+ */
+static KTX_error_code
+ktxTexture2_constructFromMemory(ktxTexture2* This,
+                                  const ktx_uint8_t* bytes, ktx_size_t size,
+                                  ktxTextureCreateFlags createFlags)
+{
+    KTX_error_code result;
+    ktxStream stream;
+
+    if (bytes == NULL || size == 0)
+        return KTX_INVALID_VALUE;
+
+    result = ktxMemStream_construct_ro(&stream, bytes, size);
+    if (result == KTX_SUCCESS)
+        result = ktxTexture2_constructFromStream(This, &stream, createFlags);
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Destruct a ktxTexture2, freeing and internal memory.
+ *
+ * @param[in] This pointer to a ktxTexture2-sized block of memory to
+ *                 initialize.
+ */
+void
+ktxTexture2_destruct(ktxTexture2* This)
+{
+    if (This->pDfd) free(This->pDfd);
+    if (This->_private) {
+      ktx_uint8_t* sgd = This->_private->_supercompressionGlobalData;
+      if (sgd) free(sgd);
+      free(This->_private);
+    }
+    ktxTexture_destruct(ktxTexture(This));
+}
+
+/**
+ * @memberof ktxTexture2
+ * @ingroup writer
+ * @~English
+ * @brief Create a new empty ktxTexture2.
+ *
+ * The address of the newly created ktxTexture2 is written to the location
+ * pointed at by @p newTex.
+ *
+ * @param[in] createInfo pointer to a ktxTextureCreateInfo struct with
+ *                       information describing the texture.
+ * @param[in] storageAllocation
+ *                       enum indicating whether or not to allocate storage
+ *                       for the texture images.
+ * @param[in,out] newTex pointer to a location in which store the address of
+ *                       the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @c glInternalFormat in @p createInfo is not a
+ *                              valid OpenGL internal format value.
+ * @exception KTX_INVALID_VALUE @c numDimensions in @p createInfo is not 1, 2
+ *                              or 3.
+ * @exception KTX_INVALID_VALUE One of <tt>base{Width,Height,Depth}</tt> in
+ *                              @p createInfo is 0.
+ * @exception KTX_INVALID_VALUE @c numFaces in @p createInfo is not 1 or 6.
+ * @exception KTX_INVALID_VALUE @c numLevels in @p createInfo is 0.
+ * @exception KTX_INVALID_OPERATION
+ *                              The <tt>base{Width,Height,Depth}</tt> specified
+ *                              in @p createInfo are inconsistent with
+ *                              @c numDimensions.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a 3D array or
+ *                              3D cubemap texture.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting a cubemap with
+ *                              non-square or non-2D images.
+ * @exception KTX_INVALID_OPERATION
+ *                              @p createInfo is requesting more mip levels
+ *                              than needed for the specified
+ *                              <tt>base{Width,Height,Depth}</tt>.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture's images.
+ */
+KTX_error_code
+ktxTexture2_Create(ktxTextureCreateInfo* createInfo,
+                  ktxTextureCreateStorageEnum storageAllocation,
+                  ktxTexture2** newTex)
+{
+    KTX_error_code result;
+
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_construct(tex, createInfo, storageAllocation);
+    if (result != KTX_SUCCESS) {
+        free(tex);
+    } else {
+        *newTex = tex;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @ingroup writer
+ * @~English
+ * @brief Create a ktxTexture2 by making a copy of a ktxTexture2.
+ *
+ * The address of the newly created ktxTexture2 is written to the location
+ * pointed at by @p newTex.
+ *
+ * @param[in]     orig   pointer to the texture to copy.
+ * @param[in,out] newTex pointer to a location in which store the address of
+ *                       the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_OUT_OF_MEMORY Not enough memory for the texture data.
+ */
+ KTX_error_code
+ ktxTexture2_CreateCopy(ktxTexture2* orig, ktxTexture2** newTex)
+ {
+    KTX_error_code result;
+
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_constructCopy(tex, orig);
+    if (result != KTX_SUCCESS) {
+        free(tex);
+    } else {
+        *newTex = tex;
+    }
+    return result;
+
+ }
+
+/**
+ * @defgroup reader Reader
+ * @brief Read KTX-formatted data.
+ * @{
+ */
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Create a ktxTexture2 from a stdio stream reading from a KTX source.
+ *
+ * The address of a newly created ktxTexture2 reflecting the contents of the
+ * stdio stream is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] stdioStream stdio FILE pointer created from the desired file.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p newTex is @c NULL.
+ * @exception KTX_FILE_DATA_ERROR
+ *                              Source data is inconsistent with the KTX
+ *                              specification.
+ * @exception KTX_FILE_READ_ERROR
+ *                              An error occurred while reading the source.
+ * @exception KTX_FILE_UNEXPECTED_EOF
+ *                              Not enough data in the source.
+ * @exception KTX_OUT_OF_MEMORY Not enough memory to create the texture object,
+ *                              load the images or load the key-value data.
+ * @exception KTX_UNKNOWN_FILE_FORMAT
+ *                              The source is not in KTX format.
+ * @exception KTX_UNSUPPORTED_TEXTURE_TYPE
+ *                              The source describes a texture type not
+ *                              supported by OpenGL or Vulkan, e.g, a 3D array.
+ */
+KTX_error_code
+ktxTexture2_CreateFromStdioStream(FILE* stdioStream,
+                                  ktxTextureCreateFlags createFlags,
+                                  ktxTexture2** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_constructFromStdioStream(tex, stdioStream,
+                                                  createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture2*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Create a ktxTexture2 from a named KTX file.
+ *
+ * The address of a newly created ktxTexture2 reflecting the contents of the
+ * file is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] filename    pointer to a char array containing the file name.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+
+ * @exception KTX_FILE_OPEN_FAILED The file could not be opened.
+ * @exception KTX_INVALID_VALUE @p filename is @c NULL.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture2_CreateFromNamedFile(const char* const filename,
+                                ktxTextureCreateFlags createFlags,
+                                ktxTexture2** newTex)
+{
+    KTX_error_code result;
+
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_constructFromNamedFile(tex, filename, createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture2*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Create a ktxTexture2 from KTX-formatted data in memory.
+ *
+ * The address of a newly created ktxTexture2 reflecting the contents of the
+ * serialized KTX data is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] bytes pointer to the memory containing the serialized KTX data.
+ * @param[in] size  length of the KTX data in bytes.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture2_CreateFromMemory(const ktx_uint8_t* bytes, ktx_size_t size,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture2** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_constructFromMemory(tex, bytes, size,
+                                             createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture2*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Create a ktxTexture2 from KTX-formatted data from a stream.
+ *
+ * The address of a newly created ktxTexture2 reflecting the contents of the
+ * serialized KTX data is written to the location pointed at by @p newTex.
+ *
+ * The create flag KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT should not be set,
+ * if the ktxTexture is ultimately to be uploaded to OpenGL or Vulkan. This
+ * will minimize memory usage by allowing, for example, loading the images
+ * directly from the source into a Vulkan staging buffer.
+ *
+ * The create flag KTX_TEXTURE_CREATE_RAW_KVDATA_BIT should not be used. It is
+ * provided solely to enable implementation of the @e libktx v1 API on top of
+ * ktxTexture.
+ *
+ * @param[in] stream pointer to the stream to read KTX data from.
+ * @param[in] createFlags bitmask requesting specific actions during creation.
+ * @param[in,out] newTex  pointer to a location in which store the address of
+ *                        the newly created texture.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE Either @p bytes is NULL or @p size is 0.
+ *
+ * For other exceptions, see ktxTexture_CreateFromStdioStream().
+ */
+KTX_error_code
+ktxTexture2_CreateFromStream(ktxStream* stream,
+                             ktxTextureCreateFlags createFlags,
+                             ktxTexture2** newTex)
+{
+    KTX_error_code result;
+    if (newTex == NULL)
+        return KTX_INVALID_VALUE;
+
+    ktxTexture2* tex = (ktxTexture2*)malloc(sizeof(ktxTexture2));
+    if (tex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    result = ktxTexture2_constructFromStream(tex, stream, createFlags);
+    if (result == KTX_SUCCESS)
+        *newTex = (ktxTexture2*)tex;
+    else {
+        free(tex);
+        *newTex = NULL;
+    }
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Destroy a ktxTexture2 object.
+ *
+ * This frees the memory associated with the texture contents and the memory
+ * of the ktxTexture2 object. This does @e not delete any OpenGL or Vulkan
+ * texture objects created by ktxTexture2_GLUpload or ktxTexture2_VkUpload.
+ *
+ * @param[in] This pointer to the ktxTexture2 object to destroy
+ */
+void
+ktxTexture2_Destroy(ktxTexture2* This)
+{
+    ktxTexture2_destruct(This);
+    free(This);
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Calculate the size of the image data for the specified number
+ *        of levels.
+ *
+ * The data size is the sum of the sizes of each level up to the number
+ * specified and includes any @c mipPadding between levels. It does
+ * not include initial @c mipPadding required in the file.
+ *
+ * @param[in] This     pointer to the ktxTexture object of interest.
+ * @param[in] levels   number of levels whose data size to return.
+ *
+ * @return the data size in bytes.
+ */
+ktx_size_t
+ktxTexture2_calcDataSizeLevels(ktxTexture2* This, ktx_uint32_t levels)
+{
+    ktx_size_t dataSize = 0;
+
+    assert(This != NULL);
+    assert(This->supercompressionScheme == KTX_SS_NONE);
+    assert(levels <= This->numLevels);
+    for (ktx_uint32_t i = levels - 1; i > 0; i--) {
+        ktx_size_t levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
+                                                        KTX_FORMAT_VERSION_TWO);
+        dataSize += _KTX_PADN(This->_private->_requiredLevelAlignment,
+                              levelSize);
+    }
+    dataSize += ktxTexture_calcLevelSize(ktxTexture(This), 0,
+                                         KTX_FORMAT_VERSION_TWO);
+    return dataSize;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ *
+ * @copydoc ktxTexture::ktxTexture_doCalcFaceLodSize
+ */
+ktx_size_t
+ktxTexture2_calcFaceLodSize(ktxTexture2* This, ktx_uint32_t level)
+{
+    assert(This != NULL);
+    assert(This->supercompressionScheme == KTX_SS_NONE);
+    /*
+     * For non-array cubemaps this is the size of a face. For everything
+     * else it is the size of the level.
+     */
+    if (This->isCubemap && !This->isArray)
+        return ktxTexture_calcImageSize(ktxTexture(This), level,
+                                        KTX_FORMAT_VERSION_TWO);
+    else
+        return This->_private->_levelIndex[level].uncompressedByteLength;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Return the offset of a level in bytes from the start of the image
+ *        data in a ktxTexture.
+ *
+ * Since the offset is from the start of the image data, it does not include the initial
+ * @c mipPadding required in the file.
+ *
+ * @param[in]     This  pointer to the ktxTexture object of interest.
+ * @param[in]     level level whose offset to return.
+ *
+ * @return the data size in bytes.
+ */
+ktx_size_t
+ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level)
+{
+  assert (This != NULL);
+  assert(This->supercompressionScheme == KTX_SS_NONE);
+  assert (level < This->numLevels);
+  ktx_size_t levelOffset = 0;
+  for (ktx_uint32_t i = This->numLevels - 1; i > level; i--) {
+      ktx_size_t levelSize;
+      levelSize = ktxTexture_calcLevelSize(ktxTexture(This), i,
+                                           KTX_FORMAT_VERSION_TWO);
+      levelOffset += _KTX_PADN(This->_private->_requiredLevelAlignment,
+                               levelSize);
+  }
+  return levelOffset;
+}
+
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Retrieve the offset of a level's first image within the KTX2 file.
+ *
+ * @param[in] This pointer to the ktxTexture object of interest.
+ */
+ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level)
+{
+    assert(This->_private->_firstLevelFileOffset != 0);
+    return This->_private->_levelIndex[level].byteOffset
+           + This->_private->_firstLevelFileOffset;
+}
+
+// Recursive function to return the greatest common divisor of a and b.
+static uint32_t
+gcd(uint32_t a, uint32_t b) {
+    if (a == 0)
+        return b;
+    return gcd(b % a, a);
+}
+
+// Function to return the least common multiple of a & 4.
+uint32_t
+lcm4(uint32_t a)
+{
+    if (!(a & 0x03))
+        return a;  // a is a multiple of 4.
+    return (a*4) / gcd(a, 4);
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Return the required alignment for levels of this texture.
+ *
+ * @param[in] This       pointer to the ktxTexture2 object of interest.
+ *
+ * @return    The required alignment for levels.
+ */
+ ktx_uint32_t
+ ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This)
+ {
+    ktx_uint32_t alignment;
+    if (This->supercompressionScheme != KTX_SS_NONE)
+        alignment = 1;
+    else
+        alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
+    return alignment;
+ }
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Return what the required alignment for levels of this texture will be after inflation.
+ *
+ * @param[in] This       pointer to the ktxTexture2 object of interest.
+ *
+ * @return    The required alignment for levels.
+ */
+ktx_uint32_t
+ktxTexture2_calcPostInflationLevelAlignment(ktxTexture2* This)
+{
+    ktx_uint32_t alignment;
+
+    // Should actually work for none supercompressed but don't want to
+    // encourage use of it.
+    assert(This->supercompressionScheme >= KTX_SS_ZSTD);
+
+    if (This->vkFormat != VK_FORMAT_UNDEFINED)
+        alignment = lcm4(This->_protected->_formatSize.blockSizeInBits / 8);
+    else
+        alignment = 16;
+
+    return alignment;
+}
+
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Return information about the components of an image in a texture.
+ *
+ * @param[in]     This           pointer to the ktxTexture object of interest.
+ * @param[in,out] pNumComponents pointer to location in which to write the
+ *                               number of components in the textures images.
+ * @param[in,out] pComponentByteLength
+ *                               pointer to the location in which to write
+ *                               byte length of a component.
+ */
+void
+ktxTexture2_GetComponentInfo(ktxTexture2* This, uint32_t* pNumComponents,
+                             uint32_t* pComponentByteLength)
+{
+    // FIXME Need to handle packed case.
+    getDFDComponentInfoUnpacked(This->pDfd, pNumComponents,
+                                pComponentByteLength);
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Return the number of components in an image of the texture.
+ *
+ * Returns the number of components indicated by the DFD's sample information
+ * in accordance with the color model. For uncompressed formats it will be the actual
+ * number of components in the image. For block-compressed formats, it will be 1 or 2
+ * according to the format's DFD color model. For Basis compressed textures, the
+ * function examines the ids of the channels indicated by the DFD and uses that
+ * information to determine and return the number of components in the image
+ * @e before encoding and deflation so it can be used to help choose a suitable
+ * transcode target format.
+ *
+ * @param[in]     This           pointer to the ktxTexture object of interest.
+ *
+ * @return the number of components.
+ */
+ktx_uint32_t
+ktxTexture2_GetNumComponents(ktxTexture2* This)
+{
+    uint32_t* pBdb = This->pDfd + 1;
+    uint32_t dfdNumComponents = getDFDNumComponents(This->pDfd);
+    uint32_t colorModel = KHR_DFDVAL(pBdb, MODEL);
+    if (colorModel < KHR_DF_MODEL_DXT1A) {
+        return dfdNumComponents;
+    } else {
+        switch (colorModel) {
+          case KHR_DF_MODEL_ETC1S:
+          {
+            uint32_t channel0Id = KHR_DFDSVAL(pBdb, 0, CHANNELID);
+            if (dfdNumComponents == 1) {
+                if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB)
+                    return 3;
+                else
+                    return 1;
+            } else {
+                uint32_t channel1Id = KHR_DFDSVAL(pBdb, 1, CHANNELID);
+                if (channel0Id == KHR_DF_CHANNEL_ETC1S_RGB
+                    && channel1Id == KHR_DF_CHANNEL_ETC1S_AAA)
+                    return 4;
+                else {
+                    // An invalid combination of channel Ids should never
+                    // have been set during creation or should have been
+                    // caught when the file was loaded.
+                    assert(channel0Id == KHR_DF_CHANNEL_ETC1S_RRR
+                           && channel1Id == KHR_DF_CHANNEL_ETC1S_GGG);
+                    return 2;
+                }
+            }
+            break;
+          }
+          case KHR_DF_MODEL_UASTC:
+            switch (KHR_DFDSVAL(pBdb, 0, CHANNELID)) {
+              case KHR_DF_CHANNEL_UASTC_RRR:
+                return 1;
+              case KHR_DF_CHANNEL_UASTC_RRRG:
+                return 2;
+              case KHR_DF_CHANNEL_UASTC_RGB:
+                return 3;
+              case KHR_DF_CHANNEL_UASTC_RGBA:
+                return 4;
+              default:
+                // Same comment as for the assert in the ETC1 case.
+                assert(false);
+                return 1;
+            }
+            break;
+          default:
+            return dfdNumComponents;
+        }
+    }
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Find the offset of an image within a ktxTexture's image data.
+ *
+ * As there is no such thing as a 3D cubemap we make the 3rd location parameter
+ * do double duty. Only works for non-supercompressed textures as
+ * there is no way to tell where an image is for a supercompressed one.
+ *
+ * @param[in]     This      pointer to the ktxTexture object of interest.
+ * @param[in]     level     mip level of the image.
+ * @param[in]     layer     array layer of the image.
+ * @param[in]     faceSlice cube map face or depth slice of the image.
+ * @param[in,out] pOffset   pointer to location to store the offset.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_OPERATION
+ *                         @p level, @p layer or @p faceSlice exceed the
+ *                         dimensions of the texture.
+ * @exception KTX_INVALID_OPERATION Texture is supercompressed.
+ * @exception KTX_INVALID_VALID @p This is NULL.
+ */
+KTX_error_code
+ktxTexture2_GetImageOffset(ktxTexture2* This, ktx_uint32_t level,
+                          ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                          ktx_size_t* pOffset)
+{
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (level >= This->numLevels || layer >= This->numLayers)
+        return KTX_INVALID_OPERATION;
+
+    if (This->supercompressionScheme != KTX_SS_NONE)
+        return KTX_INVALID_OPERATION;
+
+    if (This->isCubemap) {
+        if (faceSlice >= This->numFaces)
+            return KTX_INVALID_OPERATION;
+    } else {
+        ktx_uint32_t maxSlice = MAX(1, This->baseDepth >> level);
+        if (faceSlice >= maxSlice)
+            return KTX_INVALID_OPERATION;
+    }
+
+    // Get the offset of the start of the level.
+    *pOffset = ktxTexture2_levelDataOffset(This, level);
+
+    // All layers, faces & slices within a level are the same size.
+    if (layer != 0) {
+        ktx_size_t layerSize;
+        layerSize = ktxTexture_layerSize(ktxTexture(This), level,
+                                         KTX_FORMAT_VERSION_TWO);
+        *pOffset += layer * layerSize;
+    }
+    if (faceSlice != 0) {
+        ktx_size_t imageSize;
+        imageSize = ktxTexture2_GetImageSize(This, level);
+        *pOffset += faceSlice * imageSize;
+    }
+    return KTX_SUCCESS;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Retrieve the opto-electrical transfer function of the images.
+ *
+ * @param[in]     This      pointer to the ktxTexture2 object of interest.
+ *
+ * @return A @c khr_df_transfer enum value specifying the OETF.
+ */
+khr_df_transfer_e
+ktxTexture2_GetOETF_e(ktxTexture2* This)
+{
+    return KHR_DFDVAL(This->pDfd+1, TRANSFER);
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Retrieve the opto-electrical transfer function of the images.
+ * @deprecated Retained for backward compatibility. Use ktxTexture2\_GetOETF\_e()
+ *
+ * @param[in]     This      pointer to the ktxTexture2 object of interest.
+ *
+ * @return A @c khr_df_transfer enum value specifying the OETF, returned as
+ *         @c ktx_uint32_t.
+ */
+ktx_uint32_t
+ktxTexture2_GetOETF(ktxTexture2* This)
+{
+    return KHR_DFDVAL(This->pDfd+1, TRANSFER);
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Retrieve the DFD color model of the images.
+ *
+ * @param[in]     This      pointer to the ktxTexture2 object of interest.
+ *
+ * @return A @c khr_df_transfer enum value specifying the color model.
+ */
+khr_df_model_e
+ktxTexture2_GetColorModel_e(ktxTexture2* This)
+{
+    return KHR_DFDVAL(This->pDfd+1, MODEL);
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Retrieve whether the RGB components have been premultiplied by the alpha component.
+ *
+ * @param[in]     This      pointer to the ktxTexture2 object of interest.
+ *
+ * @return KTX\_TRUE if the components are premultiplied, KTX_FALSE otherwise.
+ */
+ktx_bool_t
+ktxTexture2_GetPremultipliedAlpha(ktxTexture2* This)
+{
+    return KHR_DFDVAL(This->pDfd+1, FLAGS) & KHR_DF_FLAG_ALPHA_PREMULTIPLIED;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Query if the images are in a transcodable format.
+ *
+ * @param[in]     This     pointer to the ktxTexture2 object of interest.
+ */
+ktx_bool_t
+ktxTexture2_NeedsTranscoding(ktxTexture2* This)
+{
+    if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_ETC1S)
+        return true;
+    else if (KHR_DFDVAL(This->pDfd + 1, MODEL) == KHR_DF_MODEL_UASTC)
+        return true;
+    else
+        return false;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Return the total size in bytes of the uncompressed data of a ktxTexture2.
+ *
+ * If supercompressionScheme == KTX_SS_NONE or
+ * KTX_SS_BASIS_LZ, returns the value of @c This->dataSize
+ * else if supercompressionScheme == KTX_SS_ZSTD,  it returns the
+ * sum of the uncompressed sizes of each mip level plus space for the level padding. With no
+ * supercompression the data size and uncompressed data size are the same. For Basis
+ * supercompression the uncompressed size cannot be known until the data is transcoded
+ * so the compressed size is returned.
+ *
+ * @param[in]     This     pointer to the ktxTexture1 object of interest.
+ */
+ktx_size_t
+ktxTexture2_GetDataSizeUncompressed(ktxTexture2* This)
+{
+    switch (This->supercompressionScheme) {
+      case KTX_SS_BASIS_LZ:
+      case KTX_SS_NONE:
+        return This->dataSize;
+      case KTX_SS_ZSTD:
+      {
+            ktx_size_t uncompressedSize = 0;
+            ktx_uint32_t uncompressedLevelAlignment;
+            ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
+
+            uncompressedLevelAlignment =
+                ktxTexture2_calcPostInflationLevelAlignment(This);
+
+            for (ktx_int32_t level = This->numLevels - 1; level >= 1; level--) {
+                ktx_size_t uncompressedLevelSize;
+                uncompressedLevelSize = levelIndex[level].uncompressedByteLength;
+                uncompressedLevelSize = _KTX_PADN(uncompressedLevelAlignment,
+                                                  uncompressedLevelSize);
+                uncompressedSize += uncompressedLevelSize;
+            }
+            uncompressedSize += levelIndex[0].uncompressedByteLength;
+            return uncompressedSize;
+      }
+      case KTX_SS_BEGIN_VENDOR_RANGE:
+      case KTX_SS_END_VENDOR_RANGE:
+      case KTX_SS_BEGIN_RESERVED:
+      default:
+        return 0;
+    }
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Calculate & return the size in bytes of an image at the specified
+ *        mip level.
+ *
+ * For arrays, this is the size of a layer, for cubemaps, the size of a face
+ * and for 3D textures, the size of a depth slice.
+ *
+ * The size reflects the padding of each row to KTX_GL_UNPACK_ALIGNMENT.
+ *
+ * @param[in]     This     pointer to the ktxTexture2 object of interest.
+ * @param[in]     level    level of interest. *
+ */
+ktx_size_t
+ktxTexture2_GetImageSize(ktxTexture2* This, ktx_uint32_t level)
+{
+    return ktxTexture_calcImageSize(ktxTexture(This), level,
+                                    KTX_FORMAT_VERSION_TWO);
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Iterate over the mip levels in a ktxTexture2 object.
+ *
+ * This is almost identical to ktxTexture_IterateLevelFaces(). The difference is
+ * that the blocks of image data for non-array cube maps include all faces of
+ * a mip level.
+ *
+ * This function works even if @p This->pData == 0 so it can be used to
+ * obtain offsets and sizes for each level by callers who have loaded the data
+ * externally.
+ *
+ * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE.
+ *
+ * @param[in]     This     handle of the ktxTexture opened on the data.
+ * @param[in,out] iterCb   the address of a callback function which is called
+ *                         with the data for each image block.
+ * @param[in,out] userdata the address of application-specific data which is
+ *                         passed to the callback along with the image data.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The
+ *          following are returned directly by this function. @p iterCb may
+ *          return these for other causes or may return additional errors.
+ *
+ * @exception KTX_FILE_DATA_ERROR   Mip level sizes are increasing not
+ *                                  decreasing
+ * @exception KTX_INVALID_OPERATION supercompressionScheme != SUPERCOMPRESSION_NONE.
+ * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL.
+ *
+ */
+KTX_error_code
+ktxTexture2_IterateLevels(ktxTexture2* This, PFNKTXITERCB iterCb, void* userdata)
+{
+    KTX_error_code  result = KTX_SUCCESS;
+    //ZSTD_DCtx* dctx;
+    //ktx_uint8_t* decompBuf;
+    ktxLevelIndexEntry* levelIndex = This->_private->_levelIndex;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (iterCb == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (This->supercompressionScheme != KTX_SS_NONE)
+        return KTX_INVALID_OPERATION;
+
+    for (ktx_int32_t level = This->numLevels - 1; level >= 0; level--)
+    {
+        ktx_uint32_t width, height, depth;
+        ktx_uint64_t levelSize;
+        ktx_uint64_t offset;
+
+        /* Array textures have the same number of layers at each mip level. */
+        width = MAX(1, This->baseWidth  >> level);
+        height = MAX(1, This->baseHeight >> level);
+        depth = MAX(1, This->baseDepth  >> level);
+
+        levelSize = levelIndex[level].uncompressedByteLength;
+        offset = ktxTexture2_levelDataOffset(This, level);
+
+        /* All array layers are passed in a group because that is how
+         * GL & Vulkan need them. Hence no
+         *    for (layer = 0; layer < This->numLayers)
+         */
+        result = iterCb(level, 0, width, height, depth,
+                        levelSize, This->pData + offset, userdata);
+        if (result != KTX_SUCCESS)
+            break;
+    }
+
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Iterate over the images in a ktxTexture2 object while loading the
+ *        image data.
+ *
+ * This operates similarly to ktxTexture_IterateLevelFaces() except that it
+ * loads the images from the ktxTexture2's source to a temporary buffer
+ * while iterating. If supercompressionScheme == KTX_SS_ZSTD,
+ * it will inflate the data before passing it to the callback. The callback function
+ * must copy the image data if it wishes to preserve it as the temporary buffer
+ * is reused for each level and is freed when this function exits.
+ *
+ * This function is helpful for reducing memory usage when uploading the data
+ * to a graphics API.
+ *
+ * Intended for use only when supercompressionScheme == SUPERCOMPRESSION_NONE
+ * or SUPERCOMPRESSION_ZSTD. As there is no access to the ktxTexture's data on
+ * conclusion of this function, destroying the texture on completion is recommended.
+ *
+ * @param[in]     This     pointer to the ktxTexture2 object of interest.
+ * @param[in,out] iterCb   the address of a callback function which is called
+ *                         with the data for each image.
+ * @param[in,out] userdata the address of application-specific data which is
+ *                         passed to the callback along with the image data.
+ *
+ * @return  KTX_SUCCESS on success, other KTX_* enum values on error. The
+ *          following are returned directly by this function. @p iterCb may
+ *          return these for other causes or may return additional errors.
+ *
+ * @exception KTX_FILE_DATA_ERROR   mip level sizes are increasing not
+ *                                  decreasing
+ * @exception KTX_INVALID_OPERATION the ktxTexture2 was not created from a
+ *                                  stream, i.e there is no data to load, or
+ *                                  this ktxTexture2's images have already
+ *                                  been loaded.
+ * @exception KTX_INVALID_OPERATION
+ *                          supercompressionScheme != SUPERCOMPRESSION_NONE.
+ *                          and supercompressionScheme != SUPERCOMPRESSION_ZSTD.
+ * @exception KTX_INVALID_VALUE     @p This is @c NULL or @p iterCb is @c NULL.
+ * @exception KTX_OUT_OF_MEMORY     not enough memory to allocate a block to
+ *                                  hold the base level image.
+ */
+KTX_error_code
+ktxTexture2_IterateLoadLevelFaces(ktxTexture2* This, PFNKTXITERCB iterCb,
+                                  void* userdata)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    ktxStream* stream = (ktxStream *)&prtctd->_stream;
+    ktxLevelIndexEntry* levelIndex;
+    ktx_size_t      dataSize = 0, uncompressedDataSize = 0;
+    KTX_error_code  result = KTX_SUCCESS;
+    ktx_uint8_t*    dataBuf = NULL;
+    ktx_uint8_t*    uncompressedDataBuf = NULL;
+    ktx_uint8_t*    pData;
+    ZSTD_DCtx*      dctx = NULL;
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (This->classId != ktxTexture2_c)
+        return KTX_INVALID_OPERATION;
+
+    if (This->supercompressionScheme != KTX_SS_NONE &&
+        This->supercompressionScheme != KTX_SS_ZSTD)
+        return KTX_INVALID_OPERATION;
+
+    if (iterCb == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (prtctd->_stream.data.file == NULL)
+        // This Texture not created from a stream or images are already loaded.
+        return KTX_INVALID_OPERATION;
+
+    levelIndex = This->_private->_levelIndex;
+
+    // Allocate memory sufficient for the base level
+    dataSize = levelIndex[0].byteLength;
+    dataBuf = malloc(dataSize);
+    if (!dataBuf)
+        return KTX_OUT_OF_MEMORY;
+    if (This->supercompressionScheme == KTX_SS_ZSTD) {
+        uncompressedDataSize = levelIndex[0].uncompressedByteLength;
+        uncompressedDataBuf = malloc(uncompressedDataSize);
+        if (!uncompressedDataBuf) {
+            result = KTX_OUT_OF_MEMORY;
+            goto cleanup;
+        }
+        dctx = ZSTD_createDCtx();
+        pData = uncompressedDataBuf;
+    } else {
+        pData = dataBuf;
+    }
+
+    for (ktx_int32_t level = This->numLevels - 1; level >= 0; --level)
+    {
+        ktx_size_t   levelSize;
+        GLsizei      width, height, depth;
+
+        // Array textures have the same number of layers at each mip level.
+        width = MAX(1, This->baseWidth  >> level);
+        height = MAX(1, This->baseHeight >> level);
+        depth = MAX(1, This->baseDepth  >> level);
+
+        levelSize = levelIndex[level].byteLength;
+        if (dataSize < levelSize) {
+            // Levels cannot be larger than the base level
+            result = KTX_FILE_DATA_ERROR;
+            goto cleanup;
+        }
+
+        // Use setpos so we skip any padding.
+        result = stream->setpos(stream,
+                                ktxTexture2_levelFileOffset(This, level));
+        if (result != KTX_SUCCESS)
+            goto cleanup;
+
+        result = stream->read(stream, dataBuf, levelSize);
+        if (result != KTX_SUCCESS)
+            goto cleanup;
+
+        if (This->supercompressionScheme == KTX_SS_ZSTD) {
+            levelSize =
+                ZSTD_decompressDCtx(dctx, uncompressedDataBuf,
+                                  uncompressedDataSize,
+                                  dataBuf,
+                                  levelSize);
+            if (ZSTD_isError(levelSize)) {
+                ZSTD_ErrorCode error = ZSTD_getErrorCode(levelSize);
+                switch(error) {
+                  case ZSTD_error_dstSize_tooSmall:
+                    return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
+                  case ZSTD_error_memory_allocation:
+                    return KTX_OUT_OF_MEMORY;
+                  default:
+                    return KTX_FILE_DATA_ERROR;
+                }
+            }
+            // We don't fix up the texture's dataSize, levelIndex or
+            // _requiredAlignment because after this function completes there
+            // is no way to get at the texture's data.
+            //nindex[level].byteOffset = levelOffset;
+            //nindex[level].uncompressedByteLength = nindex[level].byteLength =
+                                                                //levelByteLength;
+        }
+
+#if IS_BIG_ENDIAN
+        switch (prtctd->_typeSize) {
+          case 2:
+            _ktxSwapEndian16((ktx_uint16_t*)pData, levelSize / 2);
+            break;
+          case 4:
+            _ktxSwapEndian32((ktx_uint32_t*)pDest, levelSize / 4);
+            break;
+          case 8:
+            _ktxSwapEndian64((ktx_uint64_t*)pDest, levelSize / 8);
+            break;
+        }
+#endif
+
+        // With the exception of non-array cubemaps the entire level
+        // is passed at once because that is how OpenGL and Vulkan need them.
+        // Vulkan could take all the faces at once too but we iterate
+        // them separately for OpenGL.
+        if (This->isCubemap && !This->isArray) {
+            ktx_uint8_t* pFace = pData;
+            struct blockCount {
+                ktx_uint32_t x, y;
+            } blockCount;
+            ktx_size_t faceSize;
+
+            blockCount.x
+              = (uint32_t)ceilf((float)width / prtctd->_formatSize.blockWidth);
+            blockCount.y
+              = (uint32_t)ceilf((float)height / prtctd->_formatSize.blockHeight);
+            blockCount.x = MAX(prtctd->_formatSize.minBlocksX, blockCount.x);
+            blockCount.y = MAX(prtctd->_formatSize.minBlocksX, blockCount.y);
+            faceSize = blockCount.x * blockCount.y
+                       * prtctd->_formatSize.blockSizeInBits / 8;
+
+            for (ktx_uint32_t face = 0; face < This->numFaces; ++face) {
+                result = iterCb(level, face,
+                                width, height, depth,
+                                (ktx_uint32_t)faceSize, pFace, userdata);
+                pFace += faceSize;
+                if (result != KTX_SUCCESS)
+                    goto cleanup;
+            }
+        } else {
+            result = iterCb(level, 0,
+                             width, height, depth,
+                             (ktx_uint32_t)levelSize, pData, userdata);
+            if (result != KTX_SUCCESS)
+                goto cleanup;
+       }
+    }
+
+    // No further need for this.
+    stream->destruct(stream);
+    This->_private->_firstLevelFileOffset = 0;
+cleanup:
+    free(dataBuf);
+    if (uncompressedDataBuf) free(uncompressedDataBuf);
+    if (dctx) ZSTD_freeDCtx(dctx);
+
+    return result;
+}
+
+KTX_error_code
+ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
+                           ktx_uint8_t* pInflatedData,
+                           ktx_size_t inflatedDataCapacity);
+/**
+ * @memberof ktxTexture2
+ * @~English
+ * @brief Load all the image data from the ktxTexture2's source.
+ *
+ * The data will be inflated if supercompressionScheme == SUPERCOMPRESSION_ZSTD.
+ * The data is loaded into the provided buffer or to an internally allocated
+ * buffer, if @p pBuffer is @c NULL. Callers providing their own buffer must
+ * ensure the buffer large enough to hold the inflated data for files deflated
+ * with Zstd. See ktxTexture2_GetDataSizeUncompressed().
+ *
+ * The texture's levelIndex, dataSize, DFD  and supercompressionScheme will
+ * all be updated after successful inflation to reflect the inflated data.
+ *
+ * @param[in] This pointer to the ktxTexture object of interest.
+ * @param[in] pBuffer pointer to the buffer in which to load the image data.
+ * @param[in] bufSize size of the buffer pointed at by @p pBuffer.
+ *
+ * @return      KTX_SUCCESS on success, other KTX_* enum values on error.
+ *
+ * @exception KTX_INVALID_VALUE @p This is NULL.
+ * @exception KTX_INVALID_VALUE @p bufSize is less than the the image data size.
+ * @exception KTX_INVALID_OPERATION
+ *                              The data has already been loaded or the
+ *                              ktxTexture was not created from a KTX source.
+ * @exception KTX_OUT_OF_MEMORY Insufficient memory for the image data.
+ */
+KTX_error_code
+ktxTexture2_LoadImageData(ktxTexture2* This,
+                          ktx_uint8_t* pBuffer, ktx_size_t bufSize)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    DECLARE_PRIVATE(ktxTexture2);
+    ktx_uint8_t*    pDest;
+    ktx_uint8_t*    pDeflatedData = 0;
+    ktx_uint8_t*    pReadBuf;
+    KTX_error_code  result = KTX_SUCCESS;
+    ktx_size_t inflatedDataCapacity = ktxTexture2_GetDataSizeUncompressed(This);
+
+    if (This == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (This->pData != NULL)
+        return KTX_INVALID_OPERATION; // Data already loaded.
+
+    if (prtctd->_stream.data.file == NULL)
+        // This Texture not created from a stream or images already loaded;
+        return KTX_INVALID_OPERATION;
+
+    if (pBuffer == NULL) {
+        This->pData = malloc(inflatedDataCapacity);
+        if (This->pData == NULL)
+            return KTX_OUT_OF_MEMORY;
+        pDest = This->pData;
+    } else if (bufSize < inflatedDataCapacity) {
+        return KTX_INVALID_VALUE;
+    } else {
+        pDest = pBuffer;
+    }
+
+    if (This->supercompressionScheme == KTX_SS_ZSTD) {
+        // Create buffer to hold deflated data.
+        pDeflatedData = malloc(This->dataSize);
+        if (pDeflatedData == NULL)
+            return KTX_OUT_OF_MEMORY;
+        pReadBuf = pDeflatedData;
+    } else {
+        pReadBuf = pDest;
+    }
+
+    // Seek to data for first level as there may be padding between the
+    // metadata/sgd and the image data.
+
+    result = prtctd->_stream.setpos(&prtctd->_stream,
+                                    private->_firstLevelFileOffset);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    result = prtctd->_stream.read(&prtctd->_stream, pReadBuf,
+                                  This->dataSize);
+    if (result != KTX_SUCCESS)
+        return result;
+
+    if (This->supercompressionScheme == KTX_SS_ZSTD) {
+        assert(pDeflatedData != NULL);
+        result = ktxTexture2_inflateZstdInt(This, pDeflatedData, pDest,
+                                            inflatedDataCapacity);
+        free(pDeflatedData);
+        if (result != KTX_SUCCESS) {
+            if (pBuffer == NULL) {
+                free(This->pData);
+                This->pData = 0;
+            }
+            return result;
+        }
+    }
+
+    if (IS_BIG_ENDIAN) {
+        // Perform endianness conversion on texture data.
+        // To avoid mip padding, need to convert each level individually.
+        for (ktx_uint32_t level = 0; level < This->numLevels; ++level)
+        {
+            ktx_size_t levelOffset;
+            ktx_size_t levelByteLength;
+
+            levelByteLength = private->_levelIndex[level].byteLength;
+            levelOffset = ktxTexture2_levelDataOffset(This, level);
+            pDest = This->pData + levelOffset;
+            switch (prtctd->_typeSize) {
+              case 2:
+                _ktxSwapEndian16((ktx_uint16_t*)pDest, levelByteLength / 2);
+                break;
+              case 4:
+                _ktxSwapEndian32((ktx_uint32_t*)pDest, levelByteLength / 4);
+                break;
+              case 8:
+                _ktxSwapEndian64((ktx_uint64_t*)pDest, levelByteLength / 8);
+                break;
+            }
+        }
+    }
+
+    // No further need for stream or file offset.
+    prtctd->_stream.destruct(&prtctd->_stream);
+    private->_firstLevelFileOffset = 0;
+    return result;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Retrieve the offset of a level's first image within the ktxTexture2's
+ *        image data.
+ *
+ * @param[in] This pointer to the ktxTexture2 object of interest.
+ */
+ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level)
+{
+    return This->_private->_levelIndex[level].byteOffset;
+}
+
+/**
+ * @memberof ktxTexture2 @private
+ * @~English
+ * @brief Inflate the data in a ktxTexture2 object using Zstandard.
+ *
+ * The texture's levelIndex, dataSize, DFD  and supercompressionScheme will
+ * all be updated after successful inflation to reflect the inflated data.
+ *
+ * @param[in] This                    pointer to the ktxTexture2 object of interest.
+ * @param[in] pDeflatedData pointer to a buffer containing the deflated data
+ *                         of the entire texture.
+ * @param[in,out] pInflatedData pointer to a buffer in which to write the inflated
+ *                             data.
+ * @param[in] inflatedDataCapacity capacity of the buffer pointed at by
+ *                                @p pInflatedData.
+ */
+KTX_error_code
+ktxTexture2_inflateZstdInt(ktxTexture2* This, ktx_uint8_t* pDeflatedData,
+                           ktx_uint8_t* pInflatedData,
+                           ktx_size_t inflatedDataCapacity)
+{
+    DECLARE_PROTECTED(ktxTexture);
+    ktx_uint32_t levelIndexByteLength =
+                            This->numLevels * sizeof(ktxLevelIndexEntry);
+    uint64_t levelOffset = 0;
+    ktxLevelIndexEntry* cindex = This->_private->_levelIndex;
+    ktxLevelIndexEntry* nindex;
+    ktx_uint32_t uncompressedLevelAlignment;
+
+    ZSTD_DCtx* dctx;
+
+    if (pDeflatedData == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (pInflatedData == NULL)
+        return KTX_INVALID_VALUE;
+
+    if (This->supercompressionScheme != KTX_SS_ZSTD)
+        return KTX_INVALID_OPERATION;
+
+    nindex = malloc(levelIndexByteLength);
+    if (nindex == NULL)
+        return KTX_OUT_OF_MEMORY;
+
+    uncompressedLevelAlignment =
+        ktxTexture2_calcPostInflationLevelAlignment(This);
+
+    ktx_size_t inflatedByteLength = 0;
+    dctx = ZSTD_createDCtx();
+    for (int32_t level = This->numLevels - 1; level >= 0; level--) {
+        size_t levelByteLength =
+            ZSTD_decompressDCtx(dctx, pInflatedData + levelOffset,
+                              inflatedDataCapacity,
+                              &pDeflatedData[cindex[level].byteOffset],
+                              cindex[level].byteLength);
+        if (ZSTD_isError(levelByteLength)) {
+            ZSTD_ErrorCode error = ZSTD_getErrorCode(levelByteLength);
+            switch(error) {
+              case ZSTD_error_dstSize_tooSmall:
+                return KTX_INVALID_VALUE; // inflatedDataCapacity too small.
+              case ZSTD_error_memory_allocation:
+                return KTX_OUT_OF_MEMORY;
+              default:
+                return KTX_FILE_DATA_ERROR;
+            }
+        }
+        nindex[level].byteOffset = levelOffset;
+        nindex[level].uncompressedByteLength = nindex[level].byteLength =
+                                                            levelByteLength;
+        ktx_size_t paddedLevelByteLength
+              = _KTX_PADN(uncompressedLevelAlignment, levelByteLength);
+        inflatedByteLength += paddedLevelByteLength;
+        levelOffset += paddedLevelByteLength;
+        inflatedDataCapacity -= paddedLevelByteLength;
+    }
+    ZSTD_freeDCtx(dctx);
+
+    // Now modify the texture.
+
+    This->dataSize = inflatedByteLength;
+    This->supercompressionScheme = KTX_SS_NONE;
+    memcpy(cindex, nindex, levelIndexByteLength); // Update level index
+    free(nindex);
+    This->_private->_requiredLevelAlignment = uncompressedLevelAlignment;
+    // Set bytesPlane as we're now sized.
+    uint32_t* bdb = This->pDfd + 1;
+    // blockSizeInBits was set to the inflated size on file load.
+    bdb[KHR_DF_WORD_BYTESPLANE0] = prtctd->_formatSize.blockSizeInBits / 8;
+
+    return KTX_SUCCESS;
+}
+
+#if !KTX_FEATURE_WRITE
+
+/*
+ * Stubs for writer functions that return a proper error code
+ */
+
+KTX_error_code
+ktxTexture2_SetImageFromMemory(ktxTexture2* This, ktx_uint32_t level,
+                               ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                               const ktx_uint8_t* src, ktx_size_t srcSize)
+{
+    UNUSED(This);
+    UNUSED(level);
+    UNUSED(layer);
+    UNUSED(faceSlice);
+    UNUSED(src);
+    UNUSED(srcSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture2_SetImageFromStdioStream(ktxTexture2* This, ktx_uint32_t level,
+                                    ktx_uint32_t layer, ktx_uint32_t faceSlice,
+                                    FILE* src, ktx_size_t srcSize)
+{
+    UNUSED(This);
+    UNUSED(level);
+    UNUSED(layer);
+    UNUSED(faceSlice);
+    UNUSED(src);
+    UNUSED(srcSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture2_WriteToStdioStream(ktxTexture2* This, FILE* dstsstr)
+{
+    UNUSED(This);
+    UNUSED(dstsstr);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture2_WriteToNamedFile(ktxTexture2* This, const char* const dstname)
+{
+    UNUSED(This);
+    UNUSED(dstname);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture2_WriteToMemory(ktxTexture2* This,
+                          ktx_uint8_t** ppDstBytes, ktx_size_t* pSize)
+{
+    UNUSED(This);
+    UNUSED(ppDstBytes);
+    UNUSED(pSize);
+    return KTX_INVALID_OPERATION;
+}
+
+KTX_error_code
+ktxTexture2_WriteToStream(ktxTexture2* This,
+                          ktxStream* dststr)
+{
+    UNUSED(This);
+    UNUSED(dststr);
+    return KTX_INVALID_OPERATION;
+}
+
+#endif
+
+/*
+ * Initialized here at the end to avoid the need for multiple declarations of
+ * the virtual functions.
+ */
+
+struct ktxTexture_vtblInt ktxTexture2_vtblInt = {
+    (PFNCALCDATASIZELEVELS)ktxTexture2_calcDataSizeLevels,
+    (PFNCALCFACELODSIZE)ktxTexture2_calcFaceLodSize,
+    (PFNCALCLEVELOFFSET)ktxTexture2_calcLevelOffset
+};
+
+struct ktxTexture_vtbl ktxTexture2_vtbl = {
+    (PFNKTEXDESTROY)ktxTexture2_Destroy,
+    (PFNKTEXGETIMAGEOFFSET)ktxTexture2_GetImageOffset,
+    (PFNKTEXGETDATASIZEUNCOMPRESSED)ktxTexture2_GetDataSizeUncompressed,
+    (PFNKTEXGETIMAGESIZE)ktxTexture2_GetImageSize,
+    (PFNKTEXITERATELEVELS)ktxTexture2_IterateLevels,
+    (PFNKTEXITERATELOADLEVELFACES)ktxTexture2_IterateLoadLevelFaces,
+    (PFNKTEXNEEDSTRANSCODING)ktxTexture2_NeedsTranscoding,
+    (PFNKTEXLOADIMAGEDATA)ktxTexture2_LoadImageData,
+    (PFNKTEXSETIMAGEFROMMEMORY)ktxTexture2_SetImageFromMemory,
+    (PFNKTEXSETIMAGEFROMSTDIOSTREAM)ktxTexture2_SetImageFromStdioStream,
+    (PFNKTEXWRITETOSTDIOSTREAM)ktxTexture2_WriteToStdioStream,
+    (PFNKTEXWRITETONAMEDFILE)ktxTexture2_WriteToNamedFile,
+    (PFNKTEXWRITETOMEMORY)ktxTexture2_WriteToMemory,
+    (PFNKTEXWRITETOSTREAM)ktxTexture2_WriteToStream,
+};
+
+/** @} */
+
diff --git a/thirdparty/libktx/lib/texture2.h b/thirdparty/libktx/lib/texture2.h
new file mode 100644
index 00000000000..14e4115ecf7
--- /dev/null
+++ b/thirdparty/libktx/lib/texture2.h
@@ -0,0 +1,68 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab textwidth=70: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture2.h
+ * @~English
+ *
+ * @brief Declare internal ktxTexture2 functions for sharing between
+ *        compilation units.
+ *
+ * These functions are private and should not be used outside the library.
+ */
+
+#ifndef _TEXTURE2_H_
+#define _TEXTURE2_H_
+
+#include "texture.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define CLASS ktxTexture2
+#include "texture_funcs.inl"
+#undef CLASS
+
+typedef struct ktxTexture2_private {
+    ktx_uint8_t* _supercompressionGlobalData;
+    ktx_uint32_t _requiredLevelAlignment;
+    ktx_uint64_t _sgdByteLength;
+    ktx_uint64_t _firstLevelFileOffset; /*!< Always 0, unless the texture was
+                                         created from a stream and the image
+                                         data is not yet loaded. */
+    // Must be last so it can grow.
+    ktxLevelIndexEntry _levelIndex[1]; /*!< Offsets in this index are from the
+                                        start of the image data. Use
+                                        ktxTexture_levelStreamOffset() and
+                                        ktxTexture_levelDataOffset(). The former
+                                        will add the above file offset to the
+                                        index offset. */
+} ktxTexture2_private;
+
+KTX_error_code
+ktxTexture2_LoadImageData(ktxTexture2* This,
+                          ktx_uint8_t* pBuffer, ktx_size_t bufSize);
+
+KTX_error_code
+ktxTexture2_constructFromStreamAndHeader(ktxTexture2* This, ktxStream* pStream,
+                                         KTX_header2* pHeader,
+                                         ktxTextureCreateFlags createFlags);
+
+ktx_uint64_t ktxTexture2_calcDataSizeTexture(ktxTexture2* This);
+ktx_size_t ktxTexture2_calcLevelOffset(ktxTexture2* This, ktx_uint32_t level);
+ktx_uint32_t ktxTexture2_calcRequiredLevelAlignment(ktxTexture2* This);
+ktx_uint64_t ktxTexture2_levelFileOffset(ktxTexture2* This, ktx_uint32_t level);
+ktx_uint64_t ktxTexture2_levelDataOffset(ktxTexture2* This, ktx_uint32_t level);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _TEXTURE2_H_ */
diff --git a/thirdparty/libktx/lib/texture_funcs.inl b/thirdparty/libktx/lib/texture_funcs.inl
new file mode 100644
index 00000000000..5de16a396b9
--- /dev/null
+++ b/thirdparty/libktx/lib/texture_funcs.inl
@@ -0,0 +1,76 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab textwidth=70: */
+
+/*
+ * Copyright 2019-2020 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/**
+ * @internal
+ * @file texture_funcs.h
+ * @~English
+ *
+ * @brief Templates for functions common to base & derived ktxTexture classes.
+ *
+ * Define CLASS before including this file.
+ */
+
+#define CAT(c, n) PRIMITIVE_CAT(c, n)
+#define PRIMITIVE_CAT(c, n) c ## _ ## n
+
+#define CLASS_FUNC(name) CAT(CLASS, name)
+
+/*
+ ======================================
+     Virtual ktxTexture functions
+ ======================================
+*/
+
+
+void CLASS_FUNC(Destroy)(CLASS* This);
+KTX_error_code CLASS_FUNC(GetImageOffset)(CLASS* This, ktx_uint32_t level,
+                                          ktx_uint32_t layer,
+                                          ktx_uint32_t faceSlice,
+                                          ktx_size_t* pOffset);
+ktx_size_t CLASS_FUNC(GetImageSize)(CLASS* This, ktx_uint32_t level);
+KTX_error_code CLASS_FUNC(GLUpload)(CLASS* This, GLuint* pTexture,
+                                    GLenum* pTarget, GLenum* pGlerror);
+KTX_error_code CLASS_FUNC(IterateLevels)(CLASS* This,
+                                         PFNKTXITERCB iterCb,
+                                         void* userdata);
+KTX_error_code CLASS_FUNC(IterateLevelFaces)(CLASS* This,
+                                             PFNKTXITERCB iterCb,
+                                             void* userdata);
+KTX_error_code CLASS_FUNC(IterateLoadLevelFaces)(CLASS* This,
+                                                 PFNKTXITERCB iterCb,
+                                                 void* userdata);
+KTX_error_code CLASS_FUNC(LoadImageData)(CLASS* This,
+                                         ktx_uint8_t* pBuffer,
+                                         ktx_size_t bufSize);
+KTX_error_code CLASS_FUNC(SetImageFromStdioStream)(CLASS* This,
+                                    ktx_uint32_t level,ktx_uint32_t layer,
+                                    ktx_uint32_t faceSlice,
+                                    FILE* src, ktx_size_t srcSize);
+KTX_error_code CLASS_FUNC(SetImageFromMemory)(CLASS* This,
+                               ktx_uint32_t level, ktx_uint32_t layer,
+                               ktx_uint32_t faceSlice,
+                               const ktx_uint8_t* src, ktx_size_t srcSize);
+
+KTX_error_code CLASS_FUNC(WriteToStdioStream)(CLASS* This, FILE* dstsstr);
+KTX_error_code CLASS_FUNC(WriteToNamedFile)(CLASS* This,
+                                             const char* const dstname);
+KTX_error_code CLASS_FUNC(WriteToMemory)(CLASS* This,
+                          ktx_uint8_t** ppDstBytes, ktx_size_t* pSize);
+KTX_error_code CLASS_FUNC(WriteToStream)(CLASS* This,
+                          ktxStream* dststr);
+
+/*
+ ======================================
+     Internal ktxTexture functions
+ ======================================
+*/
+
+
+void CLASS_FUNC(destruct)(CLASS* This);
+
diff --git a/thirdparty/libktx/lib/uthash.h b/thirdparty/libktx/lib/uthash.h
new file mode 100644
index 00000000000..aa4729027cb
--- /dev/null
+++ b/thirdparty/libktx/lib/uthash.h
@@ -0,0 +1,942 @@
+/*
+Copyright (c) 2003-2010, Troy D. Hanson     http://uthash.sourceforge.net All rights reserved.
+SPDX-License-Identifier: BSD-1-Clause
+*/
+
+#ifndef UTHASH_H
+#define UTHASH_H
+
+#include <string.h>   /* memcmp,strlen */
+#include <stddef.h>   /* ptrdiff_t */
+
+/* These macros use decltype or the earlier __typeof GNU extension.
+   As decltype is only available in newer compilers (VS2010 or gcc 4.3+
+   when compiling c++ source) this code uses whatever method is needed
+   or, for VS2008 where neither is available, uses casting workarounds. */
+#ifdef _MSC_VER         /* MS compiler */
+#if _MSC_VER >= 1600 && __cplusplus  /* VS2010 or newer in C++ mode */
+#define DECLTYPE(x) (decltype(x))
+#else                   /* VS2008 or older (or VS2010 in C mode) */
+#define NO_DECLTYPE
+#define DECLTYPE(x)
+#endif
+#else                   /* GNU, Sun and other compilers */
+#define DECLTYPE(x) (__typeof(x))
+#endif
+
+#ifdef NO_DECLTYPE
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  char **_da_dst = (char**)(&(dst));                                             \
+  *_da_dst = (char*)(src);                                                       \
+} while(0)
+#else
+#define DECLTYPE_ASSIGN(dst,src)                                                 \
+do {                                                                             \
+  (dst) = DECLTYPE(dst)(src);                                                    \
+} while(0)
+#endif
+
+/* a number of the hash function use uint32_t which isn't defined on win32 */
+#ifdef _MSC_VER
+typedef unsigned int uint32_t;
+#else
+#include <inttypes.h>   /* uint32_t */
+#endif
+
+#define UTHASH_VERSION 1.9.1
+
+#define uthash_fatal(msg) exit(-1)        /* fatal error (out of memory,etc) */
+#define uthash_malloc(sz) malloc(sz)      /* malloc fcn                      */
+#define uthash_free(ptr) free(ptr)        /* free fcn                        */
+
+#define uthash_noexpand_fyi(tbl)          /* can be defined to log noexpand  */
+#define uthash_expand_fyi(tbl)            /* can be defined to log expands   */
+
+/* initial number of buckets */
+#define HASH_INITIAL_NUM_BUCKETS 32      /* initial number of buckets        */
+#define HASH_INITIAL_NUM_BUCKETS_LOG2 5  /* lg2 of initial number of buckets */
+#define HASH_BKT_CAPACITY_THRESH 10      /* expand when bucket count reaches */
+
+/* calculate the element whose hash handle address is hhe */
+#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho)))
+
+#define HASH_FIND(hh,head,keyptr,keylen,out)                                     \
+do {                                                                             \
+  unsigned _hf_bkt,_hf_hashv;                                                    \
+  out=NULL;                                                                      \
+  if (head) {                                                                    \
+     HASH_FCN(keyptr,keylen, (head)->hh.tbl->num_buckets, _hf_hashv, _hf_bkt);   \
+     if (HASH_BLOOM_TEST((head)->hh.tbl, _hf_hashv)) {                           \
+       HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ],  \
+                        keyptr,keylen,out);                                      \
+     }                                                                           \
+  }                                                                              \
+} while (0)
+
+#ifdef HASH_BLOOM
+#define HASH_BLOOM_BITLEN (1ULL << HASH_BLOOM)
+#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8) + ((HASH_BLOOM_BITLEN%8) ? 1:0)
+#define HASH_BLOOM_MAKE(tbl)                                                     \
+do {                                                                             \
+  (tbl)->bloom_nbits = HASH_BLOOM;                                               \
+  (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN);                 \
+  if (!((tbl)->bloom_bv))  { uthash_fatal( "out of memory"); }                   \
+  memset((tbl)->bloom_bv, 0, HASH_BLOOM_BYTELEN);                                \
+  (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE;                                       \
+} while (0);
+
+#define HASH_BLOOM_FREE(tbl)                                                     \
+do {                                                                             \
+  uthash_free((tbl)->bloom_bv);                                                  \
+} while (0);
+
+#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8] |= (1U << ((idx)%8)))
+#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8] & (1U << ((idx)%8)))
+
+#define HASH_BLOOM_ADD(tbl,hashv)                                                \
+  HASH_BLOOM_BITSET((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#define HASH_BLOOM_TEST(tbl,hashv)                                               \
+  HASH_BLOOM_BITTEST((tbl)->bloom_bv, (hashv & (uint32_t)((1ULL << (tbl)->bloom_nbits) - 1)))
+
+#else
+#define HASH_BLOOM_MAKE(tbl)
+#define HASH_BLOOM_FREE(tbl)
+#define HASH_BLOOM_ADD(tbl,hashv)
+#define HASH_BLOOM_TEST(tbl,hashv) (1)
+#endif
+
+#define HASH_MAKE_TABLE(hh,head)                                                 \
+do {                                                                             \
+  (head)->hh.tbl = (UT_hash_table*)uthash_malloc(                                \
+                  sizeof(UT_hash_table));                                        \
+  if (!((head)->hh.tbl))  { uthash_fatal( "out of memory"); }                    \
+  memset((head)->hh.tbl, 0, sizeof(UT_hash_table));                              \
+  (head)->hh.tbl->tail = &((head)->hh);                                          \
+  (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS;                        \
+  (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2;              \
+  (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head);                    \
+  (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc(                      \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  if (! (head)->hh.tbl->buckets) { uthash_fatal( "out of memory"); }             \
+  memset((head)->hh.tbl->buckets, 0,                                             \
+          HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket));               \
+  HASH_BLOOM_MAKE((head)->hh.tbl);                                               \
+  (head)->hh.tbl->signature = HASH_SIGNATURE;                                    \
+} while(0)
+
+#define HASH_ADD(hh,head,fieldname,keylen_in,add)                                \
+        HASH_ADD_KEYPTR(hh,head,&add->fieldname,keylen_in,add)
+
+#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add)                            \
+do {                                                                             \
+ unsigned _ha_bkt;                                                               \
+ (add)->hh.next = NULL;                                                          \
+ (add)->hh.key = (char*)keyptr;                                                  \
+ (add)->hh.keylen = keylen_in;                                                   \
+ if (!(head)) {                                                                  \
+    head = (add);                                                                \
+    (head)->hh.prev = NULL;                                                      \
+    HASH_MAKE_TABLE(hh,head);                                                    \
+ } else {                                                                        \
+    (head)->hh.tbl->tail->next = (add);                                          \
+    (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail);         \
+    (head)->hh.tbl->tail = &((add)->hh);                                         \
+ }                                                                               \
+ (head)->hh.tbl->num_items++;                                                    \
+ (add)->hh.tbl = (head)->hh.tbl;                                                 \
+ HASH_FCN(keyptr,keylen_in, (head)->hh.tbl->num_buckets,                         \
+         (add)->hh.hashv, _ha_bkt);                                              \
+ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt],&(add)->hh);                   \
+ HASH_BLOOM_ADD((head)->hh.tbl,(add)->hh.hashv);                                 \
+ HASH_EMIT_KEY(hh,head,keyptr,keylen_in);                                        \
+ HASH_FSCK(hh,head);                                                             \
+} while(0)
+
+#define HASH_TO_BKT( hashv, num_bkts, bkt )                                      \
+do {                                                                             \
+  bkt = ((hashv) & ((num_bkts) - 1));                                            \
+} while(0)
+
+/* delete "delptr" from the hash table.
+ * "the usual" patch-up process for the app-order doubly-linked-list.
+ * The use of _hd_hh_del below deserves special explanation.
+ * These used to be expressed using (delptr) but that led to a bug
+ * if someone used the same symbol for the head and deletee, like
+ *  HASH_DELETE(hh,users,users);
+ * We want that to work, but by changing the head (users) below
+ * we were forfeiting our ability to further refer to the deletee (users)
+ * in the patch-up process. Solution: use scratch space to
+ * copy the deletee pointer, then the latter references are via that
+ * scratch pointer rather than through the repointed (users) symbol.
+ */
+#define HASH_DELETE(hh,head,delptr)                                              \
+do {                                                                             \
+    unsigned _hd_bkt;                                                            \
+    struct UT_hash_handle *_hd_hh_del;                                           \
+    if ( ((delptr)->hh.prev == NULL) && ((delptr)->hh.next == NULL) )  {         \
+        uthash_free((head)->hh.tbl->buckets );                                   \
+        HASH_BLOOM_FREE((head)->hh.tbl);                                         \
+        uthash_free((head)->hh.tbl);                                             \
+        head = NULL;                                                             \
+    } else {                                                                     \
+        _hd_hh_del = &((delptr)->hh);                                            \
+        if ((delptr) == ELMT_FROM_HH((head)->hh.tbl,(head)->hh.tbl->tail)) {     \
+            (head)->hh.tbl->tail =                                               \
+                (UT_hash_handle*)((char*)((delptr)->hh.prev) +                   \
+                (head)->hh.tbl->hho);                                            \
+        }                                                                        \
+        if ((delptr)->hh.prev) {                                                 \
+            ((UT_hash_handle*)((char*)((delptr)->hh.prev) +                      \
+                    (head)->hh.tbl->hho))->next = (delptr)->hh.next;             \
+        } else {                                                                 \
+            DECLTYPE_ASSIGN(head,(delptr)->hh.next);                             \
+        }                                                                        \
+        if (_hd_hh_del->next) {                                                  \
+            ((UT_hash_handle*)((char*)_hd_hh_del->next +                         \
+                    (head)->hh.tbl->hho))->prev =                                \
+                    _hd_hh_del->prev;                                            \
+        }                                                                        \
+        HASH_TO_BKT( _hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt);   \
+        HASH_DEL_IN_BKT(hh,(head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del);        \
+        (head)->hh.tbl->num_items--;                                             \
+    }                                                                            \
+    HASH_FSCK(hh,head);                                                          \
+} while (0)
+
+
+/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */
+#define HASH_FIND_STR(head,findstr,out)                                          \
+    HASH_FIND(hh,head,findstr,strlen(findstr),out)
+#define HASH_ADD_STR(head,strfield,add)                                          \
+    HASH_ADD(hh,head,strfield,strlen(add->strfield),add)
+#define HASH_FIND_INT(head,findint,out)                                          \
+    HASH_FIND(hh,head,findint,sizeof(int),out)
+#define HASH_ADD_INT(head,intfield,add)                                          \
+    HASH_ADD(hh,head,intfield,sizeof(int),add)
+#define HASH_FIND_PTR(head,findptr,out)                                          \
+    HASH_FIND(hh,head,findptr,sizeof(void *),out)
+#define HASH_ADD_PTR(head,ptrfield,add)                                          \
+    HASH_ADD(hh,head,ptrfield,sizeof(void *),add)
+#define HASH_DEL(head,delptr)                                                    \
+    HASH_DELETE(hh,head,delptr)
+
+/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined.
+ * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined.
+ */
+#ifdef HASH_DEBUG
+#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0)
+#define HASH_FSCK(hh,head)                                                       \
+do {                                                                             \
+    unsigned _bkt_i;                                                             \
+    unsigned _count, _bkt_count;                                                 \
+    char *_prev;                                                                 \
+    struct UT_hash_handle *_thh;                                                 \
+    if (head) {                                                                  \
+        _count = 0;                                                              \
+        for( _bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; _bkt_i++) {       \
+            _bkt_count = 0;                                                      \
+            _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head;                      \
+            _prev = NULL;                                                        \
+            while (_thh) {                                                       \
+               if (_prev != (char*)(_thh->hh_prev)) {                            \
+                   HASH_OOPS("invalid hh_prev %p, actual %p\n",                  \
+                    _thh->hh_prev, _prev );                                      \
+               }                                                                 \
+               _bkt_count++;                                                     \
+               _prev = (char*)(_thh);                                            \
+               _thh = _thh->hh_next;                                             \
+            }                                                                    \
+            _count += _bkt_count;                                                \
+            if ((head)->hh.tbl->buckets[_bkt_i].count !=  _bkt_count) {          \
+               HASH_OOPS("invalid bucket count %d, actual %d\n",                 \
+                (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count);              \
+            }                                                                    \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid hh item count %d, actual %d\n",                   \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+        /* traverse hh in app order; check next/prev integrity, count */         \
+        _count = 0;                                                              \
+        _prev = NULL;                                                            \
+        _thh =  &(head)->hh;                                                     \
+        while (_thh) {                                                           \
+           _count++;                                                             \
+           if (_prev !=(char*)(_thh->prev)) {                                    \
+              HASH_OOPS("invalid prev %p, actual %p\n",                          \
+                    _thh->prev, _prev );                                         \
+           }                                                                     \
+           _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh);                    \
+           _thh = ( _thh->next ?  (UT_hash_handle*)((char*)(_thh->next) +        \
+                                  (head)->hh.tbl->hho) : NULL );                 \
+        }                                                                        \
+        if (_count != (head)->hh.tbl->num_items) {                               \
+            HASH_OOPS("invalid app item count %d, actual %d\n",                  \
+                (head)->hh.tbl->num_items, _count );                             \
+        }                                                                        \
+    }                                                                            \
+} while (0)
+#else
+#define HASH_FSCK(hh,head)
+#endif
+
+/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to
+ * the descriptor to which this macro is defined for tuning the hash function.
+ * The app can #include <unistd.h> to get the prototype for write(2). */
+#ifdef HASH_EMIT_KEYS
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)                                   \
+do {                                                                             \
+    unsigned _klen = fieldlen;                                                   \
+    write(HASH_EMIT_KEYS, &_klen, sizeof(_klen));                                \
+    write(HASH_EMIT_KEYS, keyptr, fieldlen);                                     \
+} while (0)
+#else
+#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen)
+#endif
+
+/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */
+#ifdef HASH_FUNCTION
+#define HASH_FCN HASH_FUNCTION
+#else
+#define HASH_FCN HASH_JEN
+#endif
+
+/* The Bernstein hash function, used in Perl prior to v5.6 */
+#define HASH_BER(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _hb_keylen=keylen;                                                    \
+  char *_hb_key=(char*)key;                                                      \
+  (hashv) = 0;                                                                   \
+  while (_hb_keylen--)  { (hashv) = ((hashv) * 33) + *_hb_key++; }               \
+  bkt = (hashv) & (num_bkts-1);                                                  \
+} while (0)
+
+
+/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at
+ * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */
+#define HASH_SAX(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _sx_i;                                                                \
+  char *_hs_key=(char*)key;                                                      \
+  hashv = 0;                                                                     \
+  for(_sx_i=0; _sx_i < keylen; _sx_i++)                                          \
+      hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i];                     \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while (0)
+
+#define HASH_FNV(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _fn_i;                                                                \
+  char *_hf_key=(char*)key;                                                      \
+  hashv = 2166136261UL;                                                          \
+  for(_fn_i=0; _fn_i < keylen; _fn_i++)                                          \
+      hashv = (hashv * 16777619) ^ _hf_key[_fn_i];                               \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0);
+
+#define HASH_OAT(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _ho_i;                                                                \
+  char *_ho_key=(char*)key;                                                      \
+  hashv = 0;                                                                     \
+  for(_ho_i=0; _ho_i < keylen; _ho_i++) {                                        \
+      hashv += _ho_key[_ho_i];                                                   \
+      hashv += (hashv << 10);                                                    \
+      hashv ^= (hashv >> 6);                                                     \
+  }                                                                              \
+  hashv += (hashv << 3);                                                         \
+  hashv ^= (hashv >> 11);                                                        \
+  hashv += (hashv << 15);                                                        \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+#define HASH_JEN_MIX(a,b,c)                                                      \
+do {                                                                             \
+  a -= b; a -= c; a ^= ( c >> 13 );                                              \
+  b -= c; b -= a; b ^= ( a << 8 );                                               \
+  c -= a; c -= b; c ^= ( b >> 13 );                                              \
+  a -= b; a -= c; a ^= ( c >> 12 );                                              \
+  b -= c; b -= a; b ^= ( a << 16 );                                              \
+  c -= a; c -= b; c ^= ( b >> 5 );                                               \
+  a -= b; a -= c; a ^= ( c >> 3 );                                               \
+  b -= c; b -= a; b ^= ( a << 10 );                                              \
+  c -= a; c -= b; c ^= ( b >> 15 );                                              \
+} while (0)
+
+#define HASH_JEN(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  unsigned _hj_i,_hj_j,_hj_k;                                                    \
+  char *_hj_key=(char*)key;                                                      \
+  hashv = 0xfeedbeef;                                                            \
+  _hj_i = _hj_j = 0x9e3779b9;                                                    \
+  _hj_k = keylen;                                                                \
+  while (_hj_k >= 12) {                                                          \
+    _hj_i +=    (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 )                      \
+        + ( (unsigned)_hj_key[2] << 16 )                                         \
+        + ( (unsigned)_hj_key[3] << 24 ) );                                      \
+    _hj_j +=    (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 )                      \
+        + ( (unsigned)_hj_key[6] << 16 )                                         \
+        + ( (unsigned)_hj_key[7] << 24 ) );                                      \
+    hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 )                         \
+        + ( (unsigned)_hj_key[10] << 16 )                                        \
+        + ( (unsigned)_hj_key[11] << 24 ) );                                     \
+                                                                                 \
+     HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                          \
+                                                                                 \
+     _hj_key += 12;                                                              \
+     _hj_k -= 12;                                                                \
+  }                                                                              \
+  hashv += keylen;                                                               \
+  switch ( _hj_k ) {                                                             \
+     case 11: hashv += ( (unsigned)_hj_key[10] << 24 );                          \
+     case 10: hashv += ( (unsigned)_hj_key[9] << 16 );                           \
+     case 9:  hashv += ( (unsigned)_hj_key[8] << 8 );                            \
+     case 8:  _hj_j += ( (unsigned)_hj_key[7] << 24 );                           \
+     case 7:  _hj_j += ( (unsigned)_hj_key[6] << 16 );                           \
+     case 6:  _hj_j += ( (unsigned)_hj_key[5] << 8 );                            \
+     case 5:  _hj_j += _hj_key[4];                                               \
+     case 4:  _hj_i += ( (unsigned)_hj_key[3] << 24 );                           \
+     case 3:  _hj_i += ( (unsigned)_hj_key[2] << 16 );                           \
+     case 2:  _hj_i += ( (unsigned)_hj_key[1] << 8 );                            \
+     case 1:  _hj_i += _hj_key[0];                                               \
+  }                                                                              \
+  HASH_JEN_MIX(_hj_i, _hj_j, hashv);                                             \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+/* The Paul Hsieh hash function */
+#undef get16bits
+#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__)             \
+  || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
+#define get16bits(d) (*((const uint16_t *) (d)))
+#endif
+
+#if !defined (get16bits)
+#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)             \
+                       +(uint32_t)(((const uint8_t *)(d))[0]) )
+#endif
+#define HASH_SFH(key,keylen,num_bkts,hashv,bkt)                                  \
+do {                                                                             \
+  char *_sfh_key=(char*)key;                                                     \
+  uint32_t _sfh_tmp, _sfh_len = keylen;                                          \
+                                                                                 \
+  int _sfh_rem = _sfh_len & 3;                                                   \
+  _sfh_len >>= 2;                                                                \
+  hashv = 0xcafebabe;                                                            \
+                                                                                 \
+  /* Main loop */                                                                \
+  for (;_sfh_len > 0; _sfh_len--) {                                              \
+    hashv    += get16bits (_sfh_key);                                            \
+    _sfh_tmp       = (get16bits (_sfh_key+2) << 11) ^ hashv;                     \
+    hashv     = (hashv << 16) ^ _sfh_tmp;                                        \
+    _sfh_key += 2*sizeof (uint16_t);                                             \
+    hashv    += hashv >> 11;                                                     \
+  }                                                                              \
+                                                                                 \
+  /* Handle end cases */                                                         \
+  switch (_sfh_rem) {                                                            \
+    case 3: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 16;                                                \
+            hashv ^= _sfh_key[sizeof (uint16_t)] << 18;                          \
+            hashv += hashv >> 11;                                                \
+            break;                                                               \
+    case 2: hashv += get16bits (_sfh_key);                                       \
+            hashv ^= hashv << 11;                                                \
+            hashv += hashv >> 17;                                                \
+            break;                                                               \
+    case 1: hashv += *_sfh_key;                                                  \
+            hashv ^= hashv << 10;                                                \
+            hashv += hashv >> 1;                                                 \
+  }                                                                              \
+                                                                                 \
+    /* Force "avalanching" of final 127 bits */                                  \
+    hashv ^= hashv << 3;                                                         \
+    hashv += hashv >> 5;                                                         \
+    hashv ^= hashv << 4;                                                         \
+    hashv += hashv >> 17;                                                        \
+    hashv ^= hashv << 25;                                                        \
+    hashv += hashv >> 6;                                                         \
+    bkt = hashv & (num_bkts-1);                                                  \
+} while(0);
+
+#ifdef HASH_USING_NO_STRICT_ALIASING
+/* The MurmurHash exploits some CPU's (e.g. x86) tolerance for unaligned reads.
+ * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error.
+ * So MurmurHash comes in two versions, the faster unaligned one and the slower
+ * aligned one. We only use the faster one on CPU's where we know it's safe.
+ *
+ * Note the preprocessor built-in defines can be emitted using:
+ *
+ *   gcc -m64 -dM -E - < /dev/null                  (on gcc)
+ *   cc -## a.c (where a.c is a simple test file)   (Sun Studio)
+ */
+#if (defined(__i386__) || defined(__x86_64__))
+#define HASH_MUR HASH_MUR_UNALIGNED
+#else
+#define HASH_MUR HASH_MUR_ALIGNED
+#endif
+
+/* Appleby's MurmurHash fast version for unaligned-tolerant archs like i386 */
+#define HASH_MUR_UNALIGNED(key,keylen,num_bkts,hashv,bkt)                        \
+do {                                                                             \
+  const unsigned int _mur_m = 0x5bd1e995;                                        \
+  const int _mur_r = 24;                                                         \
+  hashv = 0xcafebabe ^ keylen;                                                   \
+  char *_mur_key = (char *)key;                                                  \
+  uint32_t _mur_tmp, _mur_len = keylen;                                          \
+                                                                                 \
+  for (;_mur_len >= 4; _mur_len-=4) {                                            \
+    _mur_tmp = *(uint32_t *)_mur_key;                                            \
+    _mur_tmp *= _mur_m;                                                          \
+    _mur_tmp ^= _mur_tmp >> _mur_r;                                              \
+    _mur_tmp *= _mur_m;                                                          \
+    hashv *= _mur_m;                                                             \
+    hashv ^= _mur_tmp;                                                           \
+    _mur_key += 4;                                                               \
+  }                                                                              \
+                                                                                 \
+  switch(_mur_len)                                                               \
+  {                                                                              \
+    case 3: hashv ^= _mur_key[2] << 16;                                          \
+    case 2: hashv ^= _mur_key[1] << 8;                                           \
+    case 1: hashv ^= _mur_key[0];                                                \
+            hashv *= _mur_m;                                                     \
+  };                                                                             \
+                                                                                 \
+  hashv ^= hashv >> 13;                                                          \
+  hashv *= _mur_m;                                                               \
+  hashv ^= hashv >> 15;                                                          \
+                                                                                 \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+
+/* Appleby's MurmurHash version for alignment-sensitive archs like Sparc */
+#define HASH_MUR_ALIGNED(key,keylen,num_bkts,hashv,bkt)                          \
+do {                                                                             \
+  const unsigned int _mur_m = 0x5bd1e995;                                        \
+  const int _mur_r = 24;                                                         \
+  hashv = 0xcafebabe ^ keylen;                                                   \
+  char *_mur_key = (char *)key;                                                  \
+  uint32_t _mur_len = keylen;                                                    \
+  int _mur_align = (int)_mur_key & 3;                                            \
+                                                                                 \
+  if (_mur_align && (_mur_len >= 4)) {                                           \
+    unsigned _mur_t = 0, _mur_d = 0;                                             \
+    switch(_mur_align) {                                                         \
+      case 1: _mur_t |= _mur_key[2] << 16;                                       \
+      case 2: _mur_t |= _mur_key[1] << 8;                                        \
+      case 3: _mur_t |= _mur_key[0];                                             \
+    }                                                                            \
+    _mur_t <<= (8 * _mur_align);                                                 \
+    _mur_key += 4-_mur_align;                                                    \
+    _mur_len -= 4-_mur_align;                                                    \
+    int _mur_sl = 8 * (4-_mur_align);                                            \
+    int _mur_sr = 8 * _mur_align;                                                \
+                                                                                 \
+    for (;_mur_len >= 4; _mur_len-=4) {                                          \
+      _mur_d = *(unsigned *)_mur_key;                                            \
+      _mur_t = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);                        \
+      unsigned _mur_k = _mur_t;                                                  \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_t = _mur_d;                                                           \
+      _mur_key += 4;                                                             \
+    }                                                                            \
+    _mur_d = 0;                                                                  \
+    if(_mur_len >= _mur_align) {                                                 \
+      switch(_mur_align) {                                                       \
+        case 3: _mur_d |= _mur_key[2] << 16;                                     \
+        case 2: _mur_d |= _mur_key[1] << 8;                                      \
+        case 1: _mur_d |= _mur_key[0];                                           \
+      }                                                                          \
+      unsigned _mur_k = (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);               \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_k += _mur_align;                                                      \
+      _mur_len -= _mur_align;                                                    \
+                                                                                 \
+      switch(_mur_len)                                                           \
+      {                                                                          \
+        case 3: hashv ^= _mur_key[2] << 16;                                      \
+        case 2: hashv ^= _mur_key[1] << 8;                                       \
+        case 1: hashv ^= _mur_key[0];                                            \
+                hashv *= _mur_m;                                                 \
+      }                                                                          \
+    } else {                                                                     \
+      switch(_mur_len)                                                           \
+      {                                                                          \
+        case 3: _mur_d ^= _mur_key[2] << 16;                                     \
+        case 2: _mur_d ^= _mur_key[1] << 8;                                      \
+        case 1: _mur_d ^= _mur_key[0];                                           \
+        case 0: hashv ^= (_mur_t >> _mur_sr) | (_mur_d << _mur_sl);              \
+        hashv *= _mur_m;                                                         \
+      }                                                                          \
+    }                                                                            \
+                                                                                 \
+    hashv ^= hashv >> 13;                                                        \
+    hashv *= _mur_m;                                                             \
+    hashv ^= hashv >> 15;                                                        \
+  } else {                                                                       \
+    for (;_mur_len >= 4; _mur_len-=4) {                                          \
+      unsigned _mur_k = *(unsigned*)_mur_key;                                    \
+      _mur_k *= _mur_m;                                                          \
+      _mur_k ^= _mur_k >> _mur_r;                                                \
+      _mur_k *= _mur_m;                                                          \
+      hashv *= _mur_m;                                                           \
+      hashv ^= _mur_k;                                                           \
+      _mur_key += 4;                                                             \
+    }                                                                            \
+    switch(_mur_len)                                                             \
+    {                                                                            \
+      case 3: hashv ^= _mur_key[2] << 16;                                        \
+      case 2: hashv ^= _mur_key[1] << 8;                                         \
+      case 1: hashv ^= _mur_key[0];                                              \
+      hashv *= _mur_m;                                                           \
+    }                                                                            \
+                                                                                 \
+    hashv ^= hashv >> 13;                                                        \
+    hashv *= _mur_m;                                                             \
+    hashv ^= hashv >> 15;                                                        \
+  }                                                                              \
+  bkt = hashv & (num_bkts-1);                                                    \
+} while(0)
+#endif  /* HASH_USING_NO_STRICT_ALIASING */
+
+/* key comparison function; return 0 if keys equal */
+#define HASH_KEYCMP(a,b,len) memcmp(a,b,len)
+
+/* iterate over items in a known bucket to find desired item */
+#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,out)                       \
+do {                                                                             \
+ if (head.hh_head) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,head.hh_head));          \
+ else out=NULL;                                                                  \
+ while (out) {                                                                   \
+    if (out->hh.keylen == keylen_in) {                                           \
+        if ((HASH_KEYCMP(out->hh.key,keyptr,keylen_in)) == 0) break;             \
+    }                                                                            \
+    if (out->hh.hh_next) DECLTYPE_ASSIGN(out,ELMT_FROM_HH(tbl,out->hh.hh_next)); \
+    else out = NULL;                                                             \
+ }                                                                               \
+} while(0)
+
+/* add an item to a bucket  */
+#define HASH_ADD_TO_BKT(head,addhh)                                              \
+do {                                                                             \
+ head.count++;                                                                   \
+ (addhh)->hh_next = head.hh_head;                                                \
+ (addhh)->hh_prev = NULL;                                                        \
+ if (head.hh_head) { (head).hh_head->hh_prev = (addhh); }                        \
+ (head).hh_head=addhh;                                                           \
+ if (head.count >= ((head.expand_mult+1) * HASH_BKT_CAPACITY_THRESH)             \
+     && (addhh)->tbl->noexpand != 1) {                                           \
+       HASH_EXPAND_BUCKETS((addhh)->tbl);                                        \
+ }                                                                               \
+} while(0)
+
+/* remove an item from a given bucket */
+#define HASH_DEL_IN_BKT(hh,head,hh_del)                                          \
+    (head).count--;                                                              \
+    if ((head).hh_head == hh_del) {                                              \
+      (head).hh_head = hh_del->hh_next;                                          \
+    }                                                                            \
+    if (hh_del->hh_prev) {                                                       \
+        hh_del->hh_prev->hh_next = hh_del->hh_next;                              \
+    }                                                                            \
+    if (hh_del->hh_next) {                                                       \
+        hh_del->hh_next->hh_prev = hh_del->hh_prev;                              \
+    }
+
+/* Bucket expansion has the effect of doubling the number of buckets
+ * and redistributing the items into the new buckets. Ideally the
+ * items will distribute more or less evenly into the new buckets
+ * (the extent to which this is true is a measure of the quality of
+ * the hash function as it applies to the key domain).
+ *
+ * With the items distributed into more buckets, the chain length
+ * (item count) in each bucket is reduced. Thus by expanding buckets
+ * the hash keeps a bound on the chain length. This bounded chain
+ * length is the essence of how a hash provides constant time lookup.
+ *
+ * The calculation of tbl->ideal_chain_maxlen below deserves some
+ * explanation. First, keep in mind that we're calculating the ideal
+ * maximum chain length based on the *new* (doubled) bucket count.
+ * In fractions this is just n/b (n=number of items,b=new num buckets).
+ * Since the ideal chain length is an integer, we want to calculate
+ * ceil(n/b). We don't depend on floating point arithmetic in this
+ * hash, so to calculate ceil(n/b) with integers we could write
+ *
+ *      ceil(n/b) = (n/b) + ((n%b)?1:0)
+ *
+ * and in fact a previous version of this hash did just that.
+ * But now we have improved things a bit by recognizing that b is
+ * always a power of two. We keep its base 2 log handy (call it lb),
+ * so now we can write this with a bit shift and logical AND:
+ *
+ *      ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0)
+ *
+ */
+#define HASH_EXPAND_BUCKETS(tbl)                                                 \
+do {                                                                             \
+    unsigned _he_bkt;                                                            \
+    unsigned _he_bkt_i;                                                          \
+    struct UT_hash_handle *_he_thh, *_he_hh_nxt;                                 \
+    UT_hash_bucket *_he_new_buckets, *_he_newbkt;                                \
+    _he_new_buckets = (UT_hash_bucket*)uthash_malloc(                            \
+             2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));              \
+    if (!_he_new_buckets) { uthash_fatal( "out of memory"); }                    \
+    memset(_he_new_buckets, 0,                                                   \
+            2 * tbl->num_buckets * sizeof(struct UT_hash_bucket));               \
+    tbl->ideal_chain_maxlen =                                                    \
+       (tbl->num_items >> (tbl->log2_num_buckets+1)) +                           \
+       ((tbl->num_items & ((tbl->num_buckets*2)-1)) ? 1 : 0);                    \
+    tbl->nonideal_items = 0;                                                     \
+    for(_he_bkt_i = 0; _he_bkt_i < tbl->num_buckets; _he_bkt_i++)                \
+    {                                                                            \
+        _he_thh = tbl->buckets[ _he_bkt_i ].hh_head;                             \
+        while (_he_thh) {                                                        \
+           _he_hh_nxt = _he_thh->hh_next;                                        \
+           HASH_TO_BKT( _he_thh->hashv, tbl->num_buckets*2, _he_bkt);            \
+           _he_newbkt = &(_he_new_buckets[ _he_bkt ]);                           \
+           if (++(_he_newbkt->count) > tbl->ideal_chain_maxlen) {                \
+             tbl->nonideal_items++;                                              \
+             _he_newbkt->expand_mult = _he_newbkt->count /                       \
+                                        tbl->ideal_chain_maxlen;                 \
+           }                                                                     \
+           _he_thh->hh_prev = NULL;                                              \
+           _he_thh->hh_next = _he_newbkt->hh_head;                               \
+           if (_he_newbkt->hh_head) _he_newbkt->hh_head->hh_prev =               \
+                _he_thh;                                                         \
+           _he_newbkt->hh_head = _he_thh;                                        \
+           _he_thh = _he_hh_nxt;                                                 \
+        }                                                                        \
+    }                                                                            \
+    tbl->num_buckets *= 2;                                                       \
+    tbl->log2_num_buckets++;                                                     \
+    uthash_free( tbl->buckets );                                                 \
+    tbl->buckets = _he_new_buckets;                                              \
+    tbl->ineff_expands = (tbl->nonideal_items > (tbl->num_items >> 1)) ?         \
+        (tbl->ineff_expands+1) : 0;                                              \
+    if (tbl->ineff_expands > 1) {                                                \
+        tbl->noexpand=1;                                                         \
+        uthash_noexpand_fyi(tbl);                                                \
+    }                                                                            \
+    uthash_expand_fyi(tbl);                                                      \
+} while(0)
+
+
+/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */
+/* Note that HASH_SORT assumes the hash handle name to be hh.
+ * HASH_SRT was added to allow the hash handle name to be passed in. */
+#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn)
+#define HASH_SRT(hh,head,cmpfcn)                                                 \
+do {                                                                             \
+  unsigned _hs_i;                                                                \
+  unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize;               \
+  struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail;            \
+  if (head) {                                                                    \
+      _hs_insize = 1;                                                            \
+      _hs_looping = 1;                                                           \
+      _hs_list = &((head)->hh);                                                  \
+      while (_hs_looping) {                                                      \
+          _hs_p = _hs_list;                                                      \
+          _hs_list = NULL;                                                       \
+          _hs_tail = NULL;                                                       \
+          _hs_nmerges = 0;                                                       \
+          while (_hs_p) {                                                        \
+              _hs_nmerges++;                                                     \
+              _hs_q = _hs_p;                                                     \
+              _hs_psize = 0;                                                     \
+              for ( _hs_i = 0; _hs_i  < _hs_insize; _hs_i++ ) {                  \
+                  _hs_psize++;                                                   \
+                  _hs_q = (UT_hash_handle*)((_hs_q->next) ?                      \
+                          ((void*)((char*)(_hs_q->next) +                        \
+                          (head)->hh.tbl->hho)) : NULL);                         \
+                  if (! (_hs_q) ) break;                                         \
+              }                                                                  \
+              _hs_qsize = _hs_insize;                                            \
+              while ((_hs_psize > 0) || ((_hs_qsize > 0) && _hs_q )) {           \
+                  if (_hs_psize == 0) {                                          \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  } else if ( (_hs_qsize == 0) || !(_hs_q) ) {                   \
+                      _hs_e = _hs_p;                                             \
+                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                  \
+                              ((void*)((char*)(_hs_p->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_psize--;                                               \
+                  } else if ((                                                   \
+                      cmpfcn(DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_p)), \
+                             DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl,_hs_q))) \
+                             ) <= 0) {                                           \
+                      _hs_e = _hs_p;                                             \
+                      _hs_p = (UT_hash_handle*)((_hs_p->next) ?                  \
+                              ((void*)((char*)(_hs_p->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_psize--;                                               \
+                  } else {                                                       \
+                      _hs_e = _hs_q;                                             \
+                      _hs_q = (UT_hash_handle*)((_hs_q->next) ?                  \
+                              ((void*)((char*)(_hs_q->next) +                    \
+                              (head)->hh.tbl->hho)) : NULL);                     \
+                      _hs_qsize--;                                               \
+                  }                                                              \
+                  if ( _hs_tail ) {                                              \
+                      _hs_tail->next = ((_hs_e) ?                                \
+                            ELMT_FROM_HH((head)->hh.tbl,_hs_e) : NULL);          \
+                  } else {                                                       \
+                      _hs_list = _hs_e;                                          \
+                  }                                                              \
+                  _hs_e->prev = ((_hs_tail) ?                                    \
+                     ELMT_FROM_HH((head)->hh.tbl,_hs_tail) : NULL);              \
+                  _hs_tail = _hs_e;                                              \
+              }                                                                  \
+              _hs_p = _hs_q;                                                     \
+          }                                                                      \
+          _hs_tail->next = NULL;                                                 \
+          if ( _hs_nmerges <= 1 ) {                                              \
+              _hs_looping=0;                                                     \
+              (head)->hh.tbl->tail = _hs_tail;                                   \
+              DECLTYPE_ASSIGN(head,ELMT_FROM_HH((head)->hh.tbl, _hs_list));      \
+          }                                                                      \
+          _hs_insize *= 2;                                                       \
+      }                                                                          \
+      HASH_FSCK(hh,head);                                                        \
+ }                                                                               \
+} while (0)
+
+/* This function selects items from one hash into another hash.
+ * The end result is that the selected items have dual presence
+ * in both hashes. There is no copy of the items made; rather
+ * they are added into the new hash through a secondary hash
+ * hash handle that must be present in the structure. */
+#define HASH_SELECT(hh_dst, dst, hh_src, src, cond)                              \
+do {                                                                             \
+  unsigned _src_bkt, _dst_bkt;                                                   \
+  void *_last_elt=NULL, *_elt;                                                   \
+  UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL;                         \
+  ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst));                 \
+  if (src) {                                                                     \
+    for(_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) {     \
+      for(_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head;                \
+          _src_hh;                                                               \
+          _src_hh = _src_hh->hh_next) {                                          \
+          _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh);                       \
+          if (cond(_elt)) {                                                      \
+            _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho);               \
+            _dst_hh->key = _src_hh->key;                                         \
+            _dst_hh->keylen = _src_hh->keylen;                                   \
+            _dst_hh->hashv = _src_hh->hashv;                                     \
+            _dst_hh->prev = _last_elt;                                           \
+            _dst_hh->next = NULL;                                                \
+            if (_last_elt_hh) { _last_elt_hh->next = _elt; }                     \
+            if (!dst) {                                                          \
+              DECLTYPE_ASSIGN(dst,_elt);                                         \
+              HASH_MAKE_TABLE(hh_dst,dst);                                       \
+            } else {                                                             \
+              _dst_hh->tbl = (dst)->hh_dst.tbl;                                  \
+            }                                                                    \
+            HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt);    \
+            HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt],_dst_hh);            \
+            (dst)->hh_dst.tbl->num_items++;                                      \
+            _last_elt = _elt;                                                    \
+            _last_elt_hh = _dst_hh;                                              \
+          }                                                                      \
+      }                                                                          \
+    }                                                                            \
+  }                                                                              \
+  HASH_FSCK(hh_dst,dst);                                                         \
+} while (0)
+
+#define HASH_CLEAR(hh,head)                                                      \
+do {                                                                             \
+  if (head) {                                                                    \
+    uthash_free((head)->hh.tbl->buckets );                                       \
+    uthash_free((head)->hh.tbl);                                                 \
+    (head)=NULL;                                                                 \
+  }                                                                              \
+} while(0)
+
+/* obtain a count of items in the hash */
+#define HASH_COUNT(head) HASH_CNT(hh,head)
+#define HASH_CNT(hh,head) (head?(head->hh.tbl->num_items):0)
+
+typedef struct UT_hash_bucket {
+   struct UT_hash_handle *hh_head;
+   unsigned count;
+
+   /* expand_mult is normally set to 0. In this situation, the max chain length
+    * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If
+    * the bucket's chain exceeds this length, bucket expansion is triggered).
+    * However, setting expand_mult to a non-zero value delays bucket expansion
+    * (that would be triggered by additions to this particular bucket)
+    * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH.
+    * (The multiplier is simply expand_mult+1). The whole idea of this
+    * multiplier is to reduce bucket expansions, since they are expensive, in
+    * situations where we know that a particular bucket tends to be overused.
+    * It is better to let its chain length grow to a longer yet-still-bounded
+    * value, than to do an O(n) bucket expansion too often.
+    */
+   unsigned expand_mult;
+
+} UT_hash_bucket;
+
+/* random signature used only to find hash tables in external analysis */
+#define HASH_SIGNATURE 0xa0111fe1
+#define HASH_BLOOM_SIGNATURE 0xb12220f2
+
+typedef struct UT_hash_table {
+   UT_hash_bucket *buckets;
+   unsigned num_buckets, log2_num_buckets;
+   unsigned num_items;
+   struct UT_hash_handle *tail; /* tail hh in app order, for fast append    */
+   ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */
+
+   /* in an ideal situation (all buckets used equally), no bucket would have
+    * more than ceil(#items/#buckets) items. that's the ideal chain length. */
+   unsigned ideal_chain_maxlen;
+
+   /* nonideal_items is the number of items in the hash whose chain position
+    * exceeds the ideal chain maxlen. these items pay the penalty for an uneven
+    * hash distribution; reaching them in a chain traversal takes >ideal steps */
+   unsigned nonideal_items;
+
+   /* ineffective expands occur when a bucket doubling was performed, but
+    * afterward, more than half the items in the hash had nonideal chain
+    * positions. If this happens on two consecutive expansions we inhibit any
+    * further expansion, as it's not helping; this happens when the hash
+    * function isn't a good fit for the key domain. When expansion is inhibited
+    * the hash will still work, albeit no longer in constant time. */
+   unsigned ineff_expands, noexpand;
+
+   uint32_t signature; /* used only to find hash tables in external analysis */
+#ifdef HASH_BLOOM
+   uint32_t bloom_sig; /* used only to test bloom exists in external analysis */
+   uint8_t *bloom_bv;
+   char bloom_nbits;
+#endif
+
+} UT_hash_table;
+
+typedef struct UT_hash_handle {
+   struct UT_hash_table *tbl;
+   void *prev;                       /* prev element in app order      */
+   void *next;                       /* next element in app order      */
+   struct UT_hash_handle *hh_prev;   /* previous hh in bucket order    */
+   struct UT_hash_handle *hh_next;   /* next hh in bucket order        */
+   void *key;                        /* ptr to enclosing struct's key  */
+   unsigned keylen;                  /* enclosing struct's key len     */
+   unsigned hashv;                   /* result of hash-fcn(key)        */
+} UT_hash_handle;
+
+#endif /* UTHASH_H */
diff --git a/thirdparty/libktx/lib/vk_format.h b/thirdparty/libktx/lib/vk_format.h
new file mode 100644
index 00000000000..18adf33b595
--- /dev/null
+++ b/thirdparty/libktx/lib/vk_format.h
@@ -0,0 +1,1388 @@
+/*
+================================================================================================
+
+Description	:	Vulkan format properties and conversion from OpenGL.
+Author		:	J.M.P. van Waveren
+Date		:	07/17/2016
+Language	:	C99
+Format		:	Real tabs with the tab size equal to 4 spaces.
+Copyright	:	Copyright (c) 2016 Oculus VR, LLC. All Rights reserved.
+
+
+LICENSE
+=======
+
+Copyright 2016 Oculus VR, LLC.
+SPDX-License-Identifier: Apache-2.0
+
+
+DESCRIPTION
+===========
+
+This header implements several support routines to convert OpenGL formats/types
+to Vulkan formats. These routines are particularly useful for loading file
+formats that store OpenGL formats/types such as KTX and glTF.
+
+The functions in this header file convert the format, internalFormat and type
+that are used as parameters to the following OpenGL functions:
+
+void glTexImage2D( GLenum target, GLint level, GLint internalFormat,
+	GLsizei width, GLsizei height, GLint border,
+	GLenum format, GLenum type, const GLvoid * data );
+void glTexImage3D( GLenum target, GLint level, GLint internalFormat,
+	GLsizei width, GLsizei height, GLsizei depth, GLint border,
+	GLenum format, GLenum type, const GLvoid * data );
+void glCompressedTexImage2D( GLenum target, GLint level, GLenum internalformat,
+	GLsizei width, GLsizei height, GLint border,
+	GLsizei imageSize, const GLvoid * data );
+void glCompressedTexImage3D( GLenum target, GLint level, GLenum internalformat,
+	GLsizei width, GLsizei height, GLsizei depth, GLint border,
+	GLsizei imageSize, const GLvoid * data );
+void glTexStorage2D( GLenum target, GLsizei levels, GLenum internalformat,
+	GLsizei width, GLsizei height );
+void glTexStorage3D( GLenum target, GLsizei levels, GLenum internalformat,
+	GLsizei width, GLsizei height, GLsizei depth );
+void glVertexAttribPointer( GLuint index, GLint size, GLenum type, GLboolean normalized,
+	GLsizei stride, const GLvoid * pointer);
+
+
+IMPLEMENTATION
+==============
+
+This file does not include OpenGL / OpenGL ES headers because:
+
+  1. Including OpenGL / OpenGL ES headers is platform dependent and
+     may require a separate installation of an OpenGL SDK.
+  2. The OpenGL format/type constants are the same between extensions and core.
+  3. The OpenGL format/type constants are the same between OpenGL and OpenGL ES.
+  4. File formats like KTX and glTF may use OpenGL formats and types that
+     are not supported by the OpenGL implementation on the platform but are
+     supported by the Vulkan implementation.
+
+
+ENTRY POINTS
+============
+
+static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type );
+static inline VkFormat vkGetFormatFromOpenGLType( const GLenum type, const GLuint numComponents, const GLboolean normalized );
+static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat );
+static inline void vkGetFormatSize( const VkFormat format, VkFormatSize * pFormatSize );
+
+MODIFICATIONS for use in libktx
+===============================
+
+2019.5.30 Use common ktxFormatSize to return results. Mark Callow, Edgewise Consulting.
+2019.6.12 Add mapping of PVRTC formats.                             "
+
+================================================================================================
+*/
+
+#if !defined( VK_FORMAT_H )
+#define VK_FORMAT_H
+
+#include "gl_format.h"
+
+static inline VkFormat vkGetFormatFromOpenGLFormat( const GLenum format, const GLenum type )
+{
+	switch ( type )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_UNSIGNED_BYTE:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R8_UNORM;
+				case GL_RG:						return VK_FORMAT_R8G8_UNORM;
+				case GL_RGB:					return VK_FORMAT_R8G8B8_UNORM;
+				case GL_BGR:					return VK_FORMAT_B8G8R8_UNORM;
+				case GL_RGBA:					return VK_FORMAT_R8G8B8A8_UNORM;
+				case GL_BGRA:					return VK_FORMAT_B8G8R8A8_UNORM;
+				case GL_RED_INTEGER:			return VK_FORMAT_R8_UINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R8G8_UINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R8G8B8_UINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_B8G8R8_UINT;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R8G8B8A8_UINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_B8G8R8A8_UINT;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_S8_UINT;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+		case GL_BYTE:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R8_SNORM;
+				case GL_RG:						return VK_FORMAT_R8G8_SNORM;
+				case GL_RGB:					return VK_FORMAT_R8G8B8_SNORM;
+				case GL_BGR:					return VK_FORMAT_B8G8R8_SNORM;
+				case GL_RGBA:					return VK_FORMAT_R8G8B8A8_SNORM;
+				case GL_BGRA:					return VK_FORMAT_B8G8R8A8_SNORM;
+				case GL_RED_INTEGER:			return VK_FORMAT_R8_SINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R8G8_SINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R8G8B8_SINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_B8G8R8_SINT;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R8G8B8A8_SINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_B8G8R8A8_SINT;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+
+		//
+		// 16 bits per component
+		//
+		case GL_UNSIGNED_SHORT:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R16_UNORM;
+				case GL_RG:						return VK_FORMAT_R16G16_UNORM;
+				case GL_RGB:					return VK_FORMAT_R16G16B16_UNORM;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R16G16B16A16_UNORM;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R16_UINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R16G16_UINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R16G16B16_UINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R16G16B16A16_UINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_D16_UNORM;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_D16_UNORM_S8_UINT;
+			}
+			break;
+		}
+		case GL_SHORT:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R16_SNORM;
+				case GL_RG:						return VK_FORMAT_R16G16_SNORM;
+				case GL_RGB:					return VK_FORMAT_R16G16B16_SNORM;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R16G16B16A16_SNORM;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R16_SINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R16G16_SINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R16G16B16_SINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R16G16B16A16_SINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+		case GL_HALF_FLOAT:
+		case GL_HALF_FLOAT_OES:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R16_SFLOAT;
+				case GL_RG:						return VK_FORMAT_R16G16_SFLOAT;
+				case GL_RGB:					return VK_FORMAT_R16G16B16_SFLOAT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R16G16B16A16_SFLOAT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RG_INTEGER:				return VK_FORMAT_UNDEFINED;
+				case GL_RGB_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+
+		//
+		// 32 bits per component
+		//
+		case GL_UNSIGNED_INT:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R32_UINT;
+				case GL_RG:						return VK_FORMAT_R32G32_UINT;
+				case GL_RGB:					return VK_FORMAT_R32G32B32_UINT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R32G32B32A32_UINT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R32_UINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R32G32_UINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R32G32B32_UINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R32G32B32A32_UINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_X8_D24_UNORM_PACK32;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_D24_UNORM_S8_UINT;
+			}
+			break;
+		}
+		case GL_INT:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R32_SINT;
+				case GL_RG:						return VK_FORMAT_R32G32_SINT;
+				case GL_RGB:					return VK_FORMAT_R32G32B32_SINT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R32G32B32A32_SINT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R32_SINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R32G32_SINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R32G32B32_SINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R32G32B32A32_SINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+		case GL_FLOAT:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R32_SFLOAT;
+				case GL_RG:						return VK_FORMAT_R32G32_SFLOAT;
+				case GL_RGB:					return VK_FORMAT_R32G32B32_SFLOAT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R32G32B32A32_SFLOAT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RG_INTEGER:				return VK_FORMAT_UNDEFINED;
+				case GL_RGB_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_D32_SFLOAT;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_D32_SFLOAT_S8_UINT;
+			}
+			break;
+		}
+
+		//
+		// 64 bits per component
+		//
+		case GL_UNSIGNED_INT64:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R64_UINT;
+				case GL_RG:						return VK_FORMAT_R64G64_UINT;
+				case GL_RGB:					return VK_FORMAT_R64G64B64_UINT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R64G64B64A64_UINT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RG_INTEGER:				return VK_FORMAT_UNDEFINED;
+				case GL_RGB_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+		case GL_INT64:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R64_SINT;
+				case GL_RG:						return VK_FORMAT_R64G64_SINT;
+				case GL_RGB:					return VK_FORMAT_R64G64B64_SINT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R64G64B64A64_SINT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R64_SINT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R64G64_SINT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R64G64B64_SINT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R64G64B64A64_SINT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+		case GL_DOUBLE:
+		{
+			switch ( format )
+			{
+				case GL_RED:					return VK_FORMAT_R64_SFLOAT;
+				case GL_RG:						return VK_FORMAT_R64G64_SFLOAT;
+				case GL_RGB:					return VK_FORMAT_R64G64B64_SFLOAT;
+				case GL_BGR:					return VK_FORMAT_UNDEFINED;
+				case GL_RGBA:					return VK_FORMAT_R64G64B64A64_SFLOAT;
+				case GL_BGRA:					return VK_FORMAT_UNDEFINED;
+				case GL_RED_INTEGER:			return VK_FORMAT_R64_SFLOAT;
+				case GL_RG_INTEGER:				return VK_FORMAT_R64G64_SFLOAT;
+				case GL_RGB_INTEGER:			return VK_FORMAT_R64G64B64_SFLOAT;
+				case GL_BGR_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_RGBA_INTEGER:			return VK_FORMAT_R64G64B64A64_SFLOAT;
+				case GL_BGRA_INTEGER:			return VK_FORMAT_UNDEFINED;
+				case GL_STENCIL_INDEX:			return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_COMPONENT:		return VK_FORMAT_UNDEFINED;
+				case GL_DEPTH_STENCIL:			return VK_FORMAT_UNDEFINED;
+			}
+			break;
+		}
+
+		//
+		// Packed
+		//
+		case GL_UNSIGNED_BYTE_3_3_2:
+			assert( format == GL_RGB || format == GL_RGB_INTEGER );
+			return VK_FORMAT_UNDEFINED;
+		case GL_UNSIGNED_BYTE_2_3_3_REV:
+			assert( format == GL_BGR || format == GL_BGR_INTEGER );
+			return VK_FORMAT_UNDEFINED;
+		case GL_UNSIGNED_SHORT_5_6_5:
+			assert( format == GL_RGB || format == GL_RGB_INTEGER );
+			return VK_FORMAT_R5G6B5_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_5_6_5_REV:
+			assert( format == GL_BGR || format == GL_BGR_INTEGER );
+			return VK_FORMAT_B5G6R5_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_4_4_4_4:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_4_4_4_4_REV:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_5_5_5_1:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_1_5_5_5_REV:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
+		case GL_UNSIGNED_INT_8_8_8_8:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_R8G8B8A8_UINT : VK_FORMAT_R8G8B8A8_UNORM;
+		case GL_UNSIGNED_INT_8_8_8_8_REV:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A8B8G8R8_UINT_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
+		case GL_UNSIGNED_INT_10_10_10_2:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A2R10G10B10_UINT_PACK32 : VK_FORMAT_A2R10G10B10_UNORM_PACK32;
+		case GL_UNSIGNED_INT_2_10_10_10_REV:
+			assert( format == GL_RGB || format == GL_BGRA || format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER );
+			return ( format == GL_RGB_INTEGER || format == GL_BGRA_INTEGER ) ? VK_FORMAT_A2B10G10R10_UINT_PACK32 : VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+		case GL_UNSIGNED_INT_10F_11F_11F_REV:
+			assert( format == GL_RGB || format == GL_BGR );
+			return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+		case GL_UNSIGNED_INT_5_9_9_9_REV:
+			assert( format == GL_RGB || format == GL_BGR );
+			return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+		case GL_UNSIGNED_INT_24_8:
+			assert( format == GL_DEPTH_STENCIL );
+			return VK_FORMAT_D24_UNORM_S8_UINT;
+		case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:
+			assert( format == GL_DEPTH_STENCIL );
+			return VK_FORMAT_D32_SFLOAT_S8_UINT;
+	}
+
+	return VK_FORMAT_UNDEFINED;
+}
+
+static inline VkFormat vkGetFormatFromOpenGLType( const GLenum type, const GLuint numComponents, const GLboolean normalized )
+{
+	switch ( type )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_UNSIGNED_BYTE:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return normalized ? VK_FORMAT_R8_UNORM : VK_FORMAT_R8_UINT;
+				case 2:							return normalized ? VK_FORMAT_R8G8_UNORM : VK_FORMAT_R8G8_UINT;
+				case 3:							return normalized ? VK_FORMAT_R8G8B8_UNORM : VK_FORMAT_R8G8B8_UINT;
+				case 4:							return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT;
+			}
+			break;
+		}
+		case GL_BYTE:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return normalized ? VK_FORMAT_R8_SNORM : VK_FORMAT_R8_SINT;
+				case 2:							return normalized ? VK_FORMAT_R8G8_SNORM : VK_FORMAT_R8G8_SINT;
+				case 3:							return normalized ? VK_FORMAT_R8G8B8_SNORM : VK_FORMAT_R8G8B8_SINT;
+				case 4:							return normalized ? VK_FORMAT_R8G8B8A8_SNORM : VK_FORMAT_R8G8B8A8_SINT;
+			}
+			break;
+		}
+
+		//
+		// 16 bits per component
+		//
+		case GL_UNSIGNED_SHORT:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return normalized ? VK_FORMAT_R16_UNORM : VK_FORMAT_R16_UINT;
+				case 2:							return normalized ? VK_FORMAT_R16G16_UNORM : VK_FORMAT_R16G16_UINT;
+				case 3:							return normalized ? VK_FORMAT_R16G16B16_UNORM : VK_FORMAT_R16G16B16_UINT;
+				case 4:							return normalized ? VK_FORMAT_R16G16B16A16_UNORM : VK_FORMAT_R16G16B16A16_UINT;
+			}
+			break;
+		}
+		case GL_SHORT:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return normalized ? VK_FORMAT_R16_SNORM : VK_FORMAT_R16_SINT;
+				case 2:							return normalized ? VK_FORMAT_R16G16_SNORM : VK_FORMAT_R16G16_SINT;
+				case 3:							return normalized ? VK_FORMAT_R16G16B16_SNORM : VK_FORMAT_R16G16B16_SINT;
+				case 4:							return normalized ? VK_FORMAT_R16G16B16A16_SNORM : VK_FORMAT_R16G16B16A16_SINT;
+			}
+			break;
+		}
+		case GL_HALF_FLOAT:
+		case GL_HALF_FLOAT_OES:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R16_SFLOAT;
+				case 2:							return VK_FORMAT_R16G16_SFLOAT;
+				case 3:							return VK_FORMAT_R16G16B16_SFLOAT;
+				case 4:							return VK_FORMAT_R16G16B16A16_SFLOAT;
+			}
+			break;
+		}
+
+		//
+		// 32 bits per component
+		//
+		case GL_UNSIGNED_INT:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R32_UINT;
+				case 2:							return VK_FORMAT_R32G32_UINT;
+				case 3:							return VK_FORMAT_R32G32B32_UINT;
+				case 4:							return VK_FORMAT_R32G32B32A32_UINT;
+			}
+			break;
+		}
+		case GL_INT:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R32_SINT;
+				case 2:							return VK_FORMAT_R32G32_SINT;
+				case 3:							return VK_FORMAT_R32G32B32_SINT;
+				case 4:							return VK_FORMAT_R32G32B32A32_SINT;
+			}
+			break;
+		}
+		case GL_FLOAT:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R32_SFLOAT;
+				case 2:							return VK_FORMAT_R32G32_SFLOAT;
+				case 3:							return VK_FORMAT_R32G32B32_SFLOAT;
+				case 4:							return VK_FORMAT_R32G32B32A32_SFLOAT;
+			}
+			break;
+		}
+
+		//
+		// 64 bits per component
+		//
+		case GL_UNSIGNED_INT64:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R64_UINT;
+				case 2:							return VK_FORMAT_R64G64_UINT;
+				case 3:							return VK_FORMAT_R64G64B64_UINT;
+				case 4:							return VK_FORMAT_R64G64B64A64_UINT;
+			}
+			break;
+		}
+		case GL_INT64:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R64_SINT;
+				case 2:							return VK_FORMAT_R64G64_SINT;
+				case 3:							return VK_FORMAT_R64G64B64_SINT;
+				case 4:							return VK_FORMAT_R64G64B64A64_SINT;
+			}
+			break;
+		}
+		case GL_DOUBLE:
+		{
+			switch ( numComponents )
+			{
+				case 1:							return VK_FORMAT_R64_SFLOAT;
+				case 2:							return VK_FORMAT_R64G64_SFLOAT;
+				case 3:							return VK_FORMAT_R64G64B64_SFLOAT;
+				case 4:							return VK_FORMAT_R64G64B64A64_SFLOAT;
+			}
+			break;
+		}
+
+		//
+		// Packed
+		//
+		case GL_UNSIGNED_BYTE_3_3_2:			return VK_FORMAT_UNDEFINED;
+		case GL_UNSIGNED_BYTE_2_3_3_REV:		return VK_FORMAT_UNDEFINED;
+		case GL_UNSIGNED_SHORT_5_6_5:			return VK_FORMAT_R5G6B5_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_5_6_5_REV:		return VK_FORMAT_B5G6R5_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_4_4_4_4:			return VK_FORMAT_R4G4B4A4_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_4_4_4_4_REV:		return VK_FORMAT_B4G4R4A4_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_5_5_5_1:			return VK_FORMAT_R5G5B5A1_UNORM_PACK16;
+		case GL_UNSIGNED_SHORT_1_5_5_5_REV:		return VK_FORMAT_A1R5G5B5_UNORM_PACK16;
+		case GL_UNSIGNED_INT_8_8_8_8:			return normalized ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R8G8B8A8_UINT;
+		case GL_UNSIGNED_INT_8_8_8_8_REV:		return normalized ? VK_FORMAT_A8B8G8R8_UNORM_PACK32 : VK_FORMAT_A8B8G8R8_UINT_PACK32;
+		case GL_UNSIGNED_INT_10_10_10_2:		return normalized ? VK_FORMAT_A2R10G10B10_UNORM_PACK32 : VK_FORMAT_A2R10G10B10_UINT_PACK32;
+		case GL_UNSIGNED_INT_2_10_10_10_REV:	return normalized ? VK_FORMAT_A2B10G10R10_UNORM_PACK32 : VK_FORMAT_A2B10G10R10_UINT_PACK32;
+		case GL_UNSIGNED_INT_10F_11F_11F_REV:	return VK_FORMAT_B10G11R11_UFLOAT_PACK32;
+		case GL_UNSIGNED_INT_5_9_9_9_REV:		return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;
+		case GL_UNSIGNED_INT_24_8:				return VK_FORMAT_D24_UNORM_S8_UINT;
+		case GL_FLOAT_32_UNSIGNED_INT_24_8_REV:	return VK_FORMAT_D32_SFLOAT_S8_UINT;
+	}
+
+	return VK_FORMAT_UNDEFINED;
+}
+
+static inline VkFormat vkGetFormatFromOpenGLInternalFormat( const GLenum internalFormat )
+{
+	switch ( internalFormat )
+	{
+		//
+		// 8 bits per component
+		//
+		case GL_R8:												return VK_FORMAT_R8_UNORM;					// 1-component, 8-bit unsigned normalized
+		case GL_RG8:											return VK_FORMAT_R8G8_UNORM;				// 2-component, 8-bit unsigned normalized
+		case GL_RGB8:											return VK_FORMAT_R8G8B8_UNORM;				// 3-component, 8-bit unsigned normalized
+		case GL_RGBA8:											return VK_FORMAT_R8G8B8A8_UNORM;			// 4-component, 8-bit unsigned normalized
+
+		case GL_R8_SNORM:										return VK_FORMAT_R8_SNORM;					// 1-component, 8-bit signed normalized
+		case GL_RG8_SNORM:										return VK_FORMAT_R8G8_SNORM;				// 2-component, 8-bit signed normalized
+		case GL_RGB8_SNORM:										return VK_FORMAT_R8G8B8_SNORM;				// 3-component, 8-bit signed normalized
+		case GL_RGBA8_SNORM:									return VK_FORMAT_R8G8B8A8_SNORM;			// 4-component, 8-bit signed normalized
+
+		case GL_R8UI:											return VK_FORMAT_R8_UINT;					// 1-component, 8-bit unsigned integer
+		case GL_RG8UI:											return VK_FORMAT_R8G8_UINT;					// 2-component, 8-bit unsigned integer
+		case GL_RGB8UI:											return VK_FORMAT_R8G8B8_UINT;				// 3-component, 8-bit unsigned integer
+		case GL_RGBA8UI:										return VK_FORMAT_R8G8B8A8_UINT;				// 4-component, 8-bit unsigned integer
+
+		case GL_R8I:											return VK_FORMAT_R8_SINT;					// 1-component, 8-bit signed integer
+		case GL_RG8I:											return VK_FORMAT_R8G8_SINT;					// 2-component, 8-bit signed integer
+		case GL_RGB8I:											return VK_FORMAT_R8G8B8_SINT;				// 3-component, 8-bit signed integer
+		case GL_RGBA8I:											return VK_FORMAT_R8G8B8A8_SINT;				// 4-component, 8-bit signed integer
+
+		case GL_SR8:											return VK_FORMAT_R8_SRGB;					// 1-component, 8-bit sRGB
+		case GL_SRG8:											return VK_FORMAT_R8G8_SRGB;					// 2-component, 8-bit sRGB
+		case GL_SRGB8:											return VK_FORMAT_R8G8B8_SRGB;				// 3-component, 8-bit sRGB
+		case GL_SRGB8_ALPHA8:									return VK_FORMAT_R8G8B8A8_SRGB;				// 4-component, 8-bit sRGB
+
+		//
+		// 16 bits per component
+		//
+		case GL_R16:											return VK_FORMAT_R16_UNORM;					// 1-component, 16-bit unsigned normalized
+		case GL_RG16:											return VK_FORMAT_R16G16_UNORM;				// 2-component, 16-bit unsigned normalized
+		case GL_RGB16:											return VK_FORMAT_R16G16B16_UNORM;			// 3-component, 16-bit unsigned normalized
+		case GL_RGBA16:											return VK_FORMAT_R16G16B16A16_UNORM;		// 4-component, 16-bit unsigned normalized
+
+		case GL_R16_SNORM:										return VK_FORMAT_R16_SNORM;					// 1-component, 16-bit signed normalized
+		case GL_RG16_SNORM:										return VK_FORMAT_R16G16_SNORM;				// 2-component, 16-bit signed normalized
+		case GL_RGB16_SNORM:									return VK_FORMAT_R16G16B16_SNORM;			// 3-component, 16-bit signed normalized
+		case GL_RGBA16_SNORM:									return VK_FORMAT_R16G16B16A16_SNORM;		// 4-component, 16-bit signed normalized
+
+		case GL_R16UI:											return VK_FORMAT_R16_UINT;					// 1-component, 16-bit unsigned integer
+		case GL_RG16UI:											return VK_FORMAT_R16G16_UINT;				// 2-component, 16-bit unsigned integer
+		case GL_RGB16UI:										return VK_FORMAT_R16G16B16_UINT;			// 3-component, 16-bit unsigned integer
+		case GL_RGBA16UI:										return VK_FORMAT_R16G16B16A16_UINT;			// 4-component, 16-bit unsigned integer
+
+		case GL_R16I:											return VK_FORMAT_R16_SINT;					// 1-component, 16-bit signed integer
+		case GL_RG16I:											return VK_FORMAT_R16G16_SINT;				// 2-component, 16-bit signed integer
+		case GL_RGB16I:											return VK_FORMAT_R16G16B16_SINT;			// 3-component, 16-bit signed integer
+		case GL_RGBA16I:										return VK_FORMAT_R16G16B16A16_SINT;			// 4-component, 16-bit signed integer
+
+		case GL_R16F:											return VK_FORMAT_R16_SFLOAT;				// 1-component, 16-bit floating-point
+		case GL_RG16F:											return VK_FORMAT_R16G16_SFLOAT;				// 2-component, 16-bit floating-point
+		case GL_RGB16F:											return VK_FORMAT_R16G16B16_SFLOAT;			// 3-component, 16-bit floating-point
+		case GL_RGBA16F:										return VK_FORMAT_R16G16B16A16_SFLOAT;		// 4-component, 16-bit floating-point
+
+		//
+		// 32 bits per component
+		//
+		case GL_R32UI:											return VK_FORMAT_R32_UINT;					// 1-component, 32-bit unsigned integer
+		case GL_RG32UI:											return VK_FORMAT_R32G32_UINT;				// 2-component, 32-bit unsigned integer
+		case GL_RGB32UI:										return VK_FORMAT_R32G32B32_UINT;			// 3-component, 32-bit unsigned integer
+		case GL_RGBA32UI:										return VK_FORMAT_R32G32B32A32_UINT;			// 4-component, 32-bit unsigned integer
+
+		case GL_R32I:											return VK_FORMAT_R32_SINT;					// 1-component, 32-bit signed integer
+		case GL_RG32I:											return VK_FORMAT_R32G32_SINT;				// 2-component, 32-bit signed integer
+		case GL_RGB32I:											return VK_FORMAT_R32G32B32_SINT;			// 3-component, 32-bit signed integer
+		case GL_RGBA32I:										return VK_FORMAT_R32G32B32A32_SINT;			// 4-component, 32-bit signed integer
+
+		case GL_R32F:											return VK_FORMAT_R32_SFLOAT;				// 1-component, 32-bit floating-point
+		case GL_RG32F:											return VK_FORMAT_R32G32_SFLOAT;				// 2-component, 32-bit floating-point
+		case GL_RGB32F:											return VK_FORMAT_R32G32B32_SFLOAT;			// 3-component, 32-bit floating-point
+		case GL_RGBA32F:										return VK_FORMAT_R32G32B32A32_SFLOAT;		// 4-component, 32-bit floating-point
+
+		//
+		// Packed
+		//
+		case GL_R3_G3_B2:										return VK_FORMAT_UNDEFINED;					// 3-component 3:3:2,       unsigned normalized
+		case GL_RGB4:											return VK_FORMAT_UNDEFINED;					// 3-component 4:4:4,       unsigned normalized
+		case GL_RGB5:											return VK_FORMAT_R5G5B5A1_UNORM_PACK16;		// 3-component 5:5:5,       unsigned normalized
+		case GL_RGB565:											return VK_FORMAT_R5G6B5_UNORM_PACK16;		// 3-component 5:6:5,       unsigned normalized
+		case GL_RGB10:											return VK_FORMAT_A2R10G10B10_UNORM_PACK32;	// 3-component 10:10:10,    unsigned normalized
+		case GL_RGB12:											return VK_FORMAT_UNDEFINED;					// 3-component 12:12:12,    unsigned normalized
+		case GL_RGBA2:											return VK_FORMAT_UNDEFINED;					// 4-component 2:2:2:2,     unsigned normalized
+		case GL_RGBA4:											return VK_FORMAT_R4G4B4A4_UNORM_PACK16;		// 4-component 4:4:4:4,     unsigned normalized
+		case GL_RGBA12:											return VK_FORMAT_UNDEFINED;					// 4-component 12:12:12:12, unsigned normalized
+		case GL_RGB5_A1:										return VK_FORMAT_A1R5G5B5_UNORM_PACK16;		// 4-component 5:5:5:1,     unsigned normalized
+		case GL_RGB10_A2:										return VK_FORMAT_A2R10G10B10_UNORM_PACK32;	// 4-component 10:10:10:2,  unsigned normalized
+		case GL_RGB10_A2UI:										return VK_FORMAT_A2R10G10B10_UINT_PACK32;	// 4-component 10:10:10:2,  unsigned integer
+		case GL_R11F_G11F_B10F:									return VK_FORMAT_B10G11R11_UFLOAT_PACK32;	// 3-component 11:11:10,    floating-point
+		case GL_RGB9_E5:										return VK_FORMAT_E5B9G9R9_UFLOAT_PACK32;	// 3-component/exp 9:9:9/5, floating-point
+
+		//
+		// S3TC/DXT/BC
+		//
+
+		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:					return VK_FORMAT_BC1_RGB_UNORM_BLOCK;		// line through 3D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:					return VK_FORMAT_BC1_RGBA_UNORM_BLOCK;		// line through 3D space plus 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:					return VK_FORMAT_BC2_UNORM_BLOCK;			// line through 3D space plus line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:					return VK_FORMAT_BC3_UNORM_BLOCK;			// line through 3D space plus 4-bit alpha, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:					return VK_FORMAT_BC1_RGB_SRGB_BLOCK;		// line through 3D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:			return VK_FORMAT_BC1_RGBA_SRGB_BLOCK;		// line through 3D space plus 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:			return VK_FORMAT_BC2_SRGB_BLOCK;			// line through 3D space plus line through 1D space, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:			return VK_FORMAT_BC3_SRGB_BLOCK;			// line through 3D space plus 4-bit alpha, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_LUMINANCE_LATC1_EXT:					return VK_FORMAT_BC4_UNORM_BLOCK;			// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:			return VK_FORMAT_BC5_UNORM_BLOCK;			// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:			return VK_FORMAT_BC4_SNORM_BLOCK;			// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:	return VK_FORMAT_BC5_SNORM_BLOCK;			// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RED_RGTC1:							return VK_FORMAT_BC4_UNORM_BLOCK;			// line through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG_RGTC2:							return VK_FORMAT_BC5_UNORM_BLOCK;			// two lines through 1D space, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_RED_RGTC1:					return VK_FORMAT_BC4_SNORM_BLOCK;			// line through 1D space, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG_RGTC2:						return VK_FORMAT_BC5_SNORM_BLOCK;			// two lines through 1D space, 4x4 blocks, signed normalized
+
+		case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:				return VK_FORMAT_BC6H_UFLOAT_BLOCK;			// 3-component, 4x4 blocks, unsigned floating-point
+		case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:				return VK_FORMAT_BC6H_SFLOAT_BLOCK;			// 3-component, 4x4 blocks, signed floating-point
+		case GL_COMPRESSED_RGBA_BPTC_UNORM:						return VK_FORMAT_BC7_UNORM_BLOCK;			// 4-component, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:				return VK_FORMAT_BC7_SRGB_BLOCK;			// 4-component, 4x4 blocks, sRGB
+
+		//
+		// ETC
+		//
+		case GL_ETC1_RGB8_OES:									return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;	// 3-component ETC1, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_RGB8_ETC2:							return VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;	// 3-component ETC2, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK;	// 4-component ETC2 with 1-bit alpha, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA8_ETC2_EAC:						return VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK;	// 4-component ETC2, 4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ETC2:							return VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK;	// 3-component ETC2, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:		return VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK;	// 4-component ETC2 with 1-bit alpha, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:				return VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK;	// 4-component ETC2, 4x4 blocks, sRGB
+
+		case GL_COMPRESSED_R11_EAC:								return VK_FORMAT_EAC_R11_UNORM_BLOCK;		// 1-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RG11_EAC:							return VK_FORMAT_EAC_R11G11_UNORM_BLOCK;	// 2-component ETC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_SIGNED_R11_EAC:						return VK_FORMAT_EAC_R11_SNORM_BLOCK;		// 1-component ETC, 4x4 blocks, signed normalized
+		case GL_COMPRESSED_SIGNED_RG11_EAC:						return VK_FORMAT_EAC_R11G11_SNORM_BLOCK;	// 2-component ETC, 4x4 blocks, signed normalized
+
+		//
+		// PVRTC
+		//
+		case GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG:				return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;	// 3-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGB_PVRTC_4BPPV1_IMG:				return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;	// 3-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV1_IMG:				return VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG;	// 4-component PVRTC, 16x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV1_IMG:				return VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG;	// 4-component PVRTC,  8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG:				return VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG;	// 4-component PVRTC,  8x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_PVRTC_4BPPV2_IMG:				return VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG;	// 4-component PVRTC,  4x4 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT:				return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;	// 3-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_PVRTC_4BPPV1_EXT:				return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;	// 3-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV1_EXT:			return VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG;	// 4-component PVRTC, 16x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV1_EXT:			return VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG;	// 4-component PVRTC,  8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_2BPPV2_IMG:			return VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG;	// 4-component PVRTC,  8x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB_ALPHA_PVRTC_4BPPV2_IMG:			return VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG;	// 4-component PVRTC,  4x4 blocks, sRGB
+
+		//
+		// ASTC
+		//
+		case GL_COMPRESSED_RGBA_ASTC_4x4_KHR:					return VK_FORMAT_ASTC_4x4_UNORM_BLOCK;		// 4-component ASTC, 4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4_KHR:					return VK_FORMAT_ASTC_5x4_UNORM_BLOCK;		// 4-component ASTC, 5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5_KHR:					return VK_FORMAT_ASTC_5x5_UNORM_BLOCK;		// 4-component ASTC, 5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5_KHR:					return VK_FORMAT_ASTC_6x5_UNORM_BLOCK;		// 4-component ASTC, 6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6_KHR:					return VK_FORMAT_ASTC_6x6_UNORM_BLOCK;		// 4-component ASTC, 6x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x5_KHR:					return VK_FORMAT_ASTC_8x5_UNORM_BLOCK;		// 4-component ASTC, 8x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x6_KHR:					return VK_FORMAT_ASTC_8x6_UNORM_BLOCK;		// 4-component ASTC, 8x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_8x8_KHR:					return VK_FORMAT_ASTC_8x8_UNORM_BLOCK;		// 4-component ASTC, 8x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x5_KHR:					return VK_FORMAT_ASTC_10x5_UNORM_BLOCK;		// 4-component ASTC, 10x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x6_KHR:					return VK_FORMAT_ASTC_10x6_UNORM_BLOCK;		// 4-component ASTC, 10x6 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x8_KHR:					return VK_FORMAT_ASTC_10x8_UNORM_BLOCK;		// 4-component ASTC, 10x8 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_10x10_KHR:					return VK_FORMAT_ASTC_10x10_UNORM_BLOCK;	// 4-component ASTC, 10x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x10_KHR:					return VK_FORMAT_ASTC_12x10_UNORM_BLOCK;	// 4-component ASTC, 12x10 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_12x12_KHR:					return VK_FORMAT_ASTC_12x12_UNORM_BLOCK;	// 4-component ASTC, 12x12 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR:			return VK_FORMAT_ASTC_4x4_SRGB_BLOCK;		// 4-component ASTC, 4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR:			return VK_FORMAT_ASTC_5x4_SRGB_BLOCK;		// 4-component ASTC, 5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR:			return VK_FORMAT_ASTC_5x5_SRGB_BLOCK;		// 4-component ASTC, 5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR:			return VK_FORMAT_ASTC_6x5_SRGB_BLOCK;		// 4-component ASTC, 6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR:			return VK_FORMAT_ASTC_6x6_SRGB_BLOCK;		// 4-component ASTC, 6x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR:			return VK_FORMAT_ASTC_8x5_SRGB_BLOCK;		// 4-component ASTC, 8x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR:			return VK_FORMAT_ASTC_8x6_SRGB_BLOCK;		// 4-component ASTC, 8x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR:			return VK_FORMAT_ASTC_8x8_SRGB_BLOCK;		// 4-component ASTC, 8x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR:			return VK_FORMAT_ASTC_10x5_SRGB_BLOCK;		// 4-component ASTC, 10x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR:			return VK_FORMAT_ASTC_10x6_SRGB_BLOCK;		// 4-component ASTC, 10x6 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR:			return VK_FORMAT_ASTC_10x8_SRGB_BLOCK;		// 4-component ASTC, 10x8 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR:			return VK_FORMAT_ASTC_10x10_SRGB_BLOCK;		// 4-component ASTC, 10x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR:			return VK_FORMAT_ASTC_12x10_SRGB_BLOCK;		// 4-component ASTC, 12x10 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR:			return VK_FORMAT_ASTC_12x12_SRGB_BLOCK;		// 4-component ASTC, 12x12 blocks, sRGB
+
+		case GL_COMPRESSED_RGBA_ASTC_3x3x3_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 3x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x3x3_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x3x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x3_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x4x3 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_4x4x4_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x4x4_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x4x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x4_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x5x4 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_5x5x5_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x5x5_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x5x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x5_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x6x5 blocks, unsigned normalized
+		case GL_COMPRESSED_RGBA_ASTC_6x6x6_OES:					return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x6x6 blocks, unsigned normalized
+
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_3x3x3_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 3x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x3x3_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x3x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x3_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x4x3 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4x4_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 4x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4x4_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x4x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x4_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x5x4 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5x5_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 5x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5x5_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x5x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x5_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x6x5 blocks, sRGB
+		case GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6x6_OES:			return VK_FORMAT_UNDEFINED;					// 4-component ASTC, 6x6x6 blocks, sRGB
+
+		//
+		// ATC
+		//
+		case GL_ATC_RGB_AMD:									return VK_FORMAT_UNDEFINED;					// 3-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_EXPLICIT_ALPHA_AMD:					return VK_FORMAT_UNDEFINED;					// 4-component, 4x4 blocks, unsigned normalized
+		case GL_ATC_RGBA_INTERPOLATED_ALPHA_AMD:				return VK_FORMAT_UNDEFINED;					// 4-component, 4x4 blocks, unsigned normalized
+
+		//
+		// Palletized
+		//
+		case GL_PALETTE4_RGB8_OES:								return VK_FORMAT_UNDEFINED;					// 3-component 8:8:8,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA8_OES:								return VK_FORMAT_UNDEFINED;					// 4-component 8:8:8:8, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_R5_G6_B5_OES:							return VK_FORMAT_UNDEFINED;					// 3-component 5:6:5,   4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGBA4_OES:								return VK_FORMAT_UNDEFINED;					// 4-component 4:4:4:4, 4-bit palette, unsigned normalized
+		case GL_PALETTE4_RGB5_A1_OES:							return VK_FORMAT_UNDEFINED;					// 4-component 5:5:5:1, 4-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB8_OES:								return VK_FORMAT_UNDEFINED;					// 3-component 8:8:8,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA8_OES:								return VK_FORMAT_UNDEFINED;					// 4-component 8:8:8:8, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_R5_G6_B5_OES:							return VK_FORMAT_UNDEFINED;					// 3-component 5:6:5,   8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGBA4_OES:								return VK_FORMAT_UNDEFINED;					// 4-component 4:4:4:4, 8-bit palette, unsigned normalized
+		case GL_PALETTE8_RGB5_A1_OES:							return VK_FORMAT_UNDEFINED;					// 4-component 5:5:5:1, 8-bit palette, unsigned normalized
+
+		//
+		// Depth/stencil
+		//
+		case GL_DEPTH_COMPONENT16:								return VK_FORMAT_D16_UNORM;
+		case GL_DEPTH_COMPONENT24:								return VK_FORMAT_X8_D24_UNORM_PACK32;
+		case GL_DEPTH_COMPONENT32:								return VK_FORMAT_UNDEFINED;
+		case GL_DEPTH_COMPONENT32F:								return VK_FORMAT_D32_SFLOAT;
+		case GL_DEPTH_COMPONENT32F_NV:							return VK_FORMAT_D32_SFLOAT;
+		case GL_STENCIL_INDEX1:									return VK_FORMAT_UNDEFINED;
+		case GL_STENCIL_INDEX4:									return VK_FORMAT_UNDEFINED;
+		case GL_STENCIL_INDEX8:									return VK_FORMAT_S8_UINT;
+		case GL_STENCIL_INDEX16:								return VK_FORMAT_UNDEFINED;
+		case GL_DEPTH24_STENCIL8:								return VK_FORMAT_D24_UNORM_S8_UINT;
+		case GL_DEPTH32F_STENCIL8:								return VK_FORMAT_D32_SFLOAT_S8_UINT;
+		case GL_DEPTH32F_STENCIL8_NV:							return VK_FORMAT_D32_SFLOAT_S8_UINT;
+
+		default:												return VK_FORMAT_UNDEFINED;
+	}
+}
+
+static inline void vkGetFormatSize( const VkFormat format, ktxFormatSize * pFormatSize )
+{
+	pFormatSize->minBlocksX = pFormatSize->minBlocksY = 1;
+	switch ( format )
+	{
+		case VK_FORMAT_R4G4_UNORM_PACK8:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 1 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
+		case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
+		case VK_FORMAT_R5G6B5_UNORM_PACK16:
+		case VK_FORMAT_B5G6R5_UNORM_PACK16:
+		case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
+		case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
+		case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R8_UNORM:
+		case VK_FORMAT_R8_SNORM:
+		case VK_FORMAT_R8_USCALED:
+		case VK_FORMAT_R8_SSCALED:
+		case VK_FORMAT_R8_UINT:
+		case VK_FORMAT_R8_SINT:
+		case VK_FORMAT_R8_SRGB:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 1 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R8G8_UNORM:
+		case VK_FORMAT_R8G8_SNORM:
+		case VK_FORMAT_R8G8_USCALED:
+		case VK_FORMAT_R8G8_SSCALED:
+		case VK_FORMAT_R8G8_UINT:
+		case VK_FORMAT_R8G8_SINT:
+		case VK_FORMAT_R8G8_SRGB:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R8G8B8_UNORM:
+		case VK_FORMAT_R8G8B8_SNORM:
+		case VK_FORMAT_R8G8B8_USCALED:
+		case VK_FORMAT_R8G8B8_SSCALED:
+		case VK_FORMAT_R8G8B8_UINT:
+		case VK_FORMAT_R8G8B8_SINT:
+		case VK_FORMAT_R8G8B8_SRGB:
+		case VK_FORMAT_B8G8R8_UNORM:
+		case VK_FORMAT_B8G8R8_SNORM:
+		case VK_FORMAT_B8G8R8_USCALED:
+		case VK_FORMAT_B8G8R8_SSCALED:
+		case VK_FORMAT_B8G8R8_UINT:
+		case VK_FORMAT_B8G8R8_SINT:
+		case VK_FORMAT_B8G8R8_SRGB:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 3 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R8G8B8A8_UNORM:
+		case VK_FORMAT_R8G8B8A8_SNORM:
+		case VK_FORMAT_R8G8B8A8_USCALED:
+		case VK_FORMAT_R8G8B8A8_SSCALED:
+		case VK_FORMAT_R8G8B8A8_UINT:
+		case VK_FORMAT_R8G8B8A8_SINT:
+		case VK_FORMAT_R8G8B8A8_SRGB:
+		case VK_FORMAT_B8G8R8A8_UNORM:
+		case VK_FORMAT_B8G8R8A8_SNORM:
+		case VK_FORMAT_B8G8R8A8_USCALED:
+		case VK_FORMAT_B8G8R8A8_SSCALED:
+		case VK_FORMAT_B8G8R8A8_UINT:
+		case VK_FORMAT_B8G8R8A8_SINT:
+		case VK_FORMAT_B8G8R8A8_SRGB:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
+		case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
+		case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
+		case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
+		case VK_FORMAT_A8B8G8R8_UINT_PACK32:
+		case VK_FORMAT_A8B8G8R8_SINT_PACK32:
+		case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
+		case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
+		case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
+		case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
+		case VK_FORMAT_A2R10G10B10_UINT_PACK32:
+		case VK_FORMAT_A2R10G10B10_SINT_PACK32:
+		case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
+		case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
+		case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
+		case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
+		case VK_FORMAT_A2B10G10R10_UINT_PACK32:
+		case VK_FORMAT_A2B10G10R10_SINT_PACK32:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R16_UNORM:
+		case VK_FORMAT_R16_SNORM:
+		case VK_FORMAT_R16_USCALED:
+		case VK_FORMAT_R16_SSCALED:
+		case VK_FORMAT_R16_UINT:
+		case VK_FORMAT_R16_SINT:
+		case VK_FORMAT_R16_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R16G16_UNORM:
+		case VK_FORMAT_R16G16_SNORM:
+		case VK_FORMAT_R16G16_USCALED:
+		case VK_FORMAT_R16G16_SSCALED:
+		case VK_FORMAT_R16G16_UINT:
+		case VK_FORMAT_R16G16_SINT:
+		case VK_FORMAT_R16G16_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R16G16B16_UNORM:
+		case VK_FORMAT_R16G16B16_SNORM:
+		case VK_FORMAT_R16G16B16_USCALED:
+		case VK_FORMAT_R16G16B16_SSCALED:
+		case VK_FORMAT_R16G16B16_UINT:
+		case VK_FORMAT_R16G16B16_SINT:
+		case VK_FORMAT_R16G16B16_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 6 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R16G16B16A16_UNORM:
+		case VK_FORMAT_R16G16B16A16_SNORM:
+		case VK_FORMAT_R16G16B16A16_USCALED:
+		case VK_FORMAT_R16G16B16A16_SSCALED:
+		case VK_FORMAT_R16G16B16A16_UINT:
+		case VK_FORMAT_R16G16B16A16_SINT:
+		case VK_FORMAT_R16G16B16A16_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R32_UINT:
+		case VK_FORMAT_R32_SINT:
+		case VK_FORMAT_R32_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R32G32_UINT:
+		case VK_FORMAT_R32G32_SINT:
+		case VK_FORMAT_R32G32_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R32G32B32_UINT:
+		case VK_FORMAT_R32G32B32_SINT:
+		case VK_FORMAT_R32G32B32_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 12 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R32G32B32A32_UINT:
+		case VK_FORMAT_R32G32B32A32_SINT:
+		case VK_FORMAT_R32G32B32A32_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R64_UINT:
+		case VK_FORMAT_R64_SINT:
+		case VK_FORMAT_R64_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R64G64_UINT:
+		case VK_FORMAT_R64G64_SINT:
+		case VK_FORMAT_R64G64_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R64G64B64_UINT:
+		case VK_FORMAT_R64G64B64_SINT:
+		case VK_FORMAT_R64G64B64_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 24 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_R64G64B64A64_UINT:
+		case VK_FORMAT_R64G64B64A64_SINT:
+		case VK_FORMAT_R64G64B64A64_SFLOAT:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 32 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
+		case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_D16_UNORM:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 2 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_X8_D24_UNORM_PACK32:
+			pFormatSize->flags = KTX_FORMAT_SIZE_PACKED_BIT | KTX_FORMAT_SIZE_DEPTH_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_D32_SFLOAT:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_S8_UINT:
+			pFormatSize->flags = KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 1 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_D16_UNORM_S8_UINT:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 3 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_D24_UNORM_S8_UINT:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 4 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_D32_SFLOAT_S8_UINT:
+			pFormatSize->flags = KTX_FORMAT_SIZE_DEPTH_BIT | KTX_FORMAT_SIZE_STENCIL_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_BC1_RGB_UNORM_BLOCK:
+		case VK_FORMAT_BC1_RGB_SRGB_BLOCK:
+		case VK_FORMAT_BC1_RGBA_UNORM_BLOCK:
+		case VK_FORMAT_BC1_RGBA_SRGB_BLOCK:
+		case VK_FORMAT_BC4_UNORM_BLOCK:
+		case VK_FORMAT_BC4_SNORM_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_BC2_UNORM_BLOCK:
+		case VK_FORMAT_BC2_SRGB_BLOCK:
+		case VK_FORMAT_BC3_UNORM_BLOCK:
+		case VK_FORMAT_BC3_SRGB_BLOCK:
+		case VK_FORMAT_BC5_UNORM_BLOCK:
+		case VK_FORMAT_BC5_SNORM_BLOCK:
+		case VK_FORMAT_BC6H_UFLOAT_BLOCK:
+		case VK_FORMAT_BC6H_SFLOAT_BLOCK:
+		case VK_FORMAT_BC7_UNORM_BLOCK:
+		case VK_FORMAT_BC7_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+		case VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK:
+		case VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK:
+		case VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK:
+		case VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK:
+		case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+		case VK_FORMAT_EAC_R11_SNORM_BLOCK:
+		case VK_FORMAT_EAC_R11G11_UNORM_BLOCK:
+		case VK_FORMAT_EAC_R11G11_SNORM_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG:
+		case VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			pFormatSize->minBlocksX = 2;
+			pFormatSize->minBlocksY = 2;
+			break;
+		case VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG:
+		case VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG:
+		case VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			pFormatSize->minBlocksX = 2;
+			pFormatSize->minBlocksY = 2;
+			break;
+		case VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG:
+		case VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 8 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_4x4_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_4x4_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 4;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_5x4_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_5x4_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 4;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_5x5_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_5x5_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 5;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_6x5_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_6x5_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_6x6_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_6x6_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 6;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_8x5_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_8x5_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_8x6_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_8x6_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_8x8_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_8x8_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 8;
+			pFormatSize->blockHeight = 8;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_10x5_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_10x5_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 5;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_10x6_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_10x6_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 6;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_10x8_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_10x8_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 8;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_10x10_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_10x10_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 10;
+			pFormatSize->blockHeight = 10;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_12x10_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_12x10_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 12;
+			pFormatSize->blockHeight = 10;
+			pFormatSize->blockDepth = 1;
+			break;
+		case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+		case VK_FORMAT_ASTC_12x12_SRGB_BLOCK:
+			pFormatSize->flags = KTX_FORMAT_SIZE_COMPRESSED_BIT;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 16 * 8;
+			pFormatSize->blockWidth = 12;
+			pFormatSize->blockHeight = 12;
+			pFormatSize->blockDepth = 1;
+			break;
+		default:
+			pFormatSize->flags = 0;
+			pFormatSize->paletteSizeInBits = 0;
+			pFormatSize->blockSizeInBits = 0 * 8;
+			pFormatSize->blockWidth = 1;
+			pFormatSize->blockHeight = 1;
+			pFormatSize->blockDepth = 1;
+			break;
+	}
+}
+
+#endif // !VK_FORMAT_H
diff --git a/thirdparty/libktx/lib/vkformat_enum.h b/thirdparty/libktx/lib/vkformat_enum.h
new file mode 100644
index 00000000000..c6d85602bd2
--- /dev/null
+++ b/thirdparty/libktx/lib/vkformat_enum.h
@@ -0,0 +1,300 @@
+#if !defined(_VKFORMAT_ENUM_H_) && !defined(VULKAN_CORE_H_)
+#define _VKFORMAT_ENUM_H_
+
+/***************************** Do not edit.  *****************************
+ Automatically generated from vulkan_core.h version 151 by mkvkformatfiles.
+ *************************************************************************/
+
+/*
+** Copyright (c) 2015-2020 The Khronos Group Inc.
+**
+** SPDX-License-Identifier: Apache-2.0
+*/
+
+#if defined(_MSC_VER) && _MSC_VER < 1900 // Older than VS 2015.
+typedef unsigned __int32 VkFlags;
+#else
+#include <stdint.h>
+typedef uint32_t VkFlags;
+#endif
+
+typedef enum VkFormat {
+    VK_FORMAT_UNDEFINED = 0,
+    VK_FORMAT_R4G4_UNORM_PACK8 = 1,
+    VK_FORMAT_R4G4B4A4_UNORM_PACK16 = 2,
+    VK_FORMAT_B4G4R4A4_UNORM_PACK16 = 3,
+    VK_FORMAT_R5G6B5_UNORM_PACK16 = 4,
+    VK_FORMAT_B5G6R5_UNORM_PACK16 = 5,
+    VK_FORMAT_R5G5B5A1_UNORM_PACK16 = 6,
+    VK_FORMAT_B5G5R5A1_UNORM_PACK16 = 7,
+    VK_FORMAT_A1R5G5B5_UNORM_PACK16 = 8,
+    VK_FORMAT_R8_UNORM = 9,
+    VK_FORMAT_R8_SNORM = 10,
+    VK_FORMAT_R8_USCALED = 11,
+    VK_FORMAT_R8_SSCALED = 12,
+    VK_FORMAT_R8_UINT = 13,
+    VK_FORMAT_R8_SINT = 14,
+    VK_FORMAT_R8_SRGB = 15,
+    VK_FORMAT_R8G8_UNORM = 16,
+    VK_FORMAT_R8G8_SNORM = 17,
+    VK_FORMAT_R8G8_USCALED = 18,
+    VK_FORMAT_R8G8_SSCALED = 19,
+    VK_FORMAT_R8G8_UINT = 20,
+    VK_FORMAT_R8G8_SINT = 21,
+    VK_FORMAT_R8G8_SRGB = 22,
+    VK_FORMAT_R8G8B8_UNORM = 23,
+    VK_FORMAT_R8G8B8_SNORM = 24,
+    VK_FORMAT_R8G8B8_USCALED = 25,
+    VK_FORMAT_R8G8B8_SSCALED = 26,
+    VK_FORMAT_R8G8B8_UINT = 27,
+    VK_FORMAT_R8G8B8_SINT = 28,
+    VK_FORMAT_R8G8B8_SRGB = 29,
+    VK_FORMAT_B8G8R8_UNORM = 30,
+    VK_FORMAT_B8G8R8_SNORM = 31,
+    VK_FORMAT_B8G8R8_USCALED = 32,
+    VK_FORMAT_B8G8R8_SSCALED = 33,
+    VK_FORMAT_B8G8R8_UINT = 34,
+    VK_FORMAT_B8G8R8_SINT = 35,
+    VK_FORMAT_B8G8R8_SRGB = 36,
+    VK_FORMAT_R8G8B8A8_UNORM = 37,
+    VK_FORMAT_R8G8B8A8_SNORM = 38,
+    VK_FORMAT_R8G8B8A8_USCALED = 39,
+    VK_FORMAT_R8G8B8A8_SSCALED = 40,
+    VK_FORMAT_R8G8B8A8_UINT = 41,
+    VK_FORMAT_R8G8B8A8_SINT = 42,
+    VK_FORMAT_R8G8B8A8_SRGB = 43,
+    VK_FORMAT_B8G8R8A8_UNORM = 44,
+    VK_FORMAT_B8G8R8A8_SNORM = 45,
+    VK_FORMAT_B8G8R8A8_USCALED = 46,
+    VK_FORMAT_B8G8R8A8_SSCALED = 47,
+    VK_FORMAT_B8G8R8A8_UINT = 48,
+    VK_FORMAT_B8G8R8A8_SINT = 49,
+    VK_FORMAT_B8G8R8A8_SRGB = 50,
+    VK_FORMAT_A8B8G8R8_UNORM_PACK32 = 51,
+    VK_FORMAT_A8B8G8R8_SNORM_PACK32 = 52,
+    VK_FORMAT_A8B8G8R8_USCALED_PACK32 = 53,
+    VK_FORMAT_A8B8G8R8_SSCALED_PACK32 = 54,
+    VK_FORMAT_A8B8G8R8_UINT_PACK32 = 55,
+    VK_FORMAT_A8B8G8R8_SINT_PACK32 = 56,
+    VK_FORMAT_A8B8G8R8_SRGB_PACK32 = 57,
+    VK_FORMAT_A2R10G10B10_UNORM_PACK32 = 58,
+    VK_FORMAT_A2R10G10B10_SNORM_PACK32 = 59,
+    VK_FORMAT_A2R10G10B10_USCALED_PACK32 = 60,
+    VK_FORMAT_A2R10G10B10_SSCALED_PACK32 = 61,
+    VK_FORMAT_A2R10G10B10_UINT_PACK32 = 62,
+    VK_FORMAT_A2R10G10B10_SINT_PACK32 = 63,
+    VK_FORMAT_A2B10G10R10_UNORM_PACK32 = 64,
+    VK_FORMAT_A2B10G10R10_SNORM_PACK32 = 65,
+    VK_FORMAT_A2B10G10R10_USCALED_PACK32 = 66,
+    VK_FORMAT_A2B10G10R10_SSCALED_PACK32 = 67,
+    VK_FORMAT_A2B10G10R10_UINT_PACK32 = 68,
+    VK_FORMAT_A2B10G10R10_SINT_PACK32 = 69,
+    VK_FORMAT_R16_UNORM = 70,
+    VK_FORMAT_R16_SNORM = 71,
+    VK_FORMAT_R16_USCALED = 72,
+    VK_FORMAT_R16_SSCALED = 73,
+    VK_FORMAT_R16_UINT = 74,
+    VK_FORMAT_R16_SINT = 75,
+    VK_FORMAT_R16_SFLOAT = 76,
+    VK_FORMAT_R16G16_UNORM = 77,
+    VK_FORMAT_R16G16_SNORM = 78,
+    VK_FORMAT_R16G16_USCALED = 79,
+    VK_FORMAT_R16G16_SSCALED = 80,
+    VK_FORMAT_R16G16_UINT = 81,
+    VK_FORMAT_R16G16_SINT = 82,
+    VK_FORMAT_R16G16_SFLOAT = 83,
+    VK_FORMAT_R16G16B16_UNORM = 84,
+    VK_FORMAT_R16G16B16_SNORM = 85,
+    VK_FORMAT_R16G16B16_USCALED = 86,
+    VK_FORMAT_R16G16B16_SSCALED = 87,
+    VK_FORMAT_R16G16B16_UINT = 88,
+    VK_FORMAT_R16G16B16_SINT = 89,
+    VK_FORMAT_R16G16B16_SFLOAT = 90,
+    VK_FORMAT_R16G16B16A16_UNORM = 91,
+    VK_FORMAT_R16G16B16A16_SNORM = 92,
+    VK_FORMAT_R16G16B16A16_USCALED = 93,
+    VK_FORMAT_R16G16B16A16_SSCALED = 94,
+    VK_FORMAT_R16G16B16A16_UINT = 95,
+    VK_FORMAT_R16G16B16A16_SINT = 96,
+    VK_FORMAT_R16G16B16A16_SFLOAT = 97,
+    VK_FORMAT_R32_UINT = 98,
+    VK_FORMAT_R32_SINT = 99,
+    VK_FORMAT_R32_SFLOAT = 100,
+    VK_FORMAT_R32G32_UINT = 101,
+    VK_FORMAT_R32G32_SINT = 102,
+    VK_FORMAT_R32G32_SFLOAT = 103,
+    VK_FORMAT_R32G32B32_UINT = 104,
+    VK_FORMAT_R32G32B32_SINT = 105,
+    VK_FORMAT_R32G32B32_SFLOAT = 106,
+    VK_FORMAT_R32G32B32A32_UINT = 107,
+    VK_FORMAT_R32G32B32A32_SINT = 108,
+    VK_FORMAT_R32G32B32A32_SFLOAT = 109,
+    VK_FORMAT_R64_UINT = 110,
+    VK_FORMAT_R64_SINT = 111,
+    VK_FORMAT_R64_SFLOAT = 112,
+    VK_FORMAT_R64G64_UINT = 113,
+    VK_FORMAT_R64G64_SINT = 114,
+    VK_FORMAT_R64G64_SFLOAT = 115,
+    VK_FORMAT_R64G64B64_UINT = 116,
+    VK_FORMAT_R64G64B64_SINT = 117,
+    VK_FORMAT_R64G64B64_SFLOAT = 118,
+    VK_FORMAT_R64G64B64A64_UINT = 119,
+    VK_FORMAT_R64G64B64A64_SINT = 120,
+    VK_FORMAT_R64G64B64A64_SFLOAT = 121,
+    VK_FORMAT_B10G11R11_UFLOAT_PACK32 = 122,
+    VK_FORMAT_E5B9G9R9_UFLOAT_PACK32 = 123,
+    VK_FORMAT_D16_UNORM = 124,
+    VK_FORMAT_X8_D24_UNORM_PACK32 = 125,
+    VK_FORMAT_D32_SFLOAT = 126,
+    VK_FORMAT_S8_UINT = 127,
+    VK_FORMAT_D16_UNORM_S8_UINT = 128,
+    VK_FORMAT_D24_UNORM_S8_UINT = 129,
+    VK_FORMAT_D32_SFLOAT_S8_UINT = 130,
+    VK_FORMAT_BC1_RGB_UNORM_BLOCK = 131,
+    VK_FORMAT_BC1_RGB_SRGB_BLOCK = 132,
+    VK_FORMAT_BC1_RGBA_UNORM_BLOCK = 133,
+    VK_FORMAT_BC1_RGBA_SRGB_BLOCK = 134,
+    VK_FORMAT_BC2_UNORM_BLOCK = 135,
+    VK_FORMAT_BC2_SRGB_BLOCK = 136,
+    VK_FORMAT_BC3_UNORM_BLOCK = 137,
+    VK_FORMAT_BC3_SRGB_BLOCK = 138,
+    VK_FORMAT_BC4_UNORM_BLOCK = 139,
+    VK_FORMAT_BC4_SNORM_BLOCK = 140,
+    VK_FORMAT_BC5_UNORM_BLOCK = 141,
+    VK_FORMAT_BC5_SNORM_BLOCK = 142,
+    VK_FORMAT_BC6H_UFLOAT_BLOCK = 143,
+    VK_FORMAT_BC6H_SFLOAT_BLOCK = 144,
+    VK_FORMAT_BC7_UNORM_BLOCK = 145,
+    VK_FORMAT_BC7_SRGB_BLOCK = 146,
+    VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK = 147,
+    VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK = 148,
+    VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK = 149,
+    VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK = 150,
+    VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK = 151,
+    VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK = 152,
+    VK_FORMAT_EAC_R11_UNORM_BLOCK = 153,
+    VK_FORMAT_EAC_R11_SNORM_BLOCK = 154,
+    VK_FORMAT_EAC_R11G11_UNORM_BLOCK = 155,
+    VK_FORMAT_EAC_R11G11_SNORM_BLOCK = 156,
+    VK_FORMAT_ASTC_4x4_UNORM_BLOCK = 157,
+    VK_FORMAT_ASTC_4x4_SRGB_BLOCK = 158,
+    VK_FORMAT_ASTC_5x4_UNORM_BLOCK = 159,
+    VK_FORMAT_ASTC_5x4_SRGB_BLOCK = 160,
+    VK_FORMAT_ASTC_5x5_UNORM_BLOCK = 161,
+    VK_FORMAT_ASTC_5x5_SRGB_BLOCK = 162,
+    VK_FORMAT_ASTC_6x5_UNORM_BLOCK = 163,
+    VK_FORMAT_ASTC_6x5_SRGB_BLOCK = 164,
+    VK_FORMAT_ASTC_6x6_UNORM_BLOCK = 165,
+    VK_FORMAT_ASTC_6x6_SRGB_BLOCK = 166,
+    VK_FORMAT_ASTC_8x5_UNORM_BLOCK = 167,
+    VK_FORMAT_ASTC_8x5_SRGB_BLOCK = 168,
+    VK_FORMAT_ASTC_8x6_UNORM_BLOCK = 169,
+    VK_FORMAT_ASTC_8x6_SRGB_BLOCK = 170,
+    VK_FORMAT_ASTC_8x8_UNORM_BLOCK = 171,
+    VK_FORMAT_ASTC_8x8_SRGB_BLOCK = 172,
+    VK_FORMAT_ASTC_10x5_UNORM_BLOCK = 173,
+    VK_FORMAT_ASTC_10x5_SRGB_BLOCK = 174,
+    VK_FORMAT_ASTC_10x6_UNORM_BLOCK = 175,
+    VK_FORMAT_ASTC_10x6_SRGB_BLOCK = 176,
+    VK_FORMAT_ASTC_10x8_UNORM_BLOCK = 177,
+    VK_FORMAT_ASTC_10x8_SRGB_BLOCK = 178,
+    VK_FORMAT_ASTC_10x10_UNORM_BLOCK = 179,
+    VK_FORMAT_ASTC_10x10_SRGB_BLOCK = 180,
+    VK_FORMAT_ASTC_12x10_UNORM_BLOCK = 181,
+    VK_FORMAT_ASTC_12x10_SRGB_BLOCK = 182,
+    VK_FORMAT_ASTC_12x12_UNORM_BLOCK = 183,
+    VK_FORMAT_ASTC_12x12_SRGB_BLOCK = 184,
+    VK_FORMAT_G8B8G8R8_422_UNORM = 1000156000,
+    VK_FORMAT_B8G8R8G8_422_UNORM = 1000156001,
+    VK_FORMAT_G8_B8_R8_3PLANE_420_UNORM = 1000156002,
+    VK_FORMAT_G8_B8R8_2PLANE_420_UNORM = 1000156003,
+    VK_FORMAT_G8_B8_R8_3PLANE_422_UNORM = 1000156004,
+    VK_FORMAT_G8_B8R8_2PLANE_422_UNORM = 1000156005,
+    VK_FORMAT_G8_B8_R8_3PLANE_444_UNORM = 1000156006,
+    VK_FORMAT_R10X6_UNORM_PACK16 = 1000156007,
+    VK_FORMAT_R10X6G10X6_UNORM_2PACK16 = 1000156008,
+    VK_FORMAT_R10X6G10X6B10X6A10X6_UNORM_4PACK16 = 1000156009,
+    VK_FORMAT_G10X6B10X6G10X6R10X6_422_UNORM_4PACK16 = 1000156010,
+    VK_FORMAT_B10X6G10X6R10X6G10X6_422_UNORM_4PACK16 = 1000156011,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_420_UNORM_3PACK16 = 1000156012,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_420_UNORM_3PACK16 = 1000156013,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_422_UNORM_3PACK16 = 1000156014,
+    VK_FORMAT_G10X6_B10X6R10X6_2PLANE_422_UNORM_3PACK16 = 1000156015,
+    VK_FORMAT_G10X6_B10X6_R10X6_3PLANE_444_UNORM_3PACK16 = 1000156016,
+    VK_FORMAT_R12X4_UNORM_PACK16 = 1000156017,
+    VK_FORMAT_R12X4G12X4_UNORM_2PACK16 = 1000156018,
+    VK_FORMAT_R12X4G12X4B12X4A12X4_UNORM_4PACK16 = 1000156019,
+    VK_FORMAT_G12X4B12X4G12X4R12X4_422_UNORM_4PACK16 = 1000156020,
+    VK_FORMAT_B12X4G12X4R12X4G12X4_422_UNORM_4PACK16 = 1000156021,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_420_UNORM_3PACK16 = 1000156022,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_420_UNORM_3PACK16 = 1000156023,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_422_UNORM_3PACK16 = 1000156024,
+    VK_FORMAT_G12X4_B12X4R12X4_2PLANE_422_UNORM_3PACK16 = 1000156025,
+    VK_FORMAT_G12X4_B12X4_R12X4_3PLANE_444_UNORM_3PACK16 = 1000156026,
+    VK_FORMAT_G16B16G16R16_422_UNORM = 1000156027,
+    VK_FORMAT_B16G16R16G16_422_UNORM = 1000156028,
+    VK_FORMAT_G16_B16_R16_3PLANE_420_UNORM = 1000156029,
+    VK_FORMAT_G16_B16R16_2PLANE_420_UNORM = 1000156030,
+    VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM = 1000156031,
+    VK_FORMAT_G16_B16R16_2PLANE_422_UNORM = 1000156032,
+    VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM = 1000156033,
+    VK_FORMAT_PVRTC1_2BPP_UNORM_BLOCK_IMG = 1000054000,
+    VK_FORMAT_PVRTC1_4BPP_UNORM_BLOCK_IMG = 1000054001,
+    VK_FORMAT_PVRTC2_2BPP_UNORM_BLOCK_IMG = 1000054002,
+    VK_FORMAT_PVRTC2_4BPP_UNORM_BLOCK_IMG = 1000054003,
+    VK_FORMAT_PVRTC1_2BPP_SRGB_BLOCK_IMG = 1000054004,
+    VK_FORMAT_PVRTC1_4BPP_SRGB_BLOCK_IMG = 1000054005,
+    VK_FORMAT_PVRTC2_2BPP_SRGB_BLOCK_IMG = 1000054006,
+    VK_FORMAT_PVRTC2_4BPP_SRGB_BLOCK_IMG = 1000054007,
+    VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK_EXT = 1000066000,
+    VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK_EXT = 1000066001,
+    VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK_EXT = 1000066002,
+    VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK_EXT = 1000066003,
+    VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK_EXT = 1000066004,
+    VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK_EXT = 1000066005,
+    VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK_EXT = 1000066006,
+    VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK_EXT = 1000066007,
+    VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK_EXT = 1000066008,
+    VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK_EXT = 1000066009,
+    VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK_EXT = 1000066010,
+    VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK_EXT = 1000066011,
+    VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK_EXT = 1000066012,
+    VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK_EXT = 1000066013,
+    VK_FORMAT_ASTC_3x3x3_UNORM_BLOCK_EXT = 1000288000,
+    VK_FORMAT_ASTC_3x3x3_SRGB_BLOCK_EXT = 1000288001,
+    VK_FORMAT_ASTC_3x3x3_SFLOAT_BLOCK_EXT = 1000288002,
+    VK_FORMAT_ASTC_4x3x3_UNORM_BLOCK_EXT = 1000288003,
+    VK_FORMAT_ASTC_4x3x3_SRGB_BLOCK_EXT = 1000288004,
+    VK_FORMAT_ASTC_4x3x3_SFLOAT_BLOCK_EXT = 1000288005,
+    VK_FORMAT_ASTC_4x4x3_UNORM_BLOCK_EXT = 1000288006,
+    VK_FORMAT_ASTC_4x4x3_SRGB_BLOCK_EXT = 1000288007,
+    VK_FORMAT_ASTC_4x4x3_SFLOAT_BLOCK_EXT = 1000288008,
+    VK_FORMAT_ASTC_4x4x4_UNORM_BLOCK_EXT = 1000288009,
+    VK_FORMAT_ASTC_4x4x4_SRGB_BLOCK_EXT = 1000288010,
+    VK_FORMAT_ASTC_4x4x4_SFLOAT_BLOCK_EXT = 1000288011,
+    VK_FORMAT_ASTC_5x4x4_UNORM_BLOCK_EXT = 1000288012,
+    VK_FORMAT_ASTC_5x4x4_SRGB_BLOCK_EXT = 1000288013,
+    VK_FORMAT_ASTC_5x4x4_SFLOAT_BLOCK_EXT = 1000288014,
+    VK_FORMAT_ASTC_5x5x4_UNORM_BLOCK_EXT = 1000288015,
+    VK_FORMAT_ASTC_5x5x4_SRGB_BLOCK_EXT = 1000288016,
+    VK_FORMAT_ASTC_5x5x4_SFLOAT_BLOCK_EXT = 1000288017,
+    VK_FORMAT_ASTC_5x5x5_UNORM_BLOCK_EXT = 1000288018,
+    VK_FORMAT_ASTC_5x5x5_SRGB_BLOCK_EXT = 1000288019,
+    VK_FORMAT_ASTC_5x5x5_SFLOAT_BLOCK_EXT = 1000288020,
+    VK_FORMAT_ASTC_6x5x5_UNORM_BLOCK_EXT = 1000288021,
+    VK_FORMAT_ASTC_6x5x5_SRGB_BLOCK_EXT = 1000288022,
+    VK_FORMAT_ASTC_6x5x5_SFLOAT_BLOCK_EXT = 1000288023,
+    VK_FORMAT_ASTC_6x6x5_UNORM_BLOCK_EXT = 1000288024,
+    VK_FORMAT_ASTC_6x6x5_SRGB_BLOCK_EXT = 1000288025,
+    VK_FORMAT_ASTC_6x6x5_SFLOAT_BLOCK_EXT = 1000288026,
+    VK_FORMAT_ASTC_6x6x6_UNORM_BLOCK_EXT = 1000288027,
+    VK_FORMAT_ASTC_6x6x6_SRGB_BLOCK_EXT = 1000288028,
+    VK_FORMAT_ASTC_6x6x6_SFLOAT_BLOCK_EXT = 1000288029,
+    VK_FORMAT_A4R4G4B4_UNORM_PACK16_EXT = 1000340000,
+    VK_FORMAT_A4B4G4R4_UNORM_PACK16_EXT = 1000340001,
+    VK_FORMAT_MAX_ENUM = 0x7FFFFFFF
+} VkFormat;
+
+#define VK_FORMAT_MAX_STANDARD_ENUM 184
+
+#endif /* _VKFORMAT_ENUM_H_ */
diff --git a/thirdparty/libktx/other_include/KHR/khrplatform.h b/thirdparty/libktx/other_include/KHR/khrplatform.h
new file mode 100644
index 00000000000..5b55ea2b981
--- /dev/null
+++ b/thirdparty/libktx/other_include/KHR/khrplatform.h
@@ -0,0 +1,290 @@
+#ifndef __khrplatform_h_
+#define __khrplatform_h_
+
+/*
+** Copyright (c) 2008-2018 The Khronos Group Inc.
+**
+** Permission is hereby granted, free of charge, to any person obtaining a
+** copy of this software and/or associated documentation files (the
+** "Materials"), to deal in the Materials without restriction, including
+** without limitation the rights to use, copy, modify, merge, publish,
+** distribute, sublicense, and/or sell copies of the Materials, and to
+** permit persons to whom the Materials are 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 Materials.
+**
+** THE MATERIALS ARE 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
+** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
+*/
+
+/* Khronos platform-specific types and definitions.
+ *
+ * The master copy of khrplatform.h is maintained in the Khronos EGL
+ * Registry repository at https://github.com/KhronosGroup/EGL-Registry
+ * The last semantic modification to khrplatform.h was at commit ID:
+ *      67a3e0864c2d75ea5287b9f3d2eb74a745936692
+ *
+ * Adopters may modify this file to suit their platform. Adopters are
+ * encouraged to submit platform specific modifications to the Khronos
+ * group so that they can be included in future versions of this file.
+ * Please submit changes by filing pull requests or issues on
+ * the EGL Registry repository linked above.
+ *
+ *
+ * See the Implementer's Guidelines for information about where this file
+ * should be located on your system and for more details of its use:
+ *    http://www.khronos.org/registry/implementers_guide.pdf
+ *
+ * This file should be included as
+ *        #include <KHR/khrplatform.h>
+ * by Khronos client API header files that use its types and defines.
+ *
+ * The types in khrplatform.h should only be used to define API-specific types.
+ *
+ * Types defined in khrplatform.h:
+ *    khronos_int8_t              signed   8  bit
+ *    khronos_uint8_t             unsigned 8  bit
+ *    khronos_int16_t             signed   16 bit
+ *    khronos_uint16_t            unsigned 16 bit
+ *    khronos_int32_t             signed   32 bit
+ *    khronos_uint32_t            unsigned 32 bit
+ *    khronos_int64_t             signed   64 bit
+ *    khronos_uint64_t            unsigned 64 bit
+ *    khronos_intptr_t            signed   same number of bits as a pointer
+ *    khronos_uintptr_t           unsigned same number of bits as a pointer
+ *    khronos_ssize_t             signed   size
+ *    khronos_usize_t             unsigned size
+ *    khronos_float_t             signed   32 bit floating point
+ *    khronos_time_ns_t           unsigned 64 bit time in nanoseconds
+ *    khronos_utime_nanoseconds_t unsigned time interval or absolute time in
+ *                                         nanoseconds
+ *    khronos_stime_nanoseconds_t signed time interval in nanoseconds
+ *    khronos_boolean_enum_t      enumerated boolean type. This should
+ *      only be used as a base type when a client API's boolean type is
+ *      an enum. Client APIs which use an integer or other type for
+ *      booleans cannot use this as the base type for their boolean.
+ *
+ * Tokens defined in khrplatform.h:
+ *
+ *    KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
+ *
+ *    KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
+ *    KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
+ *
+ * Calling convention macros defined in this file:
+ *    KHRONOS_APICALL
+ *    KHRONOS_APIENTRY
+ *    KHRONOS_APIATTRIBUTES
+ *
+ * These may be used in function prototypes as:
+ *
+ *      KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
+ *                                  int arg1,
+ *                                  int arg2) KHRONOS_APIATTRIBUTES;
+ */
+
+#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
+#   define KHRONOS_STATIC 1
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APICALL
+ *-------------------------------------------------------------------------
+ * This precedes the return type of the function in the function prototype.
+ */
+#if defined(KHRONOS_STATIC)
+    /* If the preprocessor constant KHRONOS_STATIC is defined, make the
+     * header compatible with static linking. */
+#   define KHRONOS_APICALL
+#elif defined(_WIN32)
+#   define KHRONOS_APICALL __declspec(dllimport)
+#elif defined (__SYMBIAN32__)
+#   define KHRONOS_APICALL IMPORT_C
+#elif defined(__ANDROID__)
+#   define KHRONOS_APICALL __attribute__((visibility("default")))
+#else
+#   define KHRONOS_APICALL
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIENTRY
+ *-------------------------------------------------------------------------
+ * This follows the return type of the function  and precedes the function
+ * name in the function prototype.
+ */
+#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(KHRONOS_STATIC)
+    /* Win32 but not WinCE */
+#   define KHRONOS_APIENTRY __stdcall
+#else
+#   define KHRONOS_APIENTRY
+#endif
+
+/*-------------------------------------------------------------------------
+ * Definition of KHRONOS_APIATTRIBUTES
+ *-------------------------------------------------------------------------
+ * This follows the closing parenthesis of the function prototype arguments.
+ */
+#if defined (__ARMCC_2__)
+#define KHRONOS_APIATTRIBUTES __softfp
+#else
+#define KHRONOS_APIATTRIBUTES
+#endif
+
+/*-------------------------------------------------------------------------
+ * basic type definitions
+ *-----------------------------------------------------------------------*/
+#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
+
+
+/*
+ * Using <stdint.h>
+ */
+#include <stdint.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(__VMS ) || defined(__sgi)
+
+/*
+ * Using <inttypes.h>
+ */
+#include <inttypes.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
+
+/*
+ * Win32
+ */
+typedef __int32                 khronos_int32_t;
+typedef unsigned __int32        khronos_uint32_t;
+typedef __int64                 khronos_int64_t;
+typedef unsigned __int64        khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif defined(__sun__) || defined(__digital__)
+
+/*
+ * Sun or Digital
+ */
+typedef int                     khronos_int32_t;
+typedef unsigned int            khronos_uint32_t;
+#if defined(__arch64__) || defined(_LP64)
+typedef long int                khronos_int64_t;
+typedef unsigned long int       khronos_uint64_t;
+#else
+typedef long long int           khronos_int64_t;
+typedef unsigned long long int  khronos_uint64_t;
+#endif /* __arch64__ */
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#elif 0
+
+/*
+ * Hypothetical platform with no float or int64 support
+ */
+typedef int                     khronos_int32_t;
+typedef unsigned int            khronos_uint32_t;
+#define KHRONOS_SUPPORT_INT64   0
+#define KHRONOS_SUPPORT_FLOAT   0
+
+#else
+
+/*
+ * Generic fallback
+ */
+#include <stdint.h>
+typedef int32_t                 khronos_int32_t;
+typedef uint32_t                khronos_uint32_t;
+typedef int64_t                 khronos_int64_t;
+typedef uint64_t                khronos_uint64_t;
+#define KHRONOS_SUPPORT_INT64   1
+#define KHRONOS_SUPPORT_FLOAT   1
+
+#endif
+
+
+/*
+ * Types that are (so far) the same on all platforms
+ */
+typedef signed   char          khronos_int8_t;
+typedef unsigned char          khronos_uint8_t;
+typedef signed   short int     khronos_int16_t;
+typedef unsigned short int     khronos_uint16_t;
+
+/*
+ * Types that differ between LLP64 and LP64 architectures - in LLP64,
+ * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
+ * to be the only LLP64 architecture in current use.
+ */
+#ifdef _WIN64
+typedef signed   long long int khronos_intptr_t;
+typedef unsigned long long int khronos_uintptr_t;
+typedef signed   long long int khronos_ssize_t;
+typedef unsigned long long int khronos_usize_t;
+#else
+typedef signed   long  int     khronos_intptr_t;
+typedef unsigned long  int     khronos_uintptr_t;
+typedef signed   long  int     khronos_ssize_t;
+typedef unsigned long  int     khronos_usize_t;
+#endif
+
+#if KHRONOS_SUPPORT_FLOAT
+/*
+ * Float type
+ */
+typedef          float         khronos_float_t;
+#endif
+
+#if KHRONOS_SUPPORT_INT64
+/* Time types
+ *
+ * These types can be used to represent a time interval in nanoseconds or
+ * an absolute Unadjusted System Time.  Unadjusted System Time is the number
+ * of nanoseconds since some arbitrary system event (e.g. since the last
+ * time the system booted).  The Unadjusted System Time is an unsigned
+ * 64 bit value that wraps back to 0 every 584 years.  Time intervals
+ * may be either signed or unsigned.
+ */
+typedef khronos_uint64_t       khronos_utime_nanoseconds_t;
+typedef khronos_int64_t        khronos_stime_nanoseconds_t;
+#endif
+
+/*
+ * Dummy value used to pad enum types to 32 bits.
+ */
+#ifndef KHRONOS_MAX_ENUM
+#define KHRONOS_MAX_ENUM 0x7FFFFFFF
+#endif
+
+/*
+ * Enumerated boolean type
+ *
+ * Values other than zero should be considered to be true.  Therefore
+ * comparisons should not be made against KHRONOS_TRUE.
+ */
+typedef enum {
+    KHRONOS_FALSE = 0,
+    KHRONOS_TRUE  = 1,
+    KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
+} khronos_boolean_enum_t;
+
+#endif /* __khrplatform_h_ */
diff --git a/thirdparty/libktx/utils/unused.h b/thirdparty/libktx/utils/unused.h
new file mode 100644
index 00000000000..31870ab6395
--- /dev/null
+++ b/thirdparty/libktx/utils/unused.h
@@ -0,0 +1,37 @@
+/* -*- tab-width: 4; -*- */
+/* vi: set sw=2 ts=4 expandtab: */
+
+/* Copyright 2019-2018 The Khronos Group Inc.
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+/* I'm extending this beyond the purpose implied by its name rather than creating
+ * a new file to hold the FALLTHROUGH declaration as this
+ * file is already included in most places FALLTHROUGH
+ * is needed.
+ */
+
+#ifndef _UNUSED_H
+#define _UNUSED_H
+
+#if (__cplusplus >= 201703L)
+#define MAYBE_UNUSED [[maybe_unused]]
+#elif __GNUC__ || __clang__
+  #define MAYBE_UNUSED __attribute__((unused))
+#else
+  // Boohoo. VC++ has no equivalent
+  #define MAYBE_UNUSED
+#endif
+
+#define U_ASSERT_ONLY MAYBE_UNUSED
+
+// For unused parameters of c functions. Portable.
+#define UNUSED(x) (void)(x)
+
+#if !__clang__ && __GNUC__ // grumble ... clang ... grumble
+#define FALLTHROUGH __attribute__((fallthrough))
+#else
+#define FALLTHROUGH
+#endif
+
+#endif /* UNUSED_H */