diff --git a/src/workerd/api/global-scope.h b/src/workerd/api/global-scope.h index 5202f76851b..1e2e795860c 100644 --- a/src/workerd/api/global-scope.h +++ b/src/workerd/api/global-scope.h @@ -496,6 +496,8 @@ class ServiceWorkerGlobalScope: public WorkerGlobalScope { JSG_NESTED_TYPE_NAMED(api::gpu::GPUBufferUsage, GPUBufferUsage); 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(); diff --git a/src/workerd/api/gpu/gpu-bindgroup-layout.c++ b/src/workerd/api/gpu/gpu-bindgroup-layout.c++ index 8ab445fbfb0..7a2e9e36a5b 100644 --- a/src/workerd/api/gpu/gpu-bindgroup-layout.c++ +++ b/src/workerd/api/gpu/gpu-bindgroup-layout.c++ @@ -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; })); @@ -118,395 +89,6 @@ wgpu::TextureBindingLayout parseTextureBindingLayout(GPUTextureBindingLayout& te return kj::mv(t); } -wgpu::TextureFormat parseTextureFormat(kj::StringPtr format) { - - if (format == "r8unorm") { - return wgpu::TextureFormat::R8Unorm; - } - - if (format == "r8snorm") { - return wgpu::TextureFormat::R8Snorm; - } - - if (format == "r8uint") { - return wgpu::TextureFormat::R8Uint; - } - - if (format == "r8sint") { - return wgpu::TextureFormat::R8Sint; - } - - if (format == "r16uint") { - return wgpu::TextureFormat::R16Uint; - } - - if (format == "r16sint") { - return wgpu::TextureFormat::R16Sint; - } - - if (format == "r16float") { - return wgpu::TextureFormat::R16Float; - } - - if (format == "rg8unorm") { - return wgpu::TextureFormat::RG8Unorm; - } - - if (format == "rg8snorm") { - return wgpu::TextureFormat::RG8Snorm; - } - - if (format == "rg8uint") { - return wgpu::TextureFormat::RG8Uint; - } - - if (format == "rg8sint") { - return wgpu::TextureFormat::RG8Sint; - } - - if (format == "r32uint") { - return wgpu::TextureFormat::R32Uint; - } - - if (format == "r32sint") { - return wgpu::TextureFormat::R32Sint; - } - - if (format == "r32float") { - return wgpu::TextureFormat::R32Float; - } - - if (format == "rg16uint") { - return wgpu::TextureFormat::RG16Uint; - } - - if (format == "rg16sint") { - return wgpu::TextureFormat::RG16Sint; - } - - if (format == "rg16float") { - return wgpu::TextureFormat::RG16Float; - } - - if (format == "rgba8unorm") { - return wgpu::TextureFormat::RGBA8Unorm; - } - - if (format == "rgba8unorm-srgb") { - return wgpu::TextureFormat::RGBA8UnormSrgb; - } - - if (format == "rgba8snorm") { - return wgpu::TextureFormat::RGBA8Snorm; - } - - if (format == "rgba8uint") { - return wgpu::TextureFormat::RGBA8Uint; - } - - if (format == "rgba8sint") { - return wgpu::TextureFormat::RGBA8Sint; - } - - if (format == "bgra8unorm") { - return wgpu::TextureFormat::BGRA8Unorm; - } - - if (format == "bgra8unorm-srgb") { - return wgpu::TextureFormat::BGRA8UnormSrgb; - } - - if (format == "rgb9e5ufloat") { - return wgpu::TextureFormat::RGB9E5Ufloat; - } - - if (format == "rgb10a2unorm") { - return wgpu::TextureFormat::RGB10A2Unorm; - } - - if (format == "rg11b10ufloat") { - return wgpu::TextureFormat::RG11B10Ufloat; - } - - if (format == "rg32uint") { - return wgpu::TextureFormat::RG32Uint; - } - - if (format == "rg32sint") { - return wgpu::TextureFormat::RG32Sint; - } - - if (format == "rg32float") { - return wgpu::TextureFormat::RG32Float; - } - - if (format == "rgba16uint") { - return wgpu::TextureFormat::RGBA16Uint; - } - - if (format == "rgba16sint") { - return wgpu::TextureFormat::RGBA16Sint; - } - - if (format == "rgba16float") { - return wgpu::TextureFormat::RGBA16Float; - } - - if (format == "rgba32uint") { - return wgpu::TextureFormat::RGBA32Uint; - } - - if (format == "rgba32sint") { - return wgpu::TextureFormat::RGBA32Sint; - } - - if (format == "rgba32float") { - return wgpu::TextureFormat::RGBA32Float; - } - - if (format == "stencil8") { - return wgpu::TextureFormat::Stencil8; - } - - if (format == "depth16unorm") { - return wgpu::TextureFormat::Depth16Unorm; - } - - if (format == "depth24plus") { - return wgpu::TextureFormat::Depth24Plus; - } - - if (format == "depth24plus-stencil8") { - return wgpu::TextureFormat::Depth24PlusStencil8; - } - - if (format == "depth32float") { - return wgpu::TextureFormat::Depth32Float; - } - - if (format == "depth32float-stencil8") { - return wgpu::TextureFormat::Depth32FloatStencil8; - } - - if (format == "bc1-rgba-unorm") { - return wgpu::TextureFormat::BC1RGBAUnorm; - } - - if (format == "bc1-rgba-unorm-srgb") { - return wgpu::TextureFormat::BC1RGBAUnormSrgb; - } - - if (format == "bc2-rgba-unorm") { - return wgpu::TextureFormat::BC2RGBAUnorm; - } - - if (format == "bc2-rgba-unorm-srgb") { - return wgpu::TextureFormat::BC2RGBAUnormSrgb; - } - - if (format == "bc3-rgba-unorm") { - return wgpu::TextureFormat::BC3RGBAUnorm; - } - - if (format == "bc3-rgba-unorm-srgb") { - return wgpu::TextureFormat::BC3RGBAUnormSrgb; - } - - if (format == "bc4-r-unorm") { - return wgpu::TextureFormat::BC4RUnorm; - } - - if (format == "bc4-r-snorm") { - return wgpu::TextureFormat::BC4RSnorm; - } - - if (format == "bc5-rg-unorm") { - return wgpu::TextureFormat::BC5RGUnorm; - } - - if (format == "bc5-rg-snorm") { - return wgpu::TextureFormat::BC5RGSnorm; - } - - if (format == "bc6h-rgb-ufloat") { - return wgpu::TextureFormat::BC6HRGBUfloat; - } - - if (format == "bc6h-rgb-float") { - return wgpu::TextureFormat::BC6HRGBFloat; - } - - if (format == "bc7-rgba-unorm") { - return wgpu::TextureFormat::BC7RGBAUnorm; - } - - if (format == "bc7-rgba-unorm-srgb") { - return wgpu::TextureFormat::BC7RGBAUnormSrgb; - } - - if (format == "etc2-rgb8unorm") { - return wgpu::TextureFormat::ETC2RGB8Unorm; - } - - if (format == "etc2-rgb8unorm-srgb") { - return wgpu::TextureFormat::ETC2RGB8UnormSrgb; - } - - if (format == "etc2-rgb8a1unorm") { - return wgpu::TextureFormat::ETC2RGB8A1Unorm; - } - - if (format == "etc2-rgb8a1unorm-srgb") { - return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb; - } - - if (format == "etc2-rgba8unorm") { - return wgpu::TextureFormat::ETC2RGBA8Unorm; - } - - if (format == "etc2-rgba8unorm-srgb") { - return wgpu::TextureFormat::ETC2RGBA8UnormSrgb; - } - - if (format == "eac-r11unorm") { - return wgpu::TextureFormat::EACR11Unorm; - } - - if (format == "eac-r11snorm") { - return wgpu::TextureFormat::EACR11Snorm; - } - - if (format == "eac-rg11unorm") { - return wgpu::TextureFormat::EACRG11Unorm; - } - - if (format == "eac-rg11snorm") { - return wgpu::TextureFormat::EACRG11Snorm; - } - - if (format == "astc-4x4-unorm") { - return wgpu::TextureFormat::ASTC4x4Unorm; - } - - if (format == "astc-4x4-unorm-srgb") { - return wgpu::TextureFormat::ASTC4x4UnormSrgb; - } - - if (format == "astc-5x4-unorm") { - return wgpu::TextureFormat::ASTC5x4Unorm; - } - - if (format == "astc-5x4-unorm-srgb") { - return wgpu::TextureFormat::ASTC5x4UnormSrgb; - } - - if (format == "astc-5x5-unorm") { - return wgpu::TextureFormat::ASTC5x5Unorm; - } - - if (format == "astc-5x5-unorm-srgb") { - return wgpu::TextureFormat::ASTC5x5UnormSrgb; - } - - if (format == "astc-6x5-unorm") { - return wgpu::TextureFormat::ASTC6x5Unorm; - } - - if (format == "astc-6x5-unorm-srgb") { - return wgpu::TextureFormat::ASTC6x5UnormSrgb; - } - - if (format == "astc-6x6-unorm") { - return wgpu::TextureFormat::ASTC6x6Unorm; - } - - if (format == "astc-6x6-unorm-srgb") { - return wgpu::TextureFormat::ASTC6x6UnormSrgb; - } - - if (format == "astc-8x5-unorm") { - return wgpu::TextureFormat::ASTC8x5Unorm; - } - - if (format == "astc-8x5-unorm-srgb") { - return wgpu::TextureFormat::ASTC8x5UnormSrgb; - } - - if (format == "astc-8x6-unorm") { - return wgpu::TextureFormat::ASTC8x6Unorm; - } - - if (format == "astc-8x6-unorm-srgb") { - return wgpu::TextureFormat::ASTC8x6UnormSrgb; - } - - if (format == "astc-8x8-unorm") { - return wgpu::TextureFormat::ASTC8x8Unorm; - } - - if (format == "astc-8x8-unorm-srgb") { - return wgpu::TextureFormat::ASTC8x8UnormSrgb; - } - - if (format == "astc-10x5-unorm") { - return wgpu::TextureFormat::ASTC10x5Unorm; - } - - if (format == "astc-10x5-unorm-srgb") { - return wgpu::TextureFormat::ASTC10x5UnormSrgb; - } - - if (format == "astc-10x6-unorm") { - return wgpu::TextureFormat::ASTC10x6Unorm; - } - - if (format == "astc-10x6-unorm-srgb") { - return wgpu::TextureFormat::ASTC10x6UnormSrgb; - } - - if (format == "astc-10x8-unorm") { - return wgpu::TextureFormat::ASTC10x8Unorm; - } - - if (format == "astc-10x8-unorm-srgb") { - return wgpu::TextureFormat::ASTC10x8UnormSrgb; - } - - if (format == "astc-10x10-unorm") { - return wgpu::TextureFormat::ASTC10x10Unorm; - } - - if (format == "astc-10x10-unorm-srgb") { - return wgpu::TextureFormat::ASTC10x10UnormSrgb; - } - - if (format == "astc-12x10-unorm") { - return wgpu::TextureFormat::ASTC12x10Unorm; - } - - if (format == "astc-12x10-unorm-srgb") { - return wgpu::TextureFormat::ASTC12x10UnormSrgb; - } - - if (format == "astc-12x12-unorm") { - return wgpu::TextureFormat::ASTC12x12Unorm; - } - - if (format == "astc-12x12-unorm-srgb") { - return wgpu::TextureFormat::ASTC12x12UnormSrgb; - } - - JSG_FAIL_REQUIRE(TypeError, "unknown texture format", format); -} - -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) { @@ -524,19 +106,19 @@ wgpu::BindGroupLayoutEntry parseBindGroupLayoutEntry(GPUBindGroupLayoutEntry& en e.binding = entry.binding; e.visibility = static_cast(entry.visibility); - KJ_IF_SOME (buffer, entry.buffer) { + KJ_IF_SOME(buffer, entry.buffer) { e.buffer = parseBufferBindingLayout(buffer); } - KJ_IF_SOME (sampler, entry.sampler) { + KJ_IF_SOME(sampler, entry.sampler) { e.sampler = parseSamplerBindingLayout(sampler); } - KJ_IF_SOME (texture, entry.texture) { + KJ_IF_SOME(texture, entry.texture) { e.texture = parseTextureBindingLayout(texture); } - KJ_IF_SOME (storage, entry.storageTexture) { + KJ_IF_SOME(storage, entry.storageTexture) { e.storageTexture = parseStorageTextureBindingLayout(storage); } diff --git a/src/workerd/api/gpu/gpu-compute-pipeline.h b/src/workerd/api/gpu/gpu-compute-pipeline.h index 8c246a57029..95a782f9db9 100644 --- a/src/workerd/api/gpu/gpu-compute-pipeline.h +++ b/src/workerd/api/gpu/gpu-compute-pipeline.h @@ -27,23 +27,10 @@ class GPUComputePipeline : public jsg::Object { jsg::Ref getBindGroupLayout(uint32_t index); }; -using GPUPipelineConstantValue = double; - -struct GPUProgrammableStage { - jsg::Ref module; - kj::String entryPoint; - jsg::Optional> constants; - - JSG_STRUCT(module, entryPoint, constants); -}; - -using GPUComputePipelineLayout = - kj::OneOf, jsg::Ref>; - struct GPUComputePipelineDescriptor { jsg::Optional label; GPUProgrammableStage compute; - GPUComputePipelineLayout layout; + GPUPipelineLayoutBase layout; JSG_STRUCT(label, compute, layout); }; diff --git a/src/workerd/api/gpu/gpu-device.c++ b/src/workerd/api/gpu/gpu-device.c++ index 80e7d68b9c5..ef1b40d9179 100644 --- a/src/workerd/api/gpu/gpu-device.c++ +++ b/src/workerd/api/gpu/gpu-device.c++ @@ -12,6 +12,7 @@ #include "gpu-query-set.h" #include "gpu-queue.h" #include "gpu-sampler.h" +#include "gpu-texture.h" #include "gpu-utils.h" #include "workerd/jsg/exception.h" #include "workerd/jsg/jsg.h" @@ -28,6 +29,63 @@ jsg::Ref GPUDevice::createBuffer(jsg::Lock& js, GPUBufferDescriptor d return jsg::alloc(js, kj::mv(buffer), kj::mv(desc), device_, kj::addRef(*async_)); } +jsg::Ref GPUDevice::createTexture(jsg::Lock& js, GPUTextureDescriptor descriptor) { + wgpu::TextureDescriptor desc{}; + desc.label = descriptor.label.cStr(); + + KJ_SWITCH_ONEOF(descriptor.size) { + KJ_CASE_ONEOF(coords, jsg::Sequence) { + // 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: + desc.size.depthOrArrayLayers = coords[2]; + KJ_FALLTHROUGH; + case 2: + desc.size.height = coords[1]; + KJ_FALLTHROUGH; + case 1: + desc.size.width = coords[0]; + break; + case 0: + JSG_FAIL_REQUIRE(TypeError, "invalid value for GPUExtent3D"); + } + } + KJ_CASE_ONEOF(size, GPUExtent3DDict) { + 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; + } + } + + 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(descriptor.usage); + 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(kj::mv(texture)); +} + wgpu::CompareFunction parseCompareFunction(kj::StringPtr compare) { if (compare == "never") { return wgpu::CompareFunction::Never; @@ -188,6 +246,140 @@ jsg::Ref GPUDevice::createShaderModule(GPUShaderModuleDescripto return jsg::alloc(kj::mv(shader), kj::addRef(*async_)); } +struct ParsedRenderPipelineDescriptor { + wgpu::RenderPipelineDescriptor desc; + kj::Own depthClip; + kj::Own stencilState; + kj::Own fragment; +}; + +void parseStencilFaceState(wgpu::StencilFaceState& out, jsg::Optional& 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 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) { + JSG_REQUIRE(autoLayoutMode.value == "auto", TypeError, "unknown auto layout mode", + autoLayoutMode.value); + parsedDesc.desc.layout = nullptr; + } + KJ_CASE_ONEOF(layout, jsg::Ref) { + parsedDesc.desc.layout = *layout; + } + } + + KJ_IF_SOME(primitive, descriptor.primitive) { + if (primitive.unclippedDepth.orDefault(false)) { + auto depthClip = kj::heap(); + 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(); + 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(); + fragmentState->module = *fragment.module; + fragmentState->entryPoint = fragment.entryPoint.cStr(); + + kj::Vector 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 +GPUDevice::createRenderPipeline(GPURenderPipelineDescriptor descriptor) { + auto parsedDesc = parseRenderPipelineDescriptor(descriptor); + auto pipeline = device_.CreateRenderPipeline(&parsedDesc.desc); + return jsg::alloc(kj::mv(pipeline)); +} + jsg::Ref GPUDevice::createPipelineLayout(GPUPipelineLayoutDescriptor descriptor) { wgpu::PipelineLayoutDescriptor desc{}; diff --git a/src/workerd/api/gpu/gpu-device.h b/src/workerd/api/gpu/gpu-device.h index 5178d2234bb..54288fb5a70 100644 --- a/src/workerd/api/gpu/gpu-device.h +++ b/src/workerd/api/gpu/gpu-device.h @@ -14,10 +14,12 @@ #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" #include "gpu-supported-limits.h" +#include "gpu-texture.h" #include "workerd/jsg/promise.h" #include #include @@ -39,7 +41,9 @@ 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); JSG_METHOD(createQuerySet); JSG_METHOD(pushErrorScope); @@ -61,12 +65,14 @@ class GPUDevice : public EventTarget { kj::Own async_; bool destroyed_ = false; jsg::Ref createBuffer(jsg::Lock&, GPUBufferDescriptor); + jsg::Ref createTexture(jsg::Lock&, GPUTextureDescriptor); jsg::Ref createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor); jsg::Ref createBindGroup(GPUBindGroupDescriptor descriptor); jsg::Ref createSampler(GPUSamplerDescriptor descriptor); jsg::Ref createShaderModule(GPUShaderModuleDescriptor descriptor); jsg::Ref createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); jsg::Ref createComputePipeline(GPUComputePipelineDescriptor descriptor); + jsg::Ref createRenderPipeline(GPURenderPipelineDescriptor descriptor); jsg::Promise> createComputePipelineAsync(GPUComputePipelineDescriptor descriptor); jsg::Ref diff --git a/src/workerd/api/gpu/gpu-pipeline-layout.h b/src/workerd/api/gpu/gpu-pipeline-layout.h index 5d0f82f321d..ffefb99d300 100644 --- a/src/workerd/api/gpu/gpu-pipeline-layout.h +++ b/src/workerd/api/gpu/gpu-pipeline-layout.h @@ -30,4 +30,6 @@ struct GPUPipelineLayoutDescriptor { JSG_STRUCT(label, bindGroupLayouts); }; +using GPUPipelineLayoutBase = kj::OneOf, jsg::Ref>; + } // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-render-pipeline.h b/src/workerd/api/gpu/gpu-render-pipeline.h new file mode 100644 index 00000000000..8eda1ca89ec --- /dev/null +++ b/src/workerd/api/gpu/gpu-render-pipeline.h @@ -0,0 +1,139 @@ +// 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 + +#pragma once + +#include "gpu-pipeline-layout.h" +#include "gpu-shader-module.h" +#include +#include + +namespace workerd::api::gpu { + +class GPURenderPipeline : public jsg::Object { +public: + // Implicit cast operator to Dawn GPU object + inline operator const wgpu::RenderPipeline&() const { + return pipeline_; + } + explicit GPURenderPipeline(wgpu::RenderPipeline p) : pipeline_(kj::mv(p)){}; + JSG_RESOURCE_TYPE(GPURenderPipeline) {} + +private: + wgpu::RenderPipeline pipeline_; +}; + +struct GPUVertexAttribute { + GPUVertexFormat format; + GPUSize64 offset; + GPUIndex32 shaderLocation; + + JSG_STRUCT(format, offset, shaderLocation); +}; + +struct GPUVertexBufferLayout { + GPUSize64 arrayStride; + jsg::Optional stepMode; + jsg::Sequence attributes; + + JSG_STRUCT(arrayStride, stepMode, attributes); +}; + +struct GPUVertexState { + jsg::Ref module; + kj::String entryPoint; + jsg::Optional> constants; + jsg::Optional> buffers; + + JSG_STRUCT(module, entryPoint, constants, buffers); +}; + +struct GPUBlendComponent { + jsg::Optional operation; + jsg::Optional srcFactor; + jsg::Optional dstFactor; + + JSG_STRUCT(operation, srcFactor, dstFactor); +}; + +struct GPUBlendState { + GPUBlendComponent color; + GPUBlendComponent alpha; + + JSG_STRUCT(color, alpha); +}; + +struct GPUColorTargetState { + GPUTextureFormat format; + GPUBlendState blend; + jsg::Optional writeMask; + + JSG_STRUCT(format, blend, writeMask); +}; + +struct GPUFragmentState { + jsg::Ref module; + kj::String entryPoint; + jsg::Optional> constants; + jsg::Sequence targets; + + JSG_STRUCT(module, entryPoint, constants, targets); +}; + +struct GPUPrimitiveState { + jsg::Optional topology; + jsg::Optional stripIndexFormat; + jsg::Optional frontFace; + jsg::Optional cullMode; + jsg::Optional unclippedDepth; + + JSG_STRUCT(topology, stripIndexFormat, frontFace, cullMode, unclippedDepth); +}; + +struct GPUStencilFaceState { + jsg::Optional compare; + jsg::Optional failOp; + jsg::Optional depthFailOp; + jsg::Optional passOp; + + JSG_STRUCT(compare, failOp, depthFailOp, passOp); +}; + +struct GPUDepthStencilState { + GPUTextureFormat format; + bool depthWriteEnabled; + GPUCompareFunction depthCompare; + jsg::Optional stencilFront; + jsg::Optional stencilBack; + jsg::Optional stencilReadMask; + jsg::Optional stencilWriteMask; + jsg::Optional depthBias; + jsg::Optional depthBiasSlopeScale; + jsg::Optional depthBiasClamp; + + JSG_STRUCT(format, depthWriteEnabled, depthCompare, stencilFront, stencilBack, stencilReadMask, + stencilWriteMask, depthBias, depthBiasSlopeScale, depthBiasClamp); +}; + +struct GPUMultisampleState { + jsg::Optional count; + jsg::Optional mask; + jsg::Optional alphaToCoverageEnabled; + + JSG_STRUCT(count, mask, alphaToCoverageEnabled); +}; + +struct GPURenderPipelineDescriptor { + jsg::Optional label; + GPUPipelineLayoutBase layout; + GPUVertexState vertex; + jsg::Optional primitive; + jsg::Optional depthStencil; + jsg::Optional multisample; + jsg::Optional fragment; + + JSG_STRUCT(label, layout, vertex, primitive, depthStencil, multisample, fragment); +}; + +} // 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 d7a11217f8f..3579fc815d3 100644 --- a/src/workerd/api/gpu/gpu-shader-module.h +++ b/src/workerd/api/gpu/gpu-shader-module.h @@ -99,4 +99,12 @@ struct GPUShaderModuleDescriptor { JSG_STRUCT(label, code); }; +struct GPUProgrammableStage { + jsg::Ref module; + kj::String entryPoint; + jsg::Optional> constants; + + JSG_STRUCT(module, entryPoint, constants); +}; + } // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-texture-view.h b/src/workerd/api/gpu/gpu-texture-view.h new file mode 100644 index 00000000000..836250fb379 --- /dev/null +++ b/src/workerd/api/gpu/gpu-texture-view.h @@ -0,0 +1,41 @@ +// 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 + +#pragma once + +#include "gpu-utils.h" +#include "workerd/jsg/iterator.h" +#include +#include +#include + +namespace workerd::api::gpu { + +class GPUTextureView : public jsg::Object { +public: + // Implicit cast operator to Dawn GPU object + inline operator const wgpu::TextureView&() const { + return textureView_; + } + explicit GPUTextureView(wgpu::TextureView t) : textureView_(kj::mv(t)){}; + JSG_RESOURCE_TYPE(GPUTextureView) {} + +private: + wgpu::TextureView textureView_; +}; + +struct GPUTextureViewDescriptor { + kj::String label; + GPUTextureFormat format; + GPUTextureViewDimension dimension; + jsg::Optional aspect; + jsg::Optional baseMipLevel; + GPUIntegerCoordinate mipLevelCount; + jsg::Optional baseArrayLayer; + GPUIntegerCoordinate arrayLayerCount; + JSG_STRUCT(label, format, dimension, aspect, baseMipLevel, mipLevelCount, baseArrayLayer, + arrayLayerCount); +}; + +} // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-texture.c++ b/src/workerd/api/gpu/gpu-texture.c++ new file mode 100644 index 00000000000..f54b7f131ed --- /dev/null +++ b/src/workerd/api/gpu/gpu-texture.c++ @@ -0,0 +1,31 @@ +// 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-texture.h" +#include "workerd/api/gpu/gpu-texture-view.h" +#include "workerd/api/gpu/gpu-utils.h" +#include "workerd/jsg/exception.h" +#include "workerd/jsg/jsg.h" + +namespace workerd::api::gpu { + +jsg::Ref +GPUTexture::createView(jsg::Optional descriptor) { + wgpu::TextureViewDescriptor desc{}; + KJ_IF_SOME(d, descriptor) { + desc.label = d.label.cStr(); + desc.format = parseTextureFormat(d.format); + desc.dimension = parseTextureViewDimension(d.dimension); + desc.aspect = parseTextureAspect(d.aspect.orDefault([] { return "all"_kj; })); + desc.baseMipLevel = d.baseMipLevel.orDefault(0); + desc.mipLevelCount = d.mipLevelCount; + desc.baseArrayLayer = d.baseArrayLayer.orDefault(0); + desc.arrayLayerCount = d.arrayLayerCount; + } + + auto textureView = texture_.CreateView(&desc); + return jsg::alloc(kj::mv(textureView)); +} + +} // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-texture.h b/src/workerd/api/gpu/gpu-texture.h new file mode 100644 index 00000000000..8e3c0a71158 --- /dev/null +++ b/src/workerd/api/gpu/gpu-texture.h @@ -0,0 +1,100 @@ +// 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 + +#pragma once + +#include "gpu-texture-view.h" +#include "gpu-utils.h" +#include "workerd/jsg/iterator.h" +#include +#include +#include + +namespace workerd::api::gpu { + +class GPUTexture : public jsg::Object { +public: + // Implicit cast operator to Dawn GPU object + inline operator const wgpu::Texture&() const { + return texture_; + } + explicit GPUTexture(wgpu::Texture t) : texture_(kj::mv(t)){}; + JSG_RESOURCE_TYPE(GPUTexture) { + JSG_METHOD(createView); + JSG_METHOD(destroy); + JSG_READONLY_PROTOTYPE_PROPERTY(width, getWidth); + JSG_READONLY_PROTOTYPE_PROPERTY(height, getHeight); + JSG_READONLY_PROTOTYPE_PROPERTY(depthOrArrayLayers, getDepthOrArrayLayers); + JSG_READONLY_PROTOTYPE_PROPERTY(mipLevelCount, getMipLevelCount); + JSG_READONLY_PROTOTYPE_PROPERTY(dimension, getDimension); + JSG_READONLY_PROTOTYPE_PROPERTY(format, getFormat); + JSG_READONLY_PROTOTYPE_PROPERTY(usage, getUsage); + } + +private: + wgpu::Texture texture_; + + jsg::Ref createView(jsg::Optional descriptor); + + GPUIntegerCoordinateOut getWidth() { + return texture_.GetWidth(); + } + + GPUIntegerCoordinateOut getHeight() { + return texture_.GetHeight(); + } + + GPUIntegerCoordinateOut getDepthOrArrayLayers() { + return texture_.GetDepthOrArrayLayers(); + } + + GPUIntegerCoordinateOut getMipLevelCount() { + return texture_.GetMipLevelCount(); + } + + GPUSize32Out getSampleCount() { + return texture_.GetSampleCount(); + } + + GPUTextureDimension getDimension() { + auto dim = texture_.GetDimension(); + return getTextureDimension(dim); + } + + GPUTextureFormat getFormat() { + auto format = texture_.GetFormat(); + return getTextureFormat(format); + } + + GPUFlagsConstant getUsage() { + return GPUFlagsConstant(texture_.GetUsage()); + } + + void destroy() { + texture_.Destroy(); + } +}; + +struct GPUExtent3DDict { + GPUIntegerCoordinate width; + jsg::Optional height; + jsg::Optional depthOrArrayLayers; + JSG_STRUCT(width, height, depthOrArrayLayers); +}; + +using GPUExtent3D = kj::OneOf, GPUExtent3DDict>; + +struct GPUTextureDescriptor { + kj::String label; + GPUExtent3D size; + jsg::Optional mipLevelCount; + jsg::Optional sampleCount; + jsg::Optional dimension; + GPUTextureFormat format; + GPUTextureUsageFlags usage; + jsg::Optional> viewFormats; + JSG_STRUCT(label, size, mipLevelCount, sampleCount, dimension, format, usage, viewFormats); +}; + +} // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-utils.c++ b/src/workerd/api/gpu/gpu-utils.c++ index 67e7f2c06a3..5c2d001378a 100644 --- a/src/workerd/api/gpu/gpu-utils.c++ +++ b/src/workerd/api/gpu/gpu-utils.c++ @@ -3,6 +3,7 @@ // https://opensource.org/licenses/Apache-2.0 #include "gpu-utils.h" +#include namespace workerd::api::gpu { @@ -41,7 +42,309 @@ wgpu::FeatureName parseFeatureName(GPUFeatureName& str) { return wgpu::FeatureName::Float32Filterable; } - JSG_FAIL_REQUIRE(TypeError, "unknown GPU feature", str); + JSG_FAIL_REQUIRE(TypeError, "unknown GPU feature: ", str); +} + +GPUTextureDimension getTextureDimension(wgpu::TextureDimension& dimension) { + + switch (dimension) { + case wgpu::TextureDimension::e1D: + return kj::str("1d"); + case wgpu::TextureDimension::e2D: + return kj::str("2d"); + case wgpu::TextureDimension::e3D: + return kj::str("3d"); + } + + KJ_UNREACHABLE +} + +GPUTextureFormat getTextureFormat(wgpu::TextureFormat& format) { + switch (format) { + case wgpu::TextureFormat::R8Unorm: + return kj::str("r8unorm"); + + case wgpu::TextureFormat::R8Snorm: + return kj::str("r8snorm"); + + case wgpu::TextureFormat::R8Uint: + return kj::str("r8uint"); + + case wgpu::TextureFormat::R8Sint: + return kj::str("r8sint"); + + case wgpu::TextureFormat::R16Uint: + return kj::str("r16uint"); + + case wgpu::TextureFormat::R16Sint: + return kj::str("r16sint"); + + case wgpu::TextureFormat::R16Float: + return kj::str("r16float"); + + case wgpu::TextureFormat::RG8Unorm: + return kj::str("rg8unorm"); + + case wgpu::TextureFormat::RG8Snorm: + return kj::str("rg8snorm"); + + case wgpu::TextureFormat::RG8Uint: + return kj::str("rg8uint"); + + case wgpu::TextureFormat::RG8Sint: + return kj::str("rg8sint"); + + case wgpu::TextureFormat::R32Uint: + return kj::str("r32uint"); + + case wgpu::TextureFormat::R32Sint: + return kj::str("r32sint"); + + case wgpu::TextureFormat::R32Float: + return kj::str("r32float"); + + case wgpu::TextureFormat::RG16Uint: + return kj::str("rg16uint"); + + case wgpu::TextureFormat::RG16Sint: + return kj::str("rg16sint"); + + case wgpu::TextureFormat::RG16Float: + return kj::str("rg16float"); + + case wgpu::TextureFormat::RGBA8Unorm: + return kj::str("rgba8unorm"); + + case wgpu::TextureFormat::RGBA8UnormSrgb: + return kj::str("rgba8unorm-srgb"); + + case wgpu::TextureFormat::RGBA8Snorm: + return kj::str("rgba8snorm"); + + case wgpu::TextureFormat::RGBA8Uint: + return kj::str("rgba8uint"); + + case wgpu::TextureFormat::RGBA8Sint: + return kj::str("rgba8sint"); + + case wgpu::TextureFormat::BGRA8Unorm: + return kj::str("bgra8unorm"); + + case wgpu::TextureFormat::BGRA8UnormSrgb: + return kj::str("bgra8unorm-srgb"); + + case wgpu::TextureFormat::RGB9E5Ufloat: + return kj::str("rgb9e5ufloat"); + + case wgpu::TextureFormat::RGB10A2Unorm: + return kj::str("rgb10a2unorm"); + + case wgpu::TextureFormat::RG11B10Ufloat: + return kj::str("rg11b10ufloat"); + + case wgpu::TextureFormat::RG32Uint: + return kj::str("rg32uint"); + + case wgpu::TextureFormat::RG32Sint: + return kj::str("rg32sint"); + + case wgpu::TextureFormat::RG32Float: + return kj::str("rg32float"); + + case wgpu::TextureFormat::RGBA16Uint: + return kj::str("rgba16uint"); + + case wgpu::TextureFormat::RGBA16Sint: + return kj::str("rgba16sint"); + + case wgpu::TextureFormat::RGBA16Float: + return kj::str("rgba16float"); + + case wgpu::TextureFormat::RGBA32Uint: + return kj::str("rgba32uint"); + + case wgpu::TextureFormat::RGBA32Sint: + return kj::str("rgba32sint"); + + case wgpu::TextureFormat::RGBA32Float: + return kj::str("rgba32float"); + + case wgpu::TextureFormat::Stencil8: + return kj::str("stencil8"); + + case wgpu::TextureFormat::Depth16Unorm: + return kj::str("depth16unorm"); + + case wgpu::TextureFormat::Depth24Plus: + return kj::str("depth24plus"); + + case wgpu::TextureFormat::Depth24PlusStencil8: + return kj::str("depth24plus-stencil8"); + + case wgpu::TextureFormat::Depth32Float: + return kj::str("depth32float"); + + case wgpu::TextureFormat::Depth32FloatStencil8: + return kj::str("depth32float-stencil8"); + + case wgpu::TextureFormat::BC1RGBAUnorm: + return kj::str("bc1-rgba-unorm"); + + case wgpu::TextureFormat::BC1RGBAUnormSrgb: + return kj::str("bc1-rgba-unorm-srgb"); + + case wgpu::TextureFormat::BC2RGBAUnorm: + return kj::str("bc2-rgba-unorm"); + + case wgpu::TextureFormat::BC2RGBAUnormSrgb: + return kj::str("bc2-rgba-unorm-srgb"); + + case wgpu::TextureFormat::BC3RGBAUnorm: + return kj::str("bc3-rgba-unorm"); + + case wgpu::TextureFormat::BC3RGBAUnormSrgb: + return kj::str("bc3-rgba-unorm-srgb"); + + case wgpu::TextureFormat::BC4RUnorm: + return kj::str("bc4-r-unorm"); + + case wgpu::TextureFormat::BC4RSnorm: + return kj::str("bc4-r-snorm"); + + case wgpu::TextureFormat::BC5RGUnorm: + return kj::str("bc5-rg-unorm"); + + case wgpu::TextureFormat::BC5RGSnorm: + return kj::str("bc5-rg-snorm"); + + case wgpu::TextureFormat::BC6HRGBUfloat: + return kj::str("bc6h-rgb-ufloat"); + + case wgpu::TextureFormat::BC6HRGBFloat: + return kj::str("bc6h-rgb-float"); + + case wgpu::TextureFormat::BC7RGBAUnorm: + return kj::str("bc7-rgba-unorm"); + + case wgpu::TextureFormat::BC7RGBAUnormSrgb: + return kj::str("bc7-rgba-unorm-srgb"); + + case wgpu::TextureFormat::ETC2RGB8Unorm: + return kj::str("etc2-rgb8unorm"); + + case wgpu::TextureFormat::ETC2RGB8UnormSrgb: + return kj::str("etc2-rgb8unorm-srgb"); + + case wgpu::TextureFormat::ETC2RGB8A1Unorm: + return kj::str("etc2-rgb8a1unorm"); + + case wgpu::TextureFormat::ETC2RGB8A1UnormSrgb: + return kj::str("etc2-rgb8a1unorm-srgb"); + + case wgpu::TextureFormat::ETC2RGBA8Unorm: + return kj::str("etc2-rgba8unorm"); + + case wgpu::TextureFormat::ETC2RGBA8UnormSrgb: + return kj::str("etc2-rgba8unorm-srgb"); + + case wgpu::TextureFormat::EACR11Unorm: + return kj::str("eac-r11unorm"); + + case wgpu::TextureFormat::EACR11Snorm: + return kj::str("eac-r11snorm"); + + case wgpu::TextureFormat::EACRG11Unorm: + return kj::str("eac-rg11unorm"); + + case wgpu::TextureFormat::EACRG11Snorm: + return kj::str("eac-rg11snorm"); + + case wgpu::TextureFormat::ASTC4x4Unorm: + return kj::str("astc-4x4-unorm"); + + case wgpu::TextureFormat::ASTC4x4UnormSrgb: + return kj::str("astc-4x4-unorm-srgb"); + + case wgpu::TextureFormat::ASTC5x4Unorm: + return kj::str("astc-5x4-unorm"); + + case wgpu::TextureFormat::ASTC5x4UnormSrgb: + return kj::str("astc-5x4-unorm-srgb"); + + case wgpu::TextureFormat::ASTC5x5Unorm: + return kj::str("astc-5x5-unorm"); + + case wgpu::TextureFormat::ASTC5x5UnormSrgb: + return kj::str("astc-5x5-unorm-srgb"); + + case wgpu::TextureFormat::ASTC6x5Unorm: + return kj::str("astc-6x5-unorm"); + + case wgpu::TextureFormat::ASTC6x5UnormSrgb: + return kj::str("astc-6x5-unorm-srgb"); + + case wgpu::TextureFormat::ASTC6x6Unorm: + return kj::str("astc-6x6-unorm"); + + case wgpu::TextureFormat::ASTC6x6UnormSrgb: + return kj::str("astc-6x6-unorm-srgb"); + + case wgpu::TextureFormat::ASTC8x5Unorm: + return kj::str("astc-8x5-unorm"); + + case wgpu::TextureFormat::ASTC8x5UnormSrgb: + return kj::str("astc-8x5-unorm-srgb"); + + case wgpu::TextureFormat::ASTC8x6Unorm: + return kj::str("astc-8x6-unorm"); + + case wgpu::TextureFormat::ASTC8x6UnormSrgb: + return kj::str("astc-8x6-unorm-srgb"); + + case wgpu::TextureFormat::ASTC8x8Unorm: + return kj::str("astc-8x8-unorm"); + + case wgpu::TextureFormat::ASTC8x8UnormSrgb: + return kj::str("astc-8x8-unorm-srgb"); + + case wgpu::TextureFormat::ASTC10x5Unorm: + return kj::str("astc-10x5-unorm"); + + case wgpu::TextureFormat::ASTC10x5UnormSrgb: + return kj::str("astc-10x5-unorm-srgb"); + + case wgpu::TextureFormat::ASTC10x6Unorm: + return kj::str("astc-10x6-unorm"); + + case wgpu::TextureFormat::ASTC10x6UnormSrgb: + return kj::str("astc-10x6-unorm-srgb"); + + case wgpu::TextureFormat::ASTC10x8Unorm: + return kj::str("astc-10x8-unorm"); + + case wgpu::TextureFormat::ASTC10x8UnormSrgb: + return kj::str("astc-10x8-unorm-srgb"); + + case wgpu::TextureFormat::ASTC10x10Unorm: + return kj::str("astc-10x10-unorm"); + + case wgpu::TextureFormat::ASTC10x10UnormSrgb: + return kj::str("astc-10x10-unorm-srgb"); + + case wgpu::TextureFormat::ASTC12x10Unorm: + return kj::str("astc-12x10-unorm"); + + case wgpu::TextureFormat::ASTC12x10UnormSrgb: + return kj::str("astc-12x10-unorm-srgb"); + + case wgpu::TextureFormat::ASTC12x12Unorm: + return kj::str("astc-12x12-unorm"); + + case wgpu::TextureFormat::ASTC12x12UnormSrgb: + return kj::str("astc-12x12-unorm-srgb"); + default: + KJ_UNREACHABLE + } } kj::Maybe getFeatureName(wgpu::FeatureName& feature) { @@ -75,4 +378,555 @@ kj::Maybe getFeatureName(wgpu::FeatureName& feature) { return kj::none; } +wgpu::TextureDimension parseTextureDimension(kj::StringPtr dimension) { + if (dimension == "1d") { + return wgpu::TextureDimension::e1D; + } + + if (dimension == "2d") { + return wgpu::TextureDimension::e2D; + } + + if (dimension == "3d") { + return wgpu::TextureDimension::e3D; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown texture dimension: ", dimension); +} + +wgpu::TextureFormat parseTextureFormat(kj::StringPtr format) { + + if (format == "r8unorm") { + return wgpu::TextureFormat::R8Unorm; + } + + if (format == "r8snorm") { + return wgpu::TextureFormat::R8Snorm; + } + + if (format == "r8uint") { + return wgpu::TextureFormat::R8Uint; + } + + if (format == "r8sint") { + return wgpu::TextureFormat::R8Sint; + } + + if (format == "r16uint") { + return wgpu::TextureFormat::R16Uint; + } + + if (format == "r16sint") { + return wgpu::TextureFormat::R16Sint; + } + + if (format == "r16float") { + return wgpu::TextureFormat::R16Float; + } + + if (format == "rg8unorm") { + return wgpu::TextureFormat::RG8Unorm; + } + + if (format == "rg8snorm") { + return wgpu::TextureFormat::RG8Snorm; + } + + if (format == "rg8uint") { + return wgpu::TextureFormat::RG8Uint; + } + + if (format == "rg8sint") { + return wgpu::TextureFormat::RG8Sint; + } + + if (format == "r32uint") { + return wgpu::TextureFormat::R32Uint; + } + + if (format == "r32sint") { + return wgpu::TextureFormat::R32Sint; + } + + if (format == "r32float") { + return wgpu::TextureFormat::R32Float; + } + + if (format == "rg16uint") { + return wgpu::TextureFormat::RG16Uint; + } + + if (format == "rg16sint") { + return wgpu::TextureFormat::RG16Sint; + } + + if (format == "rg16float") { + return wgpu::TextureFormat::RG16Float; + } + + if (format == "rgba8unorm") { + return wgpu::TextureFormat::RGBA8Unorm; + } + + if (format == "rgba8unorm-srgb") { + return wgpu::TextureFormat::RGBA8UnormSrgb; + } + + if (format == "rgba8snorm") { + return wgpu::TextureFormat::RGBA8Snorm; + } + + if (format == "rgba8uint") { + return wgpu::TextureFormat::RGBA8Uint; + } + + if (format == "rgba8sint") { + return wgpu::TextureFormat::RGBA8Sint; + } + + if (format == "bgra8unorm") { + return wgpu::TextureFormat::BGRA8Unorm; + } + + if (format == "bgra8unorm-srgb") { + return wgpu::TextureFormat::BGRA8UnormSrgb; + } + + if (format == "rgb9e5ufloat") { + return wgpu::TextureFormat::RGB9E5Ufloat; + } + + if (format == "rgb10a2unorm") { + return wgpu::TextureFormat::RGB10A2Unorm; + } + + if (format == "rg11b10ufloat") { + return wgpu::TextureFormat::RG11B10Ufloat; + } + + if (format == "rg32uint") { + return wgpu::TextureFormat::RG32Uint; + } + + if (format == "rg32sint") { + return wgpu::TextureFormat::RG32Sint; + } + + if (format == "rg32float") { + return wgpu::TextureFormat::RG32Float; + } + + if (format == "rgba16uint") { + return wgpu::TextureFormat::RGBA16Uint; + } + + if (format == "rgba16sint") { + return wgpu::TextureFormat::RGBA16Sint; + } + + if (format == "rgba16float") { + return wgpu::TextureFormat::RGBA16Float; + } + + if (format == "rgba32uint") { + return wgpu::TextureFormat::RGBA32Uint; + } + + if (format == "rgba32sint") { + return wgpu::TextureFormat::RGBA32Sint; + } + + if (format == "rgba32float") { + return wgpu::TextureFormat::RGBA32Float; + } + + if (format == "stencil8") { + return wgpu::TextureFormat::Stencil8; + } + + if (format == "depth16unorm") { + return wgpu::TextureFormat::Depth16Unorm; + } + + if (format == "depth24plus") { + return wgpu::TextureFormat::Depth24Plus; + } + + if (format == "depth24plus-stencil8") { + return wgpu::TextureFormat::Depth24PlusStencil8; + } + + if (format == "depth32float") { + return wgpu::TextureFormat::Depth32Float; + } + + if (format == "depth32float-stencil8") { + return wgpu::TextureFormat::Depth32FloatStencil8; + } + + if (format == "bc1-rgba-unorm") { + return wgpu::TextureFormat::BC1RGBAUnorm; + } + + if (format == "bc1-rgba-unorm-srgb") { + return wgpu::TextureFormat::BC1RGBAUnormSrgb; + } + + if (format == "bc2-rgba-unorm") { + return wgpu::TextureFormat::BC2RGBAUnorm; + } + + if (format == "bc2-rgba-unorm-srgb") { + return wgpu::TextureFormat::BC2RGBAUnormSrgb; + } + + if (format == "bc3-rgba-unorm") { + return wgpu::TextureFormat::BC3RGBAUnorm; + } + + if (format == "bc3-rgba-unorm-srgb") { + return wgpu::TextureFormat::BC3RGBAUnormSrgb; + } + + if (format == "bc4-r-unorm") { + return wgpu::TextureFormat::BC4RUnorm; + } + + if (format == "bc4-r-snorm") { + return wgpu::TextureFormat::BC4RSnorm; + } + + if (format == "bc5-rg-unorm") { + return wgpu::TextureFormat::BC5RGUnorm; + } + + if (format == "bc5-rg-snorm") { + return wgpu::TextureFormat::BC5RGSnorm; + } + + if (format == "bc6h-rgb-ufloat") { + return wgpu::TextureFormat::BC6HRGBUfloat; + } + + if (format == "bc6h-rgb-float") { + return wgpu::TextureFormat::BC6HRGBFloat; + } + + if (format == "bc7-rgba-unorm") { + return wgpu::TextureFormat::BC7RGBAUnorm; + } + + if (format == "bc7-rgba-unorm-srgb") { + return wgpu::TextureFormat::BC7RGBAUnormSrgb; + } + + if (format == "etc2-rgb8unorm") { + return wgpu::TextureFormat::ETC2RGB8Unorm; + } + + if (format == "etc2-rgb8unorm-srgb") { + return wgpu::TextureFormat::ETC2RGB8UnormSrgb; + } + + if (format == "etc2-rgb8a1unorm") { + return wgpu::TextureFormat::ETC2RGB8A1Unorm; + } + + if (format == "etc2-rgb8a1unorm-srgb") { + return wgpu::TextureFormat::ETC2RGB8A1UnormSrgb; + } + + if (format == "etc2-rgba8unorm") { + return wgpu::TextureFormat::ETC2RGBA8Unorm; + } + + if (format == "etc2-rgba8unorm-srgb") { + return wgpu::TextureFormat::ETC2RGBA8UnormSrgb; + } + + if (format == "eac-r11unorm") { + return wgpu::TextureFormat::EACR11Unorm; + } + + if (format == "eac-r11snorm") { + return wgpu::TextureFormat::EACR11Snorm; + } + + if (format == "eac-rg11unorm") { + return wgpu::TextureFormat::EACRG11Unorm; + } + + if (format == "eac-rg11snorm") { + return wgpu::TextureFormat::EACRG11Snorm; + } + + if (format == "astc-4x4-unorm") { + return wgpu::TextureFormat::ASTC4x4Unorm; + } + + if (format == "astc-4x4-unorm-srgb") { + return wgpu::TextureFormat::ASTC4x4UnormSrgb; + } + + if (format == "astc-5x4-unorm") { + return wgpu::TextureFormat::ASTC5x4Unorm; + } + + if (format == "astc-5x4-unorm-srgb") { + return wgpu::TextureFormat::ASTC5x4UnormSrgb; + } + + if (format == "astc-5x5-unorm") { + return wgpu::TextureFormat::ASTC5x5Unorm; + } + + if (format == "astc-5x5-unorm-srgb") { + return wgpu::TextureFormat::ASTC5x5UnormSrgb; + } + + if (format == "astc-6x5-unorm") { + return wgpu::TextureFormat::ASTC6x5Unorm; + } + + if (format == "astc-6x5-unorm-srgb") { + return wgpu::TextureFormat::ASTC6x5UnormSrgb; + } + + if (format == "astc-6x6-unorm") { + return wgpu::TextureFormat::ASTC6x6Unorm; + } + + if (format == "astc-6x6-unorm-srgb") { + return wgpu::TextureFormat::ASTC6x6UnormSrgb; + } + + if (format == "astc-8x5-unorm") { + return wgpu::TextureFormat::ASTC8x5Unorm; + } + + if (format == "astc-8x5-unorm-srgb") { + return wgpu::TextureFormat::ASTC8x5UnormSrgb; + } + + if (format == "astc-8x6-unorm") { + return wgpu::TextureFormat::ASTC8x6Unorm; + } + + if (format == "astc-8x6-unorm-srgb") { + return wgpu::TextureFormat::ASTC8x6UnormSrgb; + } + + if (format == "astc-8x8-unorm") { + return wgpu::TextureFormat::ASTC8x8Unorm; + } + + if (format == "astc-8x8-unorm-srgb") { + return wgpu::TextureFormat::ASTC8x8UnormSrgb; + } + + if (format == "astc-10x5-unorm") { + return wgpu::TextureFormat::ASTC10x5Unorm; + } + + if (format == "astc-10x5-unorm-srgb") { + return wgpu::TextureFormat::ASTC10x5UnormSrgb; + } + + if (format == "astc-10x6-unorm") { + return wgpu::TextureFormat::ASTC10x6Unorm; + } + + if (format == "astc-10x6-unorm-srgb") { + return wgpu::TextureFormat::ASTC10x6UnormSrgb; + } + + if (format == "astc-10x8-unorm") { + return wgpu::TextureFormat::ASTC10x8Unorm; + } + + if (format == "astc-10x8-unorm-srgb") { + return wgpu::TextureFormat::ASTC10x8UnormSrgb; + } + + if (format == "astc-10x10-unorm") { + return wgpu::TextureFormat::ASTC10x10Unorm; + } + + if (format == "astc-10x10-unorm-srgb") { + return wgpu::TextureFormat::ASTC10x10UnormSrgb; + } + + if (format == "astc-12x10-unorm") { + return wgpu::TextureFormat::ASTC12x10Unorm; + } + + if (format == "astc-12x10-unorm-srgb") { + return wgpu::TextureFormat::ASTC12x10UnormSrgb; + } + + if (format == "astc-12x12-unorm") { + return wgpu::TextureFormat::ASTC12x12Unorm; + } + + if (format == "astc-12x12-unorm-srgb") { + return wgpu::TextureFormat::ASTC12x12UnormSrgb; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown texture format: ", format); +} + +wgpu::TextureAspect parseTextureAspect(kj::StringPtr aspect) { + if (aspect == "all") { + return wgpu::TextureAspect::All; + } + + if (aspect == "stencil-only") { + return wgpu::TextureAspect::StencilOnly; + } + + if (aspect == "depth-only") { + return wgpu::TextureAspect::DepthOnly; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown aspect: ", aspect); +} + +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::StorageTextureAccess parseStorageAccess(kj::StringPtr access) { + if (access == "write-only") { + return wgpu::StorageTextureAccess::WriteOnly; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown storage access: ", access); +} + +wgpu::PrimitiveTopology parsePrimitiveTopology(kj::StringPtr topology) { + if (topology == "point-list") { + return wgpu::PrimitiveTopology::PointList; + } + + if (topology == "line-list") { + return wgpu::PrimitiveTopology::LineList; + } + + if (topology == "line-strip") { + return wgpu::PrimitiveTopology::LineStrip; + } + + if (topology == "triangle-list") { + return wgpu::PrimitiveTopology::TriangleList; + } + + if (topology == "triangle-strip") { + return wgpu::PrimitiveTopology::TriangleList; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown primitive topology: ", topology); +} + +wgpu::IndexFormat parseIndexFormat(kj::StringPtr format) { + if (format == "uint16") { + return wgpu::IndexFormat::Uint16; + } + + if (format == "uint32") { + return wgpu::IndexFormat::Uint32; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown index format: ", format); +} + +wgpu::FrontFace parseFrontFace(kj::StringPtr frontFace) { + if (frontFace == "ccw") { + return wgpu::FrontFace::CCW; + } + + if (frontFace == "cw") { + return wgpu::FrontFace::CW; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown front face: ", frontFace); +} + +wgpu::CullMode parseCullMode(kj::StringPtr mode) { + if (mode == "none") { + return wgpu::CullMode::None; + } + + if (mode == "front") { + return wgpu::CullMode::Front; + } + + if (mode == "back") { + return wgpu::CullMode::Back; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown cull mode: ", mode); +} + +wgpu::StencilOperation parseStencilOperation(kj::StringPtr operation) { + + if (operation == "keep") { + return wgpu::StencilOperation::Keep; + } + + if (operation == "zero") { + return wgpu::StencilOperation::Zero; + } + + if (operation == "replace") { + return wgpu::StencilOperation::Replace; + } + + if (operation == "invert") { + return wgpu::StencilOperation::Invert; + } + + if (operation == "increment-clamp") { + return wgpu::StencilOperation::IncrementClamp; + } + + if (operation == "decrement-clamp") { + return wgpu::StencilOperation::DecrementClamp; + } + + if (operation == "increment-wrap") { + return wgpu::StencilOperation::IncrementWrap; + } + + if (operation == "decrement-wrap") { + return wgpu::StencilOperation::DecrementWrap; + } + + JSG_FAIL_REQUIRE(TypeError, "unknown stencil operation: ", operation); +} + } // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu-utils.h b/src/workerd/api/gpu/gpu-utils.h index cf57413f69f..42b1c156edc 100644 --- a/src/workerd/api/gpu/gpu-utils.h +++ b/src/workerd/api/gpu/gpu-utils.h @@ -17,21 +17,40 @@ using GPUTextureSampleType = kj::String; using GPUTextureViewDimension = kj::String; using GPUStorageTextureAccess = kj::String; using GPUTextureFormat = kj::String; +using GPUTextureDimension = kj::String; using GPUBufferUsageFlags = uint32_t; +using GPUTextureUsageFlags = uint32_t; using GPUFlagsConstant = uint32_t; using GPUShaderStageFlags = uint32_t; using GPUIndex32 = uint32_t; +using GPUStencilValue = uint32_t; +using GPUSampleMask = uint32_t; +using GPUDepthBias = int32_t; +using GPUIntegerCoordinate = uint32_t; +using GPUIntegerCoordinateOut = uint32_t; using GPUAddressMode = kj::String; using GPUFilterMode = kj::String; using GPUMipmapFilterMode = kj::String; using GPUCompareFunction = kj::String; using GPUSize32 = uint32_t; +using GPUSize32Out = uint32_t; using GPUBufferDynamicOffset = uint32_t; using GPUPowerPreference = kj::String; using GPUErrorFilter = kj::String; using GPUDeviceLostReason = kj::String; using GPUCompilationMessageType = kj::String; using GPUBufferMapState = kj::String; +using GPUTextureAspect = kj::String; +using GPUPipelineConstantValue = double; +using GPUVertexStepMode = kj::String; +using GPUVertexFormat = kj::String; +using GPUPrimitiveTopology = kj::String; +using GPUFrontFace = kj::String; +using GPUCullMode = kj::String; +using GPUIndexFormat = kj::String; +using GPUStencilOperation = kj::String; +using GPUBlendOperation = kj::String; +using GPUBlendFactor = kj::String; struct GPUMapMode : public jsg::Object { static constexpr GPUFlagsConstant READ = 0x0001; @@ -81,7 +100,51 @@ struct GPUBufferUsage : public jsg::Object { }; }; +struct GPUColorWrite : public jsg::Object { + static constexpr GPUFlagsConstant RED = 0x1; + static constexpr GPUFlagsConstant GREEN = 0x2; + static constexpr GPUFlagsConstant BLUE = 0x4; + static constexpr GPUFlagsConstant ALPHA = 0x8; + static constexpr GPUFlagsConstant ALL = 0xF; + + JSG_RESOURCE_TYPE(GPUColorWrite) { + JSG_STATIC_CONSTANT(RED); + JSG_STATIC_CONSTANT(GREEN); + JSG_STATIC_CONSTANT(BLUE); + JSG_STATIC_CONSTANT(ALPHA); + JSG_STATIC_CONSTANT(ALL); + }; +}; + +struct GPUTextureUsage : public jsg::Object { + static constexpr GPUFlagsConstant COPY_SRC = 0x01; + static constexpr GPUFlagsConstant COPY_DST = 0x02; + static constexpr GPUFlagsConstant TEXTURE_BINDING = 0x04; + static constexpr GPUFlagsConstant STORAGE_BINDING = 0x08; + static constexpr GPUFlagsConstant RENDER_ATTACHMENT = 0x10; + + JSG_RESOURCE_TYPE(GPUTextureUsage) { + JSG_STATIC_CONSTANT(COPY_SRC); + JSG_STATIC_CONSTANT(COPY_DST); + JSG_STATIC_CONSTANT(TEXTURE_BINDING); + JSG_STATIC_CONSTANT(STORAGE_BINDING); + JSG_STATIC_CONSTANT(RENDER_ATTACHMENT); + }; +}; + wgpu::FeatureName parseFeatureName(GPUFeatureName&); kj::Maybe getFeatureName(wgpu::FeatureName& feature); +wgpu::TextureDimension parseTextureDimension(kj::StringPtr dimension); +wgpu::TextureFormat parseTextureFormat(kj::StringPtr format); +GPUTextureDimension getTextureDimension(wgpu::TextureDimension& dimension); +GPUTextureFormat getTextureFormat(wgpu::TextureFormat& format); +wgpu::TextureViewDimension parseTextureViewDimension(kj::StringPtr dim); +wgpu::StorageTextureAccess parseStorageAccess(kj::StringPtr access); +wgpu::TextureAspect parseTextureAspect(kj::StringPtr aspect); +wgpu::PrimitiveTopology parsePrimitiveTopology(kj::StringPtr topology); +wgpu::IndexFormat parseIndexFormat(kj::StringPtr format); +wgpu::FrontFace parseFrontFace(kj::StringPtr frontFace); +wgpu::CullMode parseCullMode(kj::StringPtr mode); +wgpu::StencilOperation parseStencilOperation(kj::StringPtr operation); } // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/gpu.h b/src/workerd/api/gpu/gpu.h index d8df3260ed9..ce6e9a2210a 100644 --- a/src/workerd/api/gpu/gpu.h +++ b/src/workerd/api/gpu/gpu.h @@ -17,10 +17,13 @@ #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" #include "gpu-supported-limits.h" +#include "gpu-texture-view.h" +#include "gpu-texture.h" #include "gpu-utils.h" #include #include @@ -70,6 +73,13 @@ class GPU : public jsg::Object { api::gpu::GPUSupportedFeatures, api::gpu::GPUSupportedLimits, api::gpu::GPUError, \ api::gpu::GPUOutOfMemoryError, api::gpu::GPUInternalError, api::gpu::GPUValidationError, \ api::gpu::GPUDeviceLostInfo, api::gpu::GPUCompilationMessage, api::gpu::GPUCompilationInfo, \ - api::gpu::GPUUncapturedErrorEvent, api::gpu::GPUUncapturedErrorEventInit + api::gpu::GPUUncapturedErrorEvent, api::gpu::GPUUncapturedErrorEventInit, \ + api::gpu::GPUTextureUsage, api::gpu::GPUTextureDescriptor, api::gpu::GPUExtent3DDict, \ + api::gpu::GPUTexture, api::gpu::GPUTextureView, api::gpu::GPUTextureViewDescriptor, \ + api::gpu::GPUColorWrite, api::gpu::GPURenderPipeline, api::gpu::GPURenderPipelineDescriptor, \ + api::gpu::GPUVertexState, api::gpu::GPUVertexBufferLayout, api::gpu::GPUVertexAttribute, \ + api::gpu::GPUPrimitiveState, api::gpu::GPUStencilFaceState, api::gpu::GPUDepthStencilState, \ + api::gpu::GPUMultisampleState, api::gpu::GPUFragmentState, api::gpu::GPUColorTargetState, \ + api::gpu::GPUBlendState, api::gpu::GPUBlendComponent }; // namespace workerd::api::gpu diff --git a/src/workerd/api/gpu/webgpu-windowless-test.gpu-wd-test b/src/workerd/api/gpu/webgpu-windowless-test.gpu-wd-test new file mode 100644 index 00000000000..0521fb8e33b --- /dev/null +++ b/src/workerd/api/gpu/webgpu-windowless-test.gpu-wd-test @@ -0,0 +1,22 @@ +using Workerd = import "/workerd/workerd.capnp"; + +const unitTests :Workerd.Config = ( + services = [ + ( name = "webgpu-test", + worker = ( + modules = [ + (name = "worker", esModule = embed "webgpu-windowless-test.js") + ], + durableObjectNamespaces = [ + (className = "DurableObjectExample", uniqueKey = "210bd0cbd803ef7883a1ee9d86cce06e"), + ], + durableObjectStorage = (inMemory = void), + bindings = [ + (name = "ns", durableObjectNamespace = "DurableObjectExample"), + ], + compatibilityDate = "2023-01-15", + compatibilityFlags = ["experimental", "nodejs_compat", "webgpu"], + ) + ), + ], +); diff --git a/src/workerd/api/gpu/webgpu-windowless-test.js b/src/workerd/api/gpu/webgpu-windowless-test.js new file mode 100644 index 00000000000..9eb767a1441 --- /dev/null +++ b/src/workerd/api/gpu/webgpu-windowless-test.js @@ -0,0 +1,121 @@ +import { ok, deepEqual, equal } from "node:assert"; + +// run manually for now +// bazel run --//src/workerd/io:enable_experimental_webgpu //src/workerd/server:workerd -- test `realpath ./src/workerd/api/gpu/webgpu-windowless-test.gpu-wd-test` --verbose --experimental + +export class DurableObjectExample { + constructor(state) { + this.state = state; + } + + async fetch() { + ok(navigator.gpu); + const adapter = await navigator.gpu.requestAdapter({ + powerPreference: "high-performance", + forceFallbackAdapter: false, + }); + ok(adapter); + + const device = await adapter.requestDevice(); + ok(device); + + const textureSize = 256; + const textureDesc = { + size: { width: textureSize, height: textureSize, depthOrArrayLayers: 1 }, + format: "rgba8unorm-srgb", + usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT, + }; + const texture = device.createTexture(textureDesc); + ok(texture); + + const textureView = texture.createView(); + ok(textureView); + + const outputBufferSize = 4 * textureSize * textureSize; + const outputBuffer = device.createBuffer({ + size: outputBufferSize, + usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ, + }); + ok(outputBuffer); + + const shaderModule = device.createShaderModule({ + code: ` + // Vertex shader + + struct VertexOutput { + @builtin(position) clip_position: vec4, + }; + + @vertex + fn vs_main( + @builtin(vertex_index) in_vertex_index: u32, + ) -> VertexOutput { + var out: VertexOutput; + let x = f32(1 - i32(in_vertex_index)) * 0.5; + let y = f32(i32(in_vertex_index & 1u) * 2 - 1) * 0.5; + out.clip_position = vec4(x, y, 0.0, 1.0); + return out; + } + + // Fragment shader + + @fragment + fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return vec4(0.3, 0.2, 0.1, 1.0); + } + `, + }); + ok(shaderModule); + + const pipelineLayout = device.createPipelineLayout({ + bindGroupLayouts: [], + pushConstantRanges: [], + }); + ok(pipelineLayout); + + const renderPipeline = device.createRenderPipeline({ + layout: pipelineLayout, + vertex: { + module: shaderModule, + entryPoint: "vs_main", + buffers: [], + }, + fragment: { + module: shaderModule, + entryPoint: "fs_main", + targets: [ + { + format: textureDesc.format, + writeMask: GPUColorWrite.ALL, + blend: { + color: {}, + alpha: {}, + }, + }, + ], + }, + primitive: { + frontFace: "ccw", + GPUCullMode: "back", + }, + multisample: { + count: 1, + mask: 0xffffffff, + alphaToCoverageEnabled: false, + }, + }); + ok(renderPipeline); + + return new Response("OK"); + } +} + +export const windowless = { + async test(ctrl, env, ctx) { + let id = env.ns.idFromName("A"); + let obj = env.ns.get(id); + let res = await obj.fetch("http://foo/test"); + let text = await res.text(); + equal(text, "OK"); + }, +};