diff --git a/.clang-format b/.clang-format index 3f0c77fdc76..82134d50e76 100644 --- a/.clang-format +++ b/.clang-format @@ -6,3 +6,4 @@ WhitespaceSensitiveMacros: - JSG_STRUCT_TS_OVERRIDE - JSG_STRUCT_TS_DEFINE PointerAlignment: Left +ColumnLimit: 100 diff --git a/src/workerd/api/gpu/gpu-device.c++ b/src/workerd/api/gpu/gpu-device.c++ index 5523bfa4aa7..286af26f32e 100644 --- a/src/workerd/api/gpu/gpu-device.c++ +++ b/src/workerd/api/gpu/gpu-device.c++ @@ -191,7 +191,7 @@ GPUDevice::createShaderModule(GPUShaderModuleDescriptor descriptor) { wgsl_desc.code = descriptor.code.cStr(); auto shader = device_.CreateShaderModule(&desc); - return jsg::alloc(kj::mv(shader)); + return jsg::alloc(kj::mv(shader), kj::addRef(*async_)); } jsg::Ref diff --git a/src/workerd/api/gpu/gpu-shader-module.c++ b/src/workerd/api/gpu/gpu-shader-module.c++ new file mode 100644 index 00000000000..0f58e49cf8c --- /dev/null +++ b/src/workerd/api/gpu/gpu-shader-module.c++ @@ -0,0 +1,40 @@ +// Copyright (c) 2017-2022 Cloudflare, Inc. +// Licensed under the Apache 2.0 license found in the LICENSE file or at: +// https://opensource.org/licenses/Apache-2.0 + +#include "gpu-shader-module.h" + +namespace workerd::api::gpu { + +jsg::Promise> GPUShaderModule::getCompilationInfo() { + + struct Context { + kj::Own>> fulfiller; + AsyncTask task; + }; + auto paf = kj::newPromiseAndFulfiller>(); + // This context object will hold information for the callback, including the + // fullfiller to signal the caller with the result, and an async task that + // will ensure the device's Tick() function is called periodically. It will be + // deallocated at the end of the callback function. + auto ctx = new Context{kj::mv(paf.fulfiller), AsyncTask(kj::addRef(*async_))}; + shader_.GetCompilationInfo( + [](WGPUCompilationInfoRequestStatus status, WGPUCompilationInfo const* compilationInfo, + void* userdata) { + auto c = std::unique_ptr(static_cast(userdata)); + + kj::Vector> messages(compilationInfo->messageCount); + for (uint32_t i = 0; i < compilationInfo->messageCount; i++) { + auto& msg = compilationInfo->messages[i]; + messages.add(jsg::alloc(msg)); + } + + c->fulfiller->fulfill(jsg::alloc(kj::mv(messages))); + }, + ctx); + + auto& context = IoContext::current(); + return context.awaitIo(kj::mv(paf.promise)); +} + +} // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-shader-module.h b/src/workerd/api/gpu/gpu-shader-module.h index 6f72a2cece4..abce3823583 100644 --- a/src/workerd/api/gpu/gpu-shader-module.h +++ b/src/workerd/api/gpu/gpu-shader-module.h @@ -4,20 +4,72 @@ #pragma once +#include "gpu-async-runner.h" +#include "gpu-utils.h" #include #include namespace workerd::api::gpu { +class GPUCompilationMessage : public jsg::Object { +public: + explicit GPUCompilationMessage(const WGPUCompilationMessage& m) : message(m) {} + + JSG_RESOURCE_TYPE(GPUCompilationMessage) { + JSG_READONLY_PROTOTYPE_PROPERTY(message, getMessage); + JSG_READONLY_PROTOTYPE_PROPERTY(type, getType); + JSG_READONLY_PROTOTYPE_PROPERTY(lineNum, getLineNum); + JSG_READONLY_PROTOTYPE_PROPERTY(linePos, getLinePos); + JSG_READONLY_PROTOTYPE_PROPERTY(offset, getOffset); + JSG_READONLY_PROTOTYPE_PROPERTY(length, getLength); + } + +private: + WGPUCompilationMessage message; + + kj::StringPtr getMessage() { return message.message; } + GPUCompilationMessageType getType() { + switch (message.type) { + case WGPUCompilationMessageType_Error: + return kj::str("error"); + case WGPUCompilationMessageType_Warning: + return kj::str("warning"); + case WGPUCompilationMessageType_Info: + return kj::str("info"); + default: + KJ_UNREACHABLE + } + } + uint64_t getLineNum() { return message.lineNum; } + uint64_t getLinePos() { return message.linePos; } + uint64_t getOffset() { return message.offset; } + uint64_t getLength() { return message.length; } +}; + +class GPUCompilationInfo : public jsg::Object { +public: + explicit GPUCompilationInfo(kj::Vector> messages) + : messages_(kj::mv(messages)){}; + JSG_RESOURCE_TYPE(GPUCompilationInfo) { JSG_READONLY_PROTOTYPE_PROPERTY(messages, getMessages); } + +private: + kj::Vector> messages_; + kj::ArrayPtr> getMessages() { return messages_.asPtr(); }; + void visitForGc(jsg::GcVisitor& visitor) { visitor.visitAll(messages_); } +}; + class GPUShaderModule : public jsg::Object { public: // Implicit cast operator to Dawn GPU object - inline operator const wgpu::ShaderModule &() const { return shader_; } - explicit GPUShaderModule(wgpu::ShaderModule s) : shader_(kj::mv(s)){}; - JSG_RESOURCE_TYPE(GPUShaderModule) {} + inline operator const wgpu::ShaderModule&() const { return shader_; } + explicit GPUShaderModule(wgpu::ShaderModule s, kj::Own async) + : shader_(kj::mv(s)), async_(kj::mv(async)){}; + JSG_RESOURCE_TYPE(GPUShaderModule) { JSG_METHOD(getCompilationInfo); } private: wgpu::ShaderModule shader_; + kj::Own async_; + jsg::Promise> getCompilationInfo(); }; struct GPUShaderModuleDescriptor { diff --git a/src/workerd/api/gpu/gpu-utils.h b/src/workerd/api/gpu/gpu-utils.h index 9f741ac99f4..2cd5f2edeb2 100644 --- a/src/workerd/api/gpu/gpu-utils.h +++ b/src/workerd/api/gpu/gpu-utils.h @@ -30,6 +30,7 @@ using GPUBufferDynamicOffset = uint32_t; using GPUPowerPreference = kj::String; using GPUErrorFilter = kj::String; using GPUDeviceLostReason = kj::String; +using GPUCompilationMessageType = kj::String; struct GPUMapMode : public jsg::Object { static constexpr GPUFlagsConstant READ = 0x0001; @@ -79,7 +80,7 @@ struct GPUBufferUsage : public jsg::Object { }; }; -wgpu::FeatureName parseFeatureName(GPUFeatureName &); -kj::Maybe getFeatureName(wgpu::FeatureName &feature); +wgpu::FeatureName parseFeatureName(GPUFeatureName&); +kj::Maybe getFeatureName(wgpu::FeatureName& feature); } // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu.h b/src/workerd/api/gpu/gpu.h index cf72fe0b9e4..e06e4511c8d 100644 --- a/src/workerd/api/gpu/gpu.h +++ b/src/workerd/api/gpu/gpu.h @@ -44,7 +44,7 @@ class GPU : public jsg::Object { private: jsg::Promise>> - requestAdapter(jsg::Lock &, jsg::Optional); + requestAdapter(jsg::Lock&, jsg::Optional); dawn::native::Instance instance_; }; @@ -73,6 +73,7 @@ class GPU : public jsg::Object { api::gpu::GPURequestAdapterOptions, api::gpu::GPUAdapterInfo, \ api::gpu::GPUSupportedFeatures, api::gpu::GPUSupportedLimits, \ api::gpu::GPUError, api::gpu::GPUOOMError, api::gpu::GPUValidationError, \ - api::gpu::GPUDeviceLostInfo + api::gpu::GPUDeviceLostInfo, api::gpu::GPUCompilationMessage, \ + api::gpu::GPUCompilationInfo }; // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/webgpu-compute-test.js b/src/workerd/api/gpu/webgpu-compute-test.js index 1a32eb86479..d5f158dc86f 100644 --- a/src/workerd/api/gpu/webgpu-compute-test.js +++ b/src/workerd/api/gpu/webgpu-compute-test.js @@ -24,7 +24,7 @@ export const read_sync_stack = { ok(adapter.features.has("depth-clip-control")); ok(adapter.limits); - ok(adapter.limits.maxBufferSize) + ok(adapter.limits.maxBufferSize); const requiredFeatures = []; requiredFeatures.push("texture-compression-astc"); @@ -40,8 +40,8 @@ export const read_sync_stack = { 2 /* rows */, 4 /* columns */, 1, 2, 3, 4, 5, 6, 7, 8, ]); - device.pushErrorScope('out-of-memory'); - device.pushErrorScope('validation'); + device.pushErrorScope("out-of-memory"); + device.pushErrorScope("validation"); const gpuBufferFirstMatrix = device.createBuffer({ mappedAtCreation: true, @@ -50,7 +50,7 @@ export const read_sync_stack = { }); ok(gpuBufferFirstMatrix); - ok(await device.popErrorScope() === null); + ok((await device.popErrorScope()) === null); const arrayBufferFirstMatrix = gpuBufferFirstMatrix.getMappedRange(); ok(arrayBufferFirstMatrix); @@ -174,6 +174,9 @@ export const read_sync_stack = { }); ok(shaderModule); + const compilationInfo = await shaderModule.getCompilationInfo(); + ok(compilationInfo.messages.length == 0); + // Pipeline setup const computePipeline = device.createComputePipeline({ layout: device.createPipelineLayout({