diff options
author | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-21 00:48:46 +0000 |
---|---|---|
committer | aa@chromium.org <aa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-21 00:48:46 +0000 |
commit | d73341d1c10528683ec39d71a1152b7cf8facd9b (patch) | |
tree | 6df4853b4ccf714710fc17d4b1debcffebddc63d /mojo/apps | |
parent | 92cf2dfdeabc8ad6f0672081ae37724f605e8190 (diff) | |
download | chromium_src-d73341d1c10528683ec39d71a1152b7cf8facd9b.zip chromium_src-d73341d1c10528683ec39d71a1152b7cf8facd9b.tar.gz chromium_src-d73341d1c10528683ec39d71a1152b7cf8facd9b.tar.bz2 |
Implement more of the JavaScript GL API.
Right now I am just translating as directly as possible from the C spinning cube demo -- once it all works I will circle back through and refine the JavaScript.
R=abarth@chromium.org
Review URL: https://codereview.chromium.org/114883003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242230 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'mojo/apps')
-rw-r--r-- | mojo/apps/js/bindings/codec.js | 19 | ||||
-rw-r--r-- | mojo/apps/js/bindings/connector.js | 3 | ||||
-rw-r--r-- | mojo/apps/js/bindings/gl/context.cc | 131 | ||||
-rw-r--r-- | mojo/apps/js/bindings/gl/context.h | 27 | ||||
-rw-r--r-- | mojo/apps/js/bindings/gl/opaque.cc | 24 | ||||
-rw-r--r-- | mojo/apps/js/bindings/gl/opaque.h | 40 | ||||
-rw-r--r-- | mojo/apps/js/main.js | 215 |
7 files changed, 314 insertions, 145 deletions
diff --git a/mojo/apps/js/bindings/codec.js b/mojo/apps/js/bindings/codec.js index 0245de4..edaa005 100644 --- a/mojo/apps/js/bindings/codec.js +++ b/mojo/apps/js/bindings/codec.js @@ -44,12 +44,6 @@ define(function() { (memory[pointer + 3] << 24); } - function load64(memory, pointer) { - var low = load32(memory, pointer); - var high = load32(memory, pointer + 4); - return low + high * 0x10000; - } - var kAlignment = 8; function align(size) { @@ -98,6 +92,9 @@ define(function() { this.handles = handles; this.base = base; this.next = base; + this.viewU32 = new Uint32Array( + this.memory.buffer, 0, + Math.floor(this.memory.length / Uint32Array.BYTES_PER_ELEMENT)); } Decoder.prototype.skip = function(offset) { @@ -111,15 +108,15 @@ define(function() { }; Decoder.prototype.read32 = function() { - var result = load32(this.memory, this.next); - this.next += 4; + var result = this.viewU32[this.next / this.viewU32.BYTES_PER_ELEMENT]; + this.next += this.viewU32.BYTES_PER_ELEMENT; return result; }; Decoder.prototype.read64 = function() { - var result = load64(this.memory, this.next); - this.next += 8; - return result; + var low = this.read32(); + var high = this.read32(); + return low + high * 0x100000000; }; Decoder.prototype.decodePointer = function() { diff --git a/mojo/apps/js/bindings/connector.js b/mojo/apps/js/bindings/connector.js index 42f2f9d..89caa97 100644 --- a/mojo/apps/js/bindings/connector.js +++ b/mojo/apps/js/bindings/connector.js @@ -3,10 +3,11 @@ // found in the LICENSE file. define([ + "console", "mojo/apps/js/bindings/codec", "mojo/apps/js/bindings/core", "mojo/apps/js/bindings/support", -], function(codec, core, support) { +], function(console, codec, core, support) { function Connector(handle) { this.handle_ = handle; diff --git a/mojo/apps/js/bindings/gl/context.cc b/mojo/apps/js/bindings/gl/context.cc index c0bb74a..ade9883 100644 --- a/mojo/apps/js/bindings/gl/context.cc +++ b/mojo/apps/js/bindings/gl/context.cc @@ -7,9 +7,24 @@ #include <GLES2/gl2.h> #include "gin/arguments.h" +#include "gin/array_buffer.h" #include "gin/object_template_builder.h" #include "mojo/public/gles2/gles2.h" +namespace gin { +template<> +struct Converter<GLboolean> { + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + GLboolean* out) { + bool bool_val = false; + if (!Converter<bool>::FromV8(isolate, val, &bool_val)) + return false; + *out = static_cast<GLboolean>(bool_val); + return true; + } +}; +} + namespace mojo { namespace js { namespace gl { @@ -21,42 +36,112 @@ gin::Handle<Context> Context::Create(v8::Isolate* isolate, uint64_t encoded, return gin::CreateHandle(isolate, new Context(encoded, width, height)); } -gin::Handle<Shader> Context::CreateShader(const gin::Arguments& args, - GLenum type) { - gin::Handle<Shader> result; - GLuint glshader = glCreateShader(type); - if (glshader != 0u) { - result = Opaque::Create(args.isolate(), glshader); +void Context::BufferData(GLenum target, const gin::ArrayBufferView& buffer, + GLenum usage) { + glBufferData(target, static_cast<GLsizeiptr>(buffer.num_bytes()), + buffer.bytes(), usage); +} + +void Context::CompileShader(const gin::Arguments& args, GLuint shader) { + glCompileShader(shader); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + if (!compiled) { + args.ThrowTypeError(std::string("Could not compile shader: ") + + GetShaderInfoLog(shader)); } +} + +GLuint Context::CreateBuffer() { + GLuint result = 0; + glGenBuffers(1, &result); return result; } -void Context::ShaderSource(gin::Handle<Shader> shader, - const std::string& source) { +void Context::DrawElements(GLenum mode, GLsizei count, GLenum type, + uint64_t indices) { + // This looks scary, but it's what WebGL does too: + // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.1 + glDrawElements(mode, count, type, reinterpret_cast<void*>(indices)); +} + +GLint Context::GetAttribLocation(GLuint program, const std::string& name) { + return glGetAttribLocation(program, name.c_str()); +} + +std::string Context::GetProgramInfoLog(GLuint program) { + GLint info_log_length = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_log_length); + std::string info_log(info_log_length, 0); + glGetProgramInfoLog(program, info_log_length, NULL, &info_log.at(0)); + return info_log; +} + +std::string Context::GetShaderInfoLog(GLuint shader) { + GLint info_log_length = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &info_log_length); + std::string info_log(info_log_length, 0); + glGetShaderInfoLog(shader, info_log_length, NULL, &info_log.at(0)); + return info_log; +} + +GLint Context::GetUniformLocation(GLuint program, const std::string& name) { + return glGetUniformLocation(program, name.c_str()); +} + +void Context::ShaderSource(GLuint shader, const std::string& source) { const char* source_chars = source.c_str(); - glShaderSource(shader->value(), 1, &source_chars, NULL); + glShaderSource(shader, 1, &source_chars, NULL); } -void Context::CompileShader(const gin::Arguments& args, - gin::Handle<Shader> shader) { - glCompileShader(shader->value()); - GLint compiled = 0; - glGetShaderiv(shader->value(), GL_COMPILE_STATUS, &compiled); - if (!compiled) { - // Or should |shader| do it when it is destroyed? - glDeleteShader(shader->value()); - args.ThrowTypeError("Could not compile shader"); - return; - } +void Context::UniformMatrix4fv(GLint location, GLboolean transpose, + const gin::ArrayBufferView& buffer) { + glUniformMatrix4fv(location, 1, transpose, + static_cast<float*>(buffer.bytes())); +} + +void Context::VertexAttribPointer(GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + uint64_t offset) { + glVertexAttribPointer(index, size, type, normalized, stride, + reinterpret_cast<void*>(offset)); } gin::ObjectTemplateBuilder Context::GetObjectTemplateBuilder( v8::Isolate* isolate) { - return gin::Wrappable<Context>::GetObjectTemplateBuilder(isolate) + return gin::ObjectTemplateBuilder(isolate) + .SetValue("ARRAY_BUFFER", GL_ARRAY_BUFFER) + .SetValue("COLOR_BUFFER_BIT", GL_COLOR_BUFFER_BIT) + .SetValue("ELEMENT_ARRAY_BUFFER", GL_ELEMENT_ARRAY_BUFFER) + .SetValue("FLOAT", GL_FLOAT) + .SetValue("FRAGMENT_SHADER", GL_FRAGMENT_SHADER) + .SetValue("STATIC_DRAW", GL_STATIC_DRAW) + .SetValue("TRIANGLES", GL_TRIANGLES) + .SetValue("UNSIGNED_SHORT", GL_UNSIGNED_SHORT) .SetValue("VERTEX_SHADER", GL_VERTEX_SHADER) - .SetMethod("createShader", CreateShader) + .SetMethod("attachShader", glAttachShader) + .SetMethod("bindBuffer", glBindBuffer) + .SetMethod("bufferData", BufferData) + .SetMethod("clear", glClear) + .SetMethod("clearColor", glClearColor) + .SetMethod("compileShader", CompileShader) + .SetMethod("createBuffer", CreateBuffer) + .SetMethod("createProgram", glCreateProgram) + .SetMethod("createShader", glCreateShader) + .SetMethod("deleteShader", glDeleteShader) + .SetMethod("drawElements", DrawElements) + .SetMethod("enableVertexAttribArray", glEnableVertexAttribArray) + .SetMethod("getAttribLocation", GetAttribLocation) + .SetMethod("getProgramInfoLog", GetProgramInfoLog) + .SetMethod("getShaderInfoLog", GetShaderInfoLog) + .SetMethod("getUniformLocation", GetUniformLocation) + .SetMethod("linkProgram", glLinkProgram) .SetMethod("shaderSource", ShaderSource) - .SetMethod("compileShader", CompileShader); + .SetMethod("swapBuffers", MojoGLES2SwapBuffers) + .SetMethod("uniformMatrix4fv", UniformMatrix4fv) + .SetMethod("useProgram", glUseProgram) + .SetMethod("vertexAttribPointer", VertexAttribPointer) + .SetMethod("viewport", glViewport); } Context::Context(uint64_t encoded, int width, int height) diff --git a/mojo/apps/js/bindings/gl/context.h b/mojo/apps/js/bindings/gl/context.h index a5a3ca1..081dbb2 100644 --- a/mojo/apps/js/bindings/gl/context.h +++ b/mojo/apps/js/bindings/gl/context.h @@ -10,19 +10,17 @@ #include "gin/handle.h" #include "gin/public/wrapper_info.h" #include "gin/wrappable.h" -#include "mojo/apps/js/bindings/gl/opaque.h" #include "v8/include/v8.h" namespace gin { class Arguments; +class ArrayBufferView; } namespace mojo { namespace js { namespace gl { -typedef Opaque Shader; - // Context implements WebGLRenderingContext. class Context : public gin::Wrappable<Context> { public: @@ -30,12 +28,23 @@ class Context : public gin::Wrappable<Context> { static gin::Handle<Context> Create(v8::Isolate* isolate, uint64_t encoded, int width, int height); - static gin::Handle<Shader> CreateShader(const gin::Arguments& arguments, - GLenum type); - static void ShaderSource(gin::Handle<Shader> shader, - const std::string& source); - static void CompileShader(const gin::Arguments& arguments, - gin::Handle<Shader> shader); + + static void BufferData(GLenum target, const gin::ArrayBufferView& buffer, + GLenum usage); + static void CompileShader(const gin::Arguments& args, GLuint shader); + static GLuint CreateBuffer(); + static void DrawElements(GLenum mode, GLsizei count, GLenum type, + uint64_t indices); + static GLint GetAttribLocation(GLuint program, const std::string& name); + static std::string GetProgramInfoLog(GLuint program); + static std::string GetShaderInfoLog(GLuint shader); + static GLint GetUniformLocation(GLuint program, const std::string& name); + static void ShaderSource(GLuint shader, const std::string& source); + static void UniformMatrix4fv(GLint location, GLboolean transpose, + const gin::ArrayBufferView& buffer); + static void VertexAttribPointer(GLuint index, GLint size, GLenum type, + GLboolean normalized, GLsizei stride, + uint64_t offset); private: virtual gin::ObjectTemplateBuilder GetObjectTemplateBuilder( diff --git a/mojo/apps/js/bindings/gl/opaque.cc b/mojo/apps/js/bindings/gl/opaque.cc deleted file mode 100644 index 91365f8..0000000 --- a/mojo/apps/js/bindings/gl/opaque.cc +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "mojo/apps/js/bindings/gl/opaque.h" - -#include "gin/object_template_builder.h" - -namespace mojo { -namespace js { -namespace gl { - -gin::WrapperInfo Opaque::kWrapperInfo = { gin::kEmbedderNativeGin }; - -gin::Handle<Opaque> Opaque::Create(v8::Isolate* isolate, GLuint value) { - return gin::CreateHandle(isolate, new Opaque(value)); -} - -Opaque::Opaque(GLuint value) : value_(value) { -} - -} // namespace gl -} // namespace js -} // namespace mojo diff --git a/mojo/apps/js/bindings/gl/opaque.h b/mojo/apps/js/bindings/gl/opaque.h deleted file mode 100644 index 4755658..0000000 --- a/mojo/apps/js/bindings/gl/opaque.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MOJO_APPS_JS_BINDINGS_GL_OPAQUE_H_ -#define MOJO_APPS_JS_BINDINGS_GL_OPAQUE_H_ - -#include <GLES2/gl2.h> - -#include "gin/handle.h" -#include "gin/public/wrapper_info.h" -#include "gin/wrappable.h" -#include "v8/include/v8.h" - -namespace mojo { -namespace js { -namespace gl { - -// WebGL has many interfaces, such as WebGLObject, WebGLBuffer, etc, which wrap -// integers that are opaque to script. This class is used for all those objects. -class Opaque : public gin::Wrappable<Opaque> { - public: - static gin::WrapperInfo kWrapperInfo; - - static gin::Handle<Opaque> Create(v8::Isolate* isolate, GLuint value); - - GLuint value() const { return value_; } - void set_value(GLuint val) { value_ = val; } - - private: - Opaque(GLuint value); - - GLuint value_; -}; - -} // namespace gl -} // namespace js -} // namespace mojo - -#endif // MOJO_APPS_JS_BINDINGS_GL_CONTEXT_H_ diff --git a/mojo/apps/js/main.js b/mojo/apps/js/main.js index 88115ff..52bfc5b 100644 --- a/mojo/apps/js/main.js +++ b/mojo/apps/js/main.js @@ -3,78 +3,219 @@ // found in the LICENSE file. define([ - "console", - "mojo/apps/js/bindings/connector", - "mojo/apps/js/bindings/core", - "mojo/apps/js/bindings/gl", - "mojo/apps/js/bindings/threading", - "mojom/native_viewport", - "mojom/gles2", + 'console', + 'mojo/apps/js/bindings/connector', + 'mojo/apps/js/bindings/core', + 'mojo/apps/js/bindings/gl', + 'mojo/apps/js/bindings/threading', + 'mojom/native_viewport', + 'mojom/gles2', + 'mojom/shell', ], function(console, connector, core, gljs, threading, nativeViewport, - gles2) { + gles2, + shell) { - const VERTEX_SHADER_SOURCE = - "uniform mat4 u_mvpMatrix; \n" + - "attribute vec4 a_position; \n" + - "void main() \n" + - "{ \n" + - " gl_Position = u_mvpMatrix * a_position; \n" + - "} \n"; + const VERTEX_SHADER_SOURCE = [ + 'uniform mat4 u_mvpMatrix;', + 'attribute vec4 a_position;', + 'void main()', + '{', + ' gl_Position = u_mvpMatrix * a_position;', + '}' + ].join('\n'); - function NativeViewportClientImpl() { + const FRAGMENT_SHADER_SOURCE = [ + 'precision mediump float;', + 'void main()', + '{', + ' gl_FragColor = vec4( 0.0, 1.0, 0.0, 1.0 );', + '}' + ].join('\n'); + + function ESMatrix() { + this.m = new Float32Array(16); } + ESMatrix.prototype.loadZero = function() { + for (var i = 0; i < this.m.length; i++) { + this.m[i] = 0; + } + }; + + ESMatrix.prototype.loadIdentity = function() { + this.loadZero(); + for (var i = 0; i < 4; i++) { + this.m[i*4+i] = 1; + } + }; + + function loadProgram(gl) { + var vertexShader = gl.createShader(gl.VERTEX_SHADER); + gl.shaderSource(vertexShader, VERTEX_SHADER_SOURCE); + gl.compileShader(vertexShader); + + var fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); + gl.shaderSource(fragmentShader, FRAGMENT_SHADER_SOURCE); + gl.compileShader(fragmentShader); + + var program = gl.createProgram(); + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + + gl.linkProgram(program); + // TODO(aa): Check for errors using getProgramiv and LINK_STATUS. + + gl.deleteShader(vertexShader); + gl.deleteShader(fragmentShader); + + return program; + } + + var vboVertices; + var vboIndices; + function generateCube(gl) { + var numVertices = 24 * 3; + var numIndices = 12 * 3; + + var cubeVertices = new Float32Array([ + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + 0.5, -0.5, 0.5, + 0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5, + -0.5, -0.5, -0.5, + -0.5, 0.5, -0.5, + 0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, -0.5, 0.5, + -0.5, -0.5, -0.5, + -0.5, -0.5, 0.5, + -0.5, 0.5, 0.5, + -0.5, 0.5, -0.5, + 0.5, -0.5, -0.5, + 0.5, -0.5, 0.5, + 0.5, 0.5, 0.5, + 0.5, 0.5, -0.5 + ]); + + var cubeIndices = new Uint16Array([ + 0, 2, 1, + 0, 3, 2, + 4, 5, 6, + 4, 6, 7, + 8, 9, 10, + 8, 10, 11, + 12, 15, 14, + 12, 14, 13, + 16, 17, 18, + 16, 18, 19, + 20, 23, 22, + 20, 22, 21 + ]); + + // TODO(aa): The C++ program branches here on whether the pointer is + // non-NULL. + vboVertices = gl.createBuffer(); + gl.bindBuffer(gl.ARRAY_BUFFER, vboVertices); + gl.bufferData(gl.ARRAY_BUFFER, cubeVertices, gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, 0); + + vboIndices = gl.createBuffer(); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vboIndices); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, cubeIndices, gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, 0); + + return cubeIndices.length; + } + + function drawCube(gl, width, height, program, positionLocation, mvpLocation, + numIndices, mvpMatrix) { + gl.viewport(0, 0, width, height); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.useProgram(program); + gl.bindBuffer(gl.ARRAY_BUFFER, vboVertices); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, vboIndices); + gl.vertexAttribPointer(positionLocation, 3, gl.FLOAT, false, 12, 0); + gl.enableVertexAttribArray(positionLocation); + gl.uniformMatrix4fv(mvpLocation, false, mvpMatrix.m); + gl.drawElements(gl.TRIANGLES, numIndices, gl.UNSIGNED_SHORT, 0); + } + + function SampleApp(shell) { + this.shell_ = shell; + + var pipe = new core.createMessagePipe(); + new connector.Connection(pipe.handle0, NativeViewportClientImpl, + nativeViewport.NativeViewportProxy); + this.shell_.connect('mojo:mojo_native_viewport_service', pipe.handle1); + } // TODO(aa): It is a bummer to need this stub object in JavaScript. We should // have a 'client' object that contains both the sending and receiving bits of // the client side of the interface. Since JS is loosely typed, we do not need // a separate base class to inherit from to receive callbacks. + SampleApp.prototype = Object.create(shell.ShellClientStub.prototype); + + + function NativeViewportClientImpl(remote) { + this.remote_ = remote; + + var pipe = core.createMessagePipe(); + new connector.Connection(pipe.handle0, GLES2ClientImpl, gles2.GLES2Proxy); + + this.remote_.open(); + this.remote_.createGLES2Context(pipe.handle1); + } NativeViewportClientImpl.prototype = Object.create(nativeViewport.NativeViewportClientStub.prototype); + NativeViewportClientImpl.prototype.onCreated = function() { + console.log(['NativeViewportClientImpl.prototype.OnCreated']); + }; NativeViewportClientImpl.prototype.didOpen = function() { - console.log("NativeViewportClientImpl.prototype.DidOpen"); + console.log(['NativeViewportClientImpl.prototype.DidOpen']); }; - function GLES2ClientImpl() { + function GLES2ClientImpl(remote) { + this.remote_ = remote; } - GLES2ClientImpl.prototype = Object.create(gles2.GLES2ClientStub.prototype); GLES2ClientImpl.prototype.didCreateContext = function(encoded, width, height) { - console.log("GLES2ClientImpl.prototype.didCreateContext"); var gl = new gljs.Context(encoded, width, height); + var program = loadProgram(gl); + var positionLocation = gl.getAttribLocation(program, 'a_position'); + var mvpLocation = gl.getUniformLocation(program, 'u_mvpMatrix'); + var numIndices = generateCube(gl); + var mvpMatrix = new ESMatrix(); - var shader = gl.createShader(gl.VERTEX_SHADER); - console.log("shader is: ", String(shader)); - gl.shaderSource(shader, VERTEX_SHADER_SOURCE); - gl.compileShader(shader); - console.log("all done"); + gl.clearColor(0, 0, 0, 0); + mvpMatrix.loadIdentity(); + drawCube(gl, width, height, program, positionLocation, mvpLocation, + numIndices, mvpMatrix); + gl.swapBuffers(); }; GLES2ClientImpl.prototype.contextLost = function() { - console.log("GLES2ClientImpl.prototype.contextLost"); + console.log(['GLES2ClientImpl.prototype.contextLost']); }; - return function(handle) { - var nativeViewportConnection = new connector.Connection( - handle, - NativeViewportClientImpl, - nativeViewport.NativeViewportProxy); - - var gles2Handles = core.createMessagePipe(); - var gles2Connection = new connector.Connection( - gles2Handles.handle0, GLES2ClientImpl, gles2.GLES2Proxy); - nativeViewportConnection.remote.open(); - nativeViewportConnection.remote.createGLES2Context(gles2Handles.handle1); + return function(handle) { + new connector.Connection(handle, SampleApp, shell.ShellProxy); }; }); |