Skip to content

Commit 08741b4

Browse files
committed
Merge branch 'typed-array-clusterfuck'
2 parents 84d342c + cc5a057 commit 08741b4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+588
-7704
lines changed

Ejecta.xcodeproj/project.pbxproj

+20-38
Large diffs are not rendered by default.

README.md

+9
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
# About The typed-array-clusterfuck Branch
2+
3+
This branch of Ejecta uses the JavaScriptCore library provided by the system. This mainly means two things: The resulting binary will be much smaller and Typed Arrays are extremely slow.
4+
5+
On 32bit systems reading/writing large Typed Arrays will be pretty much unusably slow. On 64bit systems it's highly optimized to take about 8ms for reading for 1MB of data and about 20ms for writing. This _may_ be ok for simple WebGL stuff or if you don't use g`et/setImageData()` on the Canvas2D context much.
6+
7+
This branch of Ejecta _may_ be AppStore compatible for tvOS. I'm still waiting on the review, but at least the binary upload didn't fail like with the main branch.
8+
9+
110
# Ejecta
211

312
Ejecta is a fast, open source JavaScript, Canvas & Audio implementation for iOS. Think of it as a Browser that can only display a Canvas element.

Source/Ejecta/EJCanvas/2D/EJBindingImageData.m

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#import "EJBindingImageData.h"
22
#import "EJJavaScriptView.h"
3-
#import <JavaScriptCore/JSTypedArray.h>
3+
#import "EJConvertTypedArray.h"
44

55
@implementation EJBindingImageData
66
@synthesize imageData;
@@ -28,7 +28,7 @@ - (EJImageData *)imageData {
2828
int byteLength = imageData.width * imageData.height * 4;
2929

3030
[EJTexture
31-
premultiplyPixels:JSObjectGetTypedArrayDataPtr(scriptView.jsGlobalContext, dataArray, NULL)
31+
premultiplyPixels:JSObjectGetTypedArrayData(scriptView.jsGlobalContext, dataArray).bytes
3232
to:imageData.pixels.mutableBytes
3333
byteLength:byteLength format:GL_RGBA];
3434
}
@@ -48,10 +48,13 @@ - (EJTexture *)texture {
4848
dataArray = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeUint8ClampedArray, byteLength);
4949
JSValueProtect(ctx, dataArray);
5050

51+
NSMutableData *unPremultiplied = [NSMutableData dataWithLength:byteLength];
5152
[EJTexture
5253
unPremultiplyPixels:imageData.pixels.bytes
53-
to:JSObjectGetTypedArrayDataPtr(ctx, dataArray, NULL)
54+
to:unPremultiplied.mutableBytes
5455
byteLength:byteLength format:GL_RGBA];
56+
57+
JSObjectSetTypedArrayData(ctx, dataArray, unPremultiplied);
5558
}
5659
return dataArray;
5760
}

Source/Ejecta/EJCanvas/WebGL/EJBindingCanvasContextWebGL.m

+50-31
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
#import "EJConvertWebGL.h"
77
#import "EJJavaScriptView.h"
88

9-
#import <JavaScriptCore/JSTypedArray.h>
9+
#import "EJConvertTypedArray.h"
1010

1111

1212
@implementation EJBindingCanvasContextWebGL
@@ -365,10 +365,10 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
365365
glBufferData(target, psize, NULL, usage);
366366
}
367367
else if( JSValueIsObject(ctx, argv[1]) ) {
368-
size_t size;
369-
GLvoid *buffer = JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)argv[1], &size);
370-
if( buffer ) {
371-
glBufferData(target, size, buffer, usage);
368+
NSData *data = JSObjectGetTypedArrayData(ctx, (JSObjectRef)argv[1]);
369+
if( data ) {
370+
const GLvoid *buffer = data.bytes;
371+
glBufferData(target, data.length, buffer, usage);
372372
}
373373
}
374374

