From f60ab630be4a66b6a4b20468144a210d3142fa9a Mon Sep 17 00:00:00 2001 From: Jamie Wong Date: Wed, 22 Aug 2018 20:36:09 -0700 Subject: [PATCH] Fix rendering bugs when device pixel ratio changes (#147) Fixes #102 --- CHANGELOG.md | 4 ++ src/gl/canvas-context.ts | 2 +- src/gl/graphics.ts | 30 ++++++++++----- src/gl/overlay-rectangle-renderer.ts | 2 +- src/views/application-container.tsx | 1 + src/views/application.tsx | 56 ++++++++++++++++++---------- 6 files changed, 64 insertions(+), 31 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d237173..861e5c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## [Unreleased] +### Fixed + +* Fixed rendering issues when switching between screens w/ different `devicePixelRatios` [#147] + ## [0.7.1] - 2018-08-20 ### Fixed diff --git a/src/gl/canvas-context.ts b/src/gl/canvas-context.ts index 1b8e359..98bf8f9 100644 --- a/src/gl/canvas-context.ts +++ b/src/gl/canvas-context.ts @@ -49,7 +49,7 @@ export class CanvasContext { } private onBeforeFrame = () => { this.animationFrameRequest = null - this.gl.setViewport(0, 0, this.gl.renderTargetWidth, this.gl.renderTargetHeight) + this.gl.setViewport(0, 0, this.gl.renderTargetWidthInPixels, this.gl.renderTargetHeightInPixels) this.gl.clear(new Graphics.Color(1, 1, 1, 1)) for (const handler of this.beforeFrameHandlers) { diff --git a/src/gl/graphics.ts b/src/gl/graphics.ts index ee11e8c..57edd08 100644 --- a/src/gl/graphics.ts +++ b/src/gl/graphics.ts @@ -141,11 +141,11 @@ export namespace Graphics { abstract setRenderTarget(renderTarget: RenderTarget | null): void abstract setViewport(x: number, y: number, width: number, height: number): void abstract viewport: Rect - abstract width: number - abstract height: number + abstract widthInPixels: number + abstract heightInPixels: number - abstract renderTargetHeight: number - abstract renderTargetWidth: number + abstract renderTargetHeightInPixels: number + abstract renderTargetWidthInPixels: number abstract setBlendState(source: BlendOperation, target: BlendOperation): void setCopyBlendState() { @@ -304,10 +304,10 @@ export namespace WebGL { private _oldViewport = new Graphics.Rect() private _width = 0 - get width() { + get widthInPixels() { return this._width } - get height() { + get heightInPixels() { return this._height } @@ -411,13 +411,13 @@ export namespace WebGL { : this._defaultViewport } - get renderTargetWidth() { + get renderTargetWidthInPixels() { return this._currentRenderTarget != null ? this._currentRenderTarget.viewport.width : this._width } - get renderTargetHeight() { + get renderTargetHeightInPixels() { return this._currentRenderTarget != null ? this._currentRenderTarget.viewport.height : this._height @@ -455,6 +455,18 @@ export namespace WebGL { widthInAppUnits: number, heightInAppUnits: number, ) { + const bounds = this._gl.canvas.getBoundingClientRect() + + if ( + this._width === widthInAppUnits && + this._height === heightInPixels && + bounds.width === widthInAppUnits && + bounds.height === heightInAppUnits + ) { + // Nothing to do here! + return + } + let canvas = this._gl.canvas let style = canvas.style canvas.width = widthInPixels @@ -547,7 +559,7 @@ export namespace WebGL { if (this._forceStateUpdate || !this._oldViewport.equals(viewport)) { gl.viewport( viewport.x, - this.renderTargetHeight - viewport.y - viewport.height, + this.renderTargetHeightInPixels - viewport.y - viewport.height, viewport.width, viewport.height, ) diff --git a/src/gl/overlay-rectangle-renderer.ts b/src/gl/overlay-rectangle-renderer.ts index a63155b..6634355 100644 --- a/src/gl/overlay-rectangle-renderer.ts +++ b/src/gl/overlay-rectangle-renderer.ts @@ -92,7 +92,7 @@ export class ViewportRectangleRenderer { this.material.setUniformVec2('physicalOrigin', viewport.x, viewport.y) this.material.setUniformVec2('physicalSize', viewport.width, viewport.height) - this.material.setUniformFloat('framebufferHeight', this.gl.renderTargetHeight) + this.material.setUniformFloat('framebufferHeight', this.gl.renderTargetHeightInPixels) this.gl.setBlendState( Graphics.BlendOperation.SOURCE_ALPHA, diff --git a/src/views/application-container.tsx b/src/views/application-container.tsx index 49e0911..5c76250 100644 --- a/src/views/application-container.tsx +++ b/src/views/application-container.tsx @@ -43,6 +43,7 @@ export const ApplicationContainer = createContainer( return { activeProfileState, dispatch, + canvasContext: state.glCanvas ? getCanvasContext(state.glCanvas) : null, resizeCanvas: ( widthInPixels: number, heightInPixels: number, diff --git a/src/views/application.tsx b/src/views/application.tsx index b5caf30..4e0d090 100644 --- a/src/views/application.tsx +++ b/src/views/application.tsx @@ -12,6 +12,7 @@ import {StatelessComponent} from '../lib/typed-redux' import {LeftHeavyFlamechartView, ChronoFlamechartView} from './flamechart-view-container' import {SandwichViewState} from '../store/sandwich-view-state' import {FlamechartViewState} from '../store/flamechart-view-state' +import {CanvasContext} from '../gl/canvas-context' import {Graphics} from '../gl/graphics' const importModule = import('../import') @@ -169,7 +170,7 @@ export class Toolbar extends StatelessComponent { } interface GLCanvasProps { - resizeCanvas: typeof Graphics.Context.prototype.resize + canvasContext: CanvasContext | null setGLCanvas: (canvas: HTMLCanvasElement | null) => void } export class GLCanvas extends Component { @@ -185,37 +186,52 @@ export class GLCanvas extends Component { this.props.setGLCanvas(this.canvas) } - private maybeResize() { + private maybeResize = () => { if (!this.canvas) return + if (!this.props.canvasContext) return + let {width, height} = this.canvas.getBoundingClientRect() - width = Math.floor(width) * window.devicePixelRatio - height = Math.floor(height) * window.devicePixelRatio - // Still initializing: don't resize yet - if (width < 4 || height < 4) return - const oldWidth = this.canvas.width - const oldHeight = this.canvas.height + const widthInAppUnits = width + const heightInAppUnits = height + const widthInPixels = width * window.devicePixelRatio + const heightInPixels = height * window.devicePixelRatio - // Already at the right size - if (width === oldWidth && height === oldHeight) return - - this.props.resizeCanvas( - width, - height, - width / window.devicePixelRatio, - height / window.devicePixelRatio, + this.props.canvasContext.gl.resize( + widthInPixels, + heightInPixels, + widthInAppUnits, + heightInAppUnits, ) + this.props.canvasContext.gl.clear(new Graphics.Color(1, 1, 1, 1)) } onWindowResize = () => { - this.maybeResize() + if (this.props.canvasContext) { + this.props.canvasContext.requestFrame() + } window.addEventListener('resize', this.onWindowResize) } + componentWillReceiveProps(nextProps: GLCanvasProps) { + if (this.props.canvasContext !== nextProps.canvasContext) { + if (this.props.canvasContext) { + console.log('a') + this.props.canvasContext.removeBeforeFrameHandler(this.maybeResize) + } + if (nextProps.canvasContext) { + console.log('b') + nextProps.canvasContext.addBeforeFrameHandler(this.maybeResize) + nextProps.canvasContext.requestFrame() + } + } + } componentDidMount() { window.addEventListener('resize', this.onWindowResize) - requestAnimationFrame(() => this.maybeResize()) } componentWillUnmount() { + if (this.props.canvasContext) { + this.props.canvasContext.removeBeforeFrameHandler(this.maybeResize) + } window.removeEventListener('resize', this.onWindowResize) } render() { @@ -232,7 +248,6 @@ export interface ActiveProfileState { } export type ApplicationProps = ApplicationState & { - resizeCanvas: typeof Graphics.Context.prototype.resize setGLCanvas: (canvas: HTMLCanvasElement | null) => void setLoading: (loading: boolean) => void setError: (error: boolean) => void @@ -242,6 +257,7 @@ export type ApplicationProps = ApplicationState & { setFlattenRecursion: (flattenRecursion: boolean) => void setProfileIndexToView: (profileIndex: number) => void activeProfileState: ActiveProfileState | null + canvasContext: CanvasContext | null } export class Application extends StatelessComponent { @@ -615,7 +631,7 @@ export class Application extends StatelessComponent { onDragLeave={this.onDragLeave} className={css(style.root, this.props.dragActive && style.dragTargetRoot)} > - +