mpetroff / pannellum

Pannellum is a lightweight, free, and open source panorama viewer for the web.
https://pannellum.org/
MIT License
4.28k stars 726 forks source link

IE 11 & multires incompatibility with loadScene #194

Closed MmmDee closed 8 years ago

MmmDee commented 8 years ago

Pannellum (development) and IE 11 work well with equirectangular images and the combination also works "acceptably" to display multires (subject to IE's use of software rendering) when invoking the viewer initially with a multires image as the default/firstScene), but the combination does not work, or works very slowly (minutes-to-load) when loading multires image via loadScene.

Is there a difference in the manner pannellum loads multires during initial load vs loadScene?

Should we use Renderer.getCanvas/getExtension/getParameter/UNMASKED_RENDERER_WEBGL to make allowances for IE? Doing so seems to expose an unnecessary low-level detail.

Test computer is running Windows 10, hardware is an Intel Core 2 duo processor and an Nvidia GPU.

mpetroff commented 8 years ago

The only difference is that the WebGL context is reused between scenes. This was introduced in 38250c1d15d4ee3c792ae442336c53f637893f7f, so can you please test the revision prior to this change, 53c6cf85f7eb7c1accf07ab5fd2e23d6251c5151?

MmmDee commented 8 years ago

I'm not an expert at git, but this is what I did to revert back to the requested commit...

git clone <repository> p4
cd p4
git reset --hard <53c6cf85...>
cd utils/build
./build.sh

Ftp'd new files to new server directory, changed html file to point to newly uploaded version. Using Edge and IE 11, the continued "inertia" on loadScene (fixed in a later commit, 6cf5532) has returned, but the multires loadScene again works. This console message is displayed on page load:

WEBGL11135: getContext: Context attribute depth:false is not currently supported
pannellum.js (10,266)

Doing a loadScene of an equirectangular image has always worked, it's only the loadScene of a multires image that has issue.

mpetroff commented 8 years ago

As far as I can tell, this is a bug in Microsoft's software WebGL rendering. I was not able to reproduce it in a Windows 10 VM, although I did manage to crash IE 11. I'm not sure there's much I can do about it; the change to reusing the WebGL context is important enough not to roll back, since WebGL contexts can't be deleted, which caused issues after too many scenes were loaded back when a new context was created for each scene.

MmmDee commented 8 years ago

The issue for IE 11 surfaces on July 3, between 794f74f (issue not present) and f057406 (issue present). I confirmed with two different multires images.

This is the only diff between old and new: added: this.image.crossOrigin = 'anonymous';

In case this is specific to my server on GoDaddy, I emailed you at your contact email address with URLs demonstrating the issue in one commit vs no issue in the commit before that. Remember, the issue is only present in MS browsers (IE 11 and Edge). The easy fix might be to test for which web browser or WebGL renderer is involved and set or omit that CORS flag accordingly.

In the "you're smarter than me and probably already know this" category: This website, paragraph 2.6.4 seems to suggest that crossOrigin should be empty (not simply 'anonymous') to sidestep same server source where CORS is not requested (or available to be changed).

This MS thread (and those it references) suggests MS is/was having some difficulty with CORS as recently as Feb 2016. Edge does work better than IE 11 (the latter apparently will receive no further WebGL non-security updates). It's unfortunate that Edge/IE 11 report using the same WebGL renderer. It works okay in Edge, but not IE 11.

I suggest this "fix":

    if (!!document.documentMode) {               // If IE 11
           this.image.crossOrigin = '';
    } else
           this.image.crossOrigin = 'anonymous';
mpetroff commented 8 years ago

I haven't been able to reproduce the issue with IE 11 in a Windows 7 VM or a Windows 10 VM nor with Edge in a Windows 10 VM.

MmmDee commented 8 years ago

Well, it may be a combination of my hardware, ISP and/or server. I was leaning toward a server etiology because crossOrigin wouldn't seem to have any (local) hardware dependency. Just something to tuck away in the back of your mind. I'll patch my copy of the repository as I've noted above.

MmmDee commented 8 years ago

Interestingly, I had to comment-out line 986 in libpanellum.js, not simply set crossOrigin = '' in order for it to work... but that did indeed fix the issue I was having.

    if (!!document.documentMode) {               // If IE 11
           //this.image.crossOrigin = '';
    } else
           this.image.crossOrigin = 'anonymous';
mpetroff commented 8 years ago

"The empty string is also a valid keyword, and maps to the Anonymous state."

MmmDee commented 8 years ago

Thanks, I misinterpreted that statement the first time I read it. Unfortunately, while that patch fixes the issue I was having with a page I hosted on my server and accessed directly (similar to the testmultires5.html file I sent you the link for), it breaks the page I have hosted on another server (essentially with a similar page embedded), stating "Your browser does not have the necessary WebGL support to display this panorama."

For now, I'll have to give up on loadScene to reload a multires image on that other server. I still think it's somehow a CORS issue... which now I'll endeavor to learn more about :)

MmmDee commented 8 years ago

Sorry for the long post.

More info... I added .htaccess to my working directory and have this server response from a testing website (I don't yet know if that's correct for my needed CORS access):

HTTP/1.1 200 OK => Date => Sat, 16 Jul 2016 21:52:51 GMT Server => Apache/2.4.18 Access-Control-Allow-Origin => * Last-Modified => Sat, 16 Jul 2016 21:47:18 GMT ETag => "40001b6-a1f-537c7addd6514" Accept-Ranges => bytes Content-Length => 2591 Vary => Accept-Encoding,User-Agent Access-Control-Allow-Headers => origin, x-requested-with, content-type Access-Control-Allow-Methods => GET, POST, OPTIONS Connection => close Content-Type => text/html

I also noticed IE 11 task manager network access reporting continued I/O long after the page was loaded. In the figure that follows... First arrow is IE 11 "at rest, nothing loaded". Second arrow is "long after" test page loaded, note continued network I/O, memory footprint (varied 130-175MB) and CPU usage (varied). Third and 4th arrows compare Chrome and IE 11 sitting at the same test page (several minutes after the page is loaded and displayed).

image

This is the code snippet...

viewer = pannellum.viewer('panorama', {
            "hfov": 87.21,
            "yaw": -144.65,
            "compass": false,
            "autoLoad": true,
            "type": "multires",
            "multiRes": {
                "basePath": "http://mysite.net/muni/multires/foyer",
                "path": "/%l/%s%y_%x",
                "extension": "jpg",
                "tileResolution": 512,
                "maxLevel": 3,
                "cubeResolution": 1704
            }
   }
);
MmmDee commented 8 years ago

Image updated to show increased request count above and beyond the 126 (3 levels) total image files for this multires image.

This is the output of the IE 11 Network Performance Monitor (click on image for full size). The GET of http://mysite/multires/foyer/1/l0_0.jpg is repeated ad infinitum and accounts for IE's continued network I/O reported above... The infinite file retrieval is not limited to the one specified file. The result is "200/OK" in each case, yet a repeat GET is performed.

image

MmmDee commented 8 years ago

One of the IE 11 HTTP headers and responses (edited after redundant .htaccess files removed):

Request URL: http://mysite.net/muni/multires/foyer/1/l0_0.jpg Request Method: GET Status Code: 200 / OK

These are the headers in IE 11 when libpanellum, line 986 is deleted... presumably it's no longer trying CORS access (.htaccess is no longer being read). WIthout that line, IE 11 no longer exhibits the infinite GET:

Request URL: http://mysite.net/muni/multires/foyer/1/l0_0.jpg Request Method: GET Status Code: 304 / Not Modified

MmmDee commented 8 years ago

One of the Chrome HTTP headers and responses. Chrome does not repeat the file GET forever:

Request URL:http://mysite.net/muni/multires/foyer/1/l0_0.jpg Request Method:GET Status Code:304 Not Modified Remote Address:107.180.xx.xx:

mpetroff commented 8 years ago

It seems IE doesn't cache CORS-requested files: https://social.msdn.microsoft.com/Forums/ie/en-US/98bcdf4c-4a55-4243-a2ea-2064ed9d8568/xmlhttprequestcors-doesnt-use-local-cache?forum=iewebdevelopment https://connect.microsoft.com/IE/feedback/details/806034/xmlhttprequest-cors-doesnt-use-local-cache

MmmDee commented 8 years ago

Seems to be a MS issue dating back a couple years. Though, I'm not sure it's a true cache issue as the infinite GET is being done even when the image is "static"/not-moving/not-animated. I would think once the tile/texture is loaded, it would not need to be re-loaded (the only benefit of cache). My only indication it's a CORS issue is that removing the crossOrigin definition eliminates the issue. I don't see the potential for infinite looping in libpannellum, but something is repeating the GETs only for multires images. Update: see my next comment.

MmmDee commented 8 years ago

These functions keep getting "called" in libpannellum, lines 994-104 with the same src, so I suspect something in the logic is amiss (race condition with cacheTop at 1019-1022?):

// 994
        TextureImageLoader.prototype.loadTexture = function(src, texture, callback) {
            this.texture = texture;
            this.callback = callback;
            this.image.src = src;
        };

        function PendingTextureRequest(src, texture, callback) {
            this.src = src;
            this.texture = texture;
            this.callback = callback;
        };

// 1006
        function releaseTextureImageLoader(til) {
            if (pendingTextureRequests.length) {
                var req = pendingTextureRequests.shift();
                til.loadTexture(req.src, req.texture, req.callback);
            } else
                textureImageCache[cacheTop++] = til;
        }

// 1017
        return function(src, callback) {
            var texture = gl.createTexture();
            if (cacheTop)
                textureImageCache[--cacheTop].loadTexture(src, texture, callback);
            else
                pendingTextureRequests.push(new PendingTextureRequest(src, texture, callback));
            return texture;
        };

In function releaseTextureImageLoader, pendingTextureRequests.length is 13141 at the time I checked, seems quite high for a queue length (which in this case should never exceed 126). Admittedly, I don't fully understand the code, but it appears the code isn't recognizing when an image is loaded. Definitely the push's outnumber the shifts.

There are never more than cacheTop number of active GET's pending.

MmmDee commented 8 years ago

The same infinite GET issue occurs when using IE 11, the development pannellum (hosted on my server) and the multires example from the pannellum website. I wanted to test using a multires image from an https website...

mpetroff commented 8 years ago

It seems this issue is definitely related to network speed. I was finally able to reproduce it by simulating a 56kbps connection. When a tile took too long to load, the browser would try to load it multiple times at once. While this was a bug, it didn't cause a problem in most browsers, since once the first request finished loading, the other requests would immediately complete. However, since IE and Edge don't cache CORS requests, they would continue loading the other requests; since the requests were being created faster than the images were being loaded, the number of requests would keep increasing until something crashed. I fixed this bug, which should hopefully fix the issue.

MmmDee commented 8 years ago

On limited testing, it appears to have solved the issue... While my development systems are "wireless"(actually, in-house Ethernet over powerline), the rate is not as bad as 56kbps (I remember those days, and before that, 110 baud teletype, 300 baud acoustic couplers, 1200 baud Hayes modems). The issue was quite reproducible and I'm sure exacerbated by no-cache for CORS images..

Thanks again for the fix, you're the best.

MmmDee commented 8 years ago

Testing shows the issue resolved in both IE 11 and Edge (with appropriate .htaccess file). Interestingly, in IE 11 the mutlires/CORS images continue to be fetched from the server rather than cache BUT Edge (as of version 25.10586) now fetches multires/CORS images from cache :+1: