diff --git a/src/ol/render/Event.js b/src/ol/render/Event.js index dcfe5027e9..7f20aac9a2 100644 --- a/src/ol/render/Event.js +++ b/src/ol/render/Event.js @@ -17,7 +17,7 @@ class RenderEvent extends Event { /** * Transform from CSS pixels (relative to the top-left corner of the map viewport) - * to rendered pixels on this event's `context`. + * to rendered pixels on this event's `context`. Only available when a Canvas renderer is used, null otherwise. * @type {import("../transform.js").Transform|undefined} * @api */ diff --git a/src/ol/renderer/webgl/Layer.js b/src/ol/renderer/webgl/Layer.js index 9f3f008201..681b5be65d 100644 --- a/src/ol/renderer/webgl/Layer.js +++ b/src/ol/renderer/webgl/Layer.js @@ -2,6 +2,8 @@ * @module ol/renderer/webgl/Layer */ import LayerRenderer from '../Layer.js'; +import RenderEvent from '../../render/Event.js'; +import RenderEventType from '../../render/EventType.js'; import WebGLHelper from '../../webgl/Helper.js'; /** @@ -81,6 +83,36 @@ class WebGLLayerRenderer extends LayerRenderer { getShaderCompileErrors() { return this.helper.getShaderCompileErrors(); } + + /** + * @param {import("../../render/EventType.js").default} type Event type. + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @private + */ + dispatchRenderEvent_(type, frameState) { + const layer = this.getLayer(); + if (layer.hasListener(type)) { + // RenderEvent does not get a context or an inversePixelTransform, because WebGL allows much less direct editing than Canvas2d does. + const event = new RenderEvent(type, null, frameState, null); + layer.dispatchEvent(event); + } + } + + /** + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @protected + */ + preRender(frameState) { + this.dispatchRenderEvent_(RenderEventType.PRERENDER, frameState); + } + + /** + * @param {import("../../PluggableMap.js").FrameState} frameState Frame state. + * @protected + */ + postRender(frameState) { + this.dispatchRenderEvent_(RenderEventType.POSTRENDER, frameState); + } } const tmpArray_ = []; diff --git a/src/ol/renderer/webgl/PointsLayer.js b/src/ol/renderer/webgl/PointsLayer.js index a756fbb02e..7a16d585d6 100644 --- a/src/ol/renderer/webgl/PointsLayer.js +++ b/src/ol/renderer/webgl/PointsLayer.js @@ -409,6 +409,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { * @return {HTMLElement} The rendered element. */ renderFrame(frameState) { + this.preRender(frameState); + const renderCount = this.indicesBuffer_.getSize(); this.helper.drawElements(0, renderCount); this.helper.finalizeDraw(frameState); @@ -425,6 +427,8 @@ class WebGLPointsLayerRenderer extends WebGLLayerRenderer { this.hitRenderTarget_.clearCachedData(); } + this.postRender(frameState); + return canvas; } diff --git a/test/spec/ol/renderer/webgl/pointslayer.test.js b/test/spec/ol/renderer/webgl/pointslayer.test.js index c6299098ea..b44d60cff6 100644 --- a/test/spec/ol/renderer/webgl/pointslayer.test.js +++ b/test/spec/ol/renderer/webgl/pointslayer.test.js @@ -1,8 +1,10 @@ import Feature from '../../../../../src/ol/Feature.js'; +import GeoJSON from '../../../../../src/ol/format/GeoJSON.js'; import Point from '../../../../../src/ol/geom/Point.js'; import VectorLayer from '../../../../../src/ol/layer/Vector.js'; import VectorSource from '../../../../../src/ol/source/Vector.js'; import ViewHint from '../../../../../src/ol/ViewHint.js'; +import WebGLPointsLayer from '../../../../../src/ol/layer/WebGLPoints.js'; import WebGLPointsLayerRenderer from '../../../../../src/ol/renderer/webgl/PointsLayer.js'; import {WebGLWorkerMessageType} from '../../../../../src/ol/renderer/webgl/Layer.js'; import { @@ -592,4 +594,86 @@ describe('ol.renderer.webgl.PointsLayer', function () { ); }); }); + + describe('fires events', () => { + let layer, source, renderer, frameState; + + beforeEach(function () { + source = new VectorSource({ + features: new GeoJSON().readFeatures({ + 'type': 'FeatureCollection', + 'features': [ + { + 'type': 'Feature', + 'properties': {}, + 'geometry': { + 'type': 'Point', + 'coordinates': [13, 52], + }, + }, + ], + }), + }); + + layer = new WebGLPointsLayer({ + source, + style: { + symbol: { + symbolType: 'square', + }, + }, + }); + + renderer = new WebGLPointsLayerRenderer(layer, { + vertexShader: simpleVertexShader, + fragmentShader: simpleFragmentShader, + }); + + frameState = { + viewHints: [], + viewState: { + projection: getProjection('EPSG:4326'), + resolution: 0.010986328125, + rotation: 0, + center: [15, 52], + zoom: 7, + }, + extent: [ + 11.1932373046875, + 46.429931640625, + 18.8067626953125, + 57.570068359375, + ], + size: [693, 1014], + layerIndex: 0, + layerStatesArray: [ + { + layer: layer, + opacity: 1, + visible: true, + zIndex: 0, + }, + ], + }; + }); + + it('fires prerender and postrender events', function (done) { + let prerenderNotified = false; + let postrenderNotified = false; + + layer.once('prerender', (evt) => { + prerenderNotified = true; + }); + + layer.once('postrender', (evt) => { + postrenderNotified = true; + expect(prerenderNotified).to.be(true); + expect(postrenderNotified).to.be(true); + done(); + }); + + renderer.prepareFrame(frameState); + renderer.renderFrame(frameState); + }); + }); });