From 24fa665f71b89be45cd891225046859b83e5e277 Mon Sep 17 00:00:00 2001 From: Aleksander Guryanov Date: Tue, 21 Mar 2017 18:11:57 +0700 Subject: [PATCH] version_3_1 --- .../emscripten/CCTextureCacheEmscripten.cpp | 331 +++++++++++++++++- external/emscripten | 1 - 2 files changed, 323 insertions(+), 9 deletions(-) delete mode 120000 external/emscripten diff --git a/cocos2dx/platform/emscripten/CCTextureCacheEmscripten.cpp b/cocos2dx/platform/emscripten/CCTextureCacheEmscripten.cpp index 502469391..baa19ed87 100644 --- a/cocos2dx/platform/emscripten/CCTextureCacheEmscripten.cpp +++ b/cocos2dx/platform/emscripten/CCTextureCacheEmscripten.cpp @@ -27,19 +27,324 @@ THE SOFTWARE. #include "platform/CCFileUtils.h" #include #include +#include using namespace cocos2d; using namespace std; #define MULTILINE(...) #__VA_ARGS__ -// Following methods are implemented in TextureCacheEmscripten.js: -extern "C" { -void cocos2dx_newAsyncImageLoader(int textureCache, int callback__ignored); -void cocos2dx_asyncImageLoader_LoadImage(const char *path, TextureCache::AsyncStruct *asyncData); -void cocos2dx_shutdownAsyncImageLoader(); +void cocos2dx_initAsyncImageLoaderLibrary() { + static bool isInited = false; + if (isInited) { + return; + } + EM_ASM({ +/**************************************************************************** +Copyright (c) 2013 Zynga Inc. + +http://www.cocos2d-x.org + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +****************************************************************************/ + + +// Non-namespaced facade over underlying objects. See above methods for +// documentation. These methods serve two purposes: +// +// 1. convert types to/from JS equivalents. +// 2. expose a "C-like" interface, such that the symbols are easy to find +// for calling code (Emscripten provides no easy way to access nested +// dictionaries). +// +// Note that the leading '_' is because Emscripten prefixes all function +// calls like this. Since this code is being injected at runtime, +// Emscripten's compiler doesn't have a chance to add it for us. + + +var LibraryCocosHelper = { + $cocos2dx__deps: [ '_CCTextureCacheEmscripten_preMultiplyImageRegion' ], + $cocos2dx: { + objects: {}, + classes: { + // AsyncOperationQueue -- simple worker queue. Note that all functions + // passed in should effectively be "static", and have all requisite + // information contained in their args object. + AsyncOperationQueue: function() + { + this.ops = []; + this.timeoutId = null; + + // Target using 2/3 of available CPU for texture loading. + this.sliceBudget = 8; + this.sliceInterval = 4; + + /** + * If there is a valid scheduled queue consumer, cancel it. Won't cancel + * currently executing operations (as this is not multi-threaded), but will + * prevent future execution from happening until it is re-scheduled. + */ + this.unschedule = function(interval) + { + if(typeof this.timeoutId == "number") + { + clearTimeout(this.timeoutId); + this.timeoutId = null; + } + }; + + /** + * Schedule the queue-processor to run @interval ms in the future. + */ + this.schedule = function(interval) + { + this.unschedule(); + + var that = this; + var o = function() { + that.run(); + }; + + this.timeoutId = setTimeout(o, interval); + }; + + /** + * Enqueue a funtcion @fn with arguments @args to run asynchronously at + * some point in the future, then return control to the caller. Operations + * are guaranteed to be run in the order in which they are enqueued, but no + * guarantee is given about when or in what context they will be executed. + */ + this.enqueue = function(fn, args) + { + var op = { + fn: fn, + args: args + }; + this.ops.push(op); + this.schedule(this.sliceInterval); + }; + + /** + * Start running the consumer "thread". Will consume operations from the + * queue until such time as either the queue is empty, or it has exceeded + * its time budget. If the latter case (budget exceeded), it will + * reschedule itself to run again later and finish draining the queue. + * + * Rescheduling logic still in flux, but the objective is to try to + * schedule it to run at some point after the next frame has been + * requested, whilst still maintaining maximum bandwidth. + */ + this.run = function() + { + if(this.ops.length === 0) + { + return; + } + + var start = +new Date(); + var end = +new Date(); + while(this.ops.length && ((end - start) < this.sliceBudget)) + { + var op = this.ops.shift(); + op.fn(op.args); + end = +new Date(); + } + + if(this.ops.length) + { + this.schedule(this.sliceInterval); + } + }; + + /** + * Unschedule any remaining operations in the queue. + */ + this.shutdown = function() + { + this.unschedule(); + this.ops = []; + }; + }, + + /** + * Construct a new AsyncImageLoader object. @cxxTextureCache should be a + * pointer to a valid CCTextureCacheEmscripten object in C++. + */ + AsyncImageLoader: function(cxxTextureCache) + { + this.cxxTextureCache = cxxTextureCache; + this.operationQueue = new cocos2dx.classes.AsyncOperationQueue(); + + // Since there will only ever be one image being processed at a time, + // we pre-initialize a canvas object and use it for all operations so + // as to save doing this for every image, incurring (probably) both + // compute work and memory fragmentation. + this.canvas = document.createElement('canvas'); + this.ctx = this.canvas.getContext('2d'); + + this.regionSize = 128; + + /** + * "Private" and "static" method to copy an input image to a given + * rectangle in the output image. Used by @loadImage. + */ + this._blitAndPremultipyRegion = function(args) + { + // Calculate width and height for this region such that we + // do not run off the edge of the image. + var rw = Math.min(args.i + this.regionSize, args.w) - args.i; + var rh = Math.min(args.j + this.regionSize, args.h) - args.j; + + this.ctx.drawImage(args.img, args.i, args.j, rw, rh, args.i, args.j, rw, rh); + + var imgData = this.ctx.getImageData(args.i, args.j, rw, rh); + var inImgData = _malloc(rw * rh * 4); + Module.HEAP8.set(imgData.data, inImgData); + + // Call into C++ code in CCTextureCacheEmscripten.cpp to do the actual + // copy and pre-multiply. + _CCTextureCacheEmscripten_preMultiplyImageRegion(inImgData, rw, rh, args.outImgData, args.w, args.h, args.i, args.j); + + _free(inImgData); + }; + + /** + * Enqueue the image at @path (interpreted as a URL) to be asynchronously + * loaded, then call the cocos2dx callback specified in @asyncData. Image + * fetch happens asynchronously using browser asset management code, and + * the work of copying pixel data to a buffer (for use by cocos2dx) is + * spread out over several frames such that disruptions to frame rate are + * minimized. + */ + this.loadImage = function(path, asyncData) + { + var img = new Image(); + var that = this; + + img.onload = function() + { + var w = img.width; + var h = img.height; + + // Setup the canvas in the queue also so that it happens in order + // with the operations that depend on the canvas having been + // configured to this resolution. We can get away with this since + // the queue guarantees that operations are executed in the order + // in which they are enqueued. + var setupCanvas = function(args) + { + that.canvas.width = args.w; + that.canvas.height = args.h; + }; + that.operationQueue.enqueue(setupCanvas, { w: w, h: h }); + + var outImgData = _malloc(w * h * 4); + + for(var i = 0; i < w; i += that.regionSize) + { + for(var j = 0; j < h; j += that.regionSize) + { + var args = { + i: i, j: j, + w: w, h: h, + img: img, + outImgData: outImgData + }; + that.operationQueue.enqueue(function(a) { that._blitAndPremultipyRegion(a); }, args); + } + } + + var fireCallback = function(args) { + _CCTextureCacheEmscripten_addImageAsyncCallBack(that.cxxTextureCache, args.asyncData, args.imgData, args.w, args.h); + }; + var opArgs = { + asyncData: asyncData, + imgData: outImgData, + w: w, h: h + }; + that.operationQueue.enqueue(fireCallback, opArgs); + }; + + img.onerror = function() + { + console.log("Error loading '" + path + "'"); + }; + + img.src = path; + }; + + /** + * Shutdown this image loader object. Used by destructor in cocos2dx. + */ + this.shutdown = function() + { + this.operationQueue.shutdown(); + }; + } + } + }, + + /* + * Construct a new AsyncImageLoader object. Held as a singleton, referred + * to in other wrapper methods here. + * @deps__ignored is ignored, but is accepted so that we can communicate to + * the compiler what functions we need to ensure are ready by the time this + * class gets instantiated. + */ + cocos2dx_newAsyncImageLoader: function(cxxTextureCache, deps__ignored) + { + cocos2dx.objects.asyncImageLoader = new cocos2dx.classes.AsyncImageLoader(cxxTextureCache); + }, + + /** + * Shutdown the current image loader object. Used by the cocos2dx + * destructor method. + */ + cocos2dx_shutdownAsyncImageLoader: function() + { + cocos2dx.objects.asyncImageLoader.shutdown(); + cocos2dx.objects.asyncImageLoader = null; + }, + + /** + * Load a new image asynchronously. + */ + cocos2dx_asyncImageLoader_LoadImage: function(path, asyncData) + { + var opArgs = { + path: Pointer_stringify(path), + data: asyncData + }; + var op = function(args) + { + cocos2dx.objects.asyncImageLoader.loadImage(args.path, args.data); + }; + cocos2dx.objects.asyncImageLoader.operationQueue.enqueue(op, opArgs); + } }; +window['LibraryCocosHelper'] = LibraryCocosHelper; +window['cocos2dx'] = LibraryCocosHelper.$cocos2dx; + +}); +} + extern "C" { // This C interface is exposed so that the JavaScript in // TextureCacheEmscripten.js is able to call into these functions. @@ -67,7 +372,11 @@ TextureCacheEmscripten::TextureCacheEmscripten() (int)&TextureCacheEmscripten_preMultiplyImageRegion }; - cocos2dx_newAsyncImageLoader((int)this, (int)&deps); + cocos2dx_initAsyncImageLoaderLibrary(); + // cocos2dx_newAsyncImageLoader((int)this, (int)&deps); + EM_ASM_ARGS({ + LibraryCocosHelper.cocos2dx_newAsyncImageLoader($0, $1); + }, (size_t)this, (size_t)&deps); } /** @@ -78,7 +387,10 @@ TextureCacheEmscripten::TextureCacheEmscripten() */ TextureCacheEmscripten::~TextureCacheEmscripten() { - cocos2dx_shutdownAsyncImageLoader(); + // cocos2dx_shutdownAsyncImageLoader(); + EM_ASM({ + LibraryCocosHelper.cocos2dx_shutdownAsyncImageLoader(); + }); } /** @@ -185,7 +497,10 @@ void TextureCacheEmscripten::addImageAsync(const char *path, Object *target, SEL AsyncStruct *data = new AsyncStruct(fullpath, target, selector); // Call into JavaScript code in TextureCacheEmscripten.js to do the rest. - cocos2dx_asyncImageLoader_LoadImage(data->filename.c_str(), data); + // cocos2dx_asyncImageLoader_LoadImage(data->filename.c_str(), data); + EM_ASM_ARGS({ + LibraryCocosHelper.cocos2dx_asyncImageLoader_LoadImage($0, $1); + }, (size_t) data->filename.c_str(), (size_t) data); return; } diff --git a/external/emscripten b/external/emscripten deleted file mode 120000 index b3865c6ef..000000000 --- a/external/emscripten +++ /dev/null @@ -1 +0,0 @@ -/home/caiiicyuk/emscripten/sdk/emscripten \ No newline at end of file