GithubHelp home page GithubHelp logo

Comments (6)

greggman avatar greggman commented on May 27, 2024 1

The reason it probably failed before is with the gl.activeTexture at the bottom the code was doing this

  • first iteration
    • bind texture to texture unit 0 (because active texture unit defaults to 0)
    • ...set params...upload data..
    • bind texture to texture unit 0
  • second iteration
    • bind texture to texture unit 0 (it has not been changed since the last iteration)
    • ...set params...upload data..
    • bind texture to texture unit 1

At this point the second texture is bound to both units 0 and 1

unit 0 = 2nd texture
unit 1 = 2nd texture

If we had a 3rd iteration

  • third iteration
    • bind texture to texture unit 1 (it has not been changed since the last iteration)
    • ...set params...upload data..
    • bind texture to texture unit 2

now the state is this

unit 0 = 2nd texture
unit 1 = 3nd texture
unit 2 = 3rd texture

Once you moved the gl.activeTexture to the top that fixed things so after 3 iterations you'd have this

unit 0 = 1st texture
unit 1 = 2nd texture
unit 2 = 3rd texture

Comment on the code:

You don't need to make strings for GL constants. There's nothing special about the constants in WebGL. These are all the same

gl.activeTexture(gl[`TEXTURE${index}`]);
gl.activeTexture(gl.TEXUTRE0 + index);
gl.activeTexture(33984 + index);

Of course maybe you were making a string because you wanted to print it somewhere else. That's fine. Just pointing out you don't need to use the actual properties on the WebGLRenderingContext (gl). They're just numbers, they don't change.

As for deleting you should delete WebGL objects yourself. Yes, they will eventually get garbage collected but the problem is JavaScript doesn't know anything about the memory used for textures (or other WebGL objects). As far as it's concerned a texture is just some tiny tracking object to some opaque thing. JavaScript doesn't know how much VRAM there is, nor does it know that freeing these opaque texture tracking objects will free up VRAM when VRAM is full.

So, free (delete) webgl objects when you no longer need them.

I'd say the majority of WebGL apps (which are mostly visualizations like the ones here) never free anything because they just load the stuff they need and use all of it until the page is closed. For apps that do more though, like a photo editor, they would need to do more and delete things themselves.

from webgl-fundamentals.

greggman avatar greggman commented on May 27, 2024

I'm sorry you found it confusing. There's no need to specify an active texture unit at the beginning. Creating textures and putting data in them and setting their parameters is entirely unrelated to using them at render time.

There are only 8 to 32 texture units (depending on your GPU). They are how you tell a shader which textures to use. Separately you can have 1000s of textures, way more than active texture units.

Before rendering something (before calling gl.drawArrays or gl.drawElements) you need set up the texture units to point to the textures used used for that draw call.

from webgl-fundamentals.

greggman avatar greggman commented on May 27, 2024

One more example based on my comment above. Imagine we have code to make 8 textures

function makeTexture(gl, color) {
  const tex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, tex);
  gl.texImage2D(gl.TEXUTRE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(color));
  return tex;
}

const textures = {
   red:     makeTexture(gl, [255,   0,   0, 255]),
   yellow:  makeTexture(gl, [255, 255,   0, 255]),
   green:   makeTexture(gl, [  0, 255,   0, 255]),
   cyan:    makeTexture(gl, [  0, 255, 255, 255]),
   blue:    makeTexture(gl, [  0,   0, 255, 255]),
   magenta: makeTexture(gl, [255,   0, 255, 255]),
   black:   makeTexture(gl, [  0,   0,   0, 255]),
   white:   makeTexture(gl, [255, 255, 255, 255]),
};

Then at render time we can any texture on any texture unit. We just need to tell our shader which texture units we bound our textures to. Just to be random let's use texture units 3 and 7

// --- [draw something]---
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, textures.blue);       // put blue on unit 3
gl.activeTexture(gl.TEXTURE7);
gl.bindTexture(gl.TEXTURE_2D, textures.yellow);     // put yellow on unit 7

gl.uniform1i(someUniformSamplerLocation, 3);        // tell the shader to use unit 3 for this uniform sampler
gl.uniform1i(someOtherUniformSamplerLocation, 7);   // tell the shader to use unit 7 for this uniform sampler

gl.drawArrays(....);

// --- [draw something else]---
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, textures.cyan);       // put cyan on unit 3
gl.activeTexture(gl.TEXTURE7);
gl.bindTexture(gl.TEXTURE_2D, textures.magenta);    // put magenta on unit 7

gl.uniform1i(someUniformSamplerLocation, 3);        // tell the shader to use unit 3 for this uniform sampler
gl.uniform1i(someOtherUniformSamplerLocation, 7);   // tell the shader to use unit 7 for this uniform sampler

gl.drawArrays(....);

// Use different texture units just for the heck of it
gl.activeTexture(gl.TEXTURE6);
gl.bindTexture(gl.TEXTURE_2D, textures.white);      // put white on unit 6
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, textures.red);        // put red on unit 2

gl.uniform1i(someUniformSamplerLocation, 6);        // tell the shader to use unit 6 for this uniform sampler
gl.uniform1i(someOtherUniformSamplerLocation, 2);   // tell the shader to use unit 2 for this uniform sampler

