diff --git a/src/ol/map.js b/src/ol/map.js index 6b1c51fb33..1e4996babe 100644 --- a/src/ol/map.js +++ b/src/ol/map.js @@ -598,22 +598,22 @@ ol.Map.prototype.forEachFeatureAtPixel = * filter function, only layers which are visible and for which this * function returns `true` will be tested for features. By default, all * visible layers will be tested. Feature overlays will always be tested. - * @param {U=} opt_this2 Value to use as `this` when executing `layerFilter`. + * @param {U=} opt_this Value to use as `this` when executing `layerFilter`. * @return {boolean} Is there a feature at the given pixel? * @template U * @api */ ol.Map.prototype.hasFeatureAtPixel = - function(pixel, opt_layerFilter, opt_this2) { + function(pixel, opt_layerFilter, opt_this) { if (goog.isNull(this.frameState_)) { return false; } var coordinate = this.getCoordinateFromPixel(pixel); var layerFilter = goog.isDef(opt_layerFilter) ? opt_layerFilter : goog.functions.TRUE; - var thisArg2 = goog.isDef(opt_this2) ? opt_this2 : null; + var thisArg = goog.isDef(opt_this) ? opt_this : null; return this.renderer_.hasFeatureAtPixel( - coordinate, this.frameState_, layerFilter, thisArg2); + coordinate, this.frameState_, layerFilter, thisArg); }; diff --git a/src/ol/render/webgl/webglreplay.js b/src/ol/render/webgl/webglreplay.js index 4f309d0fd7..9af1caa62d 100644 --- a/src/ol/render/webgl/webglreplay.js +++ b/src/ol/render/webgl/webglreplay.js @@ -669,11 +669,12 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context, // draw! var result; if (!goog.isDef(featureCallback)) { - this.drawReplay_(gl, context); + this.drawReplay_(gl, context, skippedFeaturesHash, + this.textures_, this.groupIndices_); } else { // draw feature by feature for the hit-detection - result = this.drawHitDetectionReplay_(gl, context, featureCallback, - oneByOne, opt_hitExtent); + result = this.drawHitDetectionReplay_(gl, context, skippedFeaturesHash, + featureCallback, oneByOne, opt_hitExtent); } // disable the vertex attrib arrays @@ -691,30 +692,125 @@ ol.render.webgl.ImageReplay.prototype.replay = function(context, * @private * @param {WebGLRenderingContext} gl gl. * @param {ol.webgl.Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. + * @param {Array.} textures Textures. + * @param {Array.} groupIndices Texture group indices. */ ol.render.webgl.ImageReplay.prototype.drawReplay_ = - function(gl, context) { - goog.asserts.assert(this.textures_.length === this.groupIndices_.length); + function(gl, context, skippedFeaturesHash, textures, groupIndices) { + goog.asserts.assert(textures.length === groupIndices.length); var elementType = context.hasOESElementIndexUint ? goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; var elementSize = context.hasOESElementIndexUint ? 4 : 2; - var i, ii, start; - for (i = 0, ii = this.textures_.length, start = 0; i < ii; ++i) { - gl.bindTexture(goog.webgl.TEXTURE_2D, this.textures_[i]); - var end = this.groupIndices_[i]; - var numItems = end - start; - var offsetInBytes = start * elementSize; - gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes); - start = end; + if (!goog.object.isEmpty(skippedFeaturesHash)) { + this.drawReplaySkipping_( + gl, skippedFeaturesHash, textures, groupIndices, + elementType, elementSize); + } else { + var i, ii, start; + for (i = 0, ii = textures.length, start = 0; i < ii; ++i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]); + var end = groupIndices[i]; + this.drawElements_(gl, start, end, elementType, elementSize); + start = end; + } + } +}; + + +/** + * Draw the replay while paying attention to skipped features. + * + * This functions creates groups of features that can be drawn to together, + * so that the number of `drawElements` calls is minimized. + * + * For example given the following texture groups: + * + * Group 1: A B C + * Group 2: D [E] F G + * + * If feature E should be skipped, the following `drawElements` calls will be + * made: + * + * drawElements with feature A, B and C + * drawElements with feature D + * drawElements with feature F and G + * + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {Object} skippedFeaturesHash Ids of features to skip. + * @param {Array.} textures Textures. + * @param {Array.} groupIndices Texture group indices. + * @param {number} elementType Element type. + * @param {number} elementSize Element Size. + */ +ol.render.webgl.ImageReplay.prototype.drawReplaySkipping_ = + function(gl, skippedFeaturesHash, textures, groupIndices, + elementType, elementSize) { + var featureIndex = 0; + + var i, ii; + for (i = 0, ii = textures.length; i < ii; ++i) { + gl.bindTexture(goog.webgl.TEXTURE_2D, textures[i]); + var groupStart = (i > 0) ? groupIndices[i - 1] : 0; + var groupEnd = groupIndices[i]; + + var start = groupStart; + var end = groupStart; + while (featureIndex < this.startIndices_.length && + this.startIndices_[featureIndex] <= groupEnd) { + var feature = this.startIndicesFeature_[featureIndex]; + + var featureUid = goog.getUid(feature).toString(); + if (goog.isDef(skippedFeaturesHash[featureUid])) { + // feature should be skipped + if (start !== end) { + // draw the features so far + this.drawElements_(gl, start, end, elementType, elementSize); + } + // continue with the next feature + start = (featureIndex === this.startIndices_.length - 1) ? + groupEnd : this.startIndices_[featureIndex + 1]; + end = start; + } else { + // the feature is not skipped, augment the end index + end = (featureIndex === this.startIndices_.length - 1) ? + groupEnd : this.startIndices_[featureIndex + 1]; + } + featureIndex++; + } + + if (start !== end) { + // draw the remaining features (in case there was no skipped feature + // in this texture group, all features of a group are drawn together) + this.drawElements_(gl, start, end, elementType, elementSize); + } } }; +/** + * @private + * @param {WebGLRenderingContext} gl gl. + * @param {number} start Start index. + * @param {number} end End index. + * @param {number} elementType Element type. + * @param {number} elementSize Element Size. + */ +ol.render.webgl.ImageReplay.prototype.drawElements_ = function( + gl, start, end, elementType, elementSize) { + var numItems = end - start; + var offsetInBytes = start * elementSize; + gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes); +}; + + /** * @private * @param {WebGLRenderingContext} gl gl. * @param {ol.webgl.Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. * @param {function(ol.Feature): T|undefined} featureCallback Feature callback. * @param {boolean} oneByOne Draw features one-by-one for the hit-detecion. * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting @@ -723,21 +819,16 @@ ol.render.webgl.ImageReplay.prototype.drawReplay_ = * @template T */ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ = - function(gl, context, featureCallback, oneByOne, opt_hitExtent) { - goog.asserts.assert(this.hitDetectionTextures_.length === - this.hitDetectionGroupIndices_.length); - var elementType = context.hasOESElementIndexUint ? - goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; - var elementSize = context.hasOESElementIndexUint ? 4 : 2; - + function(gl, context, skippedFeaturesHash, featureCallback, oneByOne, + opt_hitExtent) { if (!oneByOne) { // draw all hit-detection features in "once" (by texture group) - return this.drawHitDetectionReplayAll_(gl, context, featureCallback, - elementType, elementSize); + return this.drawHitDetectionReplayAll_(gl, context, + skippedFeaturesHash, featureCallback); } else { // draw hit-detection features one by one - return this.drawHitDetectionReplayOneByOne_(gl, context, featureCallback, - elementType, elementSize, opt_hitExtent); + return this.drawHitDetectionReplayOneByOne_(gl, context, + skippedFeaturesHash, featureCallback, opt_hitExtent); } }; @@ -746,24 +837,16 @@ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplay_ = * @private * @param {WebGLRenderingContext} gl gl. * @param {ol.webgl.Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. * @param {function(ol.Feature): T|undefined} featureCallback Feature callback. - * @param {number} elementType Element type. - * @param {number} elementSize Element size. * @return {T|undefined} Callback result. * @template T */ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayAll_ = - function(gl, context, featureCallback, elementType, elementSize) { - var i, ii, start; - for (i = 0, ii = this.hitDetectionTextures_.length, start = 0; i < ii; ++i) { - gl.bindTexture(goog.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]); - var end = this.hitDetectionGroupIndices_[i]; - var numItems = end - start; - var offsetInBytes = start * elementSize; - gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.drawElements(goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes); - start = end; - } + function(gl, context, skippedFeaturesHash, featureCallback) { + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + this.drawReplay_(gl, context, skippedFeaturesHash, + this.hitDetectionTextures_, this.hitDetectionGroupIndices_); var result = featureCallback(null); if (result) { @@ -778,20 +861,24 @@ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayAll_ = * @private * @param {WebGLRenderingContext} gl gl. * @param {ol.webgl.Context} context Context. + * @param {Object} skippedFeaturesHash Ids of features to skip. * @param {function(ol.Feature): T|undefined} featureCallback Feature callback. - * @param {number} elementType Element type. - * @param {number} elementSize Element size. * @param {ol.Extent=} opt_hitExtent Hit extent: Only features intersecting * this extent are checked. * @return {T|undefined} Callback result. * @template T */ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne_ = - function(gl, context, featureCallback, elementType, elementSize, - opt_hitExtent) { - var i, groupStart, numItems, start, end, feature; - var featureIndex = this.startIndices_.length - 1; + function(gl, context, skippedFeaturesHash, featureCallback, + opt_hitExtent) { + goog.asserts.assert(this.hitDetectionTextures_.length === + this.hitDetectionGroupIndices_.length); + var elementType = context.hasOESElementIndexUint ? + goog.webgl.UNSIGNED_INT : goog.webgl.UNSIGNED_SHORT; + var elementSize = context.hasOESElementIndexUint ? 4 : 2; + var i, groupStart, start, end, feature, featureUid; + var featureIndex = this.startIndices_.length - 1; for (i = this.hitDetectionTextures_.length - 1; i >= 0; --i) { gl.bindTexture(goog.webgl.TEXTURE_2D, this.hitDetectionTextures_[i]); groupStart = (i > 0) ? this.hitDetectionGroupIndices_[i - 1] : 0; @@ -801,15 +888,14 @@ ol.render.webgl.ImageReplay.prototype.drawHitDetectionReplayOneByOne_ = while (featureIndex >= 0 && this.startIndices_[featureIndex] >= groupStart) { start = this.startIndices_[featureIndex]; - numItems = end - start; feature = this.startIndicesFeature_[featureIndex]; + featureUid = goog.getUid(feature).toString(); - if (!goog.isDef(opt_hitExtent) || ol.extent.intersects( - opt_hitExtent, feature.getGeometry().getExtent())) { - var offsetInBytes = start * elementSize; + if (!goog.isDef(skippedFeaturesHash[featureUid]) && + (!goog.isDef(opt_hitExtent) || ol.extent.intersects( + opt_hitExtent, feature.getGeometry().getExtent()))) { gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.drawElements( - goog.webgl.TRIANGLES, numItems, elementType, offsetInBytes); + this.drawElements_(gl, start, end, elementType, elementSize); var result = featureCallback(feature); if (result) {