[shaders] Make updating uniforms controls more efficient.
- Add needsRAF() and onRAF() to the UniformControl interface.
- Implement all those methods on all the uniform controls.
- Move fps module to infra-sk.
- Add uniform-fps-sk control which displays the frames per second.
- Switch shader-app-sk to use needsRAF() and onRAF() to avoid
calling _render() on every requestAnimationFrame.
Bug: skia:11272
Change-Id: I0d39de38ca1cbaccadbae6e2df6b888e5ce03404
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/373656
Commit-Queue: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Kevin Lubick <kjlubick@google.com>
diff --git a/shaders/modules/fps/fps.ts b/infra-sk/modules/fps/fps.ts
similarity index 100%
rename from shaders/modules/fps/fps.ts
rename to infra-sk/modules/fps/fps.ts
diff --git a/shaders/modules/fps/fps_test.ts b/infra-sk/modules/fps/fps_test.ts
similarity index 100%
rename from shaders/modules/fps/fps_test.ts
rename to infra-sk/modules/fps/fps_test.ts
diff --git a/shaders/modules/fps/index.ts b/infra-sk/modules/fps/index.ts
similarity index 100%
rename from shaders/modules/fps/index.ts
rename to infra-sk/modules/fps/index.ts
diff --git a/infra-sk/modules/uniform-color-sk/uniform-color-sk.ts b/infra-sk/modules/uniform-color-sk/uniform-color-sk.ts
index 15d2a15..9e173f4 100644
--- a/infra-sk/modules/uniform-color-sk/uniform-color-sk.ts
+++ b/infra-sk/modules/uniform-color-sk/uniform-color-sk.ts
@@ -104,6 +104,14 @@
}
}
+ onRAF(): void {
+ // noop.
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
+
private hasAlphaChannel(): boolean {
return this._uniform.columns === 4;
}
diff --git a/infra-sk/modules/uniform-color-sk/uniform-color-sk_test.ts b/infra-sk/modules/uniform-color-sk/uniform-color-sk_test.ts
index a9da2af..32c5cd6 100644
--- a/infra-sk/modules/uniform-color-sk/uniform-color-sk_test.ts
+++ b/infra-sk/modules/uniform-color-sk/uniform-color-sk_test.ts
@@ -116,5 +116,9 @@
};
});
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk.ts b/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk.ts
index d969c61..1f4cc3f 100644
--- a/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk.ts
+++ b/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk.ts
@@ -61,6 +61,7 @@
super(UniformDimensionsSk.template);
}
+
private static template = (ele: UniformDimensionsSk) => html`
<select @change=${ele.selectionChanged} size="1">
${choices.map((choice, index) => html`
@@ -83,6 +84,14 @@
// This is a noop, we don't restore predefined uniform values.
}
+ onRAF(): void {
+ // noop.
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
+
connectedCallback(): void {
super.connectedCallback();
this._render();
diff --git a/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk_test.ts b/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk_test.ts
index 9a88f10..b371277 100644
--- a/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk_test.ts
+++ b/infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk_test.ts
@@ -56,5 +56,9 @@
};
});
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-fps-sk/index.ts b/infra-sk/modules/uniform-fps-sk/index.ts
new file mode 100644
index 0000000..06d4572
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/index.ts
@@ -0,0 +1,2 @@
+import './uniform-fps-sk';
+import './uniform-fps-sk.scss';
diff --git a/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.html b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.html
new file mode 100644
index 0000000..42f3534
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <title>uniform-fps-sk</title>
+ <meta charset="utf-8" />
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+</head>
+
+<body>
+ <h1>uniform-fps-sk</h1>
+ <uniform-fps-sk></uniform-fps-sk>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.ts b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.ts
new file mode 100644
index 0000000..b811cfb
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk-demo.ts
@@ -0,0 +1 @@
+import './index';
diff --git a/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.scss b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.scss
new file mode 100644
index 0000000..52886e9
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.scss
@@ -0,0 +1,3 @@
+uniform-fps-sk {
+ display: block;
+}
diff --git a/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.ts b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.ts
new file mode 100644
index 0000000..da755e1
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk.ts
@@ -0,0 +1,57 @@
+/**
+ * @module modules/uniform-fps-sk
+ * @description <h2><code>uniform-fps-sk</code></h2>
+ *
+ * Displays the frames per second.
+ */
+import { define } from 'elements-sk/define';
+import { html } from 'lit-html';
+import { ElementSk } from '../ElementSk';
+import { FPS } from '../fps/fps';
+import { Uniform, UniformControl } from '../uniform/uniform';
+
+const defaultUniform: Uniform = {
+ name: 'raf',
+ rows: 0,
+ columns: 0,
+ slot: 0,
+};
+
+export class UniformFpsSk extends ElementSk implements UniformControl {
+ uniform: Uniform = defaultUniform;
+
+ private fps: FPS = new FPS();
+
+ constructor() {
+ super(UniformFpsSk.template);
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ private static template = (ele: UniformFpsSk) => html`fps`;
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ applyUniformValues(uniforms: number[]): void {
+ // noop as UniformRafSk doesn't supply uniforms.
+ }
+
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ restoreUniformValues(uniforms: number[]): void {
+ // noop as UniformRafSk doesn't supply uniforms.
+ }
+
+ onRAF(): void {
+ this.fps.raf();
+ this.textContent = `${this.fps.fps.toFixed(0)} fps`;
+ }
+
+ needsRAF(): boolean {
+ return true;
+ }
+
+ connectedCallback(): void {
+ super.connectedCallback();
+ this._render();
+ }
+}
+
+define('uniform-fps-sk', UniformFpsSk);
diff --git a/infra-sk/modules/uniform-fps-sk/uniform-fps-sk_test.ts b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk_test.ts
new file mode 100644
index 0000000..cccaf54
--- /dev/null
+++ b/infra-sk/modules/uniform-fps-sk/uniform-fps-sk_test.ts
@@ -0,0 +1,26 @@
+import './index';
+import { assert } from 'chai';
+import { UniformFpsSk } from './uniform-fps-sk';
+
+import { setUpElementUnderTest } from '../test_util';
+
+describe('uniform-fps-sk', () => {
+ const newInstance = setUpElementUnderTest<UniformFpsSk>('uniform-fps-sk');
+
+ let element: UniformFpsSk;
+ beforeEach(() => {
+ element = newInstance();
+ });
+
+ describe('uniform-fps-sk', () => {
+ it('needs raf updates', () => {
+ assert.isTrue(element.needsRAF());
+ });
+
+ it('updates on onRAF()', () => {
+ assert.equal(element.textContent, 'fps');
+ element.onRAF();
+ assert.equal(element.textContent, '0 fps');
+ });
+ });
+});
diff --git a/infra-sk/modules/uniform-generic-sk/uniform-generic-sk.ts b/infra-sk/modules/uniform-generic-sk/uniform-generic-sk.ts
index 0f87340..36ffaee 100644
--- a/infra-sk/modules/uniform-generic-sk/uniform-generic-sk.ts
+++ b/infra-sk/modules/uniform-generic-sk/uniform-generic-sk.ts
@@ -100,6 +100,14 @@
}
}
}
+
+ onRAF(): void {
+ // noop.
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
}
define('uniform-generic-sk', UniformGenericSk);
diff --git a/infra-sk/modules/uniform-generic-sk/uniform-generic-sk_test.ts b/infra-sk/modules/uniform-generic-sk/uniform-generic-sk_test.ts
index 2945a04..2f661aa 100644
--- a/infra-sk/modules/uniform-generic-sk/uniform-generic-sk_test.ts
+++ b/infra-sk/modules/uniform-generic-sk/uniform-generic-sk_test.ts
@@ -73,5 +73,9 @@
element.applyUniformValues(uniforms);
assert.deepEqual(uniforms, [0, 1, 0.5, 0.3, 1, 0]);
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk.ts b/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk.ts
index 8b65a4c..66b8845 100644
--- a/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk.ts
+++ b/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk.ts
@@ -32,6 +32,14 @@
// This is a noop, we don't restore predefined uniform values.
}
+ onRAF(): void {
+ // noop
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
+
get uniform(): Uniform {
return this._uniform;
}
diff --git a/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk_test.ts b/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk_test.ts
index 4773d0a..5b45e9b 100644
--- a/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk_test.ts
+++ b/infra-sk/modules/uniform-imageresolution-sk/uniform-imageresolution-sk_test.ts
@@ -18,5 +18,9 @@
element.applyUniformValues(uniforms);
assert.deepEqual(uniforms, [imageSize, imageSize, 0]);
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk.ts b/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk.ts
index 6046198..41d028e 100644
--- a/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk.ts
+++ b/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk.ts
@@ -44,6 +44,14 @@
// This is a noop, we don't restore predefined uniform values.
}
+ onRAF(): void {
+ // noop.
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
+
get elementToMonitor(): HTMLElement {
return this._elementToMonitor!;
}
diff --git a/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk_test.ts b/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk_test.ts
index 6baea25..aacd71c 100644
--- a/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk_test.ts
+++ b/infra-sk/modules/uniform-mouse-sk/uniform-mouse-sk_test.ts
@@ -29,5 +29,9 @@
};
});
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-slider-sk/uniform-slider-sk.ts b/infra-sk/modules/uniform-slider-sk/uniform-slider-sk.ts
index 6a9f407..314a6a3 100644
--- a/infra-sk/modules/uniform-slider-sk/uniform-slider-sk.ts
+++ b/infra-sk/modules/uniform-slider-sk/uniform-slider-sk.ts
@@ -61,6 +61,14 @@
restoreUniformValues(uniforms: number[]): void {
this.input!.valueAsNumber = uniforms[this.uniform.slot];
}
+
+ onRAF(): void {
+ // noop.
+ }
+
+ needsRAF(): boolean {
+ return false;
+ }
}
define('uniform-slider-sk', UniformSliderSk);
diff --git a/infra-sk/modules/uniform-slider-sk/uniform-slider-sk_test.ts b/infra-sk/modules/uniform-slider-sk/uniform-slider-sk_test.ts
index c91c2d5..40916ad 100644
--- a/infra-sk/modules/uniform-slider-sk/uniform-slider-sk_test.ts
+++ b/infra-sk/modules/uniform-slider-sk/uniform-slider-sk_test.ts
@@ -56,5 +56,9 @@
};
});
});
+
+ it('does not need raf updates', () => {
+ assert.isFalse(element.needsRAF());
+ });
});
});
diff --git a/infra-sk/modules/uniform-time-sk/uniform-time-sk.ts b/infra-sk/modules/uniform-time-sk/uniform-time-sk.ts
index d241840..0ee04dc 100644
--- a/infra-sk/modules/uniform-time-sk/uniform-time-sk.ts
+++ b/infra-sk/modules/uniform-time-sk/uniform-time-sk.ts
@@ -48,7 +48,7 @@
<play-arrow-icon-sk ?hidden=${ele.playing}></play-arrow-icon-sk>
<pause-icon-sk ?hidden=${!ele.playing}></pause-icon-sk>
</button>
- <span>${ele.time.toFixed(3)}</span>
+ <span id=ms>${ele.time.toFixed(3)}</span>
<span>${ele._uniform.name}</span>
`;
@@ -74,6 +74,14 @@
// This is a noop, we don't restore predefined uniform values.
}
+ onRAF(): void {
+ this.render();
+ }
+
+ needsRAF(): boolean {
+ return true;
+ }
+
/** Allows overriding the Date.now function for testing. */
get dateNow(): DateNow {
return this._dateNow;
diff --git a/infra-sk/modules/uniform-time-sk/uniform-time-sk_test.ts b/infra-sk/modules/uniform-time-sk/uniform-time-sk_test.ts
index a9ee48b..97fb11b 100644
--- a/infra-sk/modules/uniform-time-sk/uniform-time-sk_test.ts
+++ b/infra-sk/modules/uniform-time-sk/uniform-time-sk_test.ts
@@ -84,5 +84,16 @@
element.applyUniformValues(uniforms);
assert.deepEqual(uniforms, [0, 10, 0]);
});
+
+ it('needs raf updates', () => {
+ assert.isTrue(element.needsRAF());
+ });
+
+ it('updates on a call to onRAF', () => {
+ element.dateNow = () => 0; // ms
+ element.time = 10; // s
+ element.onRAF();
+ assert.equal('10.000', $$('#ms', element)?.textContent);
+ });
});
});
diff --git a/infra-sk/modules/uniform/uniform.ts b/infra-sk/modules/uniform/uniform.ts
index e64018d..60ba5eb 100644
--- a/infra-sk/modules/uniform/uniform.ts
+++ b/infra-sk/modules/uniform/uniform.ts
@@ -24,4 +24,10 @@
/** Copies the values from the uniforms array into the control. */
restoreUniformValues(uniforms: number[]): void;
+
+ /** Function to call on every requestAnimationFrame. Only called if needsRAF() returns true. */
+ onRAF(): void;
+
+ /** Returns true if this controls needs to update on every requestAnimationFrame, such as uniform-time-sk. */
+ needsRAF(): boolean;
}
diff --git a/shaders/modules/shadernode/index.ts b/shaders/modules/shadernode/index.ts
index f34ca05..c84ac25 100644
--- a/shaders/modules/shadernode/index.ts
+++ b/shaders/modules/shadernode/index.ts
@@ -32,6 +32,13 @@
export const numPredefinedUniforms = predefinedUniforms.match(/^uniform/gm)!.length - numPredefinedShaderUniforms;
/**
+ * Counts the number of controls that handle pre-defined uniforms.
+ *
+ * Takes into account the uniform-fps-sk which doesn't correspond to a uniform.
+ */
+export const numPredefinedUniformControls = numPredefinedUniforms + 1;
+
+/**
* The number of lines prefixed to every shader for predefined uniforms. Needed
* to properly adjust error line numbers.
*/
diff --git a/shaders/modules/shaders-app-sk/shaders-app-sk.ts b/shaders/modules/shaders-app-sk/shaders-app-sk.ts
index 377711e..e99df36 100644
--- a/shaders/modules/shaders-app-sk/shaders-app-sk.ts
+++ b/shaders/modules/shaders-app-sk/shaders-app-sk.ts
@@ -3,13 +3,12 @@
* @description <h2><code>shaders-app-sk</code></h2>
*
*/
-import { $ } from 'common-sk/modules/dom';
+import { $, $$ } from 'common-sk/modules/dom';
import 'codemirror/mode/clike/clike'; // Syntax highlighting for c-like languages.
import { define } from 'elements-sk/define';
import { html, TemplateResult } from 'lit-html';
import { errorMessage } from 'elements-sk/errorMessage';
import CodeMirror from 'codemirror';
-import { $$ } from 'common-sk/modules/dom';
import { stateReflector } from 'common-sk/modules/stateReflector';
import { HintableObject } from 'common-sk/modules/hintable';
import { isDarkMode } from '../../../infra-sk/modules/theme-chooser-sk/theme-chooser-sk';
@@ -27,6 +26,7 @@
import '../../../infra-sk/modules/theme-chooser-sk';
import { SKIA_VERSION } from '../../build/version';
import { ElementSk } from '../../../infra-sk/modules/ElementSk/ElementSk';
+import '../../../infra-sk/modules/uniform-fps-sk';
import '../../../infra-sk/modules/uniform-time-sk';
import '../../../infra-sk/modules/uniform-generic-sk';
import '../../../infra-sk/modules/uniform-dimensions-sk';
@@ -35,10 +35,9 @@
import '../../../infra-sk/modules/uniform-color-sk';
import '../../../infra-sk/modules/uniform-imageresolution-sk';
import { UniformControl } from '../../../infra-sk/modules/uniform/uniform';
-import { FPS } from '../fps/fps';
import { DimensionsChangedEventDetail } from '../../../infra-sk/modules/uniform-dimensions-sk/uniform-dimensions-sk';
import {
- defaultShader, numPredefinedUniformLines, predefinedUniforms, ShaderNode,
+ defaultShader, numPredefinedUniformControls, numPredefinedUniformLines, predefinedUniforms, ShaderNode,
} from '../shadernode';
// eslint-disable-next-line @typescript-eslint/no-var-requires
@@ -163,14 +162,16 @@
// stateReflector update function.
private stateChanged: stateChangedCallback | null = null;
- private fps: FPS = new FPS();
+ private uniformControlsNeedingRAF: UniformControl[] = [];
constructor() {
super(ShadersAppSk.template);
}
private static uniformControls = (ele: ShadersAppSk): TemplateResult[] => {
- const ret: TemplateResult[] = [];
+ const ret: TemplateResult[] = [
+ html`<uniform-fps-sk></uniform-fps-sk>`, // Always start with the fps control.
+ ];
const node = ele.shaderNode;
if (!node) {
return ret;
@@ -257,10 +258,7 @@
</div>
</div>
<div id=shaderControls>
- <div id=fps>
- ${ele.fps.fps.toFixed(0)} fps
- </div>
- <div id=uniformControls>
+ <div id=uniformControls @input=${ele.uniformControlsChange} @change=${ele.uniformControlsChange}>
${ShadersAppSk.uniformControls(ele)}
</div>
<button
@@ -387,6 +385,7 @@
const predefinedUniformValues = new Array(this.shaderNode!.numPredefinedUniformValues).fill(0);
this.setUniformValuesToControls(predefinedUniformValues.concat(this.shaderNode!.currentUserUniformValues));
+ this.findAllUniformControlsThatNeedRAF();
this.run();
} catch (error) {
@@ -451,9 +450,17 @@
}
/** Populate the uniforms values from the controls. */
- private getUniformValuesFromControls(): number[] {
+ private getUserUniformValuesFromControls(): number[] {
const uniforms: number[] = new Array(this.shaderNode!.getUniformFloatCount()).fill(0);
- $('#uniformControls > *').forEach((control) => {
+ $('#uniformControls > *').slice(numPredefinedUniformControls).forEach((control) => {
+ (control as unknown as UniformControl).applyUniformValues(uniforms);
+ });
+ return uniforms.slice(this.shaderNode?.numPredefinedUniformValues || 0);
+ }
+
+ private getPredefinedUniformValuesFromControls(): number[] {
+ const uniforms: number[] = new Array(this.shaderNode!.getUniformFloatCount()).fill(0);
+ $('#uniformControls > *').slice(0, numPredefinedUniformControls).forEach((control) => {
(control as unknown as UniformControl).applyUniformValues(uniforms);
});
return uniforms;
@@ -466,38 +473,33 @@
});
}
- private getCurrentUserUniformValues(uniforms: number[]): number[] {
- if (this.shaderNode) {
- return uniforms.slice(this.shaderNode.numPredefinedUniformValues);
- }
- return [];
+ private findAllUniformControlsThatNeedRAF(): void {
+ this.uniformControlsNeedingRAF = [];
+ $('#uniformControls > *').forEach((control) => {
+ const uniformControl = (control as unknown as UniformControl);
+ if (uniformControl.needsRAF()) {
+ this.uniformControlsNeedingRAF.push(uniformControl);
+ }
+ });
}
- private getPredefinedUniformValues(uniforms: number[]): number[] {
- if (this.shaderNode) {
- return uniforms.slice(0, this.shaderNode.numPredefinedUniformValues);
- }
- return [];
+ private uniformControlsChange() {
+ this.shaderNode!.currentUserUniformValues = this.getUserUniformValuesFromControls();
+ this._render();
}
private drawFrame() {
- this.fps.raf();
this.kit!.setCurrentContext(this.canvasKitContext);
- const uniformsArray = this.getUniformValuesFromControls();
-
- // TODO(jcgregorio) Change this to be event driven.
- this.shaderNode!.currentUserUniformValues = this.getCurrentUserUniformValues(uniformsArray);
-
- const shader = this.shaderNode!.getShader(this.getPredefinedUniformValues(uniformsArray));
+ const shader = this.shaderNode!.getShader(this.getPredefinedUniformValuesFromControls());
if (!shader) {
errorMessage('Failed to get shader.', 0);
return;
}
// Allow uniform controls to update, such as uniform-timer-sk.
- // TODO(jcgregorio) This is overkill, allow controls to register for a 'raf'
- // event if they need to update frequently.
- this._render();
+ this.uniformControlsNeedingRAF.forEach((element) => {
+ element.onRAF();
+ });
// Draw the shader.
this.canvas!.clear(this.kit!.BLACK);