gl.drawArrays(....);

you might find this helpful

from webgl-fundamentals.

Aiosa avatar Aiosa commented on May 27, 2024

Well, this is something I was pointing out: I might be wrong but following the javascript implementation example, calling texImage2D will upload the data to TEXTURE0 so the for loop will populate with data only the first texture unit.

I do understand that bindTexture should specify to which texture am I uploading the data, yet until I started specifying the actual texture unit before calling texImage2D it did not work.

The javascript implementation uses

var activeTexture = function(unit) {

function to set up active unit and

var texImage2D = function(target, ... args ...) {

uses the location of activeTexture as where to load the data

So either of these examples (the code you showed above and the demo javascript OpenGL api implementation) in the tutorial should be wrong...

PS: you don't need to provide any example codes - I am already kind of advanced OpenGL user - I only struggle with the basics API such as order of calls and arguments, since we usually use frameworks in C++ that automate this painful coding, yet one project in WebGL has none, so I had to re-learn this :) thanks anyway

from webgl-fundamentals.

greggman avatar greggman commented on May 27, 2024

yet until I started specifying the actual texture unit before calling texImage2D it did not work.

If you post your code we can look over it and figure out what the issue is

So either of these examples (the code you showed above and the demo javascript OpenGL api implementation) in the tutorial should be wrong...

The samples are not wrong

"Texture Units" are not "Textures". Texture Units are just a global array of references to textures. When setting up a texture which texture unit is active is mostly irrelevant. What's relevant is what they are when you call gl.drawArrays or gl.drawElements

In the code, after the first iteration of the loading loop

  // create 2 textures
  var textures = [];
  for (var ii = 0; ii < 2; ++ii) {
    var texture = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
 
    // Set the parameters so we can render any size image.
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
 
    // Upload the image into the texture.
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, images[ii]);
 
    // add the texture to the array of textures.
    textures.push(texture);
  }

the state will be like this

Screen Shot 2021-11-26 at 22 25 53

After the second iteration of the loading loop the state will be like this

Screen Shot 2021-11-26 at 22 26 13

After the setup in the rendering part of the code,

  // set which texture units to render with.
  gl.uniform1i(u_image0Location, 0);  // texture unit 0
  gl.uniform1i(u_image1Location, 1);  // texture unit 1

  // Set each texture unit to use a particular texture.
  gl.activeTexture(gl.TEXTURE0);
  gl.bindTexture(gl.TEXTURE_2D, textures[0]);
  gl.activeTexture(gl.TEXTURE1);
  gl.bindTexture(gl.TEXTURE_2D, textures[1]);

just before calling gl.drawArrays the state will be like this

Screen Shot 2021-11-26 at 22 27 13

I am already kind of advanced OpenGL user

Me too 😉 I wrote the WebGL implementation in both Chrome and WebKit, I wrote the GPU infrastructure in Chrome including an entire OpenGL ES driver, and I wrote the majority of the WebGL1 Conformance Tests 😉

from webgl-fundamentals.

Aiosa avatar Aiosa commented on May 27, 2024

Ah, I see. I did not read the whole thing and it re-uses the spot for different pointers.

 let _this = this,
 index = 0;
 const NUM_IMAGES = Math.round(image.height / tileBounds.height);
 //images are sent below each other, it can be passed at once into IMAGE_ARRAY, this is inside WebGL 1.0 strategy fallback
 this.canvas.width = image.width;
 this.canvas.height = image.height;
 this.canvasReader.drawImage(image, 0, 0);

 //the logic of not running out of tex units is somewhere else
 for (let key in visualisation.shaders) {
         let layer = visualisation.shaders[key];
         this._units.push(gl.createTexture());
                  
         let bindConst = `TEXTURE${index}`;
         gl.activeTexture(gl[bindConst]);  //started working only after calling it now
         let location = gl.getUniformLocation(program, `data_${layer.order}`);
         gl.uniform1i(location, index); 

         gl.bindTexture(gl.TEXTURE_2D, this._units[index]);
         //some params
         gl.texParameteri(...);

         let read = this.canvasReader.getImageData(0, layer.order*tileBounds.height, tileBounds.width, tileBounds.height);
         let pixels = new Uint8Array(read.data.buffer);
         gl.texImage2D(gl.TEXTURE_2D,
                        0,
                        gl.RGBA,
                        tileBounds.width,
                        tileBounds.height,
                        0,
                        gl.RGBA,
                        gl.UNSIGNED_BYTE,
                        pixels);
         //previously, the code had been here (followng the example):
         // gl.activeTexture(gl[bindConst]);
         // gl.bindTexture(gl.TEXTURE_2D, this._units[index]);

         index++;
}

So I do get what changed - I am reusing multiple spaces in the pointer array to load my data into textures and since I immediately use it actually saves me some API calls. However, why it was not working before leaves me clueless.

Offtopic: I was also wondering what about memory management: when do you DELETE textures? Almost nowhere is an example of how (or rather: when) to clean up after yourself. Is it ok to leave it just like that and all will be freed when the GC collects the canvas?

Thank you.

from webgl-fundamentals.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.