Skip to content

Commit

Permalink
wasm: fix http requests and image support
Browse files Browse the repository at this point in the history
  • Loading branch information
zenith391 committed Oct 30, 2024
1 parent ebdfead commit 67c0ccd
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 55 deletions.
2 changes: 0 additions & 2 deletions build_capy.zig
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,6 @@ pub fn runStep(step: *std.Build.Step.Compile, options: CapyRunOptions) !*std.Bui
},
.wasi => {
// Things like the image reader require more stack than given by default
// TODO: remove once ziglang/zig#12589 is merged
step.stack_size = @max(step.stack_size orelse 0, 256 * 1024);
if (step.root_module.optimize == .ReleaseSmall) {
step.root_module.strip = true;
}
Expand Down
51 changes: 16 additions & 35 deletions src/backends/wasm/capy-worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ function readBuffer(addr, len) {
return view.slice(addr, addr + len);
}

// 4 bytes for marking and 4096 bytes for data
let pendingAnswer = new SharedArrayBuffer(4100);
// 4 bytes for marking and 65536 bytes for data
let pendingAnswer = new SharedArrayBuffer(65540);
/**
@param {string} type The type of the answer, can be "int", "float", "bool" or "bytes"
**/
Expand All @@ -64,7 +64,7 @@ function waitForAnswer(type) {
return float;
case "bytes":
const length = view[1];
const bytes = new Uint8Array(pendingAnswer).slice(0x4, 0x4 + length);
const bytes = new Uint8Array(pendingAnswer).slice(0x8, 0x8 + length);
return bytes;
}

Expand Down Expand Up @@ -222,6 +222,10 @@ class WasiImplementation {
console.debug(`fd_seek(${fd}, ${offset}, ${whence})`);
return Errno.INVAL;
}

fd_filestat_get = (fd, buf) => {
// TODO
}
}

const env = {
Expand Down Expand Up @@ -366,46 +370,23 @@ const env = {
const size = stride * height;
let view = new Uint8Array(obj.instance.exports.memory.buffer);
let data = Uint8ClampedArray.from(view.slice(bytesPtr, bytesPtr + size));
return resources.push({
type: 'image',
width: width,
height: height,
stride: stride,
rgb: isRgb != 0,
bytes: data,
imageDatas: {},
}) - 1;
postMessage(["uploadImage", width, height, stride, isRgb, data]);
return waitForAnswer("int");
},

// Network
fetchHttp: function(urlPtr, urlLen) {
const url = readString(urlPtr, urlLen);
const id = networkRequests.length;
const promise = fetch(url).then(response => response.arrayBuffer()).then(response => {
networkRequestsCompletion[id] = true;
networkRequests[id] = response;
});
networkRequestsCompletion.push(false);
networkRequestsReadIdx.push(0);
return networkRequests.push(promise) - 1;
self.postMessage(["fetchHttp", readString(urlPtr, urlLen)])
return waitForAnswer("int");
},
isRequestReady: function(id) {
return networkRequestsCompletion[id];
self.postMessage(["isRequestReady", id]);
return waitForAnswer("int") != 0;
},
readRequest: function(id, bufPtr, bufLen) {
if (networkRequestsCompletion[id] === false) return 0;

const buffer = networkRequests[id];
const idx = networkRequestsReadIdx[id];

const view = new Uint8Array(buffer);
const slice = view.slice(idx, idx + bufLen);
const memoryView = new Uint8Array(obj.instance.exports.memory.buffer);
for (let i = 0; i < slice.length; i++) {
memoryView[bufPtr + i] = slice[i];
}
networkRequestsReadIdx[id] += slice.length;

self.postMessage(["readRequest", id, bufLen]);
const slice = waitForAnswer("bytes");
writeBytes(bufPtr, slice);
return slice.length;
},
yield: function() {
Expand Down
26 changes: 10 additions & 16 deletions src/backends/wasm/capy.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ async function pushAnswer(type, value) {
if (type == "bytes" && !(value instanceof Uint8Array)) {
throw Error("Type mismatch, got " + (typeof value));
}
if (type == "string") {
throw Error("Type mismatch");
}

const WAITING = 0;
const DONE = 1;
Expand All @@ -61,10 +64,10 @@ async function pushAnswer(type, value) {
view[2] = right;
} else if (type == "float") {
new DataView(view.buffer).setFloat64(0x4, value);
} else if (type == "string") {
} else if (type == "bytes") {
const length = value.length;
view[1] = length;
new Uint8Array(view.buffer).set(bytes, 0x8);
new Uint8Array(view.buffer).set(value, 0x8);
}
view[0] = DONE;
if (Atomics.notify(view, 0) != 1) {
Expand Down Expand Up @@ -438,10 +441,7 @@ let env = {
},

// Resources
uploadImage: function(width, height, stride, isRgb, bytesPtr) {
const size = stride * height;
let view = new Uint8Array(obj.instance.exports.memory.buffer);
let data = Uint8ClampedArray.from(view.slice(bytesPtr, bytesPtr + size));
uploadImage: function(width, height, stride, isRgb, data) {
return resources.push({
type: 'image',
width: width,
Expand All @@ -454,8 +454,7 @@ let env = {
},

// Network
fetchHttp: function(urlPtr, urlLen) {
const url = readString(urlPtr, urlLen);
fetchHttp: function(url) {
const id = networkRequests.length;
const promise = fetch(url).then(response => response.arrayBuffer()).then(response => {
networkRequestsCompletion[id] = true;
Expand All @@ -466,23 +465,18 @@ let env = {
return networkRequests.push(promise) - 1;
},
isRequestReady: function(id) {
return networkRequestsCompletion[id];
return networkRequestsCompletion[id] ? 1 : 0;
},
readRequest: function(id, bufPtr, bufLen) {
readRequest: function(id, bufLen) {
if (networkRequestsCompletion[id] === false) return 0;

const buffer = networkRequests[id];
const idx = networkRequestsReadIdx[id];

const view = new Uint8Array(buffer);
const slice = view.slice(idx, idx + bufLen);
const memoryView = new Uint8Array(obj.instance.exports.memory.buffer);
for (let i = 0; i < slice.length; i++) {
memoryView[bufPtr + i] = slice[i];
}
networkRequestsReadIdx[id] += slice.length;

return slice.length;
return slice;
},

// Audio
Expand Down
13 changes: 11 additions & 2 deletions src/image.zig
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,17 @@ pub const ImageData = struct {

/// Load from a png file using a buffer (which can be provided by @embedFile)
pub fn fromBuffer(allocator: std.mem.Allocator, buf: []const u8) !ImageData {
// stage1 crashes with LLVM ERROR: Unable to expand fixed point multiplication.
// const img = try zigimg.Image.fromMemory(allocator, buf);
// var img = try zigimg.Image.fromMemory(allocator, buf);
// // defer img.deinit();
// const bytes = img.rawBytes();
// return try ImageData.fromBytes(
// @as(u32, @intCast(img.width)),
// @as(u32, @intCast(img.height)),
// @as(u32, @intCast(img.rowByteSize())),
// .RGBA,
// bytes,
// allocator,
// );

var stream = std.io.StreamSource{ .const_buffer = std.io.fixedBufferStream(buf) };
return readFromStream(allocator, &stream);
Expand Down

0 comments on commit 67c0ccd

Please sign in to comment.