-
-
Save okuoku/e913cf6f132602f46a6178264b289ed9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const d = require("jsdom"); | |
const {JSDOM, ResourceLoader} = d; | |
const httpServer = require("http-server"); | |
const gl = require("gl"); | |
const WebGLRenderingContext = require("gl/src/javascript/webgl-rendering-context"); | |
const WebGLDebugUtils = require("./webgl-debug.js")(); | |
const Datauri = require("datauri"); | |
const fs = require("fs"); | |
const path = require("path"); | |
const PNG = require("pngjs").PNG; | |
const indexedDB = require("fake-indexeddb"); | |
const projroot = "c:/Users/oku/WebGLUTSTest/gltest"; | |
const port = 12344; | |
const url = "http://127.0.0.1:12344"; | |
// Blob server | |
let blob_count = 0; | |
let blobs = {}; | |
// Screenshot | |
let g_ctx = false; | |
function blob_to_url(blob){ | |
const myuri = "xblob:" + blob_count; | |
console.log(myuri, blob.type, blob); | |
blobs[myuri] = blob; | |
console.log("BLOBURI", myuri); | |
blob_count ++; | |
return myuri; | |
} | |
function fetch_blob(uri){ // => promise | |
const r = blobs[uri](); | |
return r; | |
} | |
class MyLoader extends ResourceLoader { | |
fetch(url, options){ | |
console.log("LOADER",url,options); | |
if(url == "xblob:1"){ | |
console.log("PATCH!!!"); | |
const buf = fs.readFileSync(path.resolve(__dirname, "patch1.js")); | |
return Promise.resolve(buf); | |
}else if(url.indexOf("xblob:") != -1){ | |
return fetch_blob(url); | |
}else{ | |
return super.fetch(url, options); | |
} | |
} | |
} | |
function servlog(req, res){ | |
console.log("internalserv", req.method, req.url); | |
} | |
class FakeWorker { | |
constructor(url){ | |
the_loader.fetch(url) | |
.then(buf => { | |
const text = buf.toString(); | |
const src = "const obj = function (postMessage) {" + text + "}; obj"; | |
const ex = eval(src); | |
const home = this; | |
const recvmsg = function(x,y){ | |
if(home.the_handler){ | |
let ev = {data: x}; | |
console.log("RECVMSG", x, y); | |
home.the_handler(ev); | |
}else{ | |
console.log("!! Q RECV MSG", x, y); | |
home.recvq.push([x,y]); | |
} | |
} | |
this.workerobj = {}; | |
this.workerobj._init = ex; | |
this.workerobj._init(recvmsg); | |
console.log("WORKER STANDUP", this.workerobj); | |
this.sendq.forEach(e => { | |
// FIXME: this doesn't support Queue message inside | |
// send handler | |
console.log("!! DEQ MSG", e[0], e[1]); | |
let ev = {data: e[0]}; | |
this.workerobj.onmessage(ev); | |
}); | |
this.sendq = []; | |
}); | |
}; | |
postMessage(x,y){ | |
if(!this.workerobj){ | |
console.log("!! Q MSG", x); | |
this.sendq.push([x,y]); | |
}else{ | |
let e = {data: x}; | |
console.log("SENDMSG", x); | |
this.workerobj.onmessage(e); | |
} | |
}; | |
set onmessage(fn){ | |
console.log("HANDLER SET", fn); | |
this.the_handler = fn; | |
this.recvq.forEach(e => { | |
let ev = {data: e[0]}; | |
fn(ev); | |
}); | |
this.recvq = []; | |
} | |
the_handler = false; | |
workerobj = false; | |
sendq = []; | |
recvq = []; | |
} | |
function patchWindow(w){ | |
let dump_count = 0; | |
const HTMLElement = w.HTMLElement; | |
const d = w.document; | |
d.super_createElement = d.createElement; | |
d.super_addEventListener = d.addEventListener; | |
w.super_addEventListener = w.addEventListener; | |
function createObjectURL(blob){ | |
let cache = false; | |
function cb(){ | |
if(cache){ | |
return Promise.resolve(cache); | |
}else{ | |
return new Promise((res, rej) => { | |
const the_reader = new w.FileReader(); | |
the_reader.onload = (e => { | |
const bv = the_reader.result; | |
//console.log("DONE", bv); | |
cache = Buffer.from(bv); | |
//console.log("RES", cache); | |
fs.writeFileSync("dump" + dump_count.toString() + ".js", | |
cache); | |
dump_count ++; | |
res(cache); | |
}); | |
// FIXME: ??? It seems JSDOM ArrayBuffer cannot move to | |
// Buffer object. Use readAsText instead for now... | |
the_reader.readAsText(blob); | |
}); | |
} | |
} | |
return blob_to_url(cb); | |
} | |
function revokeObjectURL(bogus) { | |
} | |
function proxyCEl(nam){ | |
console.log("PROXY CEL", nam); | |
if(nam == "canvas"){ | |
let cv = d.super_createElement("div"); | |
cv.getContext = function(type, attr){ | |
console.log("PROXY CTX", type, attr); | |
console.log(attr); | |
if(type == "2d"){ | |
console.log("???"); | |
return null; | |
}else if(type == "webgl2"){ | |
return null; | |
}else if(type == "experimental-webgl2"){ | |
return null; | |
}else{ | |
function glTrace(name, args){ | |
console.log("GL", name, args.length); | |
/* | |
let x = {}; | |
Error.stackTraceLimit = 9999; | |
Error.captureStackTrace(x); | |
console.log(x.stack); | |
*/ | |
} | |
function errorcheck(err, name, args){ | |
console.log("GLERR", err, name, args); | |
if(name == "bufferSubData"){ | |
console.log("Q", ctx0._getActiveBuffer(args[0])); | |
} | |
process.exit(1); | |
} | |
const ctx0 = gl(1920,1080,attr); | |
const orig_setError = ctx0.setError; | |
WebGLDebugUtils.init(ctx0); | |
const ctx = WebGLDebugUtils | |
.makeDebugContext(ctx0,errorcheck,glTrace); | |
ctx.canvas = cv; | |
ctx0.canvas = cv; | |
g_ctx = ctx0; | |
return ctx0; | |
} | |
} | |
return cv; | |
}else{ | |
const el = d.super_createElement(nam); | |
return el; | |
} | |
} | |
function proxyAlert(msg){ | |
console.log("ALERT", msg); | |
} | |
function sleep(ms){ | |
return new Promise((res) => setTimeout(res, ms)); | |
} | |
function proxyRAF(cb){ | |
//console.log("RAF", cb); | |
process.nextTick(async function(){ | |
await sleep(100); | |
const now = w.performance.now(); | |
console.log("RAF", now); | |
cb(now); | |
update_screenshot(); | |
}); | |
return 99.99; | |
} | |
function proxyAELW(type, cb, options){ | |
let x = {}; | |
console.log("AELW", type, cb, options); | |
Error.captureStackTrace(x); | |
console.log(x.stack); | |
w.super_addEventListener(type, cb, options); | |
} | |
function proxyAEL(type, cb, options){ | |
let x = {}; | |
console.log("AEL", type, cb, options); | |
Error.captureStackTrace(x); | |
console.log(x.stack); | |
d.super_addEventListener(type, cb, options); | |
} | |
// UnityLoader.js has `(!window.WebGLRenderingContext)` check | |
w.WebGLRenderingContext = WebGLRenderingContext; | |
w.requestAnimationFrame = proxyRAF; | |
w.alert = proxyAlert; | |
w.URL.createObjectURL = createObjectURL; | |
w.URL.revokeObjectURL = revokeObjectURL; | |
w.Worker = FakeWorker; | |
d.createElement = proxyCEl; | |
d.addEventListener = proxyAEL; | |
w.addEventListener = proxyAELW; | |
w.indexedDB = indexedDB; | |
} | |
const pixels0 = new Uint8Array(1920 * 1080 * 4); | |
const pixels1 = new Uint8Array(1920 * 1080 * 4); | |
let flip_fb = 0; | |
let shots = 0; | |
function update_screenshot(){ | |
let pixels = false; | |
if(flip_fb == 0){ | |
pixels = pixels0; | |
flip_fb = 1; | |
}else{ | |
pixels = pixels1; | |
flip_fb = 0; | |
} | |
//console.log("Pre", g_ctx.getError()); | |
g_ctx.readPixels(0, 0, 1920, 1080, g_ctx.RGBA, g_ctx.UNSIGNED_BYTE, pixels); | |
//console.log("Post", g_ctx.getError()); | |
shots++; | |
//console.log("Shot", shots); | |
let pngout = PNG.sync.write({width: 1920, height: 1080, data: pixels}); | |
fs.writeFileSync("out" + shots.toString() + ".png", pngout); | |
console.log("Save", shots); | |
} | |
const serv = httpServer.createServer({root: projroot, cache: true, | |
logFn: servlog}); | |
serv.listen(port, "127.0.0.1"); | |
const the_loader = new MyLoader(); | |
const dom = JSDOM.fromURL(url,{ | |
beforeParse: function(w){ patchWindow(w); }, | |
pretendToBeVisual: true, | |
includeNodelocations: true, | |
resources: the_loader, | |
runScripts: "dangerously" | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment