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

(feat): Upgrade deck.gl and luma.gl #805

Open
wants to merge 60 commits into
base: main
Choose a base branch
from

Conversation

xinaesthete
Copy link

Addresses #
#804

Background

As discussed #790

Fairly extensive changes will be required; discussion should probably migrate to here.

@ilan-gold
Copy link
Collaborator

I made some fixes (commits should be messaged clearly). Thanks for getting this started. More to come tomorrow!

I am not clear what was previously dataFormat should be now, but that might not be what is causing the current error:

deck: drawing XRLayer({id: 'image-sub-layer-0,135,97,0-Background-Image-ZarrPixelSource-#detail#'}) to screen: cyclic object value TypeError: cyclic object value
    setUniformsWebGL webgl-render-pipeline.js:197
    setUniformsWebGL webgl-render-pipeline.js:196
    setUniforms model.js:464
    draw xr-layer.js:257
    _drawLayer layer.js:851
    withGLParameters with-parameters.js:17
    withParametersWebGL webgl-device.js:269
    _drawLayer layer.js:845
    _drawLayersInViewport layers-pass.js:153
    _drawLayers layers-pass.js:54
    render layers-pass.js:32
    renderLayers deck-renderer.js:47
    _drawLayers deck.js:691
    redrawDeck deckgl.js:42
    _customRender deckgl.js:65
    redraw deck.js:369
    _onRenderFrame deck.js:723
    _renderFrame animation-loop.js:256
    redraw animation-loop.js:154
    _animationFrame animation-loop.js:244
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    _animationFrame animation-loop.js:245
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    _animationFrame animation-loop.js:245
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    _animationFrame animation-loop.js:245
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    _animationFrame animation-loop.js:245
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    _animationFrame animation-loop.js:245
    requestAnimationFrame request-animation-frame.js:9
    _requestAnimationFrame animation-loop.js:225
    start animation-loop.js:119
    _Deck deck.js:251
    createDeckInstance deckgl.js:47
    DeckGLWithRef deckgl.js:138
    React 8
    workLoop scheduler.development.js:266
    flushWork scheduler.development.js:239
    performWorkUntilDeadline scheduler.development.js:533
    scheduler chunk-KSA3GRLC.js:405
    scheduler chunk-KSA3GRLC.js:453
    __require2 chunk-LK32TJAX.js:18
    scheduler chunk-KSA3GRLC.js:465
    __require2 chunk-LK32TJAX.js:18
    React 2
    __require2 chunk-LK32TJAX.js:18
    dom React
    __require2 chunk-LK32TJAX.js:18
    dom React
    __require2 chunk-LK32TJAX.js:18
    <anonymous> react-dom_client.js:38

@xinaesthete
Copy link
Author

Yeah - as for clear messaging of commits, I'm generally very on-board with that, previous commit was a result of starting to mess around & seeing how far I got, then figured it was more useful to commit than just leave on my machine...

I see that as far up as model.setUniforms() is marked as deprecated, so that may be the point at which to change the code for the current issue.

@xinaesthete
Copy link
Author

https://github.com/visgl/luma.gl/blob/master/docs/legacy/porting-guide.md#quick-v9-api-overview

In progress
Reading and writing buffers is now an async operation. While WebGL does not support async reads and writes on MacOS, the API is still async to ensure portability to WebGPU.
Uniform buffers are now the standard way for the application to specify uniforms. Uniform buffers are "emulated" under WebGL.

@xinaesthete
Copy link
Author

I think that what's happening is that likely the dataFormat is indeed causing it to raise an error, and then in the process of raising it it gets circular references because the texture objects refer back to the device...

I think setting uniforms like that should work in the short-term although it'll clearly need changing once it gets to making it run on WebGPU etc.

@xinaesthete
Copy link
Author

xinaesthete commented Jun 17, 2024

https://luma.gl/docs/api-reference/core/texture-formats/ (not sure how helpful I'm being here, just dropping things in that seem relevant)

Note that luma.gl deduces dataFormat and type from format by taking the first value from the data format and data type entries in this table.

@xinaesthete
Copy link
Author

I optimistically tried changing draw() in xr-layer.js:256

      model
        .setBindings({uniforms: {
          ...uniforms,
          contrastLimits: paddedContrastLimits,
          ...textures
        }})
      model.draw(); //n.b. took a few moments to realise setBindings() returns undefined...

I've not really processed whether that's actually a correct way to be passing uniforms, but it does change the kind of error we see...

webgl-render-pipeline.js:243 Uncaught (in promise) 
Error: Error during linking: Vertex shader is not compiled.

    at WEBGLRenderPipeline._reportLinkStatus (webgl-render-pipeline.js:243:23)
    at WEBGLRenderPipeline._linkShaders (webgl-render-pipeline.js:218:18)
    at new WEBGLRenderPipeline (webgl-render-pipeline.js:54:14)
    at _WebGLDevice.createRenderPipeline (webgl-device.js:226:16)
    at _PipelineFactory.createRenderPipeline (pipeline-factory.js:29:42)
    at _Model._updatePipeline (model.js:551:50)
    at new _Model (model.js:160:30)
    at XRLayer._getModel (xr-layer.js:189:12)
    at XRLayer.updateState (xr-layer.js:159:35)
    at XRLayer._update (layer.js:778:22)

I don't know how important the warnings that appear before that (luma.gl: Model(image-sub-layer-0,131,175,0-Background-Image-TiffPixelSource-#detail#): Missing layout for buffer "instancePickingColors". etc) are.

Actually, the error thrown in _reportLinkStatus() happens before we call setUniforms/Bindings() - it's just that when we call setUniforms() then it raises another error in the process, which makes it onto the console before the other error is reported, whereas when we call setBindings() it appears happier, then in the process of drawing picks up on the link status & outputs the earlier error. Then having printed that error once, the app is running moderately happily and it'll draw the scale-bar layers (with some one-frame-behind annoyingness, at least with devtools open, I've seen that behave differently based on those kinds of conditions at times in MDV) - so it's passing through the XRLayer.draw() without incident, updating the app state, just not managing to draw anything particularly useful.

This is where things get flagged in lumagl webgl-render-pipeline.js

    // PRIVATE METHODS
    // setAttributes(attributes: Record<string, Buffer>): void {}
    // setBindings(bindings: Record<string, Binding>): void {}
    async _linkShaders() {
        const { gl } = this.device;
        gl.attachShader(this.handle, this.vs.handle);
        gl.attachShader(this.handle, this.fs.handle);
        log.time(LOG_PROGRAM_PERF_PRIORITY, `linkProgram for ${this.id}`)();
        gl.linkProgram(this.handle);
        log.timeEnd(LOG_PROGRAM_PERF_PRIORITY, `linkProgram for ${this.id}`)();
        // TODO Avoid checking program linking error in production
        if (log.level === 0) {
            // return;
        }
        if (!this.device.features.has('compilation-status-async-webgl')) {
            const status = this._getLinkStatus();
            this._reportLinkStatus(status); //<<<<<<<
            return;
        }
        // async case
        log.once(1, 'RenderPipeline linking is asynchronous')();
        await this._waitForLinkComplete();
        log.info(2, `RenderPipeline ${this.id} - async linking complete: ${this.linkStatus}`)();
        const status = this._getLinkStatus();
        this._reportLinkStatus(status);
    }

FWIW on my M1 running Chrome stable in Sonoma, it doesn't have the ''compilation-status-async-webgl' feature. Hopefully we're fairly close to the point of seeing why it hasn't linked at this stage... both fs and vs of the WebGLRenderPipeline in question have compilationStatus: "pending".

I might commit my minor change without great confidence in its correctness... just going to dig through the debugger & docs a bit more and see if I make any more progress...

@xinaesthete
Copy link
Author

ok... so a couple of things of note.

When I changed const programManager = ProgramManager.getDefaultProgramManager(device) to ShaderAssembler.getDefaultShaderAssembler(), at no point did I make any change meaning that the assembleShader() method was called on it - indeed, that seems not to be happening at present, and it seems likely this may be needed before getting to shader linking.

I could do with better understanding how pipelines are managed - bearing in mind that PipelineManager is the thing that's supposed to replace ProgramManager, I guess although changing to ShaderAssembler was enough to pass a basic build step, there may be a need to involve PipelineManager... I'm also not entirely clear on how attributes are supposed to work... in AttributeManager vs Geometry, and the new bufferLayout prop for Model... So I think I need to a) take a rest, and b) read through more of the docs.

