diff --git a/src/RenderWebGL.js b/src/RenderWebGL.js index 24b4696db..e4d2c1f26 100644 --- a/src/RenderWebGL.js +++ b/src/RenderWebGL.js @@ -132,6 +132,9 @@ class RenderWebGL extends EventEmitter { throw new Error('Could not get WebGL context: this browser or environment may not support WebGL.'); } + /** @type {RenderWebGL.UseGpuModes} */ + this._useGpuMode = RenderWebGL.UseGpuModes.Automatic; + /** @type {Drawable[]} */ this._allDrawables = []; @@ -243,6 +246,14 @@ class RenderWebGL extends EventEmitter { this._debugCanvas = canvas; } + /** + * Control the use of the GPU or CPU paths in `isTouchingColor`. + * @param {RenderWebGL.UseGpuModes} useGpuMode - automatically decide, force CPU, or force GPU. + */ + setUseGpuMode (useGpuMode) { + this._useGpuMode = useGpuMode; + } + /** * Set logical size of the stage in Scratch units. * @param {int} xLeft The left edge's x-coordinate. Scratch 2 uses -240. @@ -717,9 +728,16 @@ class RenderWebGL extends EventEmitter { const bounds = this._candidatesBounds(candidates); - // if there are just too many pixels to CPU render efficently, we - // need to let readPixels happen - if (bounds.width * bounds.height * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + const maxPixelsForCPU = this._getMaxPixelsForCPU(); + + const debugCanvasContext = this._debugCanvas && this._debugCanvas.getContext('2d'); + if (debugCanvasContext) { + this._debugCanvas.width = bounds.width; + this._debugCanvas.height = bounds.height; + } + + // if there are just too many pixels to CPU render efficiently, we need to let readPixels happen + if (bounds.width * bounds.height * (candidates.length + 1) >= maxPixelsForCPU) { this._isTouchingColorGpuStart(drawableID, candidates.map(({id}) => id).reverse(), bounds, color3b, mask3b); } @@ -728,29 +746,45 @@ class RenderWebGL extends EventEmitter { const color = __touchingColor; const hasMask = Boolean(mask3b); + // Scratch Space - +y is top for (let y = bounds.bottom; y <= bounds.top; y++) { - if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= __cpuTouchingColorPixelCount) { + if (bounds.width * (y - bounds.bottom) * (candidates.length + 1) >= maxPixelsForCPU) { return this._isTouchingColorGpuFin(bounds, color3b, y - bounds.bottom); } - // Scratch Space - +y is top for (let x = bounds.left; x <= bounds.right; x++) { point[1] = y; point[0] = x; - if ( - // if we use a mask, check our sample color - (hasMask ? - maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : - drawable.isTouching(point)) && - // and the target color is drawn at this pixel - colorMatches(RenderWebGL.sampleColor3b(point, candidates, color), color3b, 0) - ) { - return true; + // if we use a mask, check our sample color... + if (hasMask ? + maskMatches(Drawable.sampleColor4b(point, drawable, color), mask3b) : + drawable.isTouching(point)) { + RenderWebGL.sampleColor3b(point, candidates, color); + if (debugCanvasContext) { + debugCanvasContext.fillStyle = `rgb(${color[0]},${color[1]},${color[2]})`; + debugCanvasContext.fillRect(x - bounds.left, bounds.bottom - y, 1, 1); + } + // ...and the target color is drawn at this pixel + if (colorMatches(color, color3b, 0)) { + return true; + } } } } return false; } + _getMaxPixelsForCPU () { + switch (this._useGpuMode) { + case RenderWebGL.UseGpuModes.ForceCPU: + return Infinity; + case RenderWebGL.UseGpuModes.ForceGPU: + return 0; + case RenderWebGL.UseGpuModes.Automatic: + default: + return __cpuTouchingColorPixelCount; + } + } + _isTouchingColorGpuStart (drawableID, candidateIDs, bounds, color3b, mask3b) { this._doExitDrawRegion(); @@ -1770,4 +1804,25 @@ class RenderWebGL extends EventEmitter { // :3 RenderWebGL.prototype.canHazPixels = RenderWebGL.prototype.extractDrawable; +/** + * Values for setUseGPU() + * @enum {string} + */ +RenderWebGL.UseGpuModes = { + /** + * Heuristically decide whether to use the GPU path, the CPU path, or a dynamic mixture of the two. + */ + Automatic: 'Automatic', + + /** + * Always use the GPU path. + */ + ForceGPU: 'ForceGPU', + + /** + * Always use the CPU path. + */ + ForceCPU: 'ForceCPU' +}; + module.exports = RenderWebGL; diff --git a/src/playground/.eslintrc.js b/src/playground/.eslintrc.js index 0711afb03..0c483bea7 100644 --- a/src/playground/.eslintrc.js +++ b/src/playground/.eslintrc.js @@ -1,7 +1,9 @@ module.exports = { - root: true, extends: ['scratch'], env: { browser: true + }, + rules: { + 'no-console': 'off' } }; diff --git a/src/playground/getMousePosition.js b/src/playground/getMousePosition.js new file mode 100644 index 000000000..b8b654acc --- /dev/null +++ b/src/playground/getMousePosition.js @@ -0,0 +1,37 @@ +// Adapted from code by Simon Sarris: http://stackoverflow.com/a/10450761 +const getMousePos = function (event, element) { + const stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(element, null).paddingLeft, 10) || 0; + const stylePaddingTop = parseInt(document.defaultView.getComputedStyle(element, null).paddingTop, 10) || 0; + const styleBorderLeft = parseInt(document.defaultView.getComputedStyle(element, null).borderLeftWidth, 10) || 0; + const styleBorderTop = parseInt(document.defaultView.getComputedStyle(element, null).borderTopWidth, 10) || 0; + + // Some pages have fixed-position bars at the top or left of the page + // They will mess up mouse coordinates and this fixes that + const html = document.body.parentNode; + const htmlTop = html.offsetTop; + const htmlLeft = html.offsetLeft; + + // Compute the total offset. It's possible to cache this if you want + let offsetX = 0; + let offsetY = 0; + if (typeof element.offsetParent !== 'undefined') { + do { + offsetX += element.offsetLeft; + offsetY += element.offsetTop; + } while ((element = element.offsetParent)); + } + + // Add padding and border style widths to offset + // Also add the offsets in case there's a position:fixed bar + // This part is not strictly necessary, it depends on your styling + offsetX += stylePaddingLeft + styleBorderLeft + htmlLeft; + offsetY += stylePaddingTop + styleBorderTop + htmlTop; + + // We return a simple javascript object with x and y defined + return { + x: event.pageX - offsetX, + y: event.pageY - offsetY + }; +}; + +module.exports = getMousePos; diff --git a/src/playground/index.html b/src/playground/index.html index c58ecc94d..14409e458 100644 --- a/src/playground/index.html +++ b/src/playground/index.html @@ -12,7 +12,7 @@
-
- - + +
- - +