@@ -383,10 +383,10 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
383383
GLenum target = JSValueToNumberFast(ctx, argv[0]);
384384
GLintptr offset = JSValueToNumberFast(ctx, argv[1]);
385385

386-
size_t size;
387-
GLvoid *buffer = JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)argv[2], &size);
388-
if( buffer ) {
389-
glBufferSubData(target, offset, size, buffer);
386+
NSData *data = JSObjectGetTypedArrayData(ctx, (JSObjectRef)argv[2]);
387+
if( data ) {
388+
const GLvoid *buffer = data.bytes;
389+
glBufferSubData(target, offset, data.length, buffer);
390390
}
391391
return NULL;
392392
}
@@ -726,6 +726,7 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
726726
int intbuffer[4];
727727
float floatvalue;
728728
JSValueRef arrayArgs[4];
729+
NSMutableData *data;
729730

730731
switch( pname ) {
731732
// Float32Array (with 0 elements)
@@ -737,28 +738,40 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
737738
case GL_ALIASED_LINE_WIDTH_RANGE:
738739
case GL_ALIASED_POINT_SIZE_RANGE:
739740
case GL_DEPTH_RANGE:
741+
data = [[NSMutableData alloc] initWithLength:2 * sizeof(GLfloat)];
742+
glGetFloatv(pname, data.mutableBytes);
740743
ret = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeFloat32Array, 2);
741-
glGetFloatv(pname, JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)ret, NULL));
744+
JSObjectSetTypedArrayData(ctx, (JSObjectRef)ret, data);
745+
[data release];
742746
break;
743747

744748
// Float32Array (with 4 values)
745749
case GL_BLEND_COLOR:
746750
case GL_COLOR_CLEAR_VALUE:
751+
data = [[NSMutableData alloc] initWithLength:4 * sizeof(GLfloat)];
752+
glGetFloatv(pname, data.mutableBytes);
747753
ret = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeFloat32Array, 4);
748-
glGetFloatv(pname, JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)ret, NULL));
754+
JSObjectSetTypedArrayData(ctx, (JSObjectRef)ret, data);
755+
[data release];
749756
break;
750757

751758
// Int32Array (with 2 values)
752759
case GL_MAX_VIEWPORT_DIMS:
760+
data = [[NSMutableData alloc] initWithLength:2 * sizeof(GLint)];
761+
glGetIntegerv(pname, data.mutableBytes);
753762
ret = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeInt32Array, 2);
754-
glGetIntegerv(pname, JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)ret, NULL));
763+
JSObjectSetTypedArrayData(ctx, (JSObjectRef)ret, data);
764+
[data release];
755765
break;
756766

757767
// Int32Array (with 4 values)
758768
case GL_SCISSOR_BOX:
759769
case GL_VIEWPORT:
770+
data = [[NSMutableData alloc] initWithLength:4 * sizeof(GLint)];
771+
glGetIntegerv(pname, data.mutableBytes);
760772
ret = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeInt32Array, 4);
761-
glGetIntegerv(pname, JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)ret, NULL));
773+
JSObjectSetTypedArrayData(ctx, (JSObjectRef)ret, data);
774+
[data release];
762775
break;
763776

764777
// boolean[] (with 4 values)
@@ -1147,15 +1160,21 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
11471160
// Float32Array
11481161
if( type == GL_FLOAT ) {
11491162
array = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeFloat32Array, size);
1150-
void *buffer = JSObjectGetTypedArrayDataPtr(ctx, array, NULL);
1151-
glGetUniformfv(program, uniform, buffer);
1163+
NSMutableData *data = [[NSMutableData alloc] initWithLength:size * sizeof(GLfloat)];
1164+
glGetUniformfv(program, uniform, data.mutableBytes);
1165+
1166+
JSObjectSetTypedArrayData(ctx, array, data);
1167+
[data release];
11521168
}
11531169

