Skip to content

Commit

Permalink
[WIP] layered webgpu.h implementation prototype
Browse files Browse the repository at this point in the history
  • Loading branch information
kainino0x committed Dec 2, 2023
1 parent 38ab7c1 commit 55e44f9
Show file tree
Hide file tree
Showing 9 changed files with 339 additions and 0 deletions.
1 change: 1 addition & 0 deletions embuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
'libnoexit',
'sqlite3',
'sqlite3-mt',
'libwebgpu',
'libwebgpu_cpp',
]

Expand Down
167 changes: 167 additions & 0 deletions src/library_emwebgpu.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/**
* @license
* Copyright 2023 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/

/*
* WebGPU support.
* FIXME: WRITE SOME DOCUMENTATION
*/

{{{
// Helper functions for code generation
global.gpu = {
// Must be in sync with webgpu.h.
COPY_STRIDE_UNDEFINED: 0xFFFFFFFF,
LIMIT_U32_UNDEFINED: 0xFFFFFFFF,
MIP_LEVEL_COUNT_UNDEFINED: 0xFFFFFFFF,
ARRAY_LAYER_COUNT_UNDEFINED: 0xFFFFFFFF,
AdapterType: {
Unknown: 3,
},
BackendType: {
WebGPU: 1,
},
BufferMapAsyncStatus: {
Success: 0,
ValidationError: 1,
Unknown: 2,
DeviceLost: 3,
DestroyedBeforeCallback: 4,
UnmappedBeforeCallback: 5,
MappingAlreadyPending: 6,
OffsetOutOfRange: 7,
SizeOutOfRange: 8,
},
CallbackFlag: {
WaitAnyOnly: 0,
AllowProcessEvents: 1,
AllowSpontaneous: 2,
},
CompilationInfoRequestStatus: {
Success: 0,
Error: 1,
DeviceLost: 2,
Unknown: 3,
},
CreatePipelineAsyncStatus: {
Success: 0,
ValidationError: 1,
InternalError: 2,
DeviceLost: 3,
DeviceDestroyed: 4,
Unknown: 5,
},
ErrorType: {
NoError: 0,
Validation: 1,
OutOfMemory: 2,
Unknown: 3,
},
PresentMode: {
Fifo: 2,
},
LoadOp: {
Undefined: 0,
Clear: 1,
Load: 2,
},
StoreOp: {
Undefined: 0,
Store: 1,
Discard: 2,
},
MapMode: {
None: 0,
Read: 1,
Write: 2
},
RequestAdapterStatus: {
Success: 0,
Unavailable: 1,
Error: 2,
},
RequestDeviceStatus: {
Success: 0,
Error: 1,
},
SType: {
SurfaceDescriptorFromCanvasHTMLSelector: 4,
ShaderModuleSPIRVDescriptor: 5,
ShaderModuleWGSLDescriptor: 6,
PrimitiveDepthClipControl: 7,
RenderPassDescriptorMaxDrawCount: 15,
},
QueueWorkDoneStatus: {
Success: 0,
Error: 1,
},
TextureFormat: {
Undefined: 0,
},
VertexStepMode: {
Vertex: 0,
Instance: 1,
VertexBufferNotUsed: 2,
},
WaitStatus: {
Success: 0,
TimedOut: 1,
UnsupportedTimeout: 2,
UnsupportedCount: 3,
UnsupportedMixedSources: 4,
},
};
null;
}}}

var LibraryEmWebGPU = {
$EmWebGPU: {
PowerPreference: [undefined, 'low-power', 'high-performance'],
_tableNextID: 1,
_table: [],
tableInsert: value => {
const id = EmWebGPU._tableNextID++;
EmWebGPU._table[id] = value;
return id;
},
},

emwgpuSetLabel: (id, label) => {
EmWebGPU.table[id]['label'] = UTF8ToString(label);
},
emwgpuDestroy: (id) => {
EmWebGPU.table[id]['destroy']();
},
emwgpuDelete: (id) => {
delete EmWebGPU.table[id];
},

emwgpuRequestAdapter__deps: ['emwgpuOnRequestAdapter'],
emwgpuRequestAdapter: (request, powerPreference, forceFallbackAdapter) => {
if (!('gpu' in navigator)) {
_emwgpuOnRequestAdapter(request, 0, {{{ gpu.RequestAdapterStatus.Unavailable }}});
return;
}

{{{ runtimeKeepalivePush() }}}
navigator["gpu"]["requestAdapter"]({
powerPreference: EmWebGPU.PowerPreference[powerPreference],
forceFallbackAdapter,
}).then(adapter => {
{{{ runtimeKeepalivePop() }}}
if (adapter) {
_emwgpuOnRequestAdapter(request, EmWebGPU.tableInsert(adapter), {{{ gpu.RequestAdapterStatus.Success }}});
} else {
_emwgpuOnRequestAdapter(request, 0, {{{ gpu.RequestAdapterStatus.Unavailable }}});
}
}, exception => {
{{{ runtimeKeepalivePop() }}}
_emwgpuOnRequestAdapter(request, 0, {{{ gpu.RequestAdapterStatus.Error }}});
});
},
};

