Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Evaluating texture storage for different APIs #739

Closed
lexaknyazev opened this issue Oct 5, 2016 · 30 comments
Closed

Evaluating texture storage for different APIs #739

lexaknyazev opened this issue Oct 5, 2016 · 30 comments
Labels

Comments

@lexaknyazev
Copy link
Member

lexaknyazev commented Oct 5, 2016

Related previous discussions:
#620
#640
#674
#728


Considering ongoing efforts (#733) to go beyond WebGL 1.0 and to make glTF suitable for a wide range of APIs and runtimes, we can evaluate texture storage besides common web formats (such as JPEG or PNG).

Usage of raw texture data from bufferViews (as was proposed in #620) could be error-prone and doesn't allow to leverage existing tools.

Currently, there're three widely used texture containers: KTX, PVR, and DDS. They already have lots of pipeline/tooling support (PVRTexTool, MetalKit, D3DX, etc).

So, here are several comments on them and related implementation thoughts.

KTX, PVR, and DDS

Feature KTX PVR DDS API Comments
2D Textures D3D9 and OpenGL ES 2.0 / WebGL 1.0 may support only power-of-two texture dimensions.
3D Textures No support in OpenGL ES 2.0 / WebGL 1.0; D3D9 support may be limited.
MIP Levels ✅, special value (0) for at-runtime generation For uncompressed formats, MIP pyramid could be generated at runtime.
Cube maps ✅, fixed faces order, all faces should be provided ✅, arbitrary faces order, faces could be omitted ✅, fixed faces order, faces could be omitted All APIs (except D3D9) require all six cube-map faces.
2D Texture Arrays No support in OpenGL ES 2.0 / WebGL 1.0, D3D9.
Cube Map Arrays No support until OpenGL ES 3.2, D3D 10.1.
Float Textures OpenGL ES 2.0 / WebGL 1.0 support is limited, OES_texture_(half_)float is required.
Specifying Texture Origin ✅, via optional KTXorientation field ✅, via Metadata fields No, assume top-left
sRGB ✅, via format/internalformat enums ✅, via Colour Space flag ✅, via DXGI_FORMAT enum Enum values are different for OpenGL ES 2.0 / WebGL 1.0 with EXT_sRGB.
Compression Formats ✅, all GL formats via format enum ✅, PVRTC, ETC1, ETC2, BCn, ASTC ✅, BCn No universal compression format. Different versions of the same texture are expected for different hardware/API targets.

As seen from table above, all three formats provide almost the same functionality. For glTF to remain vendor-neutral, we can require only KTX support in the core spec and call community (or vendors) for PVR and DDS extensions.

For more advanced usages, an extension for Khronos Data Format support could be proposed.

glTF texture and image objects

Since almost all pixels-related properties are located in the container's header, there is no need to duplicate them in texture.

However, runtime may need to know in advance what "codec" image uses (e.g., to acquire only supported compression format).

Also, sampler property could be made optional to enable non-sampled texture units support (ES 3.2, D3D10).

Web image codecs

Working with currently supported image codecs (BMP, GIF, JPEG, PNG) could be a bit more tricky, because JavaScript runtime has no information about pixels type or color channels count.

  • BMP
    • Could have anything from 1 bit-per-pixel BW to 32-bit RGBA. Browser support for rare combinations varies (e.g., look at comments in the Chromium source).
  • GIF
    • Could have 24-bit RGB colors, but alpha is limited to 1-bit.
  • JPEG
    • Could have 24-bit RGB or 8-bit Luminance. No alpha.
  • PNG
    • Could have L, LA, RGB, or RGBA data with up to 16-bit per channel.

JSON changes

As @ParticlePeter mentioned in #640, it makes sense to move couple texture properties to image object:

  • texture.type -> image.type. This field should be optional and an asset should provide it only for web image codecs.
  • texture.format -> image.format. This field should be mandatory and in case of KTX image, it must match actual data format.

Valid enum values for image.format depend on enabled glTF and/or API extensions.

To assist non-web runtimes and to explicitly declare image container, image.mimeType is added with enum values: ["image/bmp", "image/gif", "image/jpeg", "image/ktx", "image/png"].

Unlike KTX/PVR/DDS, web image codecs don't support volume textures, cube maps, texture arrays, or MIP levels. So we can emulate such functionality in JSON to provide a fail-safe alternative.

image and texture objects could be redefined like this (example):

{
  "textures": {
    "texture01": {
      "target": 3553,
      "sampler": "sampler01", // optional
      "source": ["image01", "image02"], // list of images of different formats with texture data
      "internalformat": 6408 // optional
    }
  },
  "images": {
    "image01": {
      "mimeType": "image/png", // required
      "depth": 2, // optional; depth of the mip level 0, used for 3D textures; must be zero for 2D and cube textures
      "surfaces": 0, // optional; used for 2D texture arrays and cube map arrays
      "faces": 1, // optional; 1 or 6 (cube map)
      "levels": 2, // optional; 0 means runtime should call generateMipmap()
      "format": 6408,
      "uri": [
        "file01.png",
        "file02.png"
        "file11.png"
        "file12.png"
      ]
    },
    "image02": {
      "mimeType": "image/ktx", // required; in case of KTX uri.length must be 1
      "format": 6408,
      "uri": [
        "file.ktx"
      ]
    }
  }
}

For web codecs, the order of uri elements corresponds to the image data order in KTX format:

for each mipmap_level in levels*
    for each array_element in surfaces*
       for each face in faces
           for each z_slice in depth[mipmap_level]*
               yield
           end
       end
    end
end

* Replace with 1 if this field is 0.

Other possible image format extensions (HDR use cases, CPU decompression needed)

  • Radiance
  • OpenEXR

References


CC @pjcozzi, @mre4ce

@mre4ce
Copy link

mre4ce commented Oct 8, 2016

I would vote for only supporting KTX period. I wouldn't even support plain textures or other formats like JPEG and PNG. In most cases you don't want to render with uncompressed textures, and you don't want to transcode textures at load time. Imho transcoding is only useful when streaming textures. Otherwise you want the texture data in a GPU accessible format from the start.

However, the commonly supported texture formats are different between mobile and desktop. So to make textures work on a variety of platforms you may need at least two versions of each texture: DXT/BC, ETC2.

When using a container like KTX there is no need for the redundant info per image that is only relevant for other formats like JPEG and PNG. It just simplifies things as opposed to basically replicating the info stored in a KTX header for formats like JPEG and PNG.

@lexaknyazev
Copy link
Member Author

I wouldn't even support plain textures or other formats like JPEG and PNG.

While I generally agree with this, I don't think we could remove them from spec. In WebGL environment a browser does all loading/decoding stuff, so those formats are more popular than GPU formats. PNG compression is lossless, so it could be better to use it for UI elements instead of uncompressed GPU format.

Also with full support for npot textures in WebGL 2.0, an asset could reference some arbitrary user-generated images (such as photos from social media).

When using a container like KTX there is no need for the redundant info per image that is only relevant for other formats like JPEG and PNG.

Since we can't embed JPEG into KTX, duplicating fields seems to be the only option.

@mre4ce
Copy link

mre4ce commented Oct 8, 2016

I would argue that formats like JPEG and PNG go against the design goals of glTF. These formats fit the convenience goals of glTF but they don't fit the fast loading goals. Especially PNG is not a good format for fast load times.

In most cases these formats also won't get transcoded to a GPU compressed format. This can waste a lot of performance, bandwidth, power draw and heat. This may not be a concern on desktop hardware plugged into the wall with active cooling. However, the waste of bandwidth, power draw and heat is a crying shame on mobile. On mobile there is no such thing as "it runs fast enough". You can effectively optimize forever to minimize power draw and heat.

Formats like BC7 and ASTC are pretty adequate for textures for UI elements that need to be high quality.

@lexaknyazev
Copy link
Member Author

Do you want to remove JPEG/PNG support from the glTF core (and possible move it to an extension), or just recommend to not use them on mobile?

@mre4ce
Copy link

mre4ce commented Oct 8, 2016

I'd move it to an extension and make loading KTX files the standard. That makes the standard plain and simple. That's just my opinion though. Happy to hear counter arguments or alternate solutions :)

@lexaknyazev
Copy link
Member Author

My first concern here is that even with WebGL we don't have one commonly supported format:

  • DXT1/3/5 - desktops
  • ETC1 - Android + desktops (software decode)
  • ATC - Adreno GPUs
  • PVRTC - PowerVR GPUs

With WebGL 2.0, we'll have ETC2 everywhere (with software decode on almost all desktops).
I'm afraid we can't argue for ASTC/BC7 at the current state of ecosystem.

On the other hand, JPEG/PNGs are supported everywhere (while being slow and inefficient).


The second reason is ease of UGC exchange. Let's say someone wants to share an asset to social network website. What format should textures be?

@lexaknyazev
Copy link
Member Author

With WebGL 2.0, we'll have ETC2 everywhere (with software decode on almost all desktops).

Not anymore: KhronosGroup/WebGL#2030

@lexaknyazev
Copy link
Member Author

The second reason is ease of UGC exchange. Let's say someone wants to share an asset to social network website. What format should textures be?

I think the most interoperable approach could be like:

  1. User uploads an asset (most likely self-contained: base64 urls or .glb) with PNG/JPEGs instead of uncompressed KTX to save network bandwidth.
  2. Server-side pipeline utils re-encode textures to multiple GPU formats, pack them in KTX, and adjust glTF.
  3. Clients receive modified glTF and fetch only GPU-compatible textures.

Btw, YouTube does something similar: user uploads anything, backend prepares MP4/AVC + WebM/VPx, clients fetch what's most compatible.

@RemiArnaud
Copy link
Contributor

Exactly. Client should be able to ask what it needs and receive what it needs and only what it needs.

I guess I'm repeating myself in several threads :-)

Regards,

-- Rémi

On Oct 8, 2016, at 3:05 PM, Alexey Knyazev [email protected] wrote:

The second reason is ease of UGC exchange. Let's say someone wants to share an asset to social network website. What format should textures be?

I think the most interoperable approach could be like:

User uploads an asset (most likely self-contained: base64 urls or .glb) with PNG/JPEGs instead of uncompressed KTX to save network bandwidth.
Server-side pipeline utils re-encode textures to multiple GPU formats, pack them in KTX, and adjust glTF.
Clients receive modified glTF and fetch only GPU-compatible textures.
Btw, YouTube does something similar: user uploads anything, backend prepares MP4/AVC + WebM/VPx, clients fetch what's most compatible.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or mute the thread.

@mre4ce
Copy link

mre4ce commented Oct 9, 2016

I don't necessarily disagree with any of this but I do want to point out one of my concerns. glTF already depends on various other standards:

- OpenGL
- JSON
- base64

By allowing glTF to directly reference JPEG and PNG images we add two more dependencies. I understand that these standards are natively supported on various platforms. However, there are also many platforms where that is not the case. In other words, to support glTF on these platforms you have to find appropriate libraries for all these other standards (or you can write your own if that's your forte).

Picking the right libraries is actually not trivial. JSON is a good example. There are hundreds of open source JSON implementations out there but many of them are poorly designed, poorly tested or just really slow. So which one do you pick? To support glTF I ended up evaluating various different JSON parsers.

The following DOM-style implementations were tested:

Name        Version               Source                                   Language
-----------------------------------------------------------------------------------
Json_t      1.0                   this file                                C99
rapidjson   1.02                  https://github.com/miloyip/rapidjson     C++
sajson      Mar 24, 2016          https://github.com/chadaustin/sajson     C++
gason       Sep 16, 2015          https://github.com/vivkin/gason          C++
ujson4c     1.0 (ultrajson 1.34)  https://github.com/esnme/ujson4c         C99
cJSON       Mar 19, 2016          https://github.com/DaveGamble/cJSON      C99
OVR::JSON   Apr 9, 2013           https://developer3.oculus.com/downloads  C++
jsoncons    Aug 19, 2016          http://danielaparker.github.io/jsoncons  C++11
json11      Jun 20, 2016          https://github.com/dropbox/json11        C++11
nlohmann    2.0.0                 https://github.com/nlohmann/json         C++11

These implementations have the following general properties:

           Const   Throws  Mutable  Maint. Member       Case       Supports  Supports  Supports  Supports  Supports  <= 4u   Clamp   Robust
Name       source  except. DOM      order  storage      sensitive  UTF16     int32_t   uint32_t  int64_t   uint64_t  double  double  
-------------------------------------------------------------------------------------------------------------------------------------------
Json_t     yes     no      yes      yes    array        yes        yes       yes       yes       yes       yes       yes     yes     yes
rapidjson  yes     yes     yes      yes    array        yes        yes       yes       yes       yes       yes       yes     no      no
sajson     yes     no      no       no     array        yes        yes       no        no        no        no        no      no      no
gason      no      no      no       yes    linked-list  N/A        no        yes       yes       no        no        no      no      no
ujson4c    yes     no      yes      yes    linked-list  N/A        no        yes       yes       yes       no        no      no      no
cJSON      yes     no      yes      yes    linked-list  no         broken    yes       no        no        no        no      no      no
OVR::JSON  yes     no      yes      yes    linked-list  yes        broken    yes       yes       no        no        no      no      no
jsoncons   yes     yes     yes      yes    std::vector  yes        yes       yes       yes       yes       yes       yes     no      yes
json11     yes     no      yes      no     std::map     yes        yes       yes       no        no        no        yes     no      yes
nlohmann   yes     yes     yes      no     std::map     yes        yes       yes       yes       yes       yes       yes     no      no

So if you're on a platform that does not (properly) support exceptions then you have to disable exception handling in rapidjson. Unfortunately that makes rapidjson unstable.

If glTF depends on JPEG and PNG I'll have to pick libraries for these formats as well (for C/C++: stb, IJG, etc.). Aside from picking a library, some of these libraries are not small by any means and cause significant code bloat. Maybe this is not considered that big of a deal but I would still urge to minimize the dependencies on other standards.

Officially supporting KTX obviously also adds a dependency on another standard. However, the KTX format is so simply that it hardly needs a library. Besides there is an obvious Khronos KTX library.

@lexaknyazev
Copy link
Member Author

lexaknyazev commented Oct 9, 2016

I would still urge to minimize the dependencies on other standards.

After looking through formats' specs and implementations, I agree. True interoperability across all platforms is almost unachievable unless we define a restricted subset for each image codec.

So yes, let's move them to an extension.

@lexaknyazev
Copy link
Member Author

Updated schema could look like this (example with 4 images: external KTX, external PNGs, glb-embedded PNGs, glb-embdded DDS):

{
  "textures": {
    "texture01": {
      "target": 3553,
      "sampler": "sampler01", // optional
      "source": ["image00", "image01", "image02", "image03"], // list of images of different formats with texture data
      "internalformat": 6408 // optional
    }
  },
  "images": {
    "image00": {
      "format": 6408,
      "mimeType": "image/ktx", // optional; "image/ktx" is default
      "uri": [ // uri.length equals to 1 when no image extension is used
        "file.ktx"
      ]
    },
    "image01": {
      "format": 6408,
      "mimeType": "image/png", // to avoid runtime guessing, valid only with "KHR_image_web" enabled
      "uri": [
        "file01.png",
        "file02.png"
        "file11.png"
        "file12.png"
      ],
      "extensions": {
        "KHR_image_web": { // extension object could be omitted when all fields have default values
          "depth": 2, // optional; depth of the mip level 0, used for 3D textures; must be zero for 2D and cube textures
          "surfaces": 0, // optional; used for 2D texture arrays and cube map arrays
          "faces": 1, // optional; 1 or 6 (cube map)
          "levels": 2, // optional; 0 means runtime should call generateMipmap()
          "type": 5121 // optional;
        }
      }
    },
    "image02": {
      "format": 6408,
      "mimeType": "image/png", // valid only when "KHR_image_web" enabled
      "bufferView": [ // mutually exclusive with "image.uri"
        "bufferView01",
        "bufferView02",
        "bufferView11",
        "bufferView12"
      ],
      "extensions": {
        "KHR_image_web": {
          "depth": 2, // optional; depth of the mip level 0, used for 3D textures; must be zero for 2D and cube textures
          "surfaces": 0, // optional; used for 2D texture arrays and cube map arrays
          "faces": 1, // optional; 1 or 6 (cube map)
          "levels": 2, // optional; 0 means runtime should call generateMipmap()
          "type": 5121 // optional;
        }
      }
    },
    "image03": {
      "format": 6408,
      "mimeType": "image/dds", // valid when "EXT_image_dds" is enabled
      "bufferView": [
        "bufferView_dds"
      ]
    }
  }
}

KTX-like header fields are moved to extension object.

"image01" and "image02" could be used only when KHR_image_web extension is declared and supported; "image03" requires EXT_image_dds extension.

We may need to refine the definition of "glTF extension" to allow extensions extending valid enums for existing properties (image.mimeType in this example).

We could also promote KHR_binary_glTF fields to the core to avoid nesting extensions and properties duplication.

@lexaknyazev
Copy link
Member Author

I would also argue to not support BMP in KHR_image_web due to documentation vagueness.

@mre4ce
Copy link

mre4ce commented Oct 9, 2016

Agreed. Unless we have a solid use case now, I would generally be conservative with adding features, functionality and extensions even, in anticipation of their use. It's easy to add functionality and extensions later. It is much harder to remove functionality and glTF already suffers from that. For instance, do we really need an EXT_image_dds extension? What use cases do we have right now that makes this a critical extension?

@mre4ce mre4ce closed this as completed Oct 9, 2016
@mre4ce mre4ce reopened this Oct 9, 2016
@mre4ce
Copy link

mre4ce commented Oct 9, 2016

Fat fingered the close button ;)

@lexaknyazev
Copy link
Member Author

lexaknyazev commented Oct 9, 2016

Agreed.

Both with core schema changes and BMP removal?

@mre4ce
Copy link

mre4ce commented Oct 11, 2016

BMP removal yes. The schema changes look fine. If the standard only supports KTX then textures don't need to store the "format", "internalFormat", "target" and "type". Those fields are uniquely defined by the KTX header and are only relevant for the extensions that support other image formats. This raises the question why these properties are stored on textures anyway as opposed to images. For instance, can you load the same image and interpret it as different formats or internalFormats?

@lexaknyazev
Copy link
Member Author

lexaknyazev commented Oct 11, 2016

For instance, do we really need an EXT_image_dds extension? What use cases do we have right now that makes this a critical extension?

It's not critical at all, so it's not in the core. Use cases could be: simplifying re-usage of existing content and native DDS format support by D3D engines. Anyway, there's no urge there.

type could be needed only in KHR_image_web extension object, so it's defined only there.

texture.target could be used to avoid at-runtime target guessing (to not check KTX-stored values of faces, surfaces, depth, etc).

texture.internalformat left from current spec. I think it could be removed.

image.format is needed to avoid loading all images from texture.image[] array (in case an asset provides multiple formats of the same image: BCn/ETC/PVRTC).

@mre4ce
Copy link

mre4ce commented Oct 11, 2016

Wouldn't picking a format from an glTF image that stores multiple formats be an implementation issue? An glTF image should report which formats are available and the implementation can pick the one that is best suited for the platform.

@lexaknyazev
Copy link
Member Author

Here's what I'm talking about:

{
  "textures": {
    "texture01": {
      "target": 3553,
      "sampler": "sampler01",
      "source": ["image_bc", "image_etc", "image_pvr"]
    }
  },
  "images": {
    "image_bc": {
      "format": 0x83F3, // COMPRESSED_RGBA_S3TC_DXT5_EXT
      "uri": [
        "file_bc.ktx"
      ]
    },
    "image_etc": {
      "format": 0x9278, // COMPRESSED_RGBA8_ETC2_EAC   
      "uri": [
        "file_etc.ktx"
      ]
    },
    "image_pvr": {
      "format": 0x8C02, // COMPRESSED_RGBA_PVRTC_4BPPV1_IMG
      "uri": [
        "file_pvr.ktx"
      ]
    }
  }
}

@mre4ce
Copy link

mre4ce commented Oct 12, 2016

Right, as opposed to the "texture" specifying a "format" (like in glTF 1.0), the "image" specifies a "format". So the question is whether we should have the texture specify multiple "images" in different formats, or if an "image" specifies multiple formats. It feels more natural to me to have a texture reference a single image and have the image specify all the formats that are available. This also allows an implementation to select which image format to load without the need to know about textures. This would look like the following:

{
    "images" {
        "image0": {
            "formats": [
                {
                     "format": 37496, // GL_COMPRESSED_RGBA8_ETC2_EAC
                     "mimeType": "image/ktx"
                     "uri": "image_etc.ktx"
                }
                {
                     "format": 33779, // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
                     "mimeType": "image/ktx"
                     "uri": "image_dxt.ktx"
                }
            ]
        }
    }
    "textures": {
        "texture01": {
            "source": "image0";
            "sampler": "sampler0";
        }
    }
}

Here "format" is equivalent to the OpenGL internalFormat. I'm not a fan of the fact that KTX files can store the data in a different format than the actual internalFormat but that's the reality we live in.

Arguably, in the case of KTX files, the "format" is already specified in the KTX header. However, the implementation needs to be able to select a format that is supported by the platform and we wouldn't want to force an implementation to read each KTX header to figure out if it stores the data in a format that is supported.

At the end of the day the "texture" only cares about the flat image data. It doesn't care about the internalFormat. Only the implementation cares about selecting an image in an internalFormat that is suitable for the platform.

The KHR_image_web extension could look like this:

{
    "images": {
        "image0": {
            "formats": [
                {
                     "format": 37496, // GL_COMPRESSED_RGBA8_ETC2_EAC
                     "mimeType": "image/ktx"
                     "uri": "image_etc.ktx"
                }
                {
                     "format": 33779, // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
                     "mimeType": "image/ktx"
                     "uri": "image_dxt.ktx"
                }
                {
                     "format": 32856, // GL_RGBA8
                     "mimeType": "image/png"  // valid only when "KHR_image_web" enabled
                     "extensions": {
                         "KHR_image_web": {
                             "width": 256,
                             "height": 256,  // specify 0 for a 1D texture
                             "depth": 1, // optional; depth of mip level 0 of a 3D texture; must be one for 2D and cube textures
                             "layers": 1, // optional; used for 2D texture arrays and cube map arrays
                             "faces": 1, // optional; 1 or 6 (cube map)
                             "levels": 2, // optional; 0 means run-time should call generateMipmap()
                             "uris": [  // list images in the order: depth-layers-faces-levels
                                 "image_level0.png"
                                 "image_level1.png"
                             ]
                         }
                     }
                 }
             ]
         }
     }
}

Some subtle changes are "layers" instead of "surfaces" and defaulting to a depth of 1 and layer count of 1 for 2D textures and cube maps. I don't like a value of 0 in the latter cases because a value of 0 has no special meaning.

Also note that the texture does not specify a "target" because the target can be trivially derived from the "depth", "layers", "faces" and "height" (KTX files don't store a target either). The target also doesn't directly translate to other graphics APIs like Vulkan.

So overall an implementation can iterate over all the image formats and select a combination of image format (DXT, ETC, ASTC etc.) and file format (KTX, PNG, JPEG etc.) that are supported by the platform/implementation.

@mre4ce
Copy link

mre4ce commented Oct 12, 2016

As opposed to KHR_image_web should we be more granular and have extensions like KHR_image_png and KHR_image_jpeg?

@lexaknyazev
Copy link
Member Author

Also note that the texture does not specify a "target" because the target can be trivially derived from the "depth", "layers", "faces" and "height"

In that case, don't we need "depth": 0 (and "layers": 0) to distinguish between 2D texture and 3D texture of depth 1 (or 2D texture array of one layer)?

Why do we have KHR_image_web.width and KHR_image_web.height? PNG and JPEG have that information.

If uri goes into KHR_image_web, so does bufferView (to support embedding images into buffers, see KHR_binary_glTF). So we end up with more fields:

{
    "images": {
        "image0": {
            "formats": [
                {
                     "format": 33779, // GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
                     "mimeType": "image/ktx",
                     "uri": undefined,
                     "bufferView": "bufferView_dxt0"
                },
                {
                     "format": 32856, // GL_RGBA8
                     "mimeType": "image/png",  // valid only when "KHR_image_web" enabled
                     "uri": undefined,
                     "bufferView": undefined,
                     "extensions": {
                         "KHR_image_web": {
                             "width": 256,
                             "height": 256,  // specify 0 for a 1D texture
                             "depth": 1, // optional; depth of mip level 0 of a 3D texture; must be one for 2D and cube textures
                             "layers": 1, // optional; used for 2D texture arrays and cube map arrays
                             "faces": 1, // optional; 1 or 6 (cube map)
                             "levels": 2, // optional; 0 means run-time should call generateMipmap()
                             "uri": undefined,
                             "bufferViews": [  // list images in the order: depth-layers-faces-levels
                                 "bufferView_image_level0",
                                 "bufferView_image_level1"
                             ]
                         }
                     }
                 }
             ]
         }
     }
}

To tighten up schema, we could keep uri and bufferView in image (and make them arrays to support extension use case), so KHR_image_web will contain only metadata, which could be omitted completely for the simplest case.

As opposed to KHR_image_web should we be more granular and have extensions like KHR_image_png and KHR_image_jpeg?

We have also GIF. All web browsers and desktop image libraries support all three formats, so I don't see any reason to increase granularity there. Afaiu, we don't expect any of them on mobile platforms anyway.

@mre4ce
Copy link

mre4ce commented Oct 12, 2016

A texture with depth=1 and layers=1 is still not a 3D or array texture.

I added the "width" and "height" for clarity last minute. You're right that they can just be read from the image file.

I'm fine with keeping a list of URIs and buffer views on the image instead of the extension.

Having one KHR_image_web extension works for me. Just wanted to make sure there's no good reason to be more granular.

@lexaknyazev
Copy link
Member Author

A texture with depth=1 and layers=1 is still not a 3D or array texture.

Sorry, I'm not following. If there's nor texture.target, neither "layers": 0, how can runtime know what GL function to use: tex*Image2D or tex*Image3D?

@mre4ce
Copy link

mre4ce commented Oct 12, 2016

const GLenum target = ( ( depth > 1 ) ? GL_TEXTURE_3D :
                      ( ( faces > 1 ) ? 
                      ( ( layers > 1 ) ? GL_TEXTURE_CUBE_MAP_ARRAY : GL_TEXTURE_CUBE_MAP ) :
                      ( ( height > 1 ) ?
                      ( ( layers > 1 ) ? GL_TEXTURE_2D_ARRAY : GL_TEXTURE_2D ) :
                      ( ( layers > 1 ) ? GL_TEXTURE_1D_ARRAY : GL_TEXTURE_1D ) ) ) );

This is what you typically do to load a KTX file. Setting the depth and layers to anything less-equal to 1 results in the same target. In Vulkan you also specify a 2D texture with depth=1 and layers=1.

@lexaknyazev
Copy link
Member Author

I understand it, but such approach isn't 100% aligned with KTX spec:

2.8 pixelWidth, pixelHeight, pixelDepth
The size of the texture image for level 0, in pixels. No rounding to block sizes should be applied for block compressed textures.
For 1D textures pixelHeight and pixelDepth must be 0. For 2D and cube textures pixelDepth must be 0.

2.9 numberOfArrayElements
numberOfArrayElements specifies the number of array elements. If the texture is not an array texture, numberOfArrayElements must equal 0.

Looks like KTX allows texture arrays of 1 element. Also see libktx.

@mre4ce
Copy link

mre4ce commented Oct 12, 2016

I guess that could be useful if you want to forcibly make a texture work with a particular shader. I'm not in love with the KTX format even though I think it's our best choice. I'm mostly going off of how Vulkan works, but even there I guess it could be useful to force an image view to be an 1 element array or 3D texture with a depth of 1.

@pjcozzi
Copy link
Member

pjcozzi commented Feb 13, 2017

Is this now duplicate with #835? Or should we leave this open for notes post 2.0?

@lexaknyazev
Copy link
Member Author

Yes. And post-2.0 stuff should go there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants