webgpu-native / webgpu-headers

**NOT STABLE YET!** See README.
https://webgpu-native.github.io/webgpu-headers/
BSD 3-Clause "New" or "Revised" License
393 stars 45 forks source link

Representation of GPUBindGroupLayoutEntry #438

Open kainino0x opened 4 days ago

kainino0x commented 4 days ago
kainino0x commented 4 days ago

Nov 25 meeting:

kainino0x commented 4 days ago

EDIT: I've removed the non-layout entries from this and mirrored them over in https://github.com/webgpu-native/webgpu-headers/issues/403#issuecomment-2505028735

0 - Today:

struct WGPUBindGroupLayoutEntry {
    WGPUChainedStruct const * nextInChain;
    uint32_t binding;
    WGPUShaderStage visibility;
    WGPUBufferBindingLayout buffer;
    WGPUSamplerBindingLayout sampler;
    WGPUTextureBindingLayout texture;
    WGPUStorageTextureBindingLayout storageTexture;
} 
struct WGPUBufferBindingLayout {
    WGPUChainedStruct const * nextInChain;
    WGPUBufferBindingType type; // can be BindingNotUsed
    WGPUBool hasDynamicOffset;
    uint64_t minBindingSize;
};
struct WGPUSamplerBindingLayout {
    WGPUChainedStruct const * nextInChain;
    WGPUSamplerBindingType type;
};
struct WGPUTextureBindingLayout {
    WGPUChainedStruct const * nextInChain;
    WGPUTextureSampleType sampleType;
    WGPUTextureViewDimension viewDimension;
    WGPUBool multisampled;
};
struct WGPUStorageTextureBindingLayout {
    WGPUChainedStruct const * nextInChain;
    WGPUStorageTextureAccess access;
    WGPUTextureFormat format;
    WGPUTextureViewDimension viewDimension;
};
static const WGPUSType WGPUSType_EmscriptenExternalTextureBindingLayout = 0x00040002;
struct WGPUEmscriptenExternalTextureBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_EmscriptenExternalTextureBindingLayout
};

1 - Tagged union (using bitfield (1a) or not (1b)), instead of BindingNotUsed:

enum WGPUBindingType {
    WGPUBindingType_Buffer = ...,
    WGPUBindingType_Sampler = ...,
    WGPUBindingType_Texture = ...,
    WGPUBindingType_StorageTexture = ...,
};
static const WGPUBindingType WGPUBindingType_EmscriptenExternalTexture = ...;

struct WGPUBindGroupLayoutEntry {
    // May not be needed; or alternately could move the nextInChain out of the WGPU*BindingLayout
    // structs and allow different extensions here depending on the WGPUBindingType.
    WGPUChainedStruct const * nextInChain;
    uint32_t binding;
    // If WGPUBindingType is a bitfield, then we validate this has exactly one bit set.
    // If not, JS->C (e.g. Blink) bindings must inject an error when needed.
    WGPUBindingType type;
    union {
        // Structs same as above, except BindingNotUsed doesn't exist anymore.
        // Alternative: all of these could be behind pointers, instead of inline
        WGPUBufferBindingLayout buffer;
        WGPUSamplerBindingLayout sampler;
        WGPUTextureBindingLayout texture;
        WGPUStorageTextureBindingLayout storageTexture;
        // Pointer to a new thing (though in the case of ExternalTexture, there is none, just null)
        WGPU_NULLABLE void * other;
    };
};

2 - Use struct chaining instead of tags:

enum WGPUSType {
    // ...
    WGPUSType_BufferBindingLayout = ...,
    WGPUSType_SamplerBindingLayout = ...,
    WGPUSType_TextureBindingLayout = ...,
    WGPUSType_StorageTextureBindingLayout = ...,
};

struct WGPUBindGroupLayoutEntry {
    WGPUChainedStruct const * nextInChain; // Everything goes in here now.
    uint32_t binding;
};
// Each of these is chained in WGPUBindGroupLayoutEntry. The presence of one in the
// chain indicates the binding type, so there must be exactly one in the chain.
struct WGPUBufferBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_BufferBindingLayout
    WGPUBufferBindingType type;
    WGPUBool hasDynamicOffset;
    uint64_t minBindingSize;
};
struct WGPUSamplerBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_SamplerBindingLayout
    WGPUSamplerBindingType type;
};
struct WGPUTextureBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_TextureBindingLayout
    WGPUTextureSampleType sampleType;
    WGPUTextureViewDimension viewDimension;
    WGPUBool multisampled;
};
struct WGPUStorageTextureBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_StorageTextureBindingLayout
    WGPUStorageTextureAccess access;
    WGPUTextureFormat format;
    WGPUTextureViewDimension viewDimension;
};
static const WGPUSType WGPUSType_EmscriptenExternalTextureBindingLayout = 0x00040002;
struct WGPUEmscriptenExternalTextureBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_EmscriptenExternalTextureBindingLayout
};

3 - Using STypes but with a union of pointers:

// Same WGPUSType

struct WGPUBindGroupLayoutEntry {
    WGPUChainedStruct const * nextInChain; // May not be needed
    uint32_t binding;
    union {
        // Using the WGPU*BindingLayout definitions from 2. Tagged by SType,
        // instead of by an outside tag. It is like a normal chained struct,
        // except the first one in the chain must be one of the layout types.
        // JS->C implementations must inject an error when needed.
        WGPU_NONNULL WGPUBufferBindingLayout * buffer;
        WGPU_NONNULL WGPUSamplerBindingLayout * sampler;
        WGPU_NONNULL WGPUTextureBindingLayout * texture;
        WGPU_NONNULL WGPUStorageTextureBindingLayout * storageTexture;
        // A new thing, such as a WGPUEmscriptenExternalTextureBindingLayout
        WGPU_NONNULL WGPUChainedStruct * other;

        // Alternative: make the core ones all non-pointers; I couldn't figure
        // out a non-messy way of making it extensible, though.
    };
};
struct WGPUEmscriptenExternalTextureBindingLayout {
    WGPUChainedStruct chain; // WGPUSType_EmscriptenExternalTextureBindingLayout
};
Kangz commented 3 days ago

BGL creation is something that happens relatively often. I have quite some concerns about reworking this:

Given that BGL creation is cold, and the ergonomy regressions of other solutions, I'd suggest sticking to the current way to create BGLs.

kainino0x commented 3 days ago

cc @cwfitzgerald

kainino0x commented 3 days ago

There is one problem with WGPUBindGroupEntry (not Layout) which is that it is not currently convertible from C to JS, because what do you do if more than one of the binding types is set? This can only be solved by injecting an error (or crashing), but we already said we would do this, so it's fine.

eliemichel commented 3 days ago

My two cents FWIW: solutions 0,1,2 seem rather ok to me in terms of ergonomics but solution 3 feels weird, would introduce a new usage idiom (1 as well, but in a more straightforward way).

kainino0x commented 2 days ago

I've split out the discussion of the non-layout entries back out to #403 because I decided they're not that similar (since JS uses different patterns) and the two don't necessarily need to match.