dreemgl
Version:
DreemGL is an open-source multi-screen prototyping framework for mediated environments, with a visual editor and shader styling for webGL and DALi runtimes written in JavaScript. As a toolkit for gpu-accelerated multiscreen development, DreemGL includes
217 lines (184 loc) • 6.71 kB
JavaScript
/* DreemGL is a collaboration between Teeming Society & Samsung Electronics, sponsored by Samsung and others.
Copyright 2015-2016 Teeming Society. Licensed under the Apache License, Version 2.0 (the "License"); You may not use this file except in compliance with the License.
You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and limitations under the License.*/
define.class('$system/base/worker', function(require, exports){
var rpchub = require('$system/rpc/rpchub')
this._allocPromise = rpchub.allocPromise
this._resolveReturn = rpchub.resolveReturn
this._startWorkers = function(head, tail, count){
if(!count) count = 1
var source = head + '\n\n\n;// Worker includes \nself.define = {packaged:true,$platform:"webgl"};(' + define.inner.toString() + ')();\n'
source += '(' + define.getModule('$system/base/math.js').factory.toString() + ')();\n'
for(var key in define.paths){
source += 'define.$'+key + ' = "'+define['$'+key]+'";\n'
}
source += tail
var blob = new Blob([source], { type: "text/javascript" })
var worker_url = URL.createObjectURL(blob)
var workers = []
for(var i = 0; i < count; i ++){
var worker = new Worker(worker_url)
worker.postMessage({initid:true, workerid:i})
worker.source = source
worker.stack = 0
workers.push(worker)
}
return workers
}
this._collectDeps = function(factory, extradeps){
// lets serialize our module system into a worker
var outdeps = {}
function collectBodyDeps(body){
var intreq = define.findRequiresInFactory(body)
for(var j = 0 ; j < intreq.length; j++){
intreq[j] = define.expandVariables(intreq[j])
}
if(intreq.length) collectDeps(intreq)
}
function collectDeps(deps){
if(!deps) return
for(var i = 0; i < deps.length; i++){
var dep = deps[i]
if(outdeps[dep] || outdeps[dep+'.js']) continue
var module = define.getModule(dep)//module[dep]
if(!module) module = define.getModule(dep+'.js'), dep += '.js'
if(!module || !module.factory || typeof module.factory !== 'function') continue
// alright so lets recur on deps
outdeps[dep] = 1
if(module.factory.body){
collectBodyDeps(module.factory.body)
}
//console.log("COLLECTING", module.factory.deps)
collectDeps(module.factory.deps)
// and now add our module
if(module.factory.body){
var str = 'define.packagedClass("'+dep+'",['
if(module.factory.baseclass) str += '"'+module.factory.baseclass+'",'
str += module.factory.body.toString() + ']);\n'
outdeps[dep] = str
}
else{
outdeps[dep] = 'define(' + module.factory.toString() + ',"'+dep+'");\n'
}
}
}
collectDeps(factory.deps)
collectDeps(extradeps)
collectBodyDeps(factory.body)
return outdeps
}
this._transformThisToRPC = function(){
for(var key in this){
var prop = this[key]
if(key === 'onmessage'){
}
else if(key === 'atConstructor'){
this[key] = undefined
}
else if(typeof prop === 'function' && key[0] !== '_'){
this[key] = function(key){
// alright lets pick the lowest-queue worker from the set
var min = Infinity, tgtid = 0
for(var i = 0; i < this._workers.length; i++){
var stack = this._workers[i].stack
if(stack < min) min = stack, tgtid = i
}
var msg = {type:'call', name:key, args:[], workerid:tgtid}
for(var i = 1; i < arguments.length; i++){
msg.args[i - 1] = arguments[i]
//!TODO add typed array transfer feature
}
var worker = this._workers[tgtid]
if(!worker) return new define.Promise(function(resolve){resolve()})
var prom = this._allocPromise()
msg.uid = prom.uid
this._workers[tgtid].stack++
this._workers[tgtid].postMessage(msg)
return prom
}.bind(this,key)
}
}
}
this._atConstructor = function(cores){
if(cores === undefined) cores = 1
else if(cores < 1){
if(define.cputhreads === 2) cores = 1
else cores = define.cputhreads - 2
}
var deps = this._collectDeps(this.constructor.module.factory)
var head = 'var _myworker = ' + this.constructor.body.toString() + ';\n'
var tail = ''
for(var key in deps){
//console.log(key)
tail += deps[key]
}
tail += 'define.packagedClass("/myworker.js",["$system/base/worker",_myworker]);\n'
// lets start with requiring /myworker
tail += 'var _worker = define.require(\'/myworker\')();\n'
tail += '_worker.postMessage = function(msg,transfer){self.postMessage({message:msg,workerid:_worker.workerid},transfer)};\n'
tail += _worker_return.toString() + ';\n'
function workermsg(event){
var msg = event.data
if(msg.initid){
_worker.workerid = msg.workerid
return
}
if(msg.message){
_worker.onmessage(msg.message)
return
}
var ret = _worker[msg.name].apply(_worker, msg.args);
if(ret && ret.then) ret.then(function(value){
_worker_return(value, msg.uid, msg.workerid)
})
else _worker_return(ret, msg.uid, msg.workerid)
}
function _worker_return(ret, uid, workerid){
// fix the typed object handling
var transfer
if(ret && (typeof ret === 'object' || Array.isArray(ret))){
for(var key in ret){
var prop = ret[key]
if(prop && typeof prop.struct === 'function' && prop.array){
transfer = transfer || []
transfer.push(prop.array.buffer)
ret[key] = {
allocated: prop.allocated,
array:prop.array,
length: prop.length,
slots: prop.slots,
stride: prop.stride,
__structarray__: prop.struct.name
}
}
}
}
self.postMessage({value:ret, uid:uid, workerid:workerid}, transfer)
}
tail += 'self.onmessage = ' + workermsg.toString() + '\n'
var onmessage = function(event){
// lets plug the struct arrays
var dt = Date.now()
var data = event.data
if(data.message){
return this.onmessage(data.message)
}
//if(Date.now() - dt > 50) console.log(data)
var workerid = data.workerid
this._workers[workerid].stack --
this._resolveReturn(data)
}.bind(this)
this._workers = this._startWorkers(head, tail, cores)
for(var i = 0; i < this._workers.length; i++){
this._workers[i].onmessage = onmessage
}
this._transformThisToRPC()
this.postMessage = function(msg, transfer, tgtid){
// post to a worker
var workerid = tgtid || 0
this._workers[workerid].postMessage({message:msg}, transfer)
}
}
})