public:projects:nervland:notes:0006_landjs_using_producers

Using the Producers in the TerrainNode

  • In proland the actual usage of the TileSamplers happens in the drawTerrain method assigned to the terrain node: but from my perspective, there is no real interest to store the TileSamplers separately and then retrieve them in the draw call: we can just as well store them as elements of our TerrainNode (I think…).
  • So we add in the TerrainNode class:
        // We store the TileSamplers directly in this TerrainNode:
        protected samplers: TileSampler[] = [];
    
        // Add one TileSampler:
        public addTileSampler(sampler: TileSampler) {
            this.samplers.push(sampler);
        }
  • And we assing the samplers accordingly in our Test8 Scene setup: OK
  • ⇒ back from “vacation mode”: so now checking if everything is still working as needed on main dev server: OK everything seems to be in order.
  • Next we have to understand what's happening in the draw() call.
  • but first we need to implement a few missing methods:
    • TileSampler.getTerrain(): OK
    • TileSampler.checkUniforms(): OK
    • GPUTileStorage.getTileMap(): OK
    • GPUTileStorage.generateMipMap(): OK
    • FrameBuffer.drawQuad(): OK
  • Note: to implement the drawQuad method in FrameBuffer, we added a mechanism directly in the ResourceManager to create a default Quad object:
        public getDefaultQuad(): Mesh {
            if (this.quad == null) {
                let indexType = AttributeType.UNSIGNED_SHORT;
                let meshMode = MeshMode.TRIANGLE_STRIP;
                let meshUsage = MeshUsage.GPU_STATIC;
    
                // For each vertex, we pass the 4 position coordinates as float:
                let nverts = 4;
    
                // Size of 1 vertex in bytes:
                let vertSize = 4 * 4;
    
                // create the mesh:
                this.quad = new Mesh(vertSize, indexType, meshMode, meshUsage, nverts);
                this.quad.addAttributeType(0, 4, AttributeType.FLOAT, false);
                this.quad.addVertexElements([-1, -1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, 1, 1, 0, 1]);
            }
    
            return this.quad;
        }
  • Also implemented TileSampler.setTileMap(): OK
  • Note: One thing that we just changed that is going to break the system already is the addition of the setTexture2DArrayBuffer method in the FrameBuffer class ⇒ now we also need to setup the attachments correctly for Texture2DArray objects.
  • Ouughhtt… it seems we may have a serious problem here: when attaching a Texture2DArray with layer==-1, we are calling the opengl function glFramebufferTexture() in proland, but it seems there is nothing corresponding to this in WebGL2 ?
  • ⇒ Checking what the mipmapProg shader is doing in GPUTileStorage:
    • This is loaded with:
      this.mipmapProg = await rman.loadProgram('tileStorageMipMap');
    • Note: we are only loading that program if mipmaps are required, which doesn't seem to be the case for now ?
    • Arrgh… that shader is using a Geometry shader, and contains a gl_Layer instruction: we can probably not use this as is.
  • Next issue we have in with the non implemented TileSampler.setTile() function, let's fix that: OK
  • But now we also need the Tile.getData() method.
  • And now things are getting a bit more tricky: because this getData() method is going to check the result of a Tile generation task:
    TileStorage::Slot *TileCache::Tile::getData(bool check)
    {
        bool isDone = task->isDone();
        assert(isDone || !check);
        assert(getTId() == data->id || !check);
        return isDone ? data : NULL;
    }
  • hmmm… 🤔 how can we handle that properly ? Arrff… I think the easiest option here is to use a similar implementation with a dedicated “Task” class too.
  • So let's write the Task class: OK, now I have an initial Task class. Let's continue with the Tile class.
  • Actually, there is still something not quite clear at this level: when we check the getTId() value against the id in the TileStorageSlot: currently I have no idea where that id is written ⇒ to be clarified eventually.
  • ⇒ Surprisingly, we have now reached a point with the application will still compile and run properly 😅 So, let's make a commit and then keep messing even further lol.
  • What I find strange here, is that we call setTile() on the TileSamplers, but there is no location where we actually create a task to “produce that tile”, what am I missing in this process ?
  • ⇒ from the proland terrain1 example helloworld.xml file, we see that the TileCache object should be “linked” to the scheduler somehow: maybe this is what I'm missing then ? Checking… (actually, the point is, my Tile constructor is never called in fact.)
  • Or, in fact, something else interesting here is that in TileSampler.setTile() we will exist the method early if samplerU == null which is the case here since I haven't changed the terrain program yet.
  • Ohh, okay: there is definitely a lot missing in my TileCache implementation… for instance TileCache.getTile: this should probably be called somewhere…
  • One step further: it seems this tile creation process is all starting with a call to TileProducer.startCreateTile (which I have not implemented yet in my Elevation/normal producers).
  • Hmm, okay, so in the TerrainNode draw method, we are actually also calling TileProducer.hasTile and TileProducer.findTile at some locations ⇒ So this could be where we start the creation of the tiles ?
  • And finally, I just found that the “real magic” is probably what is happening in TileSampler.update() 😁! Where do we call that ?
  • ⇒ We have the UpdateTileSamplersTask for that.
  • Ahh! Eureka: now I have it in the helloworld.xml file:
        <sequence name="updateTerrainMethod">
            <updateTerrain name="this.terrain"/>
            <updateTileSamplers name="this.terrain"/>
        </sequence>
* hmmmm... 🥴 OK, so... I'm not really convinced anymore I can really go somewhere with this implementation of the project: in Javascript we don't have any real/proper support for multithreading: so, whenever I want to update an object on a different thread, I'm in serious trouble. And I'm just realizing that the "TaskGraph" system in use in Proland is doing precisely that all over the place 😳 So how am I doing to cope with that ? Sounds too complex for too limited results.
  • And now… I'm starting to think I should give Rust and wgpu a try instead 😭 That is so absolutely crazy and will take me so much more time to rebuild everything again from scratches… Sometimes I really wish I could simply just die instead lol [That would simplify my life a lot! 🤣]
  • ⇒ Now the nice thing is, when building an app in Rust, it seems it should then be possible to compile it to web assembly (mostly?) and then run it in a webbrowser… ? I'm not quite sure about all this. But just using plain Javascript/Typescript doesn't seem to be the way to go anyway.
  • ⇒ So let's give Rust a try now!
  • public/projects/nervland/notes/0006_landjs_using_producers.txt
  • Last modified: 2022/02/20 14:52
  • by 127.0.0.1