11541170
// Int32Array
11551171
else if( type == GL_INT ) {
11561172
array = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeInt32Array, size);
1157-
void *buffer = JSObjectGetTypedArrayDataPtr(ctx, array, NULL);
1158-
glGetUniformiv(program, uniform, buffer);
1173+
NSMutableData *data = [[NSMutableData alloc] initWithLength:size * sizeof(GLint)];
1174+
glGetUniformiv(program, uniform, data.mutableBytes);
1175+
1176+
JSObjectSetTypedArrayData(ctx, array, data);
1177+
[data release];
11591178
}
11601179

11611180
// boolean[]
@@ -1202,8 +1221,10 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
12021221
}
12031222
else if( pname == GL_CURRENT_VERTEX_ATTRIB ) {
12041223
JSObjectRef array = JSObjectMakeTypedArray(ctx, kJSTypedArrayTypeFloat32Array, 4);
1205-
GLint *values = JSObjectGetTypedArrayDataPtr(ctx, array, NULL);
1206-
glGetVertexAttribiv(index, pname, values);
1224+
NSMutableData *data = [[NSMutableData alloc] initWithLength:4 * sizeof(GLfloat)];
1225+
glGetVertexAttribiv(index, pname, data.mutableBytes);
1226+
JSObjectSetTypedArrayData(ctx, array, data);
1227+
[data release];
12071228
return array;
12081229
}
12091230
else {
@@ -1302,13 +1323,11 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
13021323

13031324
scriptView.currentRenderingContext = renderingContext;
13041325

1305-
size_t size;
1306-
void *pixels = JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)argv[6], &size);
1307-
13081326
GLuint bytesPerPixel = EJGetBytesPerPixel(type, format);
1309-
if( bytesPerPixel && size >= width * height * bytesPerPixel ) {
1310-
glReadPixels(x, y, width, height, format, type, pixels);
1311-
}
1327+
NSMutableData *data = [[NSMutableData alloc] initWithLength:width*height*bytesPerPixel];
1328+
glReadPixels(x, y, width, height, format, type, data.mutableBytes);
1329+
JSObjectSetTypedArrayData(ctx, (JSObjectRef)argv[6], data);
1330+
[data release];
13121331

13131332
return NULL;
13141333
}
@@ -1463,10 +1482,10 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
14631482
if( border == 0 && EJ_ARRAY_MATCHES_TYPE(arrayType, type) ) {
14641483
int bytesPerPixel = EJGetBytesPerPixel(type, format);
14651484

1466-
size_t byteLength;
1467-
void *pixels = JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)argv[8], &byteLength);
1485+
NSMutableData *data = JSObjectGetTypedArrayData(ctx, (JSObjectRef)argv[8]);
1486+
void *pixels = data.mutableBytes;
14681487

1469-
if( bytesPerPixel && byteLength >= width * height * bytesPerPixel ) {
1488+
if( bytesPerPixel && data.length >= width * height * bytesPerPixel ) {
14701489
if( unpackFlipY ) {
14711490
[EJTexture flipPixelsY:pixels bytesPerRow:(width * bytesPerPixel) rows:height];
14721491
}
@@ -1595,10 +1614,10 @@ - (void)deleteVertexArray:(GLuint)vertexArray {
15951614
if( EJ_ARRAY_MATCHES_TYPE(arrayType, type) ) {
15961615
int bytesPerPixel = EJGetBytesPerPixel(type, format);
15971616

1598-
size_t byteLength;
1599-
void *pixels = JSObjectGetTypedArrayDataPtr(ctx, (JSObjectRef)argv[8], &byteLength);
1617+
NSMutableData *data = JSObjectGetTypedArrayData(ctx, (JSObjectRef)argv[8]);
1618+
void *pixels = data.mutableBytes;
16001619

1601-
if( bytesPerPixel && byteLength >= width * height * bytesPerPixel ) {
1620+
if( bytesPerPixel && data.length >= width * height * bytesPerPixel ) {
16021621
if( unpackFlipY ) {
16031622
[EJTexture flipPixelsY:pixels bytesPerRow:(width * bytesPerPixel) rows:height];
16041623
}

Source/Ejecta/EJCanvas/WebGL/EJConvertWebGL.m

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#import "EJConvertWebGL.h"
22
#import "EJConvert.h"
3-
#import <JavaScriptCore/JSTypedArray.h>
3+
#import "EJConvertTypedArray.h"
44

55
// FIXME: use C++ with a template?
66
#define CREATE_JS_VALUE_TO_ARRAY_FUNC(NAME, TYPE, ARRAY_TYPE) \
@@ -10,8 +10,9 @@
1010
} \
1111
JSObjectRef jsObject = (JSObjectRef)value; \
1212
if( JSObjectGetTypedArrayType(ctx, jsObject) == ARRAY_TYPE ) { \
13-
size_t byteLength; \
14-
TYPE *arrayValue = JSObjectGetTypedArrayDataPtr(ctx, jsObject, &byteLength); \
13+
NSMutableData *data = JSObjectGetTypedArrayData(ctx, jsObject); \
14+
size_t byteLength = data.length; \
15+
TYPE *arrayValue = data.mutableBytes; \
1516
GLsizei count = (GLsizei)(byteLength/sizeof(TYPE)); \
1617
if( arrayValue && count && (count % elementSize) == 0 ) { \
1718
*numElements = count / elementSize; \

Source/Ejecta/EJConvert.h

+3-2
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,9 @@ static inline void *JSValueGetPrivate(JSValueRef v) {
1919
// undefined) will crash the app. So we check for these first.
2020

2121
#if __LP64__
22-
#define JSValueTagMask (0xffff000000000000ll | 0x2ll)
23-
return !((int64_t)v & JSValueTagMask) ? JSObjectGetPrivate((JSObjectRef)v) : NULL;
22+
return !((int64_t)v & 0xffff000000000002ll)
23+
? JSObjectGetPrivate((JSObjectRef)v)
24+
: NULL;
2425
#else
2526
return JSObjectGetPrivate((JSObjectRef)v);
2627
#endif

Source/Ejecta/EJConvert.m

+8-18
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,11 @@ JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string ) {
2424
// This functions comes in a 64bit and 32bit flavor, since the NaN-Boxing
2525
// in JSC works a bit differently on each platforms. For an explanation
2626
// of the taggging refer to JSC/runtime/JSCJSValue.h
27+
// The 32bit version just calls the normal JSValueToNumber() function
28+
// and is thus a lot slower.
2729

28-
#if __LP64__ // arm64 version
29-
double JSValueToNumberFast(JSContextRef ctx, JSValueRef v) {
30+
double JSValueToNumberFast(JSContextRef ctx, JSValueRef v) {
31+
#if __LP64__ // arm64 version
3032
union {
3133
int64_t asInt64;
3234
double asDouble;
@@ -50,22 +52,10 @@ JSValueRef NSStringToJSValue( JSContextRef ctx, NSString *string ) {
5052
else {
5153
return 0; // false, undefined, null, object
5254
}
53-
}
54-
#else // armv7 version
55-
double JSValueToNumberFast( JSContextRef ctx, JSValueRef v ) {
56-
struct {
57-
unsigned char cppClassData[4];
58-
union {
59-
double asDouble;
60-
struct { int32_t asInt; int32_t tag; } asBits;
61-
} payload;
62-
} *decoded = (void *)v;
63-
64-
return decoded->payload.asBits.tag < 0xfffffff9
65-
? decoded->payload.asDouble
66-
: decoded->payload.asBits.asInt;
67-
}
68-
#endif
55+
#else // armv7 version
56+
return JSValueToNumber(ctx, v, NULL);
57+
#endif
58+
}
6959

7060
void JSValueUnprotectSafe( JSContextRef ctx, JSValueRef v ) {
7161
if( ctx && v ) {

Source/Ejecta/EJConvertTypedArray.h

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#import <Foundation/Foundation.h>
2+
#import <JavaScriptCore/JavaScriptCore.h>
3+
4+
/*!
5+
@enum JSType
6+
@abstract A constant identifying the Typed Array type of a JSValue.
7+
@constant kJSTypedArrayTypeNone Not a Typed Array.
8+
@constant kJSTypedArrayTypeInt8Array Int8Array
9+
@constant kJSTypedArrayTypeInt16Array Int16Array
10+
@constant kJSTypedArrayTypeInt32Array Int32Array
11+
@constant kJSTypedArrayTypeUint8Array Int8Array
12+
@constant kJSTypedArrayTypeUint8ClampedArray Int8ClampedArray
13+
@constant kJSTypedArrayTypeUint16Array Uint16Array
14+
@constant kJSTypedArrayTypeUint32Array Uint32Array
15+
@constant kJSTypedArrayTypeFloat32Array Float32Array
16+
@constant kJSTypedArrayTypeFloat64Array Float64Array
17+
@constant kJSTypedArrayTypeArrayBuffer ArrayBuffer
18+
*/
19+
typedef enum {
20+
kJSTypedArrayTypeNone = 0,
21+
kJSTypedArrayTypeInt8Array = 1,
22+
kJSTypedArrayTypeInt16Array = 2,
23+
kJSTypedArrayTypeInt32Array = 3,
24+
kJSTypedArrayTypeUint8Array = 4,
25+
kJSTypedArrayTypeUint8ClampedArray = 5,
26+
kJSTypedArrayTypeUint16Array = 6,
27+
kJSTypedArrayTypeUint32Array = 7,
28+
kJSTypedArrayTypeFloat32Array = 8,
29+
kJSTypedArrayTypeFloat64Array = 9,
30+
kJSTypedArrayTypeArrayBuffer = 10
31+
} JSTypedArrayType;
32+
33+
/*!
34+
@function
35+
@abstract Setup the JSContext for use of the Typed Array functions.
36+
@param ctx The execution context to use
37+
*/
38+
void JSContextPrepareTypedArrayAPI(JSContextRef ctx);
39+
40+
/*!
41+
@function
42+
@abstract Returns a JavaScript value's Typed Array type
43+
@param ctx The execution context to use.
44+
@param value The JSObject whose Typed Array type you want to obtain.
45+
@result A value of type JSTypedArrayType that identifies value's Typed Array type
46+
*/
47+
JSTypedArrayType JSObjectGetTypedArrayType(JSContextRef ctx, JSObjectRef object);
48+
49+
/*!
50+
@function
51+
@abstract Creates an empty JavaScript Typed Array with the given number of elements
52+
@param ctx The execution context to use.
53+
@param arrayType A value of type JSTypedArrayType identifying the type of array you want to create
54+
@param numElements The number of elements for the array.
55+
@result A JSObjectRef that is a Typed Array or NULL if there was an error
56+
*/
57+
JSObjectRef JSObjectMakeTypedArray(JSContextRef ctx, JSTypedArrayType arrayType, size_t numElements);
58+
59+
/*!
60+
@function
61+
@abstract Returns a copy of the Typed Array's data
62+
@param ctx The execution context to use.
63+
@param value The JSObject whose Typed Array data you want to obtain.
64+
@result A copy of the Typed Array's data or NULL if the JSObject is not a Typed Array
65+
*/
66+
NSMutableData *JSObjectGetTypedArrayData(JSContextRef ctx, JSObjectRef object);
67+
68+
/*!
69+
@function
70+
@abstract Replaces a Typed Array's data
71+
@param ctx The execution context to use.
72+
@param value The JSObject whose Typed Array data you want to replace
73+
*/
74+
void JSObjectSetTypedArrayData(JSContextRef ctx, JSObjectRef object, NSData *data);
75+

0 commit comments

Comments
 (0)