From 330a4464b112529096578de96e14080ea8e05fa4 Mon Sep 17 00:00:00 2001 From: sjsanc Date: Mon, 13 Apr 2026 17:32:50 +0100 Subject: [PATCH] fix(core): queue pending resize when animationFrame is already scheduled to prevent silent drop --- .../ContextPoolRenderingEngine.ts | 17 +++++- packages/core/test/RenderingEngineAPI_test.js | 53 +++++++++++++++++++ 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/core/src/RenderingEngine/ContextPoolRenderingEngine.ts b/packages/core/src/RenderingEngine/ContextPoolRenderingEngine.ts index 8df47a5797..77c11ae876 100644 --- a/packages/core/src/RenderingEngine/ContextPoolRenderingEngine.ts +++ b/packages/core/src/RenderingEngine/ContextPoolRenderingEngine.ts @@ -33,6 +33,11 @@ import type { VtkOffscreenMultiRenderWindow } from '../types'; */ class ContextPoolRenderingEngine extends BaseRenderingEngine { private contextPool: WebGLContextPool; + private _pendingResizeArgs: { + vtkDrivenViewports: (IStackViewport | IVolumeViewport)[]; + keepCamera: boolean; + immediate: boolean; + } | null = null; constructor(id?: string) { super(id); @@ -197,8 +202,8 @@ class ContextPoolRenderingEngine extends BaseRenderingEngine { * Only updates the VTK offscreen size when the displayed size (canvas client size in device pixels) * differs from the current rendered size (canvas.width/height). The on-screen canvas dimensions * are updated only when the VTK render result is copied in _copyToOnscreenCanvas, avoiding - * flicker during resize. If a render is already scheduled, resize is deferred until the next - * resize() call. + * flicker during resize. If a render is already scheduled, the resize is deferred and applied + * automatically once the pending frame has been rendered. */ protected _resizeVTKViewports( vtkDrivenViewports: (IStackViewport | IVolumeViewport)[], @@ -236,6 +241,7 @@ class ContextPoolRenderingEngine extends BaseRenderingEngine { } if (this._animationFrameSet) { + this._pendingResizeArgs = { vtkDrivenViewports, keepCamera, immediate }; return; } @@ -311,6 +317,13 @@ class ContextPoolRenderingEngine extends BaseRenderingEngine { this._animationFrameSet = false; this._animationFrameHandle = null; + if (this._pendingResizeArgs) { + const { vtkDrivenViewports, keepCamera, immediate } = + this._pendingResizeArgs; + this._pendingResizeArgs = null; + this._resizeVTKViewports(vtkDrivenViewports, keepCamera, immediate); + } + // Trigger all events after rendering is complete eventDetails.forEach((eventDetail) => { if (eventDetail?.element) { diff --git a/packages/core/test/RenderingEngineAPI_test.js b/packages/core/test/RenderingEngineAPI_test.js index 45bf36bd20..a614e29fca 100644 --- a/packages/core/test/RenderingEngineAPI_test.js +++ b/packages/core/test/RenderingEngineAPI_test.js @@ -150,4 +150,57 @@ describe('RenderingEngineAPI -- ', () => { expect(stackViewports.length).toBe(1); }); }); + + describe("RenderingEngine doesn't drop frames", function () { + let renderingEngine; + + beforeEach(function () { + const testEnv = setupTestEnvironment({ + renderingEngineId, + }); + renderingEngine = testEnv.renderingEngine; + }); + + afterEach(function () { + cleanupTestEnvironment({ + renderingEngineId, + }); + }); + + it('should update canvas dimensions when resize() is called while a render is pending', function (done) { + const element1 = createViewports(renderingEngine, { + viewportId: axialViewportId, + viewportType: ViewportType.STACK, + width: 400, + height: 400, + useEnableElement: true, + }); + + const element2 = createViewports(renderingEngine, { + viewportId: sagittalViewportId, + viewportType: ViewportType.STACK, + width: 400, + height: 400, + useEnableElement: true, + }); + + renderingEngine.renderViewport(sagittalViewportId); + + element1.style.width = '250px'; + element1.style.height = '250px'; + + renderingEngine.resize(false, true); + + requestAnimationFrame(() => { + const viewport1 = renderingEngine.getViewport(axialViewportId); + if (!viewport1) { + done(); + return; + } + expect(viewport1.sWidth).toBe(250); + expect(viewport1.sHeight).toBe(250); + done(); + }); + }); + }); });