setSource(source) {
        // fire callback during load (useful for a loader)
        if(!this._sourceLoaded) {
            // texture source loaded callback
            this.renderer.nextRender.add(() => this._onSourceLoadedCallback && this._onSourceLoadedCallback());
        }

        // get new source type based on source tag
        const sourceType = source.tagName.toUpperCase() === "IMG" ? "image" : source.tagName.toLowerCase();

        // somehow if the texture type changes from image to video or canvas, the cache won't work anymore
        if(sourceType === "video" || sourceType === "canvas") {
            this._useCache = false;
        }

        // check for cache
        if(this._useCache) {
            const cachedTexture = this.renderer.cache.getTextureFromSource(source);

            // if we have a cached texture, just copy it
            if (cachedTexture && cachedTexture.uuid !== this.uuid) {
                // force texture uploaded callback
                if (!this._uploaded) {
                    // GPU uploading callback
                    this.renderer.nextRender.add(() => this._onSourceUploadedCallback && this._onSourceUploadedCallback());

                    this._uploaded = true;
                }

                this.copy(cachedTexture);

                this.resize();

                return;
            }
        }

        if(this.sourceType === "empty" || this.sourceType !== sourceType) {
            if(sourceType === "video") {
                // a video should be updated by default
                this._willUpdate = false;
                this.shouldUpdate = true;
            }
            else if(sourceType === "canvas") {
                // a canvas could change each frame so we need to update it by default
                this._willUpdate = true;
                this.shouldUpdate = true;
            }
            else if(sourceType === "image") {
                this._willUpdate = false;
                this.shouldUpdate = false;
            }
            else {
                if(!this.renderer.production) {
                    throwWarning(this.type + ": this HTML tag could not be converted into a texture:", source.tagName);
                }

                return;
            }
        }

        // set new source
        this.source = source;
        this.sourceType = sourceType;

        this._size = {
            width: this.source.naturalWidth || this.source.width || this.source.videoWidth,
            height: this.source.naturalHeight || this.source.height || this.source.videoHeight,
        };

        // our source is loaded now
        this._sourceLoaded = true;

        // no need to set WebGL active texture unit here, we'll do it at run time for each texture
        // binding the texture is enough
        this.gl.bindTexture(this.gl.TEXTURE_2D, this._sampler.texture);

        this.resize();

        this._globalParameters.flipY = true;
        this._globalParameters.premultiplyAlpha = this._globalParameters.shouldPremultiplyAlpha;

        // upload our webgl texture only if it is an image
        // canvas and video textures will be updated anyway in the rendering loop
        // thanks to the shouldUpdate and _willUpdate flags
        if(this.sourceType === "image") {
            // generate mip maps if they have not been explicitly disabled
            this.parameters.generateMipmap = this.parameters.generateMipmap || this.parameters.generateMipmap === null;
            this.parameters._shouldUpdate = this.parameters.generateMipmap;
            this._state.generateMipmap = false;

            this._upload();
        }

        // update scene
        this.renderer.needRender();
    }
This uses our source as texture

     params:
     @source (images/video/canvas): either an image, a video or a canvas
*