diff options
author | Nanley Chery <nanley.g.chery@intel.com> | 2015-07-21 11:15:30 -0700 |
---|---|---|
committer | Nanley Chery <nanley.g.chery@intel.com> | 2015-07-21 12:24:13 -0700 |
commit | fc953abe1d80b4663d850dcd2f7b2f6a0a311869 (patch) | |
tree | 7471f533177b9328d108865cb6b3f94b994d3fb8 | |
parent | 67793b1572a7d523b16771899847cf53f5f756c2 (diff) |
Start creating script to create a texture array
-rw-r--r-- | Makefile | 3 | ||||
-rw-r--r-- | gen_array.c | 194 | ||||
-rw-r--r-- | piglit_ktx.c | 843 | ||||
-rw-r--r-- | piglit_ktx.h | 271 |
4 files changed, 1310 insertions, 1 deletions
@@ -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 |