autoAddDeps(LibraryEmWebGPU, '$EmWebGPU');
addToLibrary(LibraryEmWebGPU);
1 change: 1 addition & 0 deletions src/library_webgpu.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ var LibraryWebGPU = {
$WebGPU__postset: 'WebGPU.initManagers();',
$WebGPU__deps: ['$withStackSave', '$stringToUTF8OnStack'],
$WebGPU: {

errorCallback: (callback, type, message, userdata) => {
withStackSave(() => {
var messagePtr = stringToUTF8OnStack(message);
Expand Down
4 changes: 4 additions & 0 deletions src/modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ global.LibraryManager = {
libraries.push('library_html5_webgpu.js');
}

if (USE_WEBGPU1) {
libraries.push('library_emwebgpu.js');
}

if (!STRICT) {
libraries.push('library_legacy.js');
}
Expand Down
1 change: 1 addition & 0 deletions src/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -565,6 +565,7 @@ var GL_PREINITIALIZED_CONTEXT = false;
// Enables support for WebGPU (via "webgpu/webgpu.h").
// [link]
var USE_WEBGPU = false;
var USE_WEBGPU1 = false;

// Enables building of stb-image, a tiny public-domain library for decoding
// images, allowing decoding of images without using the browser's built-in
Expand Down
139 changes: 139 additions & 0 deletions system/lib/webgpu/webgpu.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* @license
* Copyright 2023 The Emscripten Authors
* SPDX-License-Identifier: MIT
*/

/*
* Implementation of webgpu.h + html5_webgpu.h over library_emwebgpu.js.
*/

#include <webgpu/webgpu.h>

#include <atomic>
#include <cassert>
#include <cstdint>

// FIXME
#include <cstdio>

using ObjID = uint64_t;
struct AdapterRequest;

// Declarations for library_emwebgpu.js
extern "C" {

void emwgpuSetLabel(ObjID id, const char* label);
void emwgpuDestroy(ObjID id);
void emwgpuDelete(ObjID id);

using WGPUBool = int32_t;
void emwgpuRequestAdapter(AdapterRequest* request, WGPUPowerPreference powerPreference, WGPUBool forceFallbackAdapter);


// Implementation helpers

#define DEFINE_REF_RELEASE(Name) \
void wgpu##Name##Reference(WGPU##Name o) { o->Reference(); } \
void wgpu##Name##Release(WGPU##Name o) { o->Release(); }

class ManuallyRefCounted {
public:
ManuallyRefCounted() = default;
virtual ~ManuallyRefCounted() = default;

void Reference() {
++mRefCount;
}
void Release() {
uint64_t newRefCount = --mRefCount;
if (newRefCount == 0) {
OnBeforeDelete();
delete this;
}
}
protected:
virtual void OnBeforeDelete() {}
std::atomic<uint64_t> mRefCount{1};
private:
// Non-copyable
ManuallyRefCounted(const ManuallyRefCounted&) = delete;
void operator=(const ManuallyRefCounted&) = delete;
};

class ObjectFromInstance : public ManuallyRefCounted {
public:
ObjectFromInstance(WGPUInstance instance, ObjID id)
: mInstance(instance), mHandle(id) {
wgpuInstanceReference(mInstance);
}
virtual ~ObjectFromInstance() override {
emwgpuDelete(mHandle);
}
private:
WGPUInstance mInstance;
ObjID mHandle;
};

struct AdapterRequest {
WGPUInstance instance;
WGPURequestAdapterCallback callback;
void* userdata;
};

// WGPU object definitions

struct WGPUInstanceImpl : public ManuallyRefCounted {};

struct WGPUAdapterImpl : public ObjectFromInstance {
using ObjectFromInstance::ObjectFromInstance;
};


// Implementations for JS->WASM calls back from library_emwebgpu.js

void emwgpuOnRequestAdapter(AdapterRequest* request, ObjID adapterHandle, WGPURequestAdapterStatus status) {
WGPUAdapter adapter = nullptr;
const char* message = nullptr;
if (status == 0) {
assert(adapterHandle);
adapter = new WGPUAdapterImpl(request->instance, adapterHandle);
} else {
message = "failed";
}
request->callback(status, adapter, message, request->userdata);
wgpuInstanceRelease(request->instance);
delete request;
}

// Standalone functions

WGPUInstance wgpuCreateInstance(WGPUInstanceDescriptor const * descriptor) {
return new WGPUInstanceImpl();
}

// Methods of Instance

DEFINE_REF_RELEASE(Instance);

void wgpuInstanceRequestAdapter(WGPUInstance instance, WGPURequestAdapterOptions const * options, WGPURequestAdapterCallback callback, void * userdata) {
wgpuInstanceReference(instance);
auto* request = new AdapterRequest{instance, callback, userdata};
WGPUPowerPreference powerPreference = WGPUPowerPreference_Undefined;
WGPUBool forceFallbackAdapter = false;
if (options) {
powerPreference = options->powerPreference;
forceFallbackAdapter = options->forceFallbackAdapter;
}
emwgpuRequestAdapter(request, powerPreference, forceFallbackAdapter);
}

// FIXME

void wgpuBufferRelease(WGPUBuffer) {}
WGPUBuffer wgpuDeviceCreateBuffer(WGPUDevice, WGPUBufferDescriptor const *) {
return nullptr;
}
void wgpuDeviceRelease(WGPUDevice) {}

} // extern "C"
4 changes: 4 additions & 0 deletions test/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -11695,6 +11695,10 @@ def test_webgpu_compiletest(self):
for args in [[], ['-sASSERTIONS'], ['-sASSERTIONS', '--closure=1'], ['-sMAIN_MODULE=1']]:
self.run_process([EMXX, test_file('webgpu_jsvalstore.cpp'), '-sUSE_WEBGPU', '-sASYNCIFY'] + args)

def test_webgpu_compiletest1(self):
for args in [[], ['-sASSERTIONS'], ['-sASSERTIONS', '--closure=1'], ['-sMAIN_MODULE=1']]:
self.run_process([EMXX, test_file('webgpu1.cpp'), '-sUSE_WEBGPU1', '-sASYNCIFY'] + args)

def test_signature_mismatch(self):
create_file('a.c', 'void foo(); int main() { foo(); return 0; }')
create_file('b.c', 'int foo() { return 1; }')
Expand Down
11 changes: 11 additions & 0 deletions test/webgpu1.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#include <webgpu/webgpu.h>
#include <cstdio>

void OnRequestAdapter(WGPURequestAdapterStatus status, WGPUAdapter adapter, const char* message, void*) {
printf("Got! status=%d adapter=%p message=%s\n", status, adapter, message);
}

int main() {
WGPUInstance instance = wgpuCreateInstance(nullptr);
wgpuInstanceRequestAdapter(instance, nullptr, OnRequestAdapter, nullptr);
}
11 changes: 11 additions & 0 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1834,6 +1834,14 @@ def get_default_variation(cls, **kwargs):
)


class libwebgpu(MTLibrary):
name = 'libwebgpu'

cflags = ['-std=c++11']
src_dir = 'system/lib/webgpu'
src_files = ['webgpu.cpp']


class libwebgpu_cpp(MTLibrary):
name = 'libwebgpu_cpp'

Expand Down Expand Up @@ -2307,6 +2315,9 @@ def add_sanitizer_libs():
if settings.USE_WEBGPU:
add_library('libwebgpu_cpp')

if settings.USE_WEBGPU1:
add_library('libwebgpu')

if settings.WASM_WORKERS:
add_library('libwasm_workers')

Expand Down

0 comments on commit 55e44f9

Please sign in to comment.