When we call new Model(...), it creates a pipeline with its pipelineFactory, which is PipelineFactory.getDefaultPipelineFactory(this.device). That is used to make a ShaderFactory (not ShaderAssembler), which is then used in Model._updatePipeline():

    /** Update pipeline if needed */
    _updatePipeline() {
        if (this._pipelineNeedsUpdate) {
            let prevShaderVs = null;
            let prevShaderFs = null;
            if (this.pipeline) {
                log.log(1, `Model ${this.id}: Recreating pipeline because "${this._pipelineNeedsUpdate}".`)();
                prevShaderVs = this.pipeline.vs;
                prevShaderFs = this.pipeline.fs;
            }
            this._pipelineNeedsUpdate = false;
            const vs = this.shaderFactory.createShader({
                id: `${this.id}-vertex`,
                stage: 'vertex',
                source: this.source || this.vs,
                debug: this.props.debugShaders
            });
            let fs = null;
            if (this.source) {
                fs = vs;
            }
            else if (this.fs) {
                fs = this.shaderFactory.createShader({
                    id: `${this.id}-fragment`,
                    stage: 'fragment',
                    source: this.source || this.fs,
                    debug: this.props.debugShaders
                });
            }
            this.pipeline = this.pipelineFactory.createRenderPipeline({
                ...this.props,
                bufferLayout: this.bufferLayout,
                topology: this.topology,
                parameters: this.parameters,
                vs,
                fs
            });
            this._attributeInfos = getAttributeInfosFromLayouts(this.pipeline.shaderLayout, this.bufferLayout);
            if (prevShaderVs)
                this.shaderFactory.release(prevShaderVs);
            if (prevShaderFs)
                this.shaderFactory.release(prevShaderFs);
        }
        return this.pipeline;
    }

When that gets into this.pipelineFactory.createRenderPipeline(...), it has props including shaderAssembler which is indeed a reference to getShaderAssembler(device) with relevant hooks added... when it eventually gets in to new WEBGLRenderPipeline() and _linkShaders(), as noted before, the shaders have compilationStatus: "pending" and I'm not sure on what basis they'd be compiled by then. But maybe my problem is that I've spent more time stepping through code than actually reading the docs...

I notice that the _hookFunctions property is meant to be private... I wonder if there might be somewhere else to add these hooks rather than initializeState(), but that's a relatively minor concern - I don't think it'd stop anything working.

Anyway, I suppose I'll push what I have currently - not confident it's 100% correct.

@xinaesthete xinaesthete marked this pull request as draft June 19, 2024 10:19
@xinaesthete
Copy link
Author

xinaesthete commented Jun 19, 2024

OK... I think I've made relevant shader edits. Now I get

deck: drawing XRLayer({id: 'image-sub-layer-0,131,175,0-Background-Image-TiffPixelSource-#detail#'}) to screen: No value for binding channel0 in image-sub-layer-0,131,175,0-Background-Image-TiffPixelSource-#detail#-cached Error: No value for binding channel0 in image-sub-layer-0,131,175,0-Background-Image-TiffPixelSource-#detail#-cached
    at WEBGLRenderPipeline._applyBindings (webgl-render-pipeline.js:314:23)
    at WEBGLRenderPipeline.draw (webgl-render-pipeline.js:167:14)
    at _Model.draw (model.js:256:41)
    at XRLayer.draw (xr-layer.js:278:13)
    at layer.js:851:22
    at withGLParameters (with-parameters.js:17:16)
    at _WebGLDevice.withParametersWebGL (webgl-device.js:269:16)
    at XRLayer._drawLayer (layer.js:845:28)
    at DrawLayersPass._drawLayersInViewport (layers-pass.js:153:27)
    at DrawLayersPass._drawLayers (layers-pass.js:54:36)

I suspect we need to work on how the uniform buffers are arranged.

The bindings of the WebGLRenderPipeline that throws the error is looking for 'channel0' etc, but there is only key, 'pickingUniforms', associated with a WEBGLBuffer.

Putting a breakpoint on setBindings(), I see that the image layer is calling it with WEBGLBuffers for various modules, plus our plain js object 'uniforms':

Screenshot 2024-06-19 at 14 50 47

@xinaesthete
Copy link
Author

xinaesthete commented Jun 19, 2024

OK, I think we're getting close: I actually see some graphics getting drawn, albeit not very useful.

I think maybe the out fragColor just isn't bound to an actual framebuffer; if I put fragColor.r = 1.0; at the end of the fs it does nothing. more like the geometry indices / texCoords etc are dodgy - toggling channels changes colors! Exciting times!

@xinaesthete
Copy link
Author

It's starting to look like image data... something bad with some constants / parameters for setting sampler attribs or something I guess...

Screenshot 2024-06-19 at 16 26 18

@xinaesthete
Copy link
Author

xinaesthete commented Sep 30, 2024

ok @ilan-gold thanks for reviewing (good timing, I just got back from holiday), we're hopefully almost there. This is getting a bit big and noisy...

  1. For the [GL.PACK_ALIGNMENT] thing, maybe the best option is to revert that change for now, with the knowledge that there'll be a few such things to look at in the not-too-distant future.
  2. The test script still hangs on packages/layers test: ScaleBarLayer#Empty props. finally fixed this... 🤞
  3. pnpm i --prod command fails with exit code 1. Hopefully that won't take long to resolve.

