From e3024deb4661d1d236bf15ef16e1d39dc9223e98 Mon Sep 17 00:00:00 2001 From: Aiosa Date: Tue, 26 Sep 2023 15:31:43 +0200 Subject: [PATCH] Modular webgl2 drawer: fix small bugs. Add drawer IDs to demo page urls to allow refreshing/direct running --- src/webgl/drawer.js | 51 +++++++++++++++------- src/webgl/renderer.js | 8 ++-- src/webgl/webGLContext.js | 18 +++----- test/demo/basic.html | 13 +++++- test/demo/drawercomparison.html | 4 +- test/demo/drawercomparison.js | 72 ++++++++++++++++++++++++-------- test/demo/drawerperformance.html | 4 +- test/demo/drawerperformance.js | 50 ++++++++++++++++++---- 8 files changed, 156 insertions(+), 64 deletions(-) diff --git a/src/webgl/drawer.js b/src/webgl/drawer.js index 3b20f8ea..cc461ed8 100644 --- a/src/webgl/drawer.js +++ b/src/webgl/drawer.js @@ -114,6 +114,30 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase { engine.init(size.x, size.y); this.viewer.addHandler("resize", this._resizeRenderer.bind(this)); this.renderer = engine; + this.renderer.setDataBlendingEnabled(true); + + // const gl = this.renderer.gl; + // this._renderToTexture = gl.createTexture(); + // gl.activeTexture(gl.TEXTURE0); + // gl.bindTexture(gl.TEXTURE_2D, this._renderToTexture); + // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, size.x, size.y, 0, gl.RGBA, gl.UNSIGNED_BYTE, null); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + // gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + // + // // set up the framebuffer for render-to-texture + // this._glFrameBuffer = gl.createFramebuffer(); + // gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer); + // gl.framebufferTexture2D( + // gl.FRAMEBUFFER, + // gl.COLOR_ATTACHMENT0, // attach texture as COLOR_ATTACHMENT0 + // gl.TEXTURE_2D, // attach a 2D texture + // this._renderToTexture, // the texture to attach + // 0 + // ); + // gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer); + // gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._renderToTexture, 0); + return engine.canvas; } @@ -136,23 +160,19 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase { let rotMatrix = $.Mat3.makeRotation(-viewport.rotation); let viewMatrix = scaleMatrix.multiply(rotMatrix).multiply(posMatrix); - this.renderer.clear(); - this.renderer.setDataBlendingEnabled(false); + // const gl = this.renderer.gl; + // gl.bindFramebuffer(gl.FRAMEBUFFER, this._glFrameBuffer); + // clear the buffer to draw a new image + // gl.clear(gl.COLOR_BUFFER_BIT); //iterate over tiled images and draw each one using a two-pass rendering pipeline if needed - - let drawn = 0; - for (const tiledImage of tiledImages) { + console.log("START TILED IMAGE"); let tilesToDraw = tiledImage.getTilesToDraw(); - if (tilesToDraw.length === 0) { - break; - } - - if (drawn === 1) { - this.renderer.setDataBlendingEnabled(true); - } + // if (tilesToDraw.length === 0) { + // break; + // } let overallMatrix = viewMatrix; let imageRotation = tiledImage.getRotation(true); @@ -168,12 +188,15 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase { overallMatrix = viewMatrix.multiply(localMatrix); } + //todo better access to the rendering context const shader = this.renderer.specification(0).shaders.renderShader._renderContext; - // iterate over tiles and add data for each one to the buffers for (let tileIndex = 0; tileIndex < tilesToDraw.length; tileIndex++){ const tile = tilesToDraw[tileIndex].tile; + + console.log("TILE " + tile.level + "-" + tile.x + "_" + tile.y); + const matrix = this._getTileMatrix(tile, tiledImage, overallMatrix); shader.opacity.set(tile.opacity * tiledImage.opacity); @@ -205,8 +228,6 @@ $.WebGL = class WebGL extends OpenSeadragon.DrawerBase { tiles: tilesToDraw.map(info => info.tile), }); } - - drawn++; } } diff --git a/src/webgl/renderer.js b/src/webgl/renderer.js index 33654dc3..7be24859 100644 --- a/src/webgl/renderer.js +++ b/src/webgl/renderer.js @@ -144,7 +144,7 @@ $.WebGLModule = class extends $.EventSource { const options = { wrap: readGlProp("wrap", "MIRRORED_REPEAT"), magFilter: readGlProp("magFilter", "LINEAR"), - minFilter: readGlProp("minFilter", "NEAREST"), + minFilter: readGlProp("minFilter", "LINEAR"), dataLoader: contextOpts.dataLoader || "TEXTURE_2D" }; this.webglContext = new Context(this, glContext, options); @@ -592,9 +592,11 @@ $.WebGLModule = class extends $.EventSource { setDataBlendingEnabled(enabled) { if (enabled) { + // this.gl.enable(this.gl.BLEND); + // this.gl.blendEquation(this.gl.FUNC_ADD); + // this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE); this.gl.enable(this.gl.BLEND); - this.gl.blendEquation(this.gl.FUNC_ADD); - this.gl.blendFuncSeparate(this.gl.SRC_ALPHA, this.gl.ONE_MINUS_SRC_ALPHA, this.gl.ONE, this.gl.ONE); + this.gl.blendFunc(this.gl.ONE, this.gl.ONE_MINUS_SRC_ALPHA); } else { this.gl.disable(this.gl.BLEND); } diff --git a/src/webgl/webGLContext.js b/src/webgl/webGLContext.js index 72e3e5ad..8056836c 100644 --- a/src/webgl/webGLContext.js +++ b/src/webgl/webGLContext.js @@ -185,7 +185,7 @@ $.WebGLModule.WebGL20 = class extends $.WebGLModule.WebGLImplementation { } static create(canvas) { - return canvas.getContext('webgl2', { premultipliedAlpha: false, alpha: true }); + return canvas.getContext('webgl2', { premultipliedAlpha: true, alpha: true }); } //todo try to implement on the global scope version-independntly @@ -263,6 +263,7 @@ vec4 lid_${layer._index}_xo() { return `#version 300 es precision mediump float; precision mediump sampler2DArray; +precision mediump sampler2D; ${this.texture.declare(shaderDataIndexToGlobalDataIndex)} uniform float pixel_size_in_fragments; @@ -283,17 +284,7 @@ void show(vec4 color) { vec4 fg = _last_rendered_color; _last_rendered_color = color; vec4 pre_fg = vec4(fg.rgb * fg.a, fg.a); - final_color = pre_fg + final_color * (1.0-fg.a); -} - -void finalize() { - show(vec4(.0)); - - if (close(final_color.a, 0.0)) { - final_color = vec4(0.); - } else { - final_color = vec4(final_color.rgb/final_color.a, final_color.a); - } + final_color = pre_fg + final_color; } vec4 blend_equation(in vec4 foreground, in vec4 background) { @@ -317,7 +308,8 @@ ${definition} void main() { ${execution} - finalize(); + //blend last level + show(vec4(.0)); }`; } diff --git a/test/demo/basic.html b/test/demo/basic.html index 6a677420..5f4262ab 100644 --- a/test/demo/basic.html +++ b/test/demo/basic.html @@ -20,12 +20,23 @@
diff --git a/test/demo/drawercomparison.html b/test/demo/drawercomparison.html index 3e007419..d40c8ef8 100644 --- a/test/demo/drawercomparison.html +++ b/test/demo/drawercomparison.html @@ -65,12 +65,12 @@

Compare behavior of Context2d and WebGL drawers

-

Context2d drawer (default in OSD <= 4.1.0)

+

Loading...

-

New WebGL drawer

+

Loading...

diff --git a/test/demo/drawercomparison.js b/test/demo/drawercomparison.js index 59bdd11b..17d178d5 100644 --- a/test/demo/drawercomparison.js +++ b/test/demo/drawercomparison.js @@ -13,6 +13,19 @@ const labels = { bblue: 'Blue B', duomo: 'Duomo', } +const drawers = { + canvas: "Context2d drawer (default in OSD <= 4.1.0)", + webgl: "New WebGL drawer", + universal_webgl: "New WebGL (Modular)" +} + +//Support drawer type from the url +const url = new URL(window.location.href); +const drawer1 = url.searchParams.get("left") || 'canvas'; +const drawer2 = url.searchParams.get("right") || 'webgl'; + +$("#title-w1").html(drawers[drawer1]); +$("#title-w2").html(drawers[drawer2]); //Double viewer setup for comparison - CanvasDrawer and WebGLDrawer // viewer1: canvas drawer @@ -25,7 +38,7 @@ let viewer1 = window.viewer1 = OpenSeadragon({ crossOriginPolicy: 'Anonymous', ajaxWithCredentials: false, // maxImageCacheCount: 30, - drawer:'canvas', + drawer:drawer1, blendTime:0 }); @@ -39,25 +52,23 @@ let viewer2 = window.viewer2 = OpenSeadragon({ crossOriginPolicy: 'Anonymous', ajaxWithCredentials: false, // maxImageCacheCount: 30, - drawer:'webgl', + drawer:drawer2, blendTime:0, }); -// viewer3: html drawer -var viewer3 = window.viewer3 = OpenSeadragon({ - id: "htmldrawer", - drawer:'html', - blendTime:2, - prefixUrl: "../../build/openseadragon/images/", - minZoomImageRatio:0.01, - customDrawer: OpenSeadragon.HTMLDrawer, - tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']], - sequenceMode: true, - crossOriginPolicy: 'Anonymous', - ajaxWithCredentials: false -}); - - +// // viewer3: html drawer, unused +// var viewer3 = window.viewer3 = OpenSeadragon({ +// id: "htmldrawer", +// drawer:'html', +// blendTime:2, +// prefixUrl: "../../build/openseadragon/images/", +// minZoomImageRatio:0.01, +// customDrawer: OpenSeadragon.HTMLDrawer, +// tileSources: [sources['leaves'], sources['rainbow'], sources['duomo']], +// sequenceMode: true, +// crossOriginPolicy: 'Anonymous', +// ajaxWithCredentials: false +// }); // Sync navigation of viewer1 and viewer 2 @@ -126,6 +137,8 @@ Object.keys(sources).forEach((key, index)=>{ } }) +$('#image-picker').append(makeComparisonSwitcher()); + $('#image-picker input.toggle').on('change',function(){ let data = $(this).data(); if(this.checked){ @@ -288,6 +301,30 @@ function addTileSource(viewer, image, checkbox){ } } +function getAvailableDrawerSelect(name, selectedDrawer) { + return ` +`; +} + +function makeComparisonSwitcher() { + const left = getAvailableDrawerSelect("left", drawer1), + right = getAvailableDrawerSelect("right", drawer2); + return ` +
+ Note: you can run the comparison with desired drawers like this: drawercomparison.html?left=[type]&right=[type] +
+ ${left} + ${right} + +
+
`; +} + function makeImagePickerElement(key, label){ return $(`
@@ -306,7 +343,6 @@ function makeImagePickerElement(key, label){
- `.replaceAll('data-image=""', `data-image="${key}"`).replace('__title__', label)); } diff --git a/test/demo/drawerperformance.html b/test/demo/drawerperformance.html index 70715641..17e70637 100644 --- a/test/demo/drawerperformance.html +++ b/test/demo/drawerperformance.html @@ -27,9 +27,7 @@
diff --git a/test/demo/drawerperformance.js b/test/demo/drawerperformance.js index 6a52c920..03d6b946 100644 --- a/test/demo/drawerperformance.js +++ b/test/demo/drawerperformance.js @@ -13,6 +13,14 @@ const labels = { bblue: 'Blue B', duomo: 'Duomo', } +const drawers = { + canvas: "Context2d drawer (default in OSD <= 4.1.0)", + webgl: "New WebGL drawer", + universal_webgl: "New WebGL (Modular)", + html: "" +} + + let viewer; ( function () { @@ -408,6 +416,10 @@ function rStats ( settings ) { _base = document.createElement( 'div' ); _base.className = 'rs-base'; + _base.style.bottom = '0px'; + _base.style.right = '0px'; + _base.style.top = 'initial'; + _base.style.left = 'initial'; _div = document.createElement( 'div' ); _div.className = 'rs-container'; _div.style.height = 'auto'; @@ -509,6 +521,8 @@ function rStats ( settings ) { } + + var glStats = function() { var _rS = null; @@ -898,16 +912,9 @@ Stats.Panel = function ( name, fg, bg ) { // })(); - -$('#create-drawer').on('click',function(){ - let drawerType = $('#select-drawer').val(); - let num = Math.floor($('#input-number').val()); - rS('other').start(); - run(drawerType, num); -}); - - function run(drawerType, num) { + rS('other').start(); + if(viewer){ viewer.destroy(); } @@ -969,3 +976,28 @@ function makeTileSources(num){ } +const url = new URL(window.location.href); +const drawer = url.searchParams.get("drawer"); +const numberOfSources = Number.parseInt(url.searchParams.get("sources")) || 1; + +$('#create-drawer').on('click',function(){ + const drawer = $('#select-drawer').val(); + let num = Math.floor($('#input-number').val()); + + url.searchParams.set("drawer", drawer); + url.searchParams.set("sources", num); + if ("undefined" !== typeof history.replaceState) { + history.replaceState(null, window.location.title, url.toString()); + } + run(drawer, num); +}); + +$('#input-number').val(numberOfSources); +$("#select-drawer").html(Object.entries(drawers).map(([k, v]) => { + const selected = drawer === k ? "selected" : ""; + return ``; +}).join("\n")); +if (drawer) { + run(drawer, numberOfSources); +} +