1cb93a386Sopenharmony_cidescribe('Runtime shader effects', () => { 2cb93a386Sopenharmony_ci let container; 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_ci beforeEach(async () => { 5cb93a386Sopenharmony_ci await LoadCanvasKit; 6cb93a386Sopenharmony_ci container = document.createElement('div'); 7cb93a386Sopenharmony_ci container.innerHTML = ` 8cb93a386Sopenharmony_ci <canvas width=600 height=600 id=test></canvas> 9cb93a386Sopenharmony_ci <canvas width=600 height=600 id=report></canvas>`; 10cb93a386Sopenharmony_ci document.body.appendChild(container); 11cb93a386Sopenharmony_ci }); 12cb93a386Sopenharmony_ci 13cb93a386Sopenharmony_ci afterEach(() => { 14cb93a386Sopenharmony_ci document.body.removeChild(container); 15cb93a386Sopenharmony_ci }); 16cb93a386Sopenharmony_ci 17cb93a386Sopenharmony_ci // On the SW backend, atan is not supported - a shader is returned, but 18cb93a386Sopenharmony_ci // it will draw blank. 19cb93a386Sopenharmony_ci const spiralSkSL = ` 20cb93a386Sopenharmony_ciuniform float rad_scale; 21cb93a386Sopenharmony_ciuniform int2 in_center; 22cb93a386Sopenharmony_ciuniform float4 in_colors0; 23cb93a386Sopenharmony_ciuniform float4 in_colors1; 24cb93a386Sopenharmony_ci 25cb93a386Sopenharmony_cihalf4 main(float2 p) { 26cb93a386Sopenharmony_ci float2 pp = p - float2(in_center); 27cb93a386Sopenharmony_ci float radius = sqrt(dot(pp, pp)); 28cb93a386Sopenharmony_ci radius = sqrt(radius); 29cb93a386Sopenharmony_ci float angle = atan(pp.y / pp.x); 30cb93a386Sopenharmony_ci float t = (angle + 3.1415926/2) / (3.1415926); 31cb93a386Sopenharmony_ci t += radius * rad_scale; 32cb93a386Sopenharmony_ci t = fract(t); 33cb93a386Sopenharmony_ci return half4(mix(in_colors0, in_colors1, t)); 34cb93a386Sopenharmony_ci}`; 35cb93a386Sopenharmony_ci 36cb93a386Sopenharmony_ci // TODO(kjlubick) rewrite testRTShader and callers to use gm. 37cb93a386Sopenharmony_ci const testRTShader = (name, done, localMatrix) => { 38cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('test'); 39cb93a386Sopenharmony_ci expect(surface).toBeTruthy('Could not make surface'); 40cb93a386Sopenharmony_ci if (!surface) { 41cb93a386Sopenharmony_ci return; 42cb93a386Sopenharmony_ci } 43cb93a386Sopenharmony_ci const spiral = CanvasKit.RuntimeEffect.Make(spiralSkSL); 44cb93a386Sopenharmony_ci expect(spiral).toBeTruthy('could not compile program'); 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ci expect(spiral.getUniformCount() ).toEqual(4); 47cb93a386Sopenharmony_ci expect(spiral.getUniformFloatCount()).toEqual(11); 48cb93a386Sopenharmony_ci const center = spiral.getUniform(1); 49cb93a386Sopenharmony_ci expect(center).toBeTruthy('could not fetch numbered uniform'); 50cb93a386Sopenharmony_ci expect(center.slot ).toEqual(1); 51cb93a386Sopenharmony_ci expect(center.columns ).toEqual(2); 52cb93a386Sopenharmony_ci expect(center.rows ).toEqual(1); 53cb93a386Sopenharmony_ci expect(center.isInteger).toEqual(true); 54cb93a386Sopenharmony_ci const color_0 = spiral.getUniform(2); 55cb93a386Sopenharmony_ci expect(color_0).toBeTruthy('could not fetch numbered uniform'); 56cb93a386Sopenharmony_ci expect(color_0.slot ).toEqual(3); 57cb93a386Sopenharmony_ci expect(color_0.columns ).toEqual(4); 58cb93a386Sopenharmony_ci expect(color_0.rows ).toEqual(1); 59cb93a386Sopenharmony_ci expect(color_0.isInteger).toEqual(false); 60cb93a386Sopenharmony_ci expect(spiral.getUniformName(2)).toEqual('in_colors0'); 61cb93a386Sopenharmony_ci 62cb93a386Sopenharmony_ci const canvas = surface.getCanvas(); 63cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 64cb93a386Sopenharmony_ci canvas.clear(CanvasKit.BLACK); // black should not be visible 65cb93a386Sopenharmony_ci const shader = spiral.makeShader([ 66cb93a386Sopenharmony_ci 0.3, 67cb93a386Sopenharmony_ci CANVAS_WIDTH/2, CANVAS_HEIGHT/2, 68cb93a386Sopenharmony_ci 1, 0, 0, 1, // solid red 69cb93a386Sopenharmony_ci 0, 1, 0, 1], // solid green 70cb93a386Sopenharmony_ci true, /*=opaque*/ 71cb93a386Sopenharmony_ci localMatrix); 72cb93a386Sopenharmony_ci paint.setShader(shader); 73cb93a386Sopenharmony_ci canvas.drawRect(CanvasKit.LTRBRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), paint); 74cb93a386Sopenharmony_ci 75cb93a386Sopenharmony_ci paint.delete(); 76cb93a386Sopenharmony_ci shader.delete(); 77cb93a386Sopenharmony_ci spiral.delete(); 78cb93a386Sopenharmony_ci 79cb93a386Sopenharmony_ci reportSurface(surface, name, done); 80cb93a386Sopenharmony_ci }; 81cb93a386Sopenharmony_ci 82cb93a386Sopenharmony_ci it('can compile custom shader code', (done) => { 83cb93a386Sopenharmony_ci testRTShader('rtshader_spiral', done); 84cb93a386Sopenharmony_ci }); 85cb93a386Sopenharmony_ci 86cb93a386Sopenharmony_ci it('can apply a matrix to the shader', (done) => { 87cb93a386Sopenharmony_ci testRTShader('rtshader_spiral_translated', done, CanvasKit.Matrix.translated(-200, 100)); 88cb93a386Sopenharmony_ci }); 89cb93a386Sopenharmony_ci 90cb93a386Sopenharmony_ci it('can provide a error handler for compilation errors', () => { 91cb93a386Sopenharmony_ci let error = ''; 92cb93a386Sopenharmony_ci const spiral = CanvasKit.RuntimeEffect.Make(`invalid sksl code, I hope`, (e) => { 93cb93a386Sopenharmony_ci error = e; 94cb93a386Sopenharmony_ci }); 95cb93a386Sopenharmony_ci expect(spiral).toBeFalsy(); 96cb93a386Sopenharmony_ci expect(error).toContain('error'); 97cb93a386Sopenharmony_ci }); 98cb93a386Sopenharmony_ci 99cb93a386Sopenharmony_ci const loadBrick = fetch( 100cb93a386Sopenharmony_ci '/assets/brickwork-texture.jpg') 101cb93a386Sopenharmony_ci .then((response) => response.arrayBuffer()); 102cb93a386Sopenharmony_ci const loadMandrill = fetch( 103cb93a386Sopenharmony_ci '/assets/mandrill_512.png') 104cb93a386Sopenharmony_ci .then((response) => response.arrayBuffer()); 105cb93a386Sopenharmony_ci 106cb93a386Sopenharmony_ci const thresholdSkSL = ` 107cb93a386Sopenharmony_ciuniform shader before_map; 108cb93a386Sopenharmony_ciuniform shader after_map; 109cb93a386Sopenharmony_ciuniform shader threshold_map; 110cb93a386Sopenharmony_ci 111cb93a386Sopenharmony_ciuniform float cutoff; 112cb93a386Sopenharmony_ciuniform float slope; 113cb93a386Sopenharmony_ci 114cb93a386Sopenharmony_cifloat smooth_cutoff(float x) { 115cb93a386Sopenharmony_ci x = x * slope + (0.5 - slope * cutoff); 116cb93a386Sopenharmony_ci return clamp(x, 0, 1); 117cb93a386Sopenharmony_ci} 118cb93a386Sopenharmony_ci 119cb93a386Sopenharmony_cihalf4 main(float2 xy) { 120cb93a386Sopenharmony_ci half4 before = before_map.eval(xy); 121cb93a386Sopenharmony_ci half4 after = after_map.eval(xy); 122cb93a386Sopenharmony_ci 123cb93a386Sopenharmony_ci float m = smooth_cutoff(threshold_map.eval(xy).r); 124cb93a386Sopenharmony_ci return mix(before, after, half(m)); 125cb93a386Sopenharmony_ci}`; 126cb93a386Sopenharmony_ci 127cb93a386Sopenharmony_ci // TODO(kjlubick) rewrite testChildrenShader and callers to use gm. 128cb93a386Sopenharmony_ci const testChildrenShader = (name, done, localMatrix) => { 129cb93a386Sopenharmony_ci Promise.all([loadBrick, loadMandrill]).then((values) => { 130cb93a386Sopenharmony_ci catchException(done, () => { 131cb93a386Sopenharmony_ci const [brickData, mandrillData] = values; 132cb93a386Sopenharmony_ci const brickImg = CanvasKit.MakeImageFromEncoded(brickData); 133cb93a386Sopenharmony_ci expect(brickImg).toBeTruthy('brick image could not be loaded'); 134cb93a386Sopenharmony_ci const mandrillImg = CanvasKit.MakeImageFromEncoded(mandrillData); 135cb93a386Sopenharmony_ci expect(mandrillImg).toBeTruthy('mandrill image could not be loaded'); 136cb93a386Sopenharmony_ci 137cb93a386Sopenharmony_ci const thresholdEffect = CanvasKit.RuntimeEffect.Make(thresholdSkSL); 138cb93a386Sopenharmony_ci expect(thresholdEffect).toBeTruthy('threshold did not compile'); 139cb93a386Sopenharmony_ci const spiralEffect = CanvasKit.RuntimeEffect.Make(spiralSkSL); 140cb93a386Sopenharmony_ci expect(spiralEffect).toBeTruthy('spiral did not compile'); 141cb93a386Sopenharmony_ci 142cb93a386Sopenharmony_ci const brickShader = brickImg.makeShaderCubic( 143cb93a386Sopenharmony_ci CanvasKit.TileMode.Decal, CanvasKit.TileMode.Decal, 144cb93a386Sopenharmony_ci 1/3 /*B*/, 1/3 /*C*/, 145cb93a386Sopenharmony_ci CanvasKit.Matrix.scaled(CANVAS_WIDTH/brickImg.width(), 146cb93a386Sopenharmony_ci CANVAS_HEIGHT/brickImg.height())); 147cb93a386Sopenharmony_ci const mandrillShader = mandrillImg.makeShaderCubic( 148cb93a386Sopenharmony_ci CanvasKit.TileMode.Decal, CanvasKit.TileMode.Decal, 149cb93a386Sopenharmony_ci 1/3 /*B*/, 1/3 /*C*/, 150cb93a386Sopenharmony_ci CanvasKit.Matrix.scaled(CANVAS_WIDTH/mandrillImg.width(), 151cb93a386Sopenharmony_ci CANVAS_HEIGHT/mandrillImg.height())); 152cb93a386Sopenharmony_ci const spiralShader = spiralEffect.makeShader([ 153cb93a386Sopenharmony_ci 0.8, 154cb93a386Sopenharmony_ci CANVAS_WIDTH/2, CANVAS_HEIGHT/2, 155cb93a386Sopenharmony_ci 1, 1, 1, 1, 156cb93a386Sopenharmony_ci 0, 0, 0, 1], true); 157cb93a386Sopenharmony_ci 158cb93a386Sopenharmony_ci const blendShader = thresholdEffect.makeShaderWithChildren( 159cb93a386Sopenharmony_ci [0.5, 5], 160cb93a386Sopenharmony_ci true, [brickShader, mandrillShader, spiralShader], localMatrix); 161cb93a386Sopenharmony_ci 162cb93a386Sopenharmony_ci const surface = CanvasKit.MakeCanvasSurface('test'); 163cb93a386Sopenharmony_ci expect(surface).toBeTruthy('Could not make surface'); 164cb93a386Sopenharmony_ci const canvas = surface.getCanvas(); 165cb93a386Sopenharmony_ci const paint = new CanvasKit.Paint(); 166cb93a386Sopenharmony_ci canvas.clear(CanvasKit.WHITE); 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_ci paint.setShader(blendShader); 169cb93a386Sopenharmony_ci canvas.drawRect(CanvasKit.LTRBRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT), paint); 170cb93a386Sopenharmony_ci 171cb93a386Sopenharmony_ci brickImg.delete(); 172cb93a386Sopenharmony_ci mandrillImg.delete(); 173cb93a386Sopenharmony_ci thresholdEffect.delete(); 174cb93a386Sopenharmony_ci spiralEffect.delete(); 175cb93a386Sopenharmony_ci brickShader.delete(); 176cb93a386Sopenharmony_ci mandrillShader.delete(); 177cb93a386Sopenharmony_ci spiralShader.delete(); 178cb93a386Sopenharmony_ci blendShader.delete(); 179cb93a386Sopenharmony_ci paint.delete(); 180cb93a386Sopenharmony_ci 181cb93a386Sopenharmony_ci reportSurface(surface, name, done); 182cb93a386Sopenharmony_ci })(); 183cb93a386Sopenharmony_ci }); 184cb93a386Sopenharmony_ci } 185cb93a386Sopenharmony_ci 186cb93a386Sopenharmony_ci it('take other shaders as fragment processors', (done) => { 187cb93a386Sopenharmony_ci testChildrenShader('rtshader_children', done); 188cb93a386Sopenharmony_ci }); 189cb93a386Sopenharmony_ci 190cb93a386Sopenharmony_ci it('apply a local matrix to the children-based shader', (done) => { 191cb93a386Sopenharmony_ci testChildrenShader('rtshader_children_rotated', done, CanvasKit.Matrix.rotated(Math.PI/12)); 192cb93a386Sopenharmony_ci }); 193cb93a386Sopenharmony_ci}); 194