That last point also seems improved by moving glsl-colormap in package/extensions/package.json into dependencies rather than devDependencies. I have no idea why that would have been a regression in this branch.

@ilan-gold
Copy link
Collaborator

For the [GL.PACK_ALIGNMENT] thing, maybe the best option is to revert that change for now, with the knowledge that there'll be a few such things to look at in the not-too-distant future.

Thanks, yea. I think the comment is fairly extensive, but Trevor finally discovered why that was necessary to add and it makes sense so I think we should leave it. Maybe luma.gl does it by default or something but best to be on the safe side rather than have images not render with impossible-to-parse error messages.

I also agree with Trevor on the branching issue - let's split of the avivator/zustand updates from this and put in a separate small PR.

I am not sure we need a dev branch...@manzt why do a dev branch rather than just splitting this one into the Avivator-related changes and the deck.gl changes?

@xinaesthete
Copy link
Author

Some slight extra noise with moving dependencies in Avivator to get vercel deployment working with this. Can revert again if you prefer.

Comment on lines 21 to 23
"zustand": "^3.4.1",
"vite": "^5.3.2",
"@vitejs/plugin-react": "^4.2.1"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmmm, I might remove TBH. But before that why is this necessary for vercel builds? @manzt might have something more graceful up his sleeve?

Copy link
Member

Choose a reason for hiding this comment

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

I'm alI'm also confused. Why did we get rid of devDeps for vercel? what about Vercel requires this?

@manzt
Copy link
Member

manzt commented Oct 1, 2024

I am not sure we need a dev branch...@manzt why do a dev branch rather than just splitting this one into the Avivator-related changes and the deck.gl changes?

I'm just far enough away that I wasn't sure how large the changes would be to either part. Right now the policy to merge into main is generally that we aren't putting the repo in a broken state. I would assume that changes to the core require changes to avivator, so merging in those will will have broken builds of avivator until the remaining stuff is merged.... which is what worries me and hence a separate dev branch were we can review smaller PRs (even have a roadmap if necessary) seemed fitting. That said, I'm not the one making the changes or giving the reviews and I trust your judgement on which direction to go.

@xinaesthete
Copy link
Author

Argh... just realised that I had actually introduced a regression recently by not re-introducing the code to import GL when I reverted the change to remove GL.PACK_ALIGNMENT stuff... so it had briefly been broken...

I'm not aware of other outstanding issues - the various review threads have got a bit complex to navigate at this point. I'm tempted to publish an npm package so I could start developing features against it in MDV... I'd rather not but unfortunately I'm not knowledgeable enough to figure out how to import it otherwise. If I do I'll make it very clear that it's purpose is as an experimental version of the original.

@ilan-gold
Copy link
Collaborator

ilan-gold commented Oct 4, 2024

'm tempted to publish an npm package so I could start developing features against it in MDV... I'd rather not but unfortunately I'm not knowledgeable enough to figure out how to import it otherwise

Is a local install an issue? Or install from a remote branch? I think either

npm i ../path_to_viv/viv

or

npm i git+https://github.com/xinaesthete/viv.git#depbump

@xinaesthete
Copy link
Author

npm i git+https://github.com/xinaesthete/viv.git#depbump

Thanks - I'll try. I'd like to have it not-just-locally ideally, and I think the above would need some kind of extra step for it to have the build available...

Probably mostly a skill issue, and something I want to be able to do more generally - so I don't really want to rush you guys too much, but when I last tried I didn't quite manage. Don't want to pollute this thread with this too much - maybe I should start a discussion elsewhere.

@ilan-gold
Copy link
Collaborator

Probably mostly a skill issue, and something I want to be able to do more generally - so I don't really want to rush you guys too much, but when I last tried I didn't quite manage. Don't want to pollute this thread with this too much - maybe I should start a discussion elsewhere

sounds good! i have success with this in the package.json as well. you might have to google for how to do that since the syntax could be different but i believe it is the same!

@ilan-gold
Copy link
Collaborator

Right now the policy to merge into main is generally that we aren't putting the repo in a broken state. I would assume that changes to the core require changes to avivator, so merging in those will will have broken builds of avivator until the remaining stuff is merged

I agree fully. I think this PR should not put the repo into a broken state though. I think the Avivator changes are split now, so we shouldn't be intentionally breaking things. I think we can just work off this branch for now, with installing it into various envs to test out.

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.

3 participants