summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNanley Chery <nanley.g.chery@intel.com>2015-07-21 11:15:30 -0700
committerNanley Chery <nanley.g.chery@intel.com>2015-07-21 12:24:13 -0700
commitfc953abe1d80b4663d850dcd2f7b2f6a0a311869 (patch)
tree7471f533177b9328d108865cb6b3f94b994d3fb8
parent67793b1572a7d523b16771899847cf53f5f756c2 (diff)
Start creating script to create a texture array
-rw-r--r--Makefile3
-rw-r--r--gen_array.c194
-rw-r--r--piglit_ktx.c843
-rw-r--r--piglit_ktx.h271
4 files changed, 1310 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index 5a49ad3..a283aeb 100644
--- a/Makefile
+++ b/Makefile
@@ -2,8 +2,9 @@
all: default ktxes test
-default: gen_mipmaps.c fix_mali_ktx.c gen_ktx.c
+default: gen_mipmaps.c fix_mali_ktx.c gen_ktx.c gen_array.c piglit_ktx.c
gcc -g gen_mipmaps.c -o gen_mipmaps -L ~/repos/mesa/lib -lGL -lwaffle-1 -lktx
+ gcc -g gen_array.c piglit_ktx.c -o gen_array -L ~/repos/mesa/lib -lGL -lwaffle-1 -lktx
gcc -g gen_mipmaps_man.c -o gen_mipmaps_man -L ~/repos/mesa/lib -lGL -lwaffle-1 -lktx
gcc -g fix_mali_ktx.c -o fix_mali_ktx -lktx -L ~/repos/mesa/lib -lGL
gcc -g gen_ktx.c -o gen_ktx -lktx
diff --git a/gen_array.c b/gen_array.c
new file mode 100644
index 0000000..0368d2d
--- /dev/null
+++ b/gen_array.c
@@ -0,0 +1,194 @@
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <waffle-1/waffle.h>
+#include "piglit_ktx.h"
+
+/* This process requires that we construct blobs of LOD0 images, LOD1, etc
+and pass those in as images[] for libktx */
+
+GLint
+_mesa_components_in_format(GLenum format)
+{
+ switch (format) {
+
+ case GL_RGB:
+ case GL_BGR:
+ return 3;
+
+ case GL_RGBA:
+ case GL_BGRA:
+ return 4;
+
+ default:
+ return -1;
+ }
+}
+
+GLint
+_mesa_bytes_per_pixel(GLenum format, GLenum type)
+{
+ GLint comps = _mesa_components_in_format(format);
+ if (comps < 0)
+ return -1;
+
+ switch (type) {
+ case GL_BYTE:
+ case GL_UNSIGNED_BYTE:
+ return comps * sizeof(GLubyte);
+ case GL_SHORT:
+ case GL_UNSIGNED_SHORT:
+ return comps * sizeof(GLshort);
+ case GL_INT:
+ case GL_UNSIGNED_INT:
+ return comps * sizeof(GLint);
+ case GL_FLOAT:
+ return comps * sizeof(GLfloat);
+ case GL_HALF_FLOAT:
+ return comps * sizeof(GLhalf);
+ default:
+ return -1;
+ }
+}
+
+
+
+
+int
+no_dot_output_filter (const struct dirent * dir_entity)
+{
+ return strlen(dir_entity->d_name) > 2 &&
+ strstr(dir_entity->d_name, "waffles.ktx") == NULL &&
+ strstr(dir_entity->d_name, ".ktx") != NULL &&
+ strstr(dir_entity->d_name, "swp") == NULL;
+}
+
+void make_context()
+{
+ /* Generate a context */
+ struct waffle_display *dpy;
+ struct waffle_config *config;
+ struct waffle_window *window;
+ struct waffle_context *ctx;
+
+ const int32_t init_attrs[] = {
+ WAFFLE_PLATFORM, WAFFLE_PLATFORM_X11_EGL,
+ 0,
+ };
+
+ const int32_t config_attrs[] = {
+ WAFFLE_CONTEXT_API, WAFFLE_CONTEXT_OPENGL_ES2,
+ WAFFLE_RED_SIZE, 8,
+ WAFFLE_BLUE_SIZE, 8,
+ WAFFLE_GREEN_SIZE, 8,
+
+ 0,
+ };
+
+ const int32_t window_width = 320;
+ const int32_t window_height = 240;
+
+ waffle_init(init_attrs);
+ dpy = waffle_display_connect(NULL);
+
+ // Exit if OpenGL ES2 is unsupported.
+ if (!waffle_display_supports_context_api(dpy, WAFFLE_CONTEXT_OPENGL_ES2)
+ || !waffle_dl_can_open(WAFFLE_DL_OPENGL_ES2))
+ {
+ exit(EXIT_FAILURE);
+ }
+
+ config = waffle_config_choose(dpy, config_attrs);
+ window = waffle_window_create(config, window_width, window_height);
+ ctx = waffle_context_create(config, NULL);
+ waffle_make_current(dpy, window, ctx);
+}
+
+/* Accepts the mipmap dir, the output filename */
+int
+main(int argc, char *argv[])
+{
+ /* Get arguments */
+ char * filename = argv[1];
+ int miplevels = atoi(argv[2]);
+ int arg_offset = 3;
+ unsigned imgs = argc - arg_offset;
+
+ /* Create GLES2 context */
+ make_context();
+
+ /* Structs used to write the final output */
+ KTX_image_info *img_info = (KTX_image_info*)malloc(imgs * sizeof(KTX_image_info));
+ KTX_texture_info tex_info;
+
+ /* Fill out final struct */
+ /* Read in the file from memory */
+ char * cur_file = argv[arg_offset];
+ FILE* file = fopen(cur_file, "rb");
+ if (file == NULL)
+ printf("File not opened\n");
+
+ /* Skip the identifier and endianness */
+ int error = fseek(file, 16, SEEK_SET);
+ if (error)
+ printf("File seek bad\n");
+
+ /* Read the appropriate header bits */
+ size_t size_read = fread(&tex_info, 1, sizeof(tex_info), file);
+ if (size_read < sizeof(tex_info))
+ printf("bad read 1\n");
+
+ /* Close the file */
+ if (file != NULL)
+ fclose(file);
+
+ /* Update the array section */
+ tex_info.numberOfArrayElements = imgs;
+
+
+ int cur_lev;
+ int cur_img;
+ struct piglit_ktx* files[imgs];
+ for (cur_lev = 0; cur_lev < miplevels; ++cur_lev) {
+ int level_size = 0;
+ for (cur_img = 0; cur_img < imgs; ++cur_img) {
+
+ char * cur_file = argv[cur_img+arg_offset];
+ printf("%s\n", cur_file);
+
+ /* Read in the files into memory */
+ if (cur_lev == 0) {
+ files[cur_img] = piglit_ktx_read_file(cur_file);
+ }
+
+ /* Get level size */
+ level_size += piglit_ktx_get_image(files[cur_img], cur_lev, 0)->size;
+ }
+ printf("Level %d size is %d\n", cur_lev, level_size);
+
+ /* Create the texture array */
+ img_info[cur_lev].size = level_size;
+ img_info[cur_lev].data = (char*) malloc(level_size);
+ int next_offset = 0;
+ for (cur_img = 0; cur_img < imgs; ++cur_img) {
+ const struct piglit_ktx_image* pimg = piglit_ktx_get_image(files[cur_img], cur_lev, 0);
+ memcpy(img_info[cur_lev].data + next_offset, pimg->data, pimg->size);
+ next_offset += pimg->size;
+ }
+ }
+
+ /* Write memory object to file */
+ KTX_error_code kec;
+ kec = ktxWriteKTXN(filename, &tex_info, 0, NULL, miplevels, img_info);
+ printf("%s - %s\n", filename, ktxErrorString(kec));
+
+ /* Cleanup */
+ // for (cur_img = 0; cur_img < imgs; ++cur_img)
+ // free(img_info[cur_img].data);
+ // free(img_info);
+ return 0;
+}
diff --git a/piglit_ktx.c b/piglit_ktx.c
new file mode 100644
index 0000000..517e1ea
--- /dev/null
+++ b/piglit_ktx.c
@@ -0,0 +1,843 @@
+/*
+ * Copyright 2012 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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 <assert.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "piglit_ktx.h"
+
+/* FIXME: Remove #defines when piglit-dispatch gains support for GLES. */
+#define GL_TEXTURE_1D 0x0DE0
+#define GL_TEXTURE_1D_ARRAY 0x8C18
+#define GL_TEXTURE_2D 0x0DE1
+#define GL_TEXTURE_2D_ARRAY 0x8C1A
+#define GL_TEXTURE_3D 0x806F
+#define GL_TEXTURE_CUBE_MAP 0x8513
+#define GL_TEXTURE_CUBE_MAP_ARRAY 0x9009
+
+#define GL_TEXTURE_BINDING_1D 0x8068
+#define GL_TEXTURE_BINDING_1D_ARRAY 0x8C1C
+#define GL_TEXTURE_BINDING_2D 0x8069
+#define GL_TEXTURE_BINDING_2D_ARRAY 0x8C1D
+#define GL_TEXTURE_BINDING_3D 0x806A
+#define GL_TEXTURE_BINDING_CUBE_MAP 0x8514
+#define GL_TEXTURE_BINDING_CUBE_MAP_ARRAY 0x900A
+
+static const int piglit_ktx_header_length = 64;
+static const char piglit_ktx_magic_number[12] =
+ { 0xab, 'K', 'T', 'X', ' ', '1', '1', 0xbb, '\r', '\n', 0x1a, '\n' };
+
+static void
+minify(uint32_t *n)
+{
+ assert(*n != 0);
+
+ if (*n > 1)
+ *n >>= 1;
+}
+
+/**
+ * \brief KTX data.
+ */
+struct piglit_ktx {
+ struct piglit_ktx_info info;
+
+ /** \brief The raw KTX data. */
+ void *data;
+
+ /**
+ * \brief Array of images.
+ *
+ * Array length is piglit_ktx_info::num_images.
+ */
+ struct piglit_ktx_image *images;
+};
+
+static void
+piglit_ktx_error(const char *format, ...)
+{
+ va_list va;
+ va_start(va, format);
+
+ printf("error: piglit_ktx: ");
+ vprintf(format, va);
+ printf("\n");
+ fflush(stdout);
+
+ va_end(va);
+}
+
+void
+piglit_ktx_destroy(struct piglit_ktx *self)
+{
+ if (self == NULL)
+ return;
+
+ if (self->images != NULL)
+ free(self->images);
+
+ if (self->data)
+ free(self->data);
+
+ free(self);
+}
+
+/**
+ * \brief Calculate and set self->info->target.
+ */
+static bool
+piglit_ktx_calc_target(struct piglit_ktx *self)
+{
+ struct piglit_ktx_info *info = &self->info;
+
+ if (info->pixel_width == 0) {
+ goto bad_target;
+ } else if (info->pixel_height == 0) {
+ if (info->pixel_depth != 0)
+ goto bad_target;
+ if (info->num_faces != 1)
+ goto bad_target;
+
+ if (info->array_length == 0) {
+ info->target = GL_TEXTURE_1D;
+ } else {
+ info->target = GL_TEXTURE_1D_ARRAY;
+ }
+ } else if (info->pixel_depth == 0) {
+ if (info->array_length == 0) {
+ if (info->num_faces == 1)
+ info->target = GL_TEXTURE_2D;
+ else if (info->num_faces == 6)
+ info->target = GL_TEXTURE_CUBE_MAP;
+ else
+ goto bad_target;
+ } else {
+ if (info->num_faces == 1)
+ info->target = GL_TEXTURE_2D_ARRAY;
+ else if (info->num_faces == 6)
+ info->target = GL_TEXTURE_CUBE_MAP_ARRAY;
+ else
+ goto bad_target;
+ }
+ } else {
+ if (info->array_length != 0)
+ goto bad_target;
+ if (info->num_faces != 0)
+ goto bad_target;
+
+ info->target = GL_TEXTURE_3D;
+ }
+
+ return true;
+
+bad_target:
+ piglit_ktx_error("%s", "invalid texture target: pixel_size, "
+ "array_size, and num_faces are incompatible");
+ return false;
+}
+
+static bool
+piglit_ktx_parse_header(struct piglit_ktx *self)
+{
+ struct piglit_ktx_info *info = &self->info;
+ bool ok = true;
+
+ /*
+ * Used to iterate through KTX header with a step of 32 bits. The KTX
+ * spec declares all header fields as u32 values.
+ */
+ const uint32_t *u32 = self->data;
+
+ if (info->size < piglit_ktx_header_length) {
+ piglit_ktx_error("data size must be at least length of KTX "
+ "header, %d bytes", piglit_ktx_header_length);
+ return false;
+ }
+
+ if (memcmp(u32, piglit_ktx_magic_number,
+ sizeof(piglit_ktx_magic_number)) != 0) {
+ piglit_ktx_error("%s", "KTX header does not begin with KTX "
+ "magic number");
+ return false;
+ }
+
+ switch (u32[3]) {
+ case 0x04030201:
+ /* Little endian is supported. */
+ break;
+ case 0x01020304:
+ piglit_ktx_error("%s", "KTX header declares big endian data, "
+ "but Piglit supports only little endian");
+ return false;
+ default:
+ piglit_ktx_error("KTX header has bad value (0x%x) for "
+ "endianness flag", u32[3]);
+ return false;
+ }
+
+ info->gl_type = u32[4];
+ info->gl_type_size = u32[5];
+ info->gl_format = u32[6];
+ info->gl_internal_format = u32[7];
+ info->gl_base_internal_format = u32[8];
+ info->pixel_width = u32[9];
+ info->pixel_height = u32[10];
+ info->pixel_depth = u32[11];
+ info->array_length = u32[12];
+ info->num_faces = u32[13];
+ info->num_miplevels = u32[14];
+
+ if (info->num_miplevels == 0) {
+ piglit_ktx_error("%s", "KTX header requests automatic "
+ "mipmap generation, which Piglit does not "
+ "support");
+ return false;
+ }
+
+ if (u32[15] != 0) {
+ piglit_ktx_error("%s", "KTX header declares presence of "
+ "arbitrary key/value data, which Piglit "
+ "does not support");
+ return false;
+ }
+
+ ok = piglit_ktx_calc_target(self);
+ if (!ok)
+ return false;
+
+ if (info->target == GL_TEXTURE_CUBE_MAP)
+ info->num_images = 6 * info->num_miplevels;
+ else
+ info->num_images = info->num_miplevels;
+
+ return true;
+}
+
+static void
+piglit_ktx_calc_base_image_size(struct piglit_ktx *self,
+ uint32_t *width,
+ uint32_t *height,
+ uint32_t *depth)
+{
+ struct piglit_ktx_info *info = &self->info;
+
+ switch (info->target) {
+ case GL_TEXTURE_1D:
+ *width = info->pixel_width;
+ *height = 0;
+ *depth = 0;
+ break;
+ case GL_TEXTURE_1D_ARRAY:
+ *width = info->pixel_width;
+ *height = info->array_length;
+ *depth = 0;
+ break;
+ case GL_TEXTURE_2D:
+ *width = info->pixel_width;
+ *height = info->pixel_height;
+ *depth = 0;
+ break;
+ case GL_TEXTURE_2D_ARRAY:
+ *width = info->pixel_width;
+ *height = info->pixel_height;
+ *depth = info->array_length;
+ break;
+ case GL_TEXTURE_CUBE_MAP:
+ *width = info->pixel_width;
+ *height = info->pixel_height;
+ *depth = 0;
+ break;
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ *width = info->pixel_width;
+ *height = info->pixel_height;
+ *depth = 6 * info->array_length;
+ break;
+ case GL_TEXTURE_3D:
+ *width = info->pixel_width;
+ *height = info->pixel_height;
+ *depth = info->pixel_depth;
+ break;
+ default:
+ assert(0);
+ break;
+ }
+}
+
+static bool
+piglit_ktx_parse_images(struct piglit_ktx *self)
+{
+ struct piglit_ktx_info *info = &self->info;
+
+ /* Current image being parsed. */
+ struct piglit_ktx_image *image;
+
+ /*
+ * Current byte being parsed. Used to traverse data with a step of
+ * either 8 or 32 bits.
+ */
+ union piglit_ktx_position {
+ uint8_t *u8;
+ uint32_t *u32;
+ } p;
+
+ /* Size of image, as passed to glTexImage(). */
+ uint32_t pixel_width;
+ uint32_t pixel_height;
+ uint32_t pixel_depth;
+
+ /* Loop counters */
+ int miplevel;
+ int face;
+
+ piglit_ktx_calc_base_image_size(self,
+ &pixel_width,
+ &pixel_height,
+ &pixel_depth);
+
+ self->images = calloc(info->num_images, sizeof(*self->images));
+
+ /* Skip header. */
+ p.u8 = self->data;
+ p.u8 += piglit_ktx_header_length;
+
+ /* Begin parsing first image. */
+ image = &self->images[0];
+
+#define CUR_SIZE (p.u8 - (uint8_t *) self->data)
+
+ for (miplevel = 0; miplevel < info->num_miplevels; ++miplevel) {
+ uint32_t image_size;
+
+ if (info->size < CUR_SIZE + 1) {
+ /*
+ * Reading the image size below would access
+ * out-of-bounds memory.
+ */
+ piglit_ktx_error("size of data stream must be at "
+ "least %u", CUR_SIZE + 1);
+ return false;
+ }
+
+ image_size = *p.u32;
+ ++p.u32;
+
+ for (face = 0; face < 6; ++face) {
+ assert(image - self->images < info->num_images);
+
+ image->data = p.u8;
+ image->size = image_size;
+ image->miplevel = miplevel;
+ image->face = face;
+ image->pixel_width = pixel_width;
+ image->pixel_height = pixel_height;
+ image->pixel_depth = pixel_depth;
+
+ p.u8 += image_size;
+ ++image;
+
+ /* Padding */
+ while (CUR_SIZE % 4 != 0)
+ ++p.u8;
+
+ if (info->target != GL_TEXTURE_CUBE_MAP)
+ break;
+ }
+
+ switch (info->target) {
+ case GL_TEXTURE_3D:
+ minify(&pixel_width);
+ minify(&pixel_height);
+ minify(&pixel_depth);
+ break;
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_CUBE_MAP:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ minify(&pixel_width);
+ minify(&pixel_height);
+ break;
+ case GL_TEXTURE_1D:
+ case GL_TEXTURE_1D_ARRAY:
+ minify(&pixel_width);
+ break;
+ default:
+ assert(0);
+ break;
+ }
+ }
+
+ if (info->size < CUR_SIZE) {
+ /*
+ * The last image's data lies, at least partially, in
+ * out-of-bounds memory.
+ */
+ piglit_ktx_error("size of data stream must be at least %zd",
+ CUR_SIZE);
+ return false;
+ }
+
+
+ /*
+ * Up until now, info->size was an upper bound on the data size. Now
+ * the actual data size is known.
+ */
+ info->size = CUR_SIZE;
+
+ return true;
+
+#undef CUR_SIZE
+}
+
+static bool
+piglit_ktx_parse_data(struct piglit_ktx *self)
+{
+ bool ok = true;
+
+ ok = ok && piglit_ktx_parse_header(self);
+ ok = ok && piglit_ktx_parse_images(self);
+
+ return ok;
+}
+
+struct piglit_ktx*
+piglit_ktx_read_file(const char *filename)
+{
+ struct piglit_ktx *self;
+
+ FILE *file = NULL;
+ size_t size_read = 0;
+
+ bool ok = true;
+ int error = 0;
+
+ self = calloc(1, sizeof(*self));
+ if (self == NULL)
+ goto out_of_memory;
+
+ file = fopen(filename, "rb");
+ if (file == NULL)
+ goto bad_open;
+
+ error = fseek(file, 0, SEEK_END);
+ if (error)
+ goto bad_read;
+ self->info.size = ftell(file);
+ error = fseek(file, 0, SEEK_SET);
+ if (error)
+ goto bad_read;
+
+ self->data = malloc(self->info.size);
+ if (self->data == NULL)
+ goto out_of_memory;
+
+ /*
+ * On Unix, one should mmap() here rather than copy. But this code
+ * needs to support Windows too.
+ */
+ size_read = fread(self->data, 1, self->info.size, file);
+ if (size_read < self->info.size)
+ goto bad_read;
+
+ ok = piglit_ktx_parse_data(self);
+ goto end;
+
+out_of_memory:
+ ok = false;
+ piglit_ktx_error("%s", "out of memory");
+ goto end;
+
+bad_open:
+ ok = false;
+ piglit_ktx_error("failed to open file: %s", filename);
+ goto end;
+
+bad_read:
+ ok = false;
+ piglit_ktx_error("errors in reading file: %s", filename);
+ goto end;
+
+end:
+ if (file != NULL)
+ fclose(file);
+
+ if (!ok) {
+ piglit_ktx_destroy(self);
+ self = NULL;
+ }
+
+ return self;
+}
+
+struct piglit_ktx*
+ktx_file_read_bytes(const void *bytes , size_t size)
+{
+ struct piglit_ktx *self;
+ bool ok = true;
+
+ self = calloc(1, sizeof(*self));
+ if (self == NULL) {
+ piglit_ktx_error("%s", "out of memory");
+ return NULL;
+ }
+
+ self->info.size = size;
+ memcpy(self->data, bytes, size);
+
+ ok = piglit_ktx_parse_data(self);
+ if (!ok) {
+ piglit_ktx_destroy(self);
+ return NULL;
+ }
+
+ return self;
+}
+
+bool
+piglit_ktx_write_file(struct piglit_ktx *self, const char *filename)
+{
+ FILE *file = NULL;
+ size_t size_written = 0;
+ bool ok = true;
+
+ file = fopen(filename, "wb");
+ if (file == NULL)
+ goto bad_open;
+
+ size_written = fwrite(self->data, self->info.size, 1, file);
+ if (size_written < self->info.size)
+ goto bad_write;
+
+ goto end;
+
+bad_open:
+ ok = false;
+ piglit_ktx_error("failed to open file: %s", filename);
+ goto end;
+
+bad_write:
+ ok = false;
+ piglit_ktx_error("errors in writing file: %s", filename);
+ goto end;
+
+end:
+ if (file != NULL)
+ fclose(file);
+
+ return ok;
+}
+
+bool
+piglit_ktx_write_bytes(struct piglit_ktx *self, void *bytes)
+{
+ memcpy(bytes, self->data, self->info.size);
+ return true;
+}
+
+const struct piglit_ktx_image*
+piglit_ktx_get_image(struct piglit_ktx *self,
+ int miplevel,
+ int cube_face)
+{
+ const struct piglit_ktx_info *info = &self->info;
+
+ if (miplevel < 0 || miplevel >= info->num_miplevels) {
+ piglit_ktx_error("bad miplevel %d", miplevel);
+ return NULL;
+ }
+
+ if (cube_face < 0 || cube_face >= 6) {
+ piglit_ktx_error("bad cube_face %d", cube_face);
+ return NULL;
+ }
+
+ if (cube_face != 0 && info->target != GL_TEXTURE_CUBE_MAP) {
+ piglit_ktx_error("cube face %d was requested. cube face may "
+ "be requested only for non-array cubemaps",
+ cube_face);
+ return NULL;
+ }
+
+ if (info->target == GL_TEXTURE_CUBE_MAP)
+ return &self->images[6 * miplevel + cube_face];
+ else
+ return &self->images[miplevel];
+}
+
+static bool
+piglit_ktx_load_cubeface(struct piglit_ktx *self,
+ int image,
+ GLenum *gl_error)
+{
+ const struct piglit_ktx_info *info = &self->info;
+ const struct piglit_ktx_image *img = &self->images[image];
+
+ GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + (image % 6);
+ int level = image / 6;
+
+ if (info->gl_type == 0)
+ glCompressedTexImage2D(face,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ 0 /*border*/,
+ img->size,
+ img->data);
+ else
+ glTexImage2D(face,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ 0 /*border*/,
+ info->gl_format,
+ info->gl_type,
+ img->data);
+
+ *gl_error = glGetError();
+ return *gl_error == 0;
+}
+
+static bool
+piglit_ktx_load_noncubeface(struct piglit_ktx *self,
+ int image,
+ GLenum *gl_error)
+{
+ const struct piglit_ktx_info *info = &self->info;
+ const struct piglit_ktx_image *img = &self->images[image];
+ int level = image;
+
+ switch (info->target) {
+ case GL_TEXTURE_1D:
+ if (info->gl_type == 0)
+ glCompressedTexImage1D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ 0 /*border*/,
+ img->size,
+ img->data);
+ else
+ glTexImage1D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ 0 /*border*/,
+ info->gl_format,
+ info->gl_type,
+ img->data);
+ break;
+ case GL_TEXTURE_1D_ARRAY:
+ case GL_TEXTURE_2D:
+ case GL_TEXTURE_CUBE_MAP:
+ if (info->gl_type == 0)
+ glCompressedTexImage2D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ 0 /*border*/,
+ img->size,
+ img->data);
+ else
+ glTexImage2D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ 0 /*border*/,
+ info->gl_format,
+ info->gl_type,
+ img->data);
+ break;
+ case GL_TEXTURE_2D_ARRAY:
+ case GL_TEXTURE_3D:
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ if (info->gl_type == 0)
+ glCompressedTexImage3D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ img->pixel_depth,
+ 0 /*border*/,
+ img->size,
+ img->data);
+ else
+ glTexImage3D(info->target,
+ level,
+ info->gl_internal_format,
+ img->pixel_width,
+ img->pixel_height,
+ img->pixel_depth,
+ 0 /*border*/,
+ info->gl_format,
+ info->gl_type,
+ img->data);
+ break;
+ default:
+ *gl_error = 0;
+ piglit_ktx_error("bad texture target 0x%x",
+ info->target);
+ return false;
+ }
+
+ *gl_error = glGetError();
+ return *gl_error == 0;
+
+unsupported_on_gles:
+ *gl_error = 0;
+ piglit_ktx_error("%s", "GLES supports only GL_TEXTURE_2D and "
+ "GL_TEXTURE_CUBE_MAP");
+ return false;
+}
+
+static bool
+piglit_ktx_load_image(struct piglit_ktx *self,
+ int image,
+ GLenum *gl_error)
+{
+ if (self->info.target == GL_TEXTURE_CUBE_MAP)
+ return piglit_ktx_load_cubeface(self, image, gl_error);
+ else
+ return piglit_ktx_load_noncubeface(self, image, gl_error);
+}
+
+static GLuint
+target_to_texture_binding(GLuint target)
+{
+ switch (target) {
+ case GL_TEXTURE_1D:
+ return GL_TEXTURE_BINDING_1D;
+ case GL_TEXTURE_1D_ARRAY:
+ return GL_TEXTURE_BINDING_1D_ARRAY;
+ case GL_TEXTURE_2D:
+ return GL_TEXTURE_BINDING_2D;
+ case GL_TEXTURE_2D_ARRAY:
+ return GL_TEXTURE_BINDING_2D_ARRAY;
+ case GL_TEXTURE_CUBE_MAP:
+ return GL_TEXTURE_BINDING_2D;
+ case GL_TEXTURE_CUBE_MAP_ARRAY:
+ return GL_TEXTURE_BINDING_CUBE_MAP_ARRAY;
+ break;
+ case GL_TEXTURE_3D:
+ return GL_TEXTURE_BINDING_3D;
+ default:
+ assert(0);
+ break;
+ }
+
+ return 0;
+}
+
+bool
+piglit_ktx_load_texture(struct piglit_ktx *self,
+ GLuint *tex_name,
+ GLenum *gl_error)
+{
+ const struct piglit_ktx_info *info = &self->info;
+
+ /*
+ * Local store for glGetError(). A local store is needed because
+ * gl_error may be null.
+ */
+ GLenum my_gl_error = GL_NO_ERROR;
+
+ /*
+ * The texture object bound to the texture target before this function call.
+ * Before returning, we rebind the texture.
+ */
+ GLint old_bound_tex;
+
+ /*
+ * The GL_UNPACK_ALIGNMENT before this function call.
+ * For KTX, the alignment must be set to 4. Before returning, we restore the unpack alignment
+ *
+ */
+ GLint old_unpack_alignment;
+
+ bool made_texture = false;
+
+ bool ok = true;
+ int i;
+
+ assert(tex_name != NULL);
+
+ glGetIntegerv(target_to_texture_binding(info->target),
+ &old_bound_tex);
+ glGetIntegerv(GL_UNPACK_ALIGNMENT, &old_unpack_alignment);
+
+ /* Reset GL error state. */
+ while (glGetError())
+ ;
+
+ if (*tex_name == 0) {
+ glGenTextures(1, tex_name);
+ made_texture = true;
+
+ my_gl_error = glGetError();
+ if (my_gl_error)
+ goto fail;
+ }
+
+ glBindTexture(info->target, *tex_name);
+ my_gl_error = glGetError();
+ if (my_gl_error)
+ goto fail;
+
+ for (i = 0; i < info->num_images; ++i) {
+ ok = piglit_ktx_load_image(self, i, &my_gl_error);
+ if (!ok)
+ goto fail;
+ }
+
+ goto cleanup;
+
+fail:
+ ok = false;
+
+ if (gl_error != NULL)
+ *gl_error = my_gl_error;
+
+ if (made_texture && *tex_name != 0) {
+ glDeleteTextures(1, tex_name);
+ *tex_name = 0;
+ }
+
+cleanup:
+ /* Reset GL error state. */
+ while (glGetError())
+ ;;
+
+ glBindTexture(info->target, old_bound_tex);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, old_unpack_alignment);
+ return ok;
+}
+
+const struct piglit_ktx_info*
+piglit_ktx_get_info(struct piglit_ktx *self)
+{
+ return &self->info;
+}
diff --git a/piglit_ktx.h b/piglit_ktx.h
new file mode 100644
index 0000000..f7925cd
--- /dev/null
+++ b/piglit_ktx.h
@@ -0,0 +1,271 @@
+/*
+ * Copyright 2012 Intel Corporation
+ *
+ * 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 (including the next
+ * paragraph) 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.
+ */
+
+/**
+ * \file
+ *
+ * \brief Utilities for the KTX file format.
+ *
+ * The KTX (Khronos texture) file format specifies a simple format for
+ * storing texture miptrees. The file format allows texture data for any
+ * GL texture format and any GL texture target.
+ *
+ * \see http://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdint.h>
+#define KTX_OPENGL 1
+#include <ktx.h>
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct piglit_ktx;
+
+struct piglit_ktx_info {
+ /** \brief Size in bytes of the raw KTX data. */
+ size_t size;
+
+ /**
+ * \brief GL texture target.
+ *
+ * This is the `target` agument passed to glTexImage() It is
+ * completely determined by pixel_size, array_length, and num_faces.
+ * Valid values are
+ * - GL_TEXTURE_1D
+ * - GL_TEXTURE_1D_ARRAY
+ * - GL_TEXTURE_2D
+ * - GL_TEXTURE_2D_ARRAY
+ * - GL_TEXTURE_3D
+ * - GL_TEXTURE_CUBE_MAP
+ * - GL_TEXTURE_CUBE_MAP_ARRAY
+ */
+ GLenum target;
+
+ /**
+ * For compressed textures, gl_type is 0. For non-compressed textures,
+ * gl_type is the 'type' argument passed to glTexImage(). For example,
+ * GL_FLOAT.
+ */
+ uint32_t gl_type;
+
+ /**
+ * For compressed textures, gl_type_size is 1. For non-compressed
+ * textures, gl_type_size is the size in bytes of gl_type. For example,
+ * if gl_type is GL_FLOAT, gl_type_size is 4.
+ */
+ uint32_t gl_type_size;
+
+ /**
+ * For compressed textures, gl_format is 0. For non-compressed textures,
+ * gl_format is the 'format' argument passed to glTexImage(). For
+ * example, GL_RGBA.
+ */
+ uint32_t gl_format;
+
+ /**
+ * For compressed and non-compressed textures, gl_internal_format is the
+ * 'internal_format' argument passed to glTexImage(). For
+ * non-compressed textures, this is always a sized format. For example,
+ * GL_RGBA32F.
+ */
+ uint32_t gl_internal_format;
+
+ /**
+ * For compressed textures, gl_base_internal_format is the same as
+ * gl_internal_format. For non-compressed textures,
+ * gl_base_internal_format is the same as gl_internal_format.
+ *
+ * (I (chadv) dont' understand what purpose this field serves, But
+ * the KTX spec requires it in the header).
+ */
+ uint32_t gl_base_internal_format;
+
+ /**
+ * \name Size of texture, in pixels.
+ * \{
+ *
+ * For 1D textures, height and depth are 0. For 2D and cube textures,
+ * depth is 0. For block compressed textures, the sizes are not
+ * rounded to block size.
+ *
+ * Note: The sizes here are those in the KTX header, which differ from
+ * the sizes passed to glTexImage().
+ */
+ uint32_t pixel_width;
+ uint32_t pixel_height;
+ uint32_t pixel_depth;
+ /** \} */
+
+ /**
+ * If the texture is not an array texture, then array_lengthis 0.
+ */
+ uint32_t array_length;
+
+ /**
+ * For cubemaps and cubemap arrays, num_faces is 6. For all other
+ * textures, it is 1.
+ */
+ uint32_t num_faces;
+
+ /**
+ * For non-mipmapped textures, num_miplevels is 1.
+ */
+ uint32_t num_miplevels;
+
+ /**
+ * For non-array cubemaps, the number of images is 6 * num_miplevels.
+ * For all other textures, the number of images and miplevels is the
+ * same.
+ */
+ uint32_t num_images;
+};
+
+struct piglit_ktx_image {
+ /**
+ * \brief The raw image data.
+ *
+ * This points to the image located in piglit_ktx_info::data. It may
+ * be passed as the 'data' argument to glTexImage().
+ */
+ const void *data;
+
+ /**
+ * \brief Size of image, in bytes.
+ *
+ * This is 'imageSize' argument passed to glTexImage(). It does not
+ * include any padding which may be present in ktx_info::bytes.
+ */
+ size_t size;
+
+ /**
+ * In range [0, num_miplevels).
+ */
+ uint32_t miplevel;
+
+ /**
+ * For non-array cubemap textures, `face` is in range [0, 6). For all
+ * other textures, it is 0.
+ */
+ uint32_t face;
+
+ /**
+ * \name Size of image, in pixels.
+ * \{
+ *
+ * These are the sizes passed to glTexImage().
+ * Note: These sizes differ from those in the KTX header.
+ */
+ uint32_t pixel_width;
+ uint32_t pixel_height;
+ uint32_t pixel_depth;
+ /** \} */
+};
+
+void
+piglit_ktx_destroy(struct piglit_ktx *self);
+
+/**
+ * \brief Read KTX data from a file.
+ *
+ * The file is read until EOF.
+ *
+ * Return null on error, including I/O error and invalid data.
+ */
+struct piglit_ktx*
+piglit_ktx_read_file(const char *filename);
+
+/**
+ * \brief Read KTX data from a byte array.
+ *
+ * Read at most \a size bytes.
+ *
+ * The given \a size is not used to calculate the expected length of the KTX
+ * data; that is completely determined by the content of the KTX header.
+ * Instead, the given \a size is a safeguard against reading out-of-bounds
+ * memory when incorrect data is present into the header.
+ *
+ * Return null on error, including invalid data and insufficient \a size.
+ */
+struct piglit_ktx*
+piglit_ktx_read_bytes(const void *bytes, size_t size);
+
+/**
+ * \brief Write KTX data to a file.
+ *
+ * The number of bytes written is `piglit_ktx_get_info()->size`.
+ */
+bool
+piglit_ktx_write_file(struct piglit_ktx *self, const char *filename);
+
+/**
+ * \brief Write KTX data to a byte array.
+ *
+ * The number of bytes written is `piglit_ktx_get_info()->size`.
+ */
+bool
+piglit_ktx_write_bytes(struct piglit_ktx *self, void *bytes);
+
+const struct piglit_ktx_info*
+piglit_ktx_get_info(struct piglit_ktx *self);
+
+/**
+ * \brief Get a texture image from a KTX file.
+ *
+ * The given \a miplevel must be in the range `[0,
+ * piglit_ktx_info::num_miplevels)`. For cubemap non-array textures, \a
+ * cube_face must be in the range [0, 5]. For all other textures, \a
+ * cube_face must be 0. If the above is not satisfied, an error is produced.
+ *
+ * Note: For cubemap array textures, \a cube_face must be 0 because
+ * piglit_ktx_image::data is the data that would be passed to glTexImage3D(),
+ * which is not separated into individual faces.
+ */
+const struct piglit_ktx_image*
+piglit_ktx_get_image(struct piglit_ktx *self,
+ int miplevel,
+ int cube_face);
+
+/**
+ * \brief Load texture into the GL with glTexImage().
+ *
+ * If \a *tex_name is non-zero, then that texture is bound to
+ * `piglit_ktx_info::target` and the texture images are loaded into it with
+ * glTexImage(). If \a *tex_name is 0, then a new texture is first created.
+ * The new texture name is returned \a tex_name.
+ *
+ * Return false on failure. If failure is due to a GL error and \a gl_error is
+ * not null, then the value of glGetError() is returned in \a gl_error.
+ */
+bool
+piglit_ktx_load_texture(struct piglit_ktx *self,
+ GLuint *tex_name,
+ GLenum *gl_error);
+
+#ifdef __cplusplus
+}
+#endif