-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Draco with dequantization in shader #6357
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mechanics look solid here. Once quantization is on by default it might be good to go through all the draco sample models and make sure they still work. Another thing to try is loading the draco version of the NYC tileset and comparing the total memory usage in the 3D Tiles inspector with quantization on and off.
Finally, does it ever make sense to draco-encode a model with pre-quantized attributes? Or is the preferred method to do what is done here, converting draco-encoded to quantized form at runtime? I would think the second case, but just want to be sure.
Source/Scene/DracoLoader.js
Outdated
@@ -130,7 +130,7 @@ define([ | |||
* | |||
* @private | |||
*/ | |||
DracoLoader.parse = function(model) { | |||
DracoLoader.parse = function(model, dequantizeInShader) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we just grab model._dequantizeInShader
instead of passing it through separately?
Source/Scene/Model.js
Outdated
@@ -288,6 +288,7 @@ define([ | |||
* @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. | |||
* @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. | |||
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. | |||
* @param {String[]} [options.dequantizeInShader] The list of {@link https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_draco_mesh_compression/README.md|KHR_draco_mesh_compression} encoded attributes to dequantize in the shader. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is too fine grained of an option. It should just be a boolean and default to true
, meaning all attributes will be dequantized in the shader. (Under the hood there might be exceptions, in case there's an issue with bone indices or something).
Also it should be set to false if the draco extension is not in use. In its current form a model crashes if it doesn't use draco but a value like ['POSITION'] is passed in here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can only skip attributes by attribute type (https://github.com/google/draco/blob/master/src/draco/javascript/emscripten/draco_web_decoder.idl#L22), not each attribute. I think it may be best to limit to POSITION
, NORMAL
,
COLOR
, and TEX_COORD
.
(Three.js only skips quantization for position)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, hardcoding those to be quantized sounds fine.
Source/Scene/Model.js
Outdated
@@ -1710,6 +1713,7 @@ define([ | |||
programPrimitives = []; | |||
model._programPrimitives[programId] = programPrimitives; | |||
} | |||
primitive.id = id + '.primitive.' + i; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If possible we should avoid dynamically adding a property to the glTF in this way.
I can't remember why we cache quantized primitives according to their program, but maybe the step can be simplified?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was implemented for WEB3D_quantized_attributes
, I'll see if it can be simplified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I turned the list into a dictionary, and used the id as the key, so I am no longer adding the id
attribute to the primitive itself.
Source/Scene/Model.js
Outdated
|
||
if (!defined(primitives)) { | ||
return shader; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this check for?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the program primitives weren't cached, they are not quantized, and the shader does not need to be modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll add a comment if unclear.
Source/Workers/decodeDraco.js
Outdated
@@ -82,6 +84,21 @@ define([ | |||
componentDatatype : ComponentDatatype.fromTypedArray(vertexArray) | |||
} | |||
}; | |||
|
|||
transform = new draco.AttributeQuantizationTransform(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It should be ok to remove the var transform;
above and have this be var transform = new draco.AttributeQuantizationTransform();
Source/Workers/decodeDraco.js
Outdated
|
||
transform = new draco.AttributeQuantizationTransform(); | ||
if (transform.InitFromAttribute(attribute)) { | ||
var minValues = new Float32Array(numComponents); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't really matter, but could this be a plain Array instead? Since the array is pretty small using a Float32Array
almost seems misleading.
Preferred method is the second, see google/draco#18 |
Why isn't it the first? With the second, you would need to re-quantize after decompression. After reading that issue, it seems like you would quantize attributes and then draco compress with the option to remove the built-in quantization. |
With the second the quantization values are stored in the draco geometry itself, so there's not need to store them separately. With the current iteration in glTF-pipeline, I don't believe it is possible to quantize the attributes then draco encode. |
Thanks @lilleyse, updated! |
Numbers with NYC tileset:
|
a4dd562
to
2044d9a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Getting close! In a bit I should be able to test the final memory usage numbers.
Source/Scene/DracoLoader.js
Outdated
@@ -131,11 +136,12 @@ define([ | |||
* @private | |||
*/ | |||
DracoLoader.parse = function(model) { | |||
if (!hasExtension(model)) { | |||
if (!this.hasExtension(model)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's better style to use DracoLoader.hasExtension
instead here and below since it is a static functions.
Source/Scene/Model.js
Outdated
@@ -1429,7 +1433,7 @@ define([ | |||
|
|||
// Only ARRAY_BUFFER here. ELEMENT_ARRAY_BUFFER created below. | |||
ForEach.bufferView(model.gltf, function(bufferView, id) { | |||
if (bufferView.target === WebGLConstants.ARRAY_BUFFER) { | |||
if (bufferView.target === WebGLConstants.ARRAY_BUFFER && !DracoLoader.hasExtension(model)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the extension check needed? Did you run into a situation where a buffer view was marked as ARRAY_BUFFER
but shouldn't have been?
Source/Workers/decodeDraco.js
Outdated
@@ -4,13 +4,15 @@ define([ | |||
'../Core/IndexDatatype', | |||
'../Core/RuntimeError', | |||
'../ThirdParty/draco-decoder-gltf', | |||
'../ThirdParty/GltfPipeline/byteLengthForComponentType', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It doesn't matter in terms of functionality, but use the Cesium version instead: ComponentDatatype.getSizeInBytes
@@ -288,6 +288,7 @@ define([ | |||
* @param {Color} [options.silhouetteColor=Color.RED] The silhouette color. If more than 256 models have silhouettes enabled, there is a small chance that overlapping models will have minor artifacts. | |||
* @param {Number} [options.silhouetteSize=0.0] The size of the silhouette in pixels. | |||
* @param {ClippingPlaneCollection} [options.clippingPlanes] The {@link ClippingPlaneCollection} used to selectively disable rendering the model. | |||
* @param {Boolean} [options.dequantizeInShader=true] Determines if a {@link https://github.com/google/draco|Draco} encoded model is dequantized on the GPU. This decreases total memory usage for encoded models. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mention in CHANGES.md
that this flag is added. It can go alongside the Draco bullet.
Source/Workers/decodeDraco.js
Outdated
attributeData = new draco.DracoInt32Array(); | ||
// Uint16Array is used becasue there is not currently a way to retreive the maximum | ||
vertexArray = new Int16Array(vertexArrayLength); | ||
dracoDecoder.GetAttributeInt32ForAllPoints(dracoGeometry, attribute, attributeData); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the attribute is oct encoded I think we can use a smaller array length since the number of components should be 2 instead of 3.
Also quantization should use a Uint16Array
while oct-encoding should use a Int16Array
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When the attribute is oct encoded, the loader already return 2 for the number of components, so the array length is being set to the correct length.
I have updated Uint16
vs Int16
however.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah ok, I should have checked that myself. Thanks!
Thanks @lilleyse, updated! |
Thanks, looks good. |
@lilleyse Addresses #6191 (comment)
Skip dequantization in the draco module and instead dequantizes in the shader, for an optionally specified list of attribute types to skip.