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

Texture compression #4758

Merged
merged 34 commits into from
Feb 2, 2017
Merged

Texture compression #4758

merged 34 commits into from
Feb 2, 2017

Conversation

bagnell
Copy link
Contributor

@bagnell bagnell commented Dec 15, 2016

Adds support for:

  • DXT, PVR, and ECT1 texture compression when the extensions are available.
  • loading KTX files for compressed textures.
  • models that link to an external KTX file or embed the KTX file binary.

There are a few features of KTX files that I didn't implement, but if they are important I can add support in this PR:

  • Switch to little endian if the file is big endian
  • Make file metadata available (key-value pairs)
  • 3D textures
  • Texture arrays
  • Cubemaps
  • Mipmaps

TODO:

  • Add test for models with an embedded KTX image.
  • Add test for binary glTF models with an embedded KTX image.
  • Update CHANGES.md
  • Squash commits
  • Test with Natural Earth

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

There are a few features of KTX files that I didn't implement, but if they are important I can add support in this PR...

Just document that these are not supported in the reference doc.

Later, as the glTF spec for this stabilizes, we'll add whatever features we need to be fully conformant.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Is the plan to also add ASTC to this PR?

@@ -271,7 +271,10 @@ define([
this._depthTexture = !!getExtension(gl, ['WEBGL_depth_texture', 'WEBKIT_WEBGL_depth_texture']);
this._textureFloat = !!getExtension(gl, ['OES_texture_float']);
this._fragDepth = !!getExtension(gl, ['EXT_frag_depth']);
this._debugShaders = getExtension(gl, ['WEBGL_debug_shaders']);
this._debugShaders = !!getExtension(gl, ['WEBGL_debug_shaders']);
this._s3tc = !!getExtension(gl, ['WEBGL_compressed_s3tc', 'MOZ_WEBGL_compressed_texture_s3tc', 'WEBKIT_WEBGL_compressed_texture_s3tc']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and below, do you know if the 'MOZ_and WEBKIT_ prefixes are in widespread use? If this is just for older browsers, we can drop them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about the MOZ_ prefix, but the WEBKIT_ prefix was on the iPad I tested.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What the WEBKIT_ prefix the only option? If so, just leave these.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it was the only one.

@@ -1395,6 +1399,21 @@ define([
};
}

function ktxLoad(model, id) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? Because of internalFormat? Could this be combined with imageLoad?

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Can you please provide a summary of the current glTF changes you made, e.g., can now reference .ktx, how it works with binary glTF, how it works when embedded, etc.?

var endianness = view.getUint32(byteOffset, true);
byteOffset += sizeOfUint32;
if (endianness !== endiannessTest) {
// TODO: Switch endianness?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually given how trivial this will be, I would just do it now.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to ignore this for now. I couldn't find a big endian file to test with without generating one from the same code. The KTX library has an issue to add tests as well: KhronosGroup/KTX-Software#26

var fakeXHR;

beforeEach(function() {
fakeXHR = jasmine.createSpyObj('XMLHttpRequest', ['send', 'open', 'setRequestHeader', 'abort', 'getAllResponseHeaders']);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this copy and pasted a few times in the tests? Perhaps it should be a utility in the Specs root directory?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, all of the load* functions have similar tests. Since many of them are slightly different, I'm not going to do it in this PR.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

@bagnell wow, this looks really good.

Once we have the summary, #4758 (comment), I can start discussing with the glTF folks.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 15, 2016

Part of #4679 / #4678

@bagnell
Copy link
Contributor Author

bagnell commented Dec 15, 2016

Is the plan to also add ASTC to this PR?

I added it but couldn't test it. I have the extension on my Galaxy S6 but it isn't reported by WebGL Report. It also isn't on WebGL stats. Is it a draft extension?

https://www.khronos.org/registry/webgl/extensions/WEBGL_compressed_texture_astc/

@bagnell
Copy link
Contributor Author

bagnell commented Dec 15, 2016

Can you please provide a summary of the current glTF changes you made, e.g., can now reference .ktx, how it works with binary glTF, how it works when embedded, etc.?

  • The image is loaded as a KTX is the extension is .ktx.
  • Binary images are parsed as a KTX file when the mime type is image/ktx
  • Embedded KTX files as a data uri do not currently work. I'm looking into it.

The KTX files must be a single 2D texture. The texture can be compressed or uncompressed.
If the texture is compressed and the format is unsupported, an exception will be thrown. The app is responsible for requesting a glTF with supported compressed texture formats. If the KTX has mipmaps, only the level 0 texture is loaded.

@bagnell bagnell force-pushed the texture-compression branch from 6020a01 to 4cc7559 Compare December 19, 2016 22:45
@bagnell
Copy link
Contributor Author

bagnell commented Dec 19, 2016

@pjcozzi This is ready for another look. I squashed the commits to remove all of the binary test data from the history during implementation.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 20, 2016

These three ShaderProgram tests fail in this branch and pass in master:

Renderer/ShaderProgram fails vertex shader compile
Expected function to throw RuntimeError.
Error: Expected function to throw RuntimeError.
    at stack (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1475:17)
    at buildExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1445:14)
    at Spec.expectationResultFactory (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:584:18)
    at Spec.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:332:34)
    at Expectation.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:528:21)
    at Expectation.toThrowRuntimeError (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1399:12)
    at http://localhost:8080/Specs/Renderer/ShaderProgramSpec.js:523:12
    at Object.<anonymous> (http://localhost:8080/Specs/spec-main.js:139:30)
    at attemptAsync (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1793:24)
    at QueueRunner.run (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1746:26)
Renderer/ShaderProgram fails fragment shader compile
Expected function to throw RuntimeError.
Error: Expected function to throw RuntimeError.
    at stack (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1475:17)
    at buildExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1445:14)
    at Spec.expectationResultFactory (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:584:18)
    at Spec.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:332:34)
    at Expectation.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:528:21)
    at Expectation.toThrowRuntimeError (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1399:12)
    at http://localhost:8080/Specs/Renderer/ShaderProgramSpec.js:537:12
    at Object.<anonymous> (http://localhost:8080/Specs/spec-main.js:139:30)
    at attemptAsync (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1793:24)
    at QueueRunner.run (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1746:26)
Renderer/ShaderProgram fails to link
Expected function to throw RuntimeError.
Error: Expected function to throw RuntimeError.
    at stack (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1475:17)
    at buildExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1445:14)
    at Spec.expectationResultFactory (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:584:18)
    at Spec.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:332:34)
    at Expectation.addExpectationResult (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:528:21)
    at Expectation.toThrowRuntimeError (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1399:12)
    at http://localhost:8080/Specs/Renderer/ShaderProgramSpec.js:551:12
    at Object.<anonymous> (http://localhost:8080/Specs/spec-main.js:139:30)
    at attemptAsync (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1793:24)
    at QueueRunner.run (http://localhost:8080/ThirdParty/jasmine-2.2.0/jasmine.js:1746:26)

* @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
* @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A}
*/
function loadKTX(urlOrBuffer, headers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bagnell we should encourage users to use this broadly. What do you think about adding this to a Sandcastle example? Perhaps the materials one? Just use a small simple KTX file like the Cesium logo.

texture = new Uint8Array(texture.buffer, 0, levelSize);
}

return {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we document this type? Even if it is a trivial constructor + readonly properties?

I don't know if there is an easier way to document this (other than making the type) like how we can document callback functions.

}

if (!PixelFormat.validate(glInternalFormat)) {
throw new RuntimeError('glInternalFormat is not a valid format.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Document all exceptions. Are you sure these are runtime errors? I think they are developer errors. For example: https://github.com/AnalyticalGraphicsInc/cesium/blob/3d-tiles/Source/Scene/Batched3DModel3DTileContent.js#L215

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't all file loading/parsing be a runtime error? The developer might not have control over the file. KML and GeoJSON work this way. Shouldn't parsing a 3D tile also throw runtime errors when they fail to parse?

throw new RuntimeError('3D textures are unsupported.');
}

// TODO: support texture arrays and cubemaps
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the TODO here and below, and just add bullets for the limitations to the reference doc.

var bytesOfKeyValueByteSize = view.getUint32(byteOffset, true);
byteOffset += sizeOfUint32;

// TODO: read metadata? At least need to check for KTXorientation
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO as well.

@pjcozzi
Copy link
Contributor

pjcozzi commented Dec 20, 2016

@bagnell just those comments.

Can you please also open a separate PR to merge this into 3d-tiles? I am comfortable doing that today even before the glTF spec/extension work.

@lilleyse
Copy link
Contributor

I'm having trouble loading the samples on my machine.

Box-textured-ktx-binary
box-textured-ktx-binary
Box-textured-ktx-embedded
box-textured-ktx-embedded
Box-textured-ktx
box-textured-ktx

I have these extensions:

WEBGL_compressed_texture_etc1
WEBGL_compressed_texture_s3tc
WEBKIT_WEBGL_compressed_texture_s3tc

* @see {@link http://www.w3.org/TR/cors/|Cross-Origin Resource Sharing}
* @see {@link http://wiki.commonjs.org/wiki/Promises/A|CommonJS Promises/A}
*/
function loadCRN(urlOrBuffer, headers) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just want to bump my question in the other PR about if we can implement some of these in terms of a generic @private load* function to avoid some duplication?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather keep them separate. The only thing that is the same is checking if the parameter is a url or array buffer.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK.

Sandcastle.declare(applyCompressedTextureMaterial); // For highlighting in Sandcastle.

var compressedImageUrl;
var context = scene.context;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context is not part of the public API. You could expose these on Scene or perhaps a scene.getTextureFormatSupported(String) function. Whatever you think is clean.

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 10, 2017

Really nice job, just those comments @bagnell.

We'll also have to park this PR while we work on the glTF spec, but this branch should come into master well before 3d-tiles.

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 26, 2017

Related glTF Pipeline changes: CesiumGS/gltf-pipeline#204

@pjcozzi
Copy link
Contributor

pjcozzi commented Jan 26, 2017

@bagnell given that this is cleanly going into a glTF extras property, it will be OK to merge this into master when ready; we'll also update the online model converter.

@bagnell
Copy link
Contributor Author

bagnell commented Jan 26, 2017

@pjcozzi This has been updated. Just waiting of the gltf-pipeline changes to generate the binary and embedded test models.

@bagnell
Copy link
Contributor Author

bagnell commented Jan 30, 2017

@pjcozzi This is ready for another look.

@@ -1419,8 +1419,8 @@ define([
var gltfImage = images[textures[id].source];
var extras = gltfImage.extras;

var binary;
var uri;
var binary = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

@pjcozzi pjcozzi merged commit b6b3ad2 into master Feb 2, 2017
@pjcozzi pjcozzi deleted the texture-compression branch February 2, 2017 12:33
@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 2, 2017

Also merged into 3d-tiles, e05898a

@pjcozzi pjcozzi mentioned this pull request Feb 3, 2017
53 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants