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

Pick translucent geometry #4979

Merged
merged 22 commits into from
Feb 22, 2017
Merged

Pick translucent geometry #4979

merged 22 commits into from
Feb 22, 2017

Conversation

bagnell
Copy link
Contributor

@bagnell bagnell commented Feb 9, 2017

Adds Scene.pickTranslucentDepth which can enable picking the depth of translucent pixels.

Decreases performance as the scene has to be rendered again where translucent fragments write depth. It may not be as bad because we use a culling frustum and scissor test for a single pixel.

  • Test performance
  • Add doc
  • Add tests
  • Update CHANGES.md

#4897 #4511 #4678

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 9, 2017

CC @pmconne

Part of #4678
Part of #4897

@@ -2727,6 +2799,9 @@ define([
var uniformState = context.uniformState;

var drawingBufferPosition = SceneTransforms.transformWindowToDrawingBuffer(this, windowPosition, scratchPosition);
if (this.pickTranslucentDepth) {
renderForPickDepth(this, drawingBufferPosition);
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps rename to renderTranslucentDepthForPick?

Copy link
Contributor

Choose a reason for hiding this comment

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

Are you sure pickTranslucentDepth need to be a member of Scene? It is only checked here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Are you sure pickTranslucentDepth need to be a member of Scene?

Yes, unless you have a better suggestion. We call pickPosition in ScreenSpaceCameraController for panning, zooming, terrain collision, etc. If we don't have the option on Scene, would we do it by default?

// Update with previous frame's number and time, assuming that render is called before picking.
updateFrameState(scene, frameState.frameNumber, frameState.time);
frameState.cullingVolume = getPickCullingVolume(scene, drawingBufferPosition, 1, 1);
frameState.passes.render = true;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it better for this to be

frameState.passes.pick = true;

Or to do whatever the shadow map pass does since they only writes depth.

If doc, please add a comment.

Copy link
Contributor

Choose a reason for hiding this comment

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

Another thing to consider here is that we do not want a pick to cause 3D Tiles to make new requests or process new tiles (which could happen because the cull volume is different) so this needs to be pick/depth or we need to signal to pause requests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The shadow map pass happens during the render pass. It uses derived commands that disables color writes and enables depth writes. Also, the framebuffer only has a depth attachment.

I could change it to pick, but the depth copies for each frustum only happen during the render pass. Another option is to add a new depth pass.

Copy link
Contributor

Choose a reason for hiding this comment

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

the depth copies for each frustum only happen during the render pass

I'm not following. What depth copies?

Another option is to add a new depth pass

Could work, but what about this: could we just derive commands from the previous frame's commands? Instead of calling all the update functions, we rely on the state of the frustums/commands from the previous frame. We would need to guarantee that WebGL resources pointed to by the commands were not deleted, we could even rename the function to indicate this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What depth copies?

We copy the frustum depth to a texture after each frustum is rendered here.

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've updated this in e522ffe. There wasn't a need for derived commands. Everything is updated for the pick and depth pass which essentially queues pick commands for each command that was used in the previous render pass.

Do 3D Tiles work this way or does it select new tiles on pick?

@@ -2696,6 +2706,68 @@ define([
return object;
};

var scratchPickDepthPassState;

function renderForPickDepth(scene, drawingBufferPosition) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps add a PERFORMANCE_IDEA about rendering translucent only and merging with the previous frame; I still think it could work if it checks the state is in sync.


var passState = scratchPickDepthPassState;
if (!defined(passState)) {
passState = scratchPickDepthPassState = new PassState(context);
Copy link
Contributor

Choose a reason for hiding this comment

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

What if the app has more than one context? Should scratchPickDepthPassState be part of the Scene?

var width = context.drawingBufferWidth;
var height = context.drawingBufferHeight;

var framebuffer = scene._pickDepthFramebuffer;
Copy link
Contributor

Choose a reason for hiding this comment

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

_pickDepthFramebuffer should be declared in the constructor function.

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 9, 2017

Looks good. I expect performance will be reasonable.

@bagnell
Copy link
Contributor Author

bagnell commented Feb 13, 2017

The performance is OK. The function is still bound by readPixels.

@bagnell
Copy link
Contributor Author

bagnell commented Feb 15, 2017

@pjcozzi This is ready for another look.

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 15, 2017

Do 3D Tiles work this way or does it select new tiles on pick?

3D Tiles use the same tiles for picking.

frameState.passes.pick = true;
frameState.passes.depth = true;

us.update(frameState);
Copy link
Contributor

Choose a reason for hiding this comment

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

Offline we discussed using the commands from the previous frame. Why didn't you take that approach? Because some primitives will want to generate different commands here? Which precisely since pick ids won't matter.

If we stick with this approach, let's add a PERFORMANCE_IDEA about reusing the commands.

I've been thinking about this in three levels of reuse/efficiency:

  • The most efficient is what was previously done; reuse the previous frame's depth buffer. This is not possible unless the render pass writes an extra depth buffer with translucent primitives.
  • Reuse the previous frame's commands. From a GL perspective, this is no more efficient, but it saves CPU overhead in Cesium.
  • Reuse nothing from the previous frame, and explicitly update/render.

var height = context.drawingBufferHeight;

var framebuffer = scene._pickDepthFramebuffer;
var pickDepthFBWidth = scene._pickDepthFramebufferWidth;
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 and height below declared in the constructor function?

@@ -294,6 +302,9 @@ define([
this._pickDepths = [];
this._debugGlobeDepths = [];

this._pickDepthPassState = undefined;
this._pickDepthFramebuffer = undefined;
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be destroyed in the Scene's destroy function?

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 15, 2017

Tweaked reference doc in be613f9.

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 15, 2017

Also merge in master.

@bagnell
Copy link
Contributor Author

bagnell commented Feb 15, 2017

@pjcozzi This is ready for another look.

I updated this to use the commands from the previous frame and generate derived commands like we discussed.

A simple way to test is to click the 'pick position' button in the picking Sandcastle example and replace the model entity code with:

    viewer.scene.pickTranslucentDepth = true;
    var modelEntity = viewer.entities.add({
        name : 'milktruck',
        position : Cesium.Cartesian3.fromDegrees(-123.0744619, 44.0503706),
        model : {
            uri : '../../SampleData/models/CesiumMilkTruck/CesiumMilkTruck-kmc.gltf',
            color : Cesium.Color.WHITE.withAlpha(0.5),
            colorBlendAmount : 0.5,
            colorBlendMode : Cesium.ColorBlendMode.MIX
        }
    });

var attributeLocations = shaderProgram._attributeLocations;
var fs = shaderProgram.fragmentShaderSource;

var writesFragDepth = false;
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps combine this into one: writesDepthOrDiscards and simplify the logic below?

var fragDepthRegex = /\bgl_FragDepthEXT\b/;
var discardRegex = /\bdiscard\b/;

function getDepthOnlyShaderProgram(scene, context, shaderProgram) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps add a comment here or somewhere that this is an optimization because we're not sure that drivers will replace the fragment shader when it doesn't discard/write-depth when the color mask is all falses and the framebuffer doesn't have a color attachment.

Who knows in the future, this might not be required.

attributeLocations : attributeLocations
});

cache[id] = shader;
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it a concern that this cache always grows and never shrinks? Should it be possible for the original shader program to basically own this and free it when the ref count goes to zero? Instead of being an object literal, this could be its own shader program cache instance if useful.

Also, is all this being shared with the shadow map pass code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it a concern that this cache always grows and never shrinks?

Yes, the derived commands for OIT are using a separate cache and has the same problem.

Also, is all this being shared with the shadow map pass code?

No. The shadow map derived commands don't have a cache. The source is generated each time the shader on the command changes. Also, it is never released.

});

scene.pickTranslucentDepth = true;
expect(scene).toRenderAndCall(function() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this do a second pick and spy on each one or do some other check to make sure the cached derived commands work OK?

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 16, 2017

This is super close.

@bagnell
Copy link
Contributor Author

bagnell commented Feb 17, 2017

@pjcozzi This is ready for another look.

@pjcozzi pjcozzi mentioned this pull request Feb 18, 2017
7 tasks
@@ -138,14 +139,86 @@ define([
return cachedShader.shaderProgram;
};

ShaderCache.prototype.getDerivedShaderProgram = function(shaderProgram, keyword) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Add unit tests for the new stuff here.

@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 18, 2017

Can you merge this into 3d-tiles and test before we merge this into master? Also, if any of the 3D Tiles renderer needs to be hooked up to this (e.g., for translucent/opaque styling), it would be good to do that right away in case it impacts the interface.

@bagnell
Copy link
Contributor Author

bagnell commented Feb 22, 2017

Can you merge this into 3d-tiles and test before we merge this into master?

I tested in the 3d-tiles-pick-translucent branch. Everything works well. There wasn't any need to change the styling to use the cache because the derived commands only modify the render state and uniform map. The original commands have the modified shaders through the callbacks.

This should be ready to go. I can merge 3d-tiles-pick-translucent to 3d-tiles now or when this gets merged.

This was referenced Feb 22, 2017
@pjcozzi
Copy link
Contributor

pjcozzi commented Feb 22, 2017

Great! Please merge into 3d-tiles now however you see fit.

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

Successfully merging this pull request may close these issues.

2 participants