Skip to content

Commit

Permalink
webgpu: RenderPipeline-related objects
Browse files Browse the repository at this point in the history
Added createRenderPipeline() on the device object and related
descriptors and structs.
  • Loading branch information
edevil committed Oct 24, 2023
1 parent bfa0888 commit c20c61b
Show file tree
Hide file tree
Showing 15 changed files with 675 additions and 69 deletions.
1 change: 1 addition & 0 deletions src/workerd/api/global-scope.h
Original file line number Diff line number Diff line change
Expand Up @@ -497,6 +497,7 @@ class ServiceWorkerGlobalScope: public WorkerGlobalScope {
JSG_NESTED_TYPE_NAMED(api::gpu::GPUShaderStage, GPUShaderStage);
JSG_NESTED_TYPE_NAMED(api::gpu::GPUMapMode, GPUMapMode);
JSG_NESTED_TYPE_NAMED(api::gpu::GPUTextureUsage, GPUTextureUsage);
JSG_NESTED_TYPE_NAMED(api::gpu::GPUColorWrite, GPUColorWrite);
#endif

JSG_TS_ROOT();
Expand Down
37 changes: 0 additions & 37 deletions src/workerd/api/gpu/gpu-bindgroup-layout.c++
Original file line number Diff line number Diff line change
Expand Up @@ -79,35 +79,6 @@ wgpu::TextureSampleType parseTextureSampleType(kj::StringPtr sType) {
JSG_FAIL_REQUIRE(TypeError, "unknown texture sample type", sType);
}

wgpu::TextureViewDimension parseTextureViewDimension(kj::StringPtr dim) {

if (dim == "1d") {
return wgpu::TextureViewDimension::e1D;
}

if (dim == "2d") {
return wgpu::TextureViewDimension::e2D;
}

if (dim == "2d-array") {
return wgpu::TextureViewDimension::e2DArray;
}

if (dim == "cube") {
return wgpu::TextureViewDimension::Cube;
}

if (dim == "cube-array") {
return wgpu::TextureViewDimension::CubeArray;
}

if (dim == "3d") {
return wgpu::TextureViewDimension::e3D;
}

JSG_FAIL_REQUIRE(TypeError, "unknown texture view dimension", dim);
}

wgpu::TextureBindingLayout parseTextureBindingLayout(GPUTextureBindingLayout& texture) {
wgpu::TextureBindingLayout t;
t.sampleType = parseTextureSampleType(texture.sampleType.orDefault([] { return "float"_kj; }));
Expand All @@ -118,14 +89,6 @@ wgpu::TextureBindingLayout parseTextureBindingLayout(GPUTextureBindingLayout& te
return kj::mv(t);
}

wgpu::StorageTextureAccess parseStorageAccess(kj::StringPtr access) {
if (access == "write-only") {
return wgpu::StorageTextureAccess::WriteOnly;
}

JSG_FAIL_REQUIRE(TypeError, "unknown storage access", access);
}

wgpu::StorageTextureBindingLayout
parseStorageTextureBindingLayout(GPUStorageTextureBindingLayout& storage) {

Expand Down
15 changes: 1 addition & 14 deletions src/workerd/api/gpu/gpu-compute-pipeline.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,10 @@ class GPUComputePipeline : public jsg::Object {
jsg::Ref<GPUBindGroupLayout> getBindGroupLayout(uint32_t index);
};

using GPUPipelineConstantValue = double;

struct GPUProgrammableStage {
jsg::Ref<GPUShaderModule> module;
kj::String entryPoint;
jsg::Optional<jsg::Dict<GPUPipelineConstantValue>> constants;

JSG_STRUCT(module, entryPoint, constants);
};

using GPUComputePipelineLayout =
kj::OneOf<jsg::NonCoercible<kj::String>, jsg::Ref<GPUPipelineLayout>>;

struct GPUComputePipelineDescriptor {
jsg::Optional<kj::String> label;
GPUProgrammableStage compute;
GPUComputePipelineLayout layout;
GPUPipelineLayoutBase layout;

JSG_STRUCT(label, compute, layout);
};
Expand Down
168 changes: 158 additions & 10 deletions src/workerd/api/gpu/gpu-device.c++
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ jsg::Ref<GPUTexture> GPUDevice::createTexture(jsg::Lock& js, GPUTextureDescripto

KJ_SWITCH_ONEOF(descriptor.size) {
KJ_CASE_ONEOF(coords, jsg::Sequence<GPUIntegerCoordinate>) {
// if we have a sequence of coordinates we assume that the order is: width, heigth, depth, if
// available, and ignore all the rest.
switch (coords.size()) {
default:
case 3:
Expand All @@ -51,22 +53,34 @@ jsg::Ref<GPUTexture> GPUDevice::createTexture(jsg::Lock& js, GPUTextureDescripto
}
}
KJ_CASE_ONEOF(size, GPUExtent3DDict) {
desc.size.depthOrArrayLayers = size.depthOrArrayLayers.orDefault(1);
desc.size.height = size.height.orDefault(1);
KJ_IF_SOME(depthOrArrayLayers, size.depthOrArrayLayers) {
desc.size.depthOrArrayLayers = depthOrArrayLayers;
}
KJ_IF_SOME(height, size.height) {
desc.size.height = height;
}
desc.size.width = size.width;
}
}

desc.mipLevelCount = descriptor.mipLevelCount.orDefault(1);
desc.sampleCount = descriptor.sampleCount.orDefault(1);
desc.dimension = parseTextureDimension(descriptor.dimension.orDefault(kj::str("2d")));
KJ_IF_SOME(mipLevelCount, descriptor.mipLevelCount) {
desc.mipLevelCount = mipLevelCount;
}
KJ_IF_SOME(sampleCount, descriptor.sampleCount) {
desc.sampleCount = sampleCount;
}
KJ_IF_SOME(dimension, descriptor.dimension) {
desc.dimension = parseTextureDimension(dimension);
}
desc.format = parseTextureFormat(descriptor.format);
desc.usage = static_cast<wgpu::TextureUsage>(descriptor.usage);
auto viewFormats = KJ_MAP(format, descriptor.viewFormats.orDefault({}))->wgpu::TextureFormat {
return parseTextureFormat(format);
};
desc.viewFormats = viewFormats.begin();
desc.viewFormatCount = viewFormats.size();
KJ_IF_SOME(viewFormatsSeq, descriptor.viewFormats) {
auto viewFormats = KJ_MAP(format, viewFormatsSeq)->wgpu::TextureFormat {
return parseTextureFormat(format);
};
desc.viewFormats = viewFormats.begin();
desc.viewFormatCount = viewFormats.size();
}

auto texture = device_.CreateTexture(&desc);
return jsg::alloc<GPUTexture>(kj::mv(texture));
Expand Down Expand Up @@ -232,6 +246,140 @@ jsg::Ref<GPUShaderModule> GPUDevice::createShaderModule(GPUShaderModuleDescripto
return jsg::alloc<GPUShaderModule>(kj::mv(shader), kj::addRef(*async_));
}

struct ParsedRenderPipelineDescriptor {
wgpu::RenderPipelineDescriptor desc;
kj::Own<wgpu::PrimitiveDepthClipControl> depthClip;
kj::Own<wgpu::DepthStencilState> stencilState;
kj::Own<wgpu::FragmentState> fragment;
};

void parseStencilFaceState(wgpu::StencilFaceState& out, jsg::Optional<GPUStencilFaceState>& in) {
KJ_IF_SOME(stencilFront, in) {
out.compare = parseCompareFunction(stencilFront.compare.orDefault([] { return "always"_kj; }));
out.failOp = parseStencilOperation(stencilFront.failOp.orDefault([] { return "keep"_kj; }));
out.depthFailOp =
parseStencilOperation(stencilFront.depthFailOp.orDefault([] { return "keep"_kj; }));
out.passOp = parseStencilOperation(stencilFront.passOp.orDefault([] { return "keep"_kj; }));
}
}

ParsedRenderPipelineDescriptor
parseRenderPipelineDescriptor(GPURenderPipelineDescriptor& descriptor) {
ParsedRenderPipelineDescriptor parsedDesc{};

KJ_IF_SOME(label, descriptor.label) {
parsedDesc.desc.label = label.cStr();
}

parsedDesc.desc.vertex.module = *descriptor.vertex.module;
parsedDesc.desc.vertex.entryPoint = descriptor.vertex.entryPoint.cStr();

kj::Vector<wgpu::ConstantEntry> constants;
KJ_IF_SOME(cDict, descriptor.vertex.constants) {
for (auto& f : cDict.fields) {
wgpu::ConstantEntry e;
e.key = f.name.cStr();
e.value = f.value;
constants.add(kj::mv(e));
}
}

parsedDesc.desc.vertex.constants = constants.begin();
parsedDesc.desc.vertex.constantCount = constants.size();

// TODO(soon): descriptor.vertex.buffers

KJ_SWITCH_ONEOF(descriptor.layout) {
KJ_CASE_ONEOF(autoLayoutMode, jsg::NonCoercible<kj::String>) {
JSG_REQUIRE(autoLayoutMode.value == "auto", TypeError, "unknown auto layout mode",
autoLayoutMode.value);
parsedDesc.desc.layout = nullptr;
}
KJ_CASE_ONEOF(layout, jsg::Ref<GPUPipelineLayout>) {
parsedDesc.desc.layout = *layout;
}
}

KJ_IF_SOME(primitive, descriptor.primitive) {
if (primitive.unclippedDepth.orDefault(false)) {
auto depthClip = kj::heap<wgpu::PrimitiveDepthClipControl>();
depthClip->unclippedDepth = true;
parsedDesc.depthClip = kj::mv(depthClip);
parsedDesc.desc.nextInChain = parsedDesc.depthClip;
}

parsedDesc.desc.primitive.topology =
parsePrimitiveTopology(primitive.topology.orDefault([] { return "triangle-list"_kj; }));

KJ_IF_SOME(indexFormat, primitive.stripIndexFormat) {
parsedDesc.desc.primitive.stripIndexFormat = parseIndexFormat(indexFormat);
}

parsedDesc.desc.primitive.frontFace =
parseFrontFace(primitive.frontFace.orDefault([] { return "ccw"_kj; }));
parsedDesc.desc.primitive.cullMode =
parseCullMode(primitive.cullMode.orDefault([] { return "none"_kj; }));
}

KJ_IF_SOME(depthStencil, descriptor.depthStencil) {
auto depthStencilState = kj::heap<wgpu::DepthStencilState>();
depthStencilState->format = parseTextureFormat(depthStencil.format);
depthStencilState->depthWriteEnabled = depthStencil.depthWriteEnabled;

parseStencilFaceState(depthStencilState->stencilFront, depthStencil.stencilFront);
parseStencilFaceState(depthStencilState->stencilBack, depthStencil.stencilBack);

depthStencilState->stencilReadMask = depthStencil.stencilReadMask.orDefault(0xFFFFFFFF);
depthStencilState->stencilWriteMask = depthStencil.stencilWriteMask.orDefault(0xFFFFFFFF);
depthStencilState->depthBias = depthStencil.depthBias.orDefault(0);
depthStencilState->depthBiasSlopeScale = depthStencil.depthBiasSlopeScale.orDefault(0);
depthStencilState->depthBiasClamp = depthStencil.depthBiasClamp.orDefault(0);

parsedDesc.stencilState = kj::mv(depthStencilState);
parsedDesc.desc.depthStencil = parsedDesc.stencilState;
}

KJ_IF_SOME(multisample, descriptor.multisample) {
parsedDesc.desc.multisample.count = multisample.count.orDefault(1);
parsedDesc.desc.multisample.mask = multisample.mask.orDefault(0xFFFFFFFF);
parsedDesc.desc.multisample.alphaToCoverageEnabled =
multisample.alphaToCoverageEnabled.orDefault(false);
}

KJ_IF_SOME(fragment, descriptor.fragment) {
auto fragmentState = kj::heap<wgpu::FragmentState>();
fragmentState->module = *fragment.module;
fragmentState->entryPoint = fragment.entryPoint.cStr();

kj::Vector<wgpu::ConstantEntry> constants;
KJ_IF_SOME(cDict, fragment.constants) {
for (auto& f : cDict.fields) {
wgpu::ConstantEntry e;
e.key = f.name.cStr();
e.value = f.value;
constants.add(kj::mv(e));
}
}

fragmentState->constants = constants.begin();
fragmentState->constantCount = constants.size();

// TODO(soon): fragment.targets

parsedDesc.fragment = kj::mv(fragmentState);
parsedDesc.desc.fragment = parsedDesc.fragment;
}

return kj::mv(parsedDesc);
}

jsg::Ref<GPURenderPipeline>
GPUDevice::createRenderPipeline(GPURenderPipelineDescriptor descriptor) {
auto parsedDesc = parseRenderPipelineDescriptor(descriptor);
auto pipeline = device_.CreateRenderPipeline(&parsedDesc.desc);
return jsg::alloc<GPURenderPipeline>(kj::mv(pipeline));
}

jsg::Ref<GPUPipelineLayout>
GPUDevice::createPipelineLayout(GPUPipelineLayoutDescriptor descriptor) {
wgpu::PipelineLayoutDescriptor desc{};
Expand Down
3 changes: 3 additions & 0 deletions src/workerd/api/gpu/gpu-device.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "gpu-pipeline-layout.h"
#include "gpu-query-set.h"
#include "gpu-queue.h"
#include "gpu-render-pipeline.h"
#include "gpu-sampler.h"
#include "gpu-shader-module.h"
#include "gpu-supported-features.h"
Expand All @@ -40,6 +41,7 @@ class GPUDevice : public EventTarget {
JSG_METHOD(createShaderModule);
JSG_METHOD(createPipelineLayout);
JSG_METHOD(createComputePipeline);
JSG_METHOD(createRenderPipeline);
JSG_METHOD(createCommandEncoder);
JSG_METHOD(createTexture);
JSG_METHOD(destroy);
Expand Down Expand Up @@ -70,6 +72,7 @@ class GPUDevice : public EventTarget {
jsg::Ref<GPUShaderModule> createShaderModule(GPUShaderModuleDescriptor descriptor);
jsg::Ref<GPUPipelineLayout> createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
jsg::Ref<GPUComputePipeline> createComputePipeline(GPUComputePipelineDescriptor descriptor);
jsg::Ref<GPURenderPipeline> createRenderPipeline(GPURenderPipelineDescriptor descriptor);
jsg::Promise<jsg::Ref<GPUComputePipeline>>
createComputePipelineAsync(GPUComputePipelineDescriptor descriptor);
jsg::Ref<GPUCommandEncoder>
Expand Down
2 changes: 2 additions & 0 deletions src/workerd/api/gpu/gpu-pipeline-layout.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,6 @@ struct GPUPipelineLayoutDescriptor {
JSG_STRUCT(label, bindGroupLayouts);
};

using GPUPipelineLayoutBase = kj::OneOf<jsg::NonCoercible<kj::String>, jsg::Ref<GPUPipelineLayout>>;

} // namespace workerd::api::gpu
Loading

0 comments on commit c20c61b

Please sign in to comment.