blob: a9853929ef62998de0f5c67ca3de05cdef08c528 [file] [log] [blame]
/*
* Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
* for details. All rights reserved. Use of this source code is governed by a
* BSD-style license that can be found in the LICENSE file.
*/
library webgl_test;
import 'dart:html';
import 'dart:web_gl' as wgl;
import "../../../../testcommon.dart";
import "pwd.dart";
assertMsg(assertion, msg) {
if (assertion) {
testPassed(msg);
} else {
testFailed(msg);
}
}
webglTestLog(msg) {
print(msg);
var log = document.getElementById("console");
if (log != null) {
log.innerHtml += msg + "<br>";
}
}
//
// create3DContext
//
// Returns the WebGLRenderingContext for any known implementation.
//
create3DContext([canvas, attributes]) {
if (canvas == null)
canvas = document.createElement("canvas");
var context = null;
tryContext(canvas, id, attributes) {
var ctx;
try {
if (attributes == null)
ctx = canvas.getContext(id);
else
ctx = canvas.getContext(id, attributes);
} catch(_) {}
return ctx;
}
if (context == null)
context = tryContext(canvas, "experimental-webgl", attributes);
if (context == null)
context = tryContext(canvas, "webkit-3d", attributes);
if (context == null)
context = tryContext(canvas, "moz-webgl", attributes);
if (context == null)
throw "Unable to fetch WebGL rendering context for Canvas";
return context;
}
getGLErrorAsString(ctx, err) {
if (err == wgl.NO_ERROR) {
return "NO_ERROR";
}
/*for (var name in ctx) {
if (ctx[name] === err) {
return name;
}
}*/
return "0x" + err.toRadixString(16);
}
// Pass undefined for glError to test that it at least throws some error
shouldGenerateGLError(ctx, glErrors, func) {
if (glErrors is num) {
glErrors = [glErrors];
}
var exception;
try {
func();
} catch (e) {
exception = e;
}
if (exception != null) {
testFailed("shouldGenerateGLError: Threw unexpected exception: $exception");
} else {
var err = ctx.getError();
var errStrs = [];
for (var ii = 0; ii < glErrors.length; ++ii) {
errStrs.add(getGLErrorAsString(ctx, glErrors[ii]));
}
var expected = errStrs.join(" or ");
if (glErrors.indexOf(err) < 0) {
testFailed("shouldGenerateGLError: Expected: " + expected + ". Was " + getGLErrorAsString(ctx, err) + ".");
} else {
var msg = (glErrors.length == 1) ? "shouldGenerateGLError: Generated expected GL error: " :
"shouldGenerateGLError: Generated one of expected GL errors: ";
testPassed(msg + expected + ".");
}
}
}
/**
* Tests that the first error GL returns is the specified error.
* @param {!WebGLContext} gl The WebGLContext to use.
* @param {number|!Array.<number>} glError The expected gl
* error. Multiple errors can be passed in using an
* array.
* @param {string} opt_msg Optional additional message.
*/
glErrorShouldBe(gl, glErrors, [opt_msg=""]) {
if (glErrors is num) {
glErrors = [glErrors];
}
var err = gl.getError();
var ndx = glErrors.indexOf(err);
var errStrs = [];
for (var ii = 0; ii < glErrors.length; ++ii) {
errStrs.add(getGLErrorAsString(gl, glErrors[ii]));
}
var expected = errStrs.join(" or ");
if (ndx < 0) {
var msg = "getError expected" + ((glErrors.length > 1) ? " one of: " : ": ");
testFailed(msg + expected + ". Was " + getGLErrorAsString(gl, err) + " : " + opt_msg);
} else {
var msg = "getError was " + ((glErrors.length > 1) ? "one of: " : "expected value: ");
testPassed(msg + expected + " : " + opt_msg);
}
}
//
// createProgram
//
// Create and return a program object, attaching each of the given shaders.
//
// If attribs are given, bind an attrib with that name at that index.
//
createProgram(gl, vshaders, fshaders, [attribs])
{
if (vshaders is String)
vshaders = [vshaders];
if (fshaders is String)
fshaders = [fshaders];
var shaders = [];
var i;
for (i = 0; i < vshaders.length; ++i) {
var shader = loadShader(gl, vshaders[i], wgl.VERTEX_SHADER);
if (shader == null)
return null;
shaders.add(shader);
}
for (i = 0; i < fshaders.length; ++i) {
var shader = loadShader(gl, fshaders[i], wgl.FRAGMENT_SHADER);
if (shader == null)
return null;
shaders.add(shader);
}
var prog = gl.createProgram();
for (i = 0; i < shaders.length; ++i) {
gl.attachShader(prog, shaders[i]);
}
if (attribs != null) {
for (var i=0; i<attribs.length; ++i) {
gl.bindAttribLocation (prog, i, attribs[i]);
}
}
gl.linkProgram(prog);
// Check the link status
var linked = gl.getProgramParameter(prog, wgl.LINK_STATUS);
if (!linked) {
// something went wrong with the link
var error = gl.getProgramInfoLog(prog);
webglTestLog("Error in program linking:" + error);
gl.deleteProgram(prog);
for (i = 0; i < shaders.length; ++i)
gl.deleteShader(shaders[i]);
return null;
}
return prog;
}
//
// initWebGL
//
// Initialize the Canvas element with the passed name as a WebGL object and
// return the WebGLRenderingContext.
//
// Load shaders with the passed names and create a program with them. Return
// this program in the 'program' property of the returned context.
//
// [for dart just return a context and program is accessible by
// gl.getParameter(wgl.CURRENT_PROGRAM)]
//
// For each string in the passed attribs array, bind an attrib with that name
// at that index. Once the attribs are bound, link the program and then use
// it.
//
// Set the clear color to the passed array (4 values) and set the clear depth
// to the passed value. Enable depth testing and blending with a blend func of
// (SRC_ALPHA, ONE_MINUS_SRC_ALPHA)
//
initWebGL(canvasName, vshader, fshader, attribs, clearColor, clearDepth, [contextAttribs])
{
var canvas = document.getElementById(canvasName);
var gl = create3DContext(canvas, contextAttribs);
if (gl == null) {
testFailed("No WebGL context found");
return null;
}
// Create the program object
var program = createProgram(gl, vshader, fshader, attribs);
if (program == null)
return null;
gl.useProgram(program);
gl.clearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
gl.clearDepth(clearDepth);
gl.enable(wgl.DEPTH_TEST);
gl.enable(wgl.BLEND);
gl.blendFunc(wgl.SRC_ALPHA, wgl.ONE_MINUS_SRC_ALPHA);
return gl;
}
//
// getShaderSource
//
// Load the source from the passed shader file.
//
getShaderSource(file)
{
var xhr = new HttpRequest();
xhr.open("GET", file, async:false);
xhr.send();
return xhr.responseText;
}
//
// loadShader
//
// 'shader' is either the id of a <script> element containing the shader source
// string, the shader string itself, or the URL of a file containing the shader
// source. Load this shader and return the WebGLShader object corresponding to
// it.
//
loadShader(ctx, shaderId, shaderType, [isFile=false])
{
var shaderSource = "";
if (isFile)
shaderSource = getShaderSource(shaderId);
else {
var shaderScript = document.getElementById(shaderId);
if (shaderScript == null) {
shaderSource = shaderId;
} else {
if (shaderScript.type == "x-shader/x-vertex") {
shaderType = wgl.VERTEX_SHADER;
} else if (shaderScript.type == "x-shader/x-fragment") {
shaderType = wgl.FRAGMENT_SHADER;
} else if (shaderType != wgl.VERTEX_SHADER && shaderType != wgl.FRAGMENT_SHADER) {
webglTestLog("*** Error: unknown shader type");
return null;
}
shaderSource = shaderScript.text;
}
}
// Create the shader object
var shader = ctx.createShader(shaderType);
if (shader == null) {
webglTestLog("*** Error: unable to create shader '"+shaderId+"'");
return null;
}
// Load the shader source
ctx.shaderSource(shader, shaderSource);
// Compile the shader
ctx.compileShader(shader);
// Check the compile status
var compiled = ctx.getShaderParameter(shader, wgl.COMPILE_STATUS);
if (!compiled) {
// Something went wrong during compilation; get the error
var error = ctx.getShaderInfoLog(shader);
webglTestLog("*** Error compiling shader: $error, shaderSource: $shaderSource");
ctx.deleteShader(shader);
return null;
}
return shader;
}
loadShaderFromFile(ctx, file, type)
{
return loadShader(ctx, file, type, true);
}
loadShaderFromScript(ctx, script)
{
return loadShader(ctx, script, 0, false);
}
loadStandardProgram(context) {
var program = context.createProgram();
context.attachShader(program, loadStandardVertexShader(context));
context.attachShader(program, loadStandardFragmentShader(context));
context.linkProgram(program);
return program;
}
loadProgram(context, vertexShaderPath, fragmentShaderPath, [isFile=true]) {
var program = context.createProgram();
context.attachShader(program, loadShader(context, vertexShaderPath, wgl.VERTEX_SHADER, isFile));
context.attachShader(program, loadShader(context, fragmentShaderPath, wgl.FRAGMENT_SHADER, isFile));
context.linkProgram(program);
return program;
}
loadStandardVertexShader(context) {
return loadShader(context, "$root/vertexShader.vert", wgl.VERTEX_SHADER, true);
}
loadStandardFragmentShader(context) {
return loadShader(context, "$root/fragmentShader.frag", wgl.FRAGMENT_SHADER, true);
}