• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef WasmCommon_DEFINED
9 #define WasmCommon_DEFINED
10 
11 #include <emscripten.h>
12 #include <emscripten/bind.h>
13 #include "include/core/SkColor.h"
14 #include "include/core/SkSpan.h"
15 #include "include/private/base/SkMalloc.h"
16 
17 using namespace emscripten;
18 
19 // Self-documenting types
20 using JSColor = int32_t;
21 using JSArray = emscripten::val;
22 using JSObject = emscripten::val;
23 using JSString = emscripten::val;
24 using SkPathOrNull = emscripten::val;
25 using TypedArray = emscripten::val;
26 using Uint8Array = emscripten::val;
27 using Uint16Array = emscripten::val;
28 using Uint32Array = emscripten::val;
29 using Float32Array = emscripten::val;
30 
31 // If we are using C++ and EMSCRIPTEN_BINDINGS, we can't have primitive pointers in our function
32 // type signatures. (this gives an error message like "Cannot call foo due to unbound
33 // types Pi, Pf").  But, we can just pretend they are numbers and cast them to be pointers and
34 // the compiler is happy.
35 // These types refer to the TypedArray that the JS interface wrote into or will read out of.
36 // This doesn't stop us from using these as different types; e.g. a float* can be treated as an
37 // SkPoint* in some APIs.
38 using WASMPointerF32 = uintptr_t;
39 using WASMPointerI32 = uintptr_t;
40 using WASMPointerU8  = uintptr_t;
41 using WASMPointerU16 = uintptr_t;
42 using WASMPointerU32 = uintptr_t;
43 using WASMPointer = uintptr_t;
44 
45 #define SPECIALIZE_JSARRAYTYPE(type, name)                  \
46     template <> struct JSArrayType<type> {                  \
47         static constexpr const char* const gName = name;    \
48     }
49 
50 template <typename T> struct JSArrayType {};
51 
52 SPECIALIZE_JSARRAYTYPE( int8_t,    "Int8Array");
53 SPECIALIZE_JSARRAYTYPE(uint8_t,   "Uint8Array");
54 SPECIALIZE_JSARRAYTYPE( int16_t,  "Int16Array");
55 SPECIALIZE_JSARRAYTYPE(uint16_t, "Uint16Array");
56 SPECIALIZE_JSARRAYTYPE( int32_t,  "Int32Array");
57 SPECIALIZE_JSARRAYTYPE(uint32_t, "Uint32Array");
58 SPECIALIZE_JSARRAYTYPE(float,   "Float32Array");
59 
60 #undef SPECIALIZE_JSARRAYTYPE
61 
62 /**
63  *  Create a typed-array (in the JS heap) and initialize it with the provided
64  *  data (from the wasm heap).
65  */
MakeTypedArray(int count,const T src[])66 template <typename T> TypedArray MakeTypedArray(int count, const T src[]) {
67     emscripten::val length = emscripten::val(count);
68     emscripten::val jarray = emscripten::val::global(JSArrayType<T>::gName).new_(count);
69     jarray.call<void>("set", val(typed_memory_view(count, src)));
70     return jarray;
71 }
72 
73 /**
74  *  Gives read access to a JSArray
75  *
76  *  We explicitly use malloc/free (not new/delete) so this can be used with allocations from the JS
77  *  side (ala CanvasKit.Malloc).
78  */
79 template <typename T> class JSSpan {
80 public:
81     // Note: Use of this constructor is 5-20x slower than manually copying the data on the JS side
82     // and sending over a pointer, length, and boolean for the other constructor.
JSSpan(JSArray src)83     JSSpan(JSArray src) {
84         const size_t len = src["length"].as<size_t>();
85         T* data;
86 
87         // If the buffer was allocated via CanvasKit' Malloc, we can peek directly at it!
88         if (src["_ck"].isTrue()) {
89             fOwned = false;
90             data = reinterpret_cast<T*>(src["byteOffset"].as<size_t>());
91         } else {
92             fOwned = true;
93             data = static_cast<T*>(sk_malloc_throw(len, sizeof(T)));
94 
95             // now actually copy into 'data'
96             if (src.instanceof(emscripten::val::global(JSArrayType<T>::gName))) {
97                 auto dst_view = emscripten::val(typed_memory_view(len, data));
98                 dst_view.call<void>("set", src);
99             } else {
100                 for (size_t i = 0; i < len; ++i) {
101                     data[i] = src[i].as<T>();
102                 }
103             }
104         }
105         fSpan = SkSpan(data, len);
106     }
107 
JSSpan(WASMPointer ptr,size_t len,bool takeOwnership)108     JSSpan(WASMPointer ptr, size_t len, bool takeOwnership): fOwned(takeOwnership) {
109         fSpan = SkSpan(reinterpret_cast<T*>(ptr), len);
110     }
111 
~JSSpan()112     ~JSSpan() {
113         if (fOwned) {
114             sk_free(fSpan.data());
115         }
116     }
117 
data()118     const T* data() const { return fSpan.data(); }
size()119     size_t size() const { return fSpan.size(); }
120 
121 private:
122     SkSpan<T>   fSpan;
123     bool        fOwned;
124 };
125 
126 #endif
127