WebGPU-2

直接试着翻译和解释官方Spec的连接:
https://gpuweb.github.io/gpuweb/spec/
摘要
WebGPU是一套运行在GPU上的用于渲染和计算的暴露出来的接口。(-_-有点废话,和WebGL一个作用呗)

1. 介绍:
这份说明屌爆了。(厚颜无耻)

2. 数据类型定义(Type Definitions)

typedef long i32;
    typedef unsigned long u32;
    typedef unsigned long long u64;
    
    dictionary GPUColor {
        required float r;
        required float g;
        required float b;
        required float a;
    };
    
    dictionary GPUOrigin3D {
        u32 x = 0;
        u32 y = 0;
        u32 z = 0;
    };
    
    dictionary GPUExtent3D {
        required u32 width;
        required u32 height;
        required u32 depth;
    };

i32就是一般的int,
u32是无符号的int,
u64是无符号的长整型。
在这个基础上出来了
GPUColor,包含R-G-B-A4个float,
GPUOrigin3D,包含X-Y-Z三个u32,
GPUExtent3D,包含长,宽,及深度3个u32
注:为什么GPUOrigin3D和GPUExtent3D都是u32的,没有负数,难道只是做比较的?

3. 缓冲区(Buffers)

typedef u32 GPUBufferUsageFlags;
    
    interface GPUBufferUsage {
        const u32 NONE = 0;
        const u32 MAP_READ = 1;
        const u32 MAP_WRITE = 2;
        const u32 TRANSFER_SRC = 4;
        const u32 TRANSFER_DST = 8;
        const u32 INDEX = 16;
        const u32 VERTEX = 32;
        const u32 UNIFORM = 64;
        const u32 STORAGE = 128;
    };
    
    dictionary GPUBufferDescriptor {
        required u64 size;
        required GPUBufferUsageFlags usage;
    };
    
    interface GPUBuffer {
        Promise<ArrayBuffer> mapReadAsync();
        Promise<ArrayBuffer> mapWriteAsync();
        void unmap();
    
        void destroy();
    };

从下到上,GPUBufrer是一套interface,包含两个Promise,需要实现unmap和destroy两个函数,GPUBufferDescriptor是一个buffer描述,要知会buffer的大小和用途,而用途是一个Enum,里面有9种用途。
TODO:具体使用方法要补上,不然真不知道干嘛使的!尤其是具体buffer的9种用法,还是挺繁杂的

4. 纹理(Textures)

enum GPUTextureDimension {
    "1d",
    "2d",
    "3d"
};

enum GPUTextureFormat {
    /* Normal 8 bit formats */
    "r8unorm",
    "r8unorm-srgb",
    "r8snorm",
    "r8uint",
    "r8sint",
    /* Normal 16 bit formats */
    "r16unorm",
    "r16snorm",
    "r16uint",
    "r16sint",
    "r16float",
    "rg8unorm",
    "rg8unorm-srgb",
    "rg8snorm",
    "rg8uint",
    "rg8sint",
    /* Packed 16 bit formats */
    "b5g6r5unorm",
    /* Normal 32 bit formats */
    "r32uint",
    "r32sint",
    "r32float",
    "rg16unorm",
    "rg16snorm",
    "rg16uint",
    "rg16sint",
    "rg16float",
    "rgba8unorm",
    "rgba8unorm-srgb",
    "rgba8snorm",
    "rgba8uint",
    "rgba8sint",
    "bgra8unorm",
    "bgra8unorm-srgb",
    /* Packed 32 bit formats */
    "rgb10a2unorm",
    "rg11b10float",
    /* Normal 64 bit formats */
    "rg32uint",
    "rg32sint",
    "rg32float",
    "rgba16unorm",
    "rgba16snorm",
    "rgba16uint",
    "rgba16sint",
    "rgba16float",
    /* Normal 128 bit formats */
    "rgba32uint",
    "rgba32sint",
    "rgba32float",
    /* Depth and Stencil formats */
    "depth32float",
    "depth32float-stencil8"
};

typedef u32 GPUTextureUsageFlags;

interface GPUTextureUsage {
    const u32 NONE = 0;
    const u32 TRANSFER_SRC = 1;
    const u32 TRANSFER_DST = 2;
    const u32 SAMPLED = 4;
    const u32 STORAGE = 8;
    const u32 OUTPUT_ATTACHMENT = 16;
};

dictionary GPUTextureDescriptor {
    required GPUExtent3D size;
    u32 arrayLayerCount = 1;
    u32 mipLevelCount = 1;
    u32 sampleCount = 1;
    GPUTextureDimension dimension = "2d";
    required GPUTextureFormat format;
    required GPUTextureUsageFlags usage;
};

// Texture view
enum GPUTextureViewDimension {
    "1d",
    "2d",
    "2d-array",
    "cube",
    "cube-array",
    "3d"
};

enum GPUTextureAspect {
    "all",
    "stencil-only",
    "depth-only"
};

dictionary GPUTextureViewDescriptor {
    required GPUTextureFormat format;
    required GPUTextureViewDimension dimension;
    required GPUTextureAspect aspect;
    u32 baseMipLevel = 0;
    u32 mipLevelCount = 1;
    u32 baseArrayLayer = 0;
    u32 arrayLayerCount = 1;
};

interface GPUTextureView {
};

interface GPUTexture {
    GPUTextureView createView(GPUTextureViewDescriptor desc);
    GPUTextureView createDefaultView();

    void destroy();
};

说明的是:纹理的格式,格式的名字表明了分量的顺序,每个分量占的位数,还有分量的数据类型
Unorm就是normalized + 无符号
Snorm就是normalized + 带符号
这些应该足够说明了
此外发现还有纹理支持了2D-array,cube-Array,这是到webgl2才支持的特性

5. 采样(Samplers)

enum GPUAddressMode {
    "clamp-to-edge",
    "repeat",
    "mirror-repeat"
};

enum GPUFilterMode {
    "nearest",
    "linear"
};

enum GPUCompareFunction {
    "never",
    "less",
    "equal",
    "less-equal",
    "greater",
    "not-equal",
    "greater-equal",
    "always"
};

dictionary GPUSamplerDescriptor {
    GPUAddressMode addressModeU = "clamp-to-edge";
    GPUAddressMode addressModeV = "clamp-to-edge";
    GPUAddressMode addressModeW = "clamp-to-edge";
    GPUFilterMode magFilter = "nearest";
    GPUFilterMode minFilter = "nearest";
    GPUFilterMode mipmapFilter = "nearest";
    float lodMinClamp = 0;
    float lodMaxClamp = 0xffffffff; // TODO: What should this be? Was Number.MAX_VALUE.
    GPUCompareFunction compareFunction = "never";
};

interface GPUSampler {
};

这里基本和Opengl提供的基本纹理采样选项差不多,只能说打了个包而已。

6. 绑定和排布(Binding and Layout)

typedef u32 GPUShaderStageFlags;

interface GPUShaderStageBit {
    const u32 NONE = 0;
    const u32 VERTEX = 1;
    const u32 FRAGMENT = 2;
    const u32 COMPUTE = 4;
};

enum GPUBindingType {
    "uniform-buffer",
    "dynamic-uniform-buffer",
    "sampler",
    "sampled-texture",
    "storage-buffer",
    "dynamic-storage-buffer"
    // TODO other binding types
};

dictionary GPUBindGroupLayoutBinding {
    required u32 binding;
    required GPUShaderStageFlags visibility;
    required GPUBindingType type;
};

dictionary GPUBindGroupLayoutDescriptor {
    required sequence<GPUBindGroupLayoutBinding> bindings;
};

interface GPUBindGroupLayout {
};

dictionary GPUPipelineLayoutDescriptor {
    required sequence<GPUBindGroupLayout> bindGroupLayouts;
};

interface GPUPipelineLayout {
};

dictionary GPUBufferBinding {
    required GPUBuffer buffer;
    u64 offset = 0;
    required u64 size;
};

typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource;

dictionary GPUBindGroupBinding {
    required u32 binding;
    required GPUBindingResource resource;
};

dictionary GPUBindGroupDescriptor {
    required GPUBindGroupLayout layout;
    required sequence<GPUBindGroupBinding> bindings;
};

interface GPUBindGroup {
};

这些主要是和shader相关的,看到在GPUShaderStageBit中支持了COMPUTE,在webgl2里是不支持的。此外还有送入GPU的数据的排布方式,GPUBufferBinding的字典里有offset,buffer,size的分量,可以向在CPU中调整内存一样灵活的把玩送入GPU的buffer。

7. Shader模块

// Note: While the choice of shader language is undecided,
// GPUShaderModuleDescriptor will temporarily accept both
// text and binary input.
typedef (ArrayBuffer or DOMString) ArrayBufferOrDOMString;

dictionary GPUShaderModuleDescriptor {
    required ArrayBufferOrDOMString code;
};

interface GPUShaderModule {
};

支持二进制和字符串两种,PS(二进制的shader模块是啥,还没具体试过,看例子)

8. 管线创建

// RasterizationState
enum GPUFrontFace {
    "ccw",
    "cw"
};

enum GPUCullMode {
    "none",
    "front",
    "back"
};

dictionary GPURasterizationStateDescriptor {
    required GPUFrontFace frontFace;
    GPUCullMode cullMode = "none";

    i32 depthBias = 0;
    float depthBiasSlopeScale = 0;
    float depthBiasClamp = 0;
};

// BlendState
enum GPUBlendFactor {
    "zero",
    "one",
    "src-color",
    "one-minus-src-color",
    "src-alpha",
    "one-minus-src-alpha",
    "dst-color",
    "one-minus-dst-color",
    "dst-alpha",
    "one-minus-dst-alpha",
    "src-alpha-saturated",
    "blend-color",
    "one-minus-blend-color"
};

enum GPUBlendOperation {
    "add",
    "subtract",
    "reverse-subtract",
    "min",
    "max"
};

typedef u32 GPUColorWriteFlags;
interface GPUColorWriteBits {
    const u32 NONE = 0;
    const u32 RED = 1;
    const u32 GREEN = 2;
    const u32 BLUE = 4;
    const u32 ALPHA = 8;
    const u32 ALL = 15;
};

dictionary GPUBlendDescriptor {
    GPUBlendFactor srcFactor = "one";
    GPUBlendFactor dstFactor = "zero";
    GPUBlendOperation operation = "add";
};

dictionary GPUColorStateDescriptor {
    required GPUTextureFormat format;

    required GPUBlendDescriptor alphaBlend;
    required GPUBlendDescriptor colorBlend;
    GPUColorWriteFlags writeMask = GPUColorWriteBits.ALL;
};

enum GPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "invert",
    "increment-clamp",
    "decrement-clamp",
    "increment-wrap",
    "decrement-wrap"
};

dictionary GPUStencilStateFaceDescriptor {
    GPUCompareFunction compare = "always";
    GPUStencilOperation failOp = "keep";
    GPUStencilOperation depthFailOp = "keep";
    GPUStencilOperation passOp = "keep";
};

dictionary GPUDepthStencilStateDescriptor {
    required GPUTextureFormat format;

    boolean depthWriteEnabled = false;
    GPUCompareFunction depthCompare = "always";

    required GPUStencilStateFaceDescriptor stencilFront;
    required GPUStencilStateFaceDescriptor stencilBack;

    u32 stencilReadMask = 0xFFFFFFFF;
    u32 stencilWriteMask = 0xFFFFFFFF;
};

// Vertex Input

enum GPUIndexFormat {
    "uint16",
    "uint32"
};

// Vertex formats
// The name of the format specifies the data type of the component, the number of
// values, and whether the data is normalized.
//     uchar = unsigned 8-bit value
//     char = signed 8-bit value
//     ushort = unsigned 16-bit value
//     short = signed 16-bit value
//     half = half-precision 16-bit floating point value
//     float = 32-bit floating point value
//     uint = unsigned 32-bit integer value
//     int = signed 32-bit integer value
// If no number of values is given in the name, a single value is provided.
// If the format has the "-bgra" suffix, it means the values are arranged as
// blue, green, red and alpha values.

enum GPUVertexFormat {
    "uchar2",
    "uchar4",
    "char2",
    "char4",
    "uchar2norm",
    "uchar4norm",
    "char2norm",
    "char4norm",
    "ushort2",
    "ushort4",
    "short2",
    "short4",
    "ushort2norm",
    "ushort4norm",
    "short2norm",
    "short4norm",
    "half2",
    "half4",
    "float",
    "float2",
    "float3",
    "float4",
    "uint",
    "uint2",
    "uint3",
    "uint4",
    "int",
    "int2",
    "int3",
    "int4"
};

enum GPUInputStepMode {
    "vertex",
    "instance"
};

dictionary GPUVertexAttributeDescriptor {
    u64 offset = 0;
    required GPUVertexFormat format;
    required u32 shaderLocation;
};

dictionary GPUVertexBufferDescriptor {
    required u64 stride;
    GPUInputStepMode stepMode = "vertex";
    required sequence<GPUVertexAttributeDescriptor> attributeSet;
};

dictionary GPUVertexInputDescriptor {
    GPUIndexFormat indexFormat = "uint32";
    required sequence<GPUVertexBufferDescriptor?> vertexBuffers;
};

可控制(GPURasterizationStateDescriptor)光栅化的顺逆指针顺序,CullFace的模式,深度信息的控制,Blend的合并模式(GPUBlendFactor),操作方式(GPUBlendOperation)。还有Stencil的操作,还有送入Vertex Shader中的类似Position,Normal,UV等等的格式,字节数等。其中要强调的是,很多格式是从webgl2才开始有的,比如half2和half4。

9. 管线描述(Pipeline Descriptors)

dictionary GPUPipelineStageDescriptor {
    required GPUShaderModule module;
    required DOMString entryPoint;
    // TODO other stuff like specialization constants?
};

dictionary GPUPipelineDescriptorBase {
    required GPUPipelineLayout layout;
};

得标明用的是那个类型的shader(GPUShaderModule)。入口点,以及排布情况。

10. 计算管线(Compute Pipeline)

dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
    required GPUPipelineStageDescriptor computeStage;
};

interface GPUComputePipeline {
};

把上面的9包了一层做计算。

11. 渲染管线(Render Pipeline)

enum GPUPrimitiveTopology {
    "point-list",
    "line-list",
    "line-strip",
    "triangle-list",
    "triangle-strip"
};

dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
    required GPUPipelineStageDescriptor vertexStage;
    GPUPipelineStageDescriptor? fragmentStage = null;

    required GPUPrimitiveTopology primitiveTopology;
    required GPURasterizationStateDescriptor rasterizationState;
    required sequence<GPUColorStateDescriptor> colorStates;
    GPUDepthStencilStateDescriptor? depthStencilState = null;
    required GPUVertexInputDescriptor vertexInput;

    // Number of MSAA samples
    u32 sampleCount = 1;
    // TODO other properties
};

interface GPURenderPipeline {
};

先标明渲染的是什么,三角形,点,线,还是什么别的,都是WebGL早有的格式。除了所有需要的注入vertex,fragment,光栅化的要求,颜色,深度等等,还能制定抗锯齿的采样次数。在WebGL1中是通过扩展来支持extension抗锯齿的,且FBO不支持多重采样抗锯齿,这个特性是在WebGL2中才有的。

12. 命令记录(Command Recording)

interface GPUProgrammablePassEncoder {
    void endPass();

    // Allowed in both compute and render passes
    void setBindGroup(u32 index, GPUBindGroup bindGroup, optional sequence<u64> dynamicOffsets);
};

interface GPURenderPassEncoder : GPUProgrammablePassEncoder {
    void setPipeline(GPURenderPipeline pipeline);
    void setBlendColor(GPUColor color);
    void setStencilReference(u32 reference);

    // The default viewport is (0.0, 0.0, w, h, 0.0, 1.0), where w and h are the dimensions of back buffer
    void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);

    // The default scissor rectangle is (0, 0, w, h), where w and h are the dimensions of back buffer.
    // Width and height must be greater than 0. Otherwise, an error will be generated.
    void setScissorRect(u32 x, u32 y, u32 width, u32 height);

    void setIndexBuffer(GPUBuffer buffer, u64 offset);
    void setVertexBuffers(u32 startSlot, sequence<GPUBuffer> buffers, sequence<u64> offsets);

    void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
    void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);

    // TODO add missing commands
};

interface GPUComputePassEncoder : GPUProgrammablePassEncoder {
    void setPipeline(GPUComputePipeline pipeline);
    void dispatch(u32 x, u32 y, u32 z);

    // TODO add missing commands
};


enum GPULoadOp {
    "clear",
    "load"
};

enum GPUStoreOp {
    "store"
};

dictionary GPURenderPassColorAttachmentDescriptor {
    required GPUTextureView attachment;
    GPUTextureView? resolveTarget = null;

    required GPULoadOp loadOp;
    required GPUStoreOp storeOp;
    GPUColor clearColor = {r: 0.0, g: 0.0, b: 0.0, a: 1.0};
};

dictionary GPURenderPassDepthStencilAttachmentDescriptor {
    required GPUTextureView attachment;

    required GPULoadOp depthLoadOp;
    required GPUStoreOp depthStoreOp;
    required float clearDepth;

    required GPULoadOp stencilLoadOp;
    required GPUStoreOp stencilStoreOp;
    u32 clearStencil = 0;
};

dictionary GPURenderPassDescriptor {
    required sequence<GPURenderPassColorAttachmentDescriptor> colorAttachments;
    GPURenderPassDepthStencilAttachmentDescriptor? depthStencilAttachment = null;
};

dictionary GPUBufferCopyView {
    required GPUBuffer buffer;
    u64 offset = 0;
    required u32 rowPitch;
    required u32 imageHeight;
};

dictionary GPUTextureCopyView {
    required GPUTexture texture;
    u32 mipLevel = 0;
    u32 arrayLayer = 0;
    GPUOrigin3D origin = {x: 0, y: 0, z: 0};
};

interface GPUCommandBuffer {
};

interface GPUCommandEncoder {
    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
    GPUComputePassEncoder beginComputePass();

    // Commands allowed outside of "passes"
    void copyBufferToBuffer(
        GPUBuffer src,
        u64 srcOffset,
        GPUBuffer dst,
        u64 dstOffset,
        u64 size);

    void copyBufferToTexture(
        GPUBufferCopyView source,
        GPUTextureCopyView destination,
        GPUExtent3D copySize);

    void copyTextureToBuffer(
        GPUTextureCopyView source,
        GPUBufferCopyView destination,
        GPUExtent3D copySize);

    void copyTextureToTexture(
        GPUTextureCopyView source,
        GPUTextureCopyView destination,
        GPUExtent3D copySize);

    GPUCommandBuffer finish();
};

dictionary GPUCommandEncoderDescriptor {
    //TODO: reusability flag?
};

TODO:看不出来是干什么的

13. 栅栏(Fence)

dictionary GPUFenceDescriptor {
    u64 initialValue = 0;
};

interface GPUFence {
    u64 getCompletedValue();
    Promise<void> onCompletion(u64 completionValue);
};

这个操作有点像Webgl中的readPixel,主要是从GPU中抓取信息用的,但是要注意,一般这样的操作会block住整个管线,因为CPU和GPU是异步操作的,这种强行要获取信息的API都会把GPU的渲染流程生生的插入一个拦截的命令,从而降低渲染效率,慎用。

14. 队列(Queues)

interface GPUQueue {
    void submit(sequence<GPUCommandBuffer> buffers);

    GPUFence createFence(GPUFenceDescriptor descriptor);
    void signal(GPUFence fence, u64 signalValue);
};

对13的一个封装

15. 画布渲染和交换链(Canvas Rendering and Swap Chain)

interface GPUCanvasContext {
    // Calling configureSwapChain a second time invalidates the previous one,
    // and all of the textures it’s produced.
    GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor);

    Promise<GPUTextureFormat> getSwapChainPreferredFormat(GPUDevice device);
}

dictionary GPUSwapChainDescriptor {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = GPUTextureUsage.OUTPUT_ATTACHMENT;
};

interface GPUSwapChain {
    GPUTexture getCurrentTexture();
};

类似WebGL中的FBO,切换渲染的目标,做离屏渲染用的。

16. 设备(Device)

dictionary GPUExtensions {
    boolean anisotropicFiltering = false;
};

dictionary GPULimits {
    u32 maxBindGroups = 4;
};

// Device
interface GPUDevice {
    readonly attribute GPUExtensions extensions;
    readonly attribute GPULimits limits;
    readonly attribute GPUAdapter adapter;

    GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
    (GPUBuffer, ArrayBuffer) createBufferMapped(GPUBufferDescriptor descriptor);
    Promise<(GPUBuffer, ArrayBuffer)> createBufferMappedAsync(GPUBufferDescriptor descriptor);
    GPUTexture createTexture(GPUTextureDescriptor descriptor);
    GPUSampler createSampler(GPUSamplerDescriptor descriptor);

    GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
    GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
    GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);

    GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
    GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
    GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);

    GPUCommandEncoder createCommandEncoder(GPUCommandEncoderDescriptor descriptor);

    GPUQueue getQueue();
};

dictionary GPUDeviceDescriptor {
    GPUExtensions extensions;
    GPULimits limits;

    // TODO are other things configurable like queues?
};

interface GPUAdapter {
    readonly attribute DOMString name;
    readonly attribute GPUExtensions extensions;
    //readonly attribute GPULimits limits; Don’t expose higher limits for now.

    // May reject with DOMException  // TODO: DOMException("OperationError")?
    Promise<GPUDevice> requestDevice(GPUDeviceDescriptor descriptor);
};

enum GPUPowerPreference {
    "low-power",
    "high-performance"
};

dictionary GPURequestAdapterOptions {
    GPUPowerPreference? powerPreference;
};

[Exposed=Window]
interface GPU {
    // May reject with DOMException  // TODO: DOMException("OperationError")?
    Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options);
};

[Exposed=Window]
partial interface Navigator {
    [SameObject] readonly attribute GPU gpu;
};

[Exposed=DedicatedWorker]
partial interface WorkerNavigator {
    [SameObject] readonly attribute GPU gpu;
};

包含了创建和获取所有buffer, extensions,textures,shaders等的操作封装。

17. 致命错误(Fatal Errors)

interface GPUDeviceLostInfo {
    readonly attribute DOMString message;
};

partial interface GPUDevice {
    readonly attribute Promise<GPUDeviceLostInfo> lost;
};

返回一些错误信息,帮助debug

18. 错误范畴(Error Scopes)

enum GPUErrorFilter {
    "none",
    "out-of-memory",
    "validation"
};

[
    Constructor()
]
interface GPUOutOfMemoryError {};

[
    Constructor(DOMString message)
]
interface GPUValidationError {
    readonly attribute DOMString message;
};

typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;

partial interface GPUDevice {
    void pushErrorScope(GPUErrorFilter filter);
    Promise<GPUError?> popErrorScope();
};

现阶段支持的错误。

19. 测量记录(Telementry)

[
    Constructor(DOMString type, GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict),
    Exposed=Window
]
interface GPUUncapturedErrorEvent : Event {
    readonly attribute GPUError error;
};

dictionary GPUUncapturedErrorEventInit : EventInit {
    required GPUError error;
};

// TODO: is it possible to expose the EventTarget only on the main thread?
partial interface GPUDevice : EventTarget {
    [Exposed=Window]
    attribute EventHandler onuncapturederror;
};

针对设备和未识别错误的封装?

20. Debugging助手

partial interface GPUProgrammablePassEncoder {
    void pushDebugGroup(DOMString groupLabel);
    void popDebugGroup();
    void insertDebugMarker(DOMString markerLabel);
};

interface mixin GPUDebugLabel {
    attribute DOMString label;
};

GPUBindGroup includes GPUDebugLabel;
GPUBindGroupLayout includes GPUDebugLabel;
GPUBuffer includes GPUDebugLabel;
GPUCommandBuffer includes GPUDebugLabel;
GPUCommandEncoder includes GPUDebugLabel;
GPUComputePipeline includes GPUDebugLabel;
GPUFence includes GPUDebugLabel;
GPUPipelineLayout includes GPUDebugLabel;
GPUProgrammablePassEncoder includes GPUDebugLabel;
GPUQueue includes GPUDebugLabel;
GPURenderPipeline includes GPUDebugLabel;
GPUSampler includes GPUDebugLabel;
GPUShaderModule includes GPUDebugLabel;
GPUTexture includes GPUDebugLabel;
GPUTextureView includes GPUDebugLabel;

partial dictionary GPUCommandEncoderDescriptor {
    DOMString label;
};

partial dictionary GPUFenceDescriptor {
    DOMString label;
};

partial dictionary GPUPipelineDescriptorBase {
    DOMString label;
};

partial dictionary GPUShaderModuleDescriptor {
    DOMString label;
};

关于如何检查错误信息的API

IDL 目录(IDL Index)(IDL = Interface description language)

typedef long i32;
typedef unsigned long u32;
typedef unsigned long long u64;

dictionary GPUColor {
    required float r;
    required float g;
    required float b;
    required float a;
};

dictionary GPUOrigin3D {
    u32 x = 0;
    u32 y = 0;
    u32 z = 0;
};

dictionary GPUExtent3D {
    required u32 width;
    required u32 height;
    required u32 depth;
};

typedef u32 GPUBufferUsageFlags;

interface GPUBufferUsage {
    const u32 NONE = 0;
    const u32 MAP_READ = 1;
    const u32 MAP_WRITE = 2;
    const u32 TRANSFER_SRC = 4;
    const u32 TRANSFER_DST = 8;
    const u32 INDEX = 16;
    const u32 VERTEX = 32;
    const u32 UNIFORM = 64;
    const u32 STORAGE = 128;
};

dictionary GPUBufferDescriptor {
    required u64 size;
    required GPUBufferUsageFlags usage;
};

interface GPUBuffer {
    Promise<ArrayBuffer> mapReadAsync();
    Promise<ArrayBuffer> mapWriteAsync();
    void unmap();

    void destroy();
};

enum GPUTextureDimension {
    "1d",
    "2d",
    "3d"
};

// Texture formats
// The name of the format specifies the order of components, bits per component, and data type for
// the component.
//     r, g, b, a = red, green, blue, alpha
//     unorm = unsigned normalized
//     snorm = signed normalized
//     uint = unsigned int
//     sint = signed int
//     float = floating point
// If the format has the "-srgb" suffix, then sRGB gamma compression and decompression are
// applied during the reading and writing of color values in the pixel.
// Compressed texture formats are provided by extensions. Their naming should follow the
// convention here, with the texture name as a prefix. e.g. "etc2-rgba8unorm".

enum GPUTextureFormat {
    /* Normal 8 bit formats */
    "r8unorm",
    "r8unorm-srgb",
    "r8snorm",
    "r8uint",
    "r8sint",
    /* Normal 16 bit formats */
    "r16unorm",
    "r16snorm",
    "r16uint",
    "r16sint",
    "r16float",
    "rg8unorm",
    "rg8unorm-srgb",
    "rg8snorm",
    "rg8uint",
    "rg8sint",
    /* Packed 16 bit formats */
    "b5g6r5unorm",
    /* Normal 32 bit formats */
    "r32uint",
    "r32sint",
    "r32float",
    "rg16unorm",
    "rg16snorm",
    "rg16uint",
    "rg16sint",
    "rg16float",
    "rgba8unorm",
    "rgba8unorm-srgb",
    "rgba8snorm",
    "rgba8uint",
    "rgba8sint",
    "bgra8unorm",
    "bgra8unorm-srgb",
    /* Packed 32 bit formats */
    "rgb10a2unorm",
    "rg11b10float",
    /* Normal 64 bit formats */
    "rg32uint",
    "rg32sint",
    "rg32float",
    "rgba16unorm",
    "rgba16snorm",
    "rgba16uint",
    "rgba16sint",
    "rgba16float",
    /* Normal 128 bit formats */
    "rgba32uint",
    "rgba32sint",
    "rgba32float",
    /* Depth and Stencil formats */
    "depth32float",
    "depth32float-stencil8"
};

typedef u32 GPUTextureUsageFlags;

interface GPUTextureUsage {
    const u32 NONE = 0;
    const u32 TRANSFER_SRC = 1;
    const u32 TRANSFER_DST = 2;
    const u32 SAMPLED = 4;
    const u32 STORAGE = 8;
    const u32 OUTPUT_ATTACHMENT = 16;
};

dictionary GPUTextureDescriptor {
    required GPUExtent3D size;
    u32 arrayLayerCount = 1;
    u32 mipLevelCount = 1;
    u32 sampleCount = 1;
    GPUTextureDimension dimension = "2d";
    required GPUTextureFormat format;
    required GPUTextureUsageFlags usage;
};

// Texture view
enum GPUTextureViewDimension {
    "1d",
    "2d",
    "2d-array",
    "cube",
    "cube-array",
    "3d"
};

enum GPUTextureAspect {
    "all",
    "stencil-only",
    "depth-only"
};

dictionary GPUTextureViewDescriptor {
    required GPUTextureFormat format;
    required GPUTextureViewDimension dimension;
    required GPUTextureAspect aspect;
    u32 baseMipLevel = 0;
    u32 mipLevelCount = 1;
    u32 baseArrayLayer = 0;
    u32 arrayLayerCount = 1;
};

interface GPUTextureView {
};

interface GPUTexture {
    GPUTextureView createView(GPUTextureViewDescriptor desc);
    GPUTextureView createDefaultView();

    void destroy();
};

enum GPUAddressMode {
    "clamp-to-edge",
    "repeat",
    "mirror-repeat"
};

enum GPUFilterMode {
    "nearest",
    "linear"
};

enum GPUCompareFunction {
    "never",
    "less",
    "equal",
    "less-equal",
    "greater",
    "not-equal",
    "greater-equal",
    "always"
};

dictionary GPUSamplerDescriptor {
    GPUAddressMode addressModeU = "clamp-to-edge";
    GPUAddressMode addressModeV = "clamp-to-edge";
    GPUAddressMode addressModeW = "clamp-to-edge";
    GPUFilterMode magFilter = "nearest";
    GPUFilterMode minFilter = "nearest";
    GPUFilterMode mipmapFilter = "nearest";
    float lodMinClamp = 0;
    float lodMaxClamp = 0xffffffff; // TODO: What should this be? Was Number.MAX_VALUE.
    GPUCompareFunction compareFunction = "never";
};

interface GPUSampler {
};

typedef u32 GPUShaderStageFlags;

interface GPUShaderStageBit {
    const u32 NONE = 0;
    const u32 VERTEX = 1;
    const u32 FRAGMENT = 2;
    const u32 COMPUTE = 4;
};

enum GPUBindingType {
    "uniform-buffer",
    "dynamic-uniform-buffer",
    "sampler",
    "sampled-texture",
    "storage-buffer",
    "dynamic-storage-buffer"
    // TODO other binding types
};

dictionary GPUBindGroupLayoutBinding {
    required u32 binding;
    required GPUShaderStageFlags visibility;
    required GPUBindingType type;
};

dictionary GPUBindGroupLayoutDescriptor {
    required sequence<GPUBindGroupLayoutBinding> bindings;
};

interface GPUBindGroupLayout {
};

dictionary GPUPipelineLayoutDescriptor {
    required sequence<GPUBindGroupLayout> bindGroupLayouts;
};

interface GPUPipelineLayout {
};

dictionary GPUBufferBinding {
    required GPUBuffer buffer;
    u64 offset = 0;
    required u64 size;
};

typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource;

dictionary GPUBindGroupBinding {
    required u32 binding;
    required GPUBindingResource resource;
};

dictionary GPUBindGroupDescriptor {
    required GPUBindGroupLayout layout;
    required sequence<GPUBindGroupBinding> bindings;
};

interface GPUBindGroup {
};

// Note: While the choice of shader language is undecided,
// GPUShaderModuleDescriptor will temporarily accept both
// text and binary input.
typedef (ArrayBuffer or DOMString) ArrayBufferOrDOMString;

dictionary GPUShaderModuleDescriptor {
    required ArrayBufferOrDOMString code;
};

interface GPUShaderModule {
};

// RasterizationState
enum GPUFrontFace {
    "ccw",
    "cw"
};

enum GPUCullMode {
    "none",
    "front",
    "back"
};

dictionary GPURasterizationStateDescriptor {
    required GPUFrontFace frontFace;
    GPUCullMode cullMode = "none";

    i32 depthBias = 0;
    float depthBiasSlopeScale = 0;
    float depthBiasClamp = 0;
};

// BlendState
enum GPUBlendFactor {
    "zero",
    "one",
    "src-color",
    "one-minus-src-color",
    "src-alpha",
    "one-minus-src-alpha",
    "dst-color",
    "one-minus-dst-color",
    "dst-alpha",
    "one-minus-dst-alpha",
    "src-alpha-saturated",
    "blend-color",
    "one-minus-blend-color"
};

enum GPUBlendOperation {
    "add",
    "subtract",
    "reverse-subtract",
    "min",
    "max"
};

typedef u32 GPUColorWriteFlags;
interface GPUColorWriteBits {
    const u32 NONE = 0;
    const u32 RED = 1;
    const u32 GREEN = 2;
    const u32 BLUE = 4;
    const u32 ALPHA = 8;
    const u32 ALL = 15;
};

dictionary GPUBlendDescriptor {
    GPUBlendFactor srcFactor = "one";
    GPUBlendFactor dstFactor = "zero";
    GPUBlendOperation operation = "add";
};

dictionary GPUColorStateDescriptor {
    required GPUTextureFormat format;

    required GPUBlendDescriptor alphaBlend;
    required GPUBlendDescriptor colorBlend;
    GPUColorWriteFlags writeMask = GPUColorWriteBits.ALL;
};

enum GPUStencilOperation {
    "keep",
    "zero",
    "replace",
    "invert",
    "increment-clamp",
    "decrement-clamp",
    "increment-wrap",
    "decrement-wrap"
};

dictionary GPUStencilStateFaceDescriptor {
    GPUCompareFunction compare = "always";
    GPUStencilOperation failOp = "keep";
    GPUStencilOperation depthFailOp = "keep";
    GPUStencilOperation passOp = "keep";
};

dictionary GPUDepthStencilStateDescriptor {
    required GPUTextureFormat format;

    boolean depthWriteEnabled = false;
    GPUCompareFunction depthCompare = "always";

    required GPUStencilStateFaceDescriptor stencilFront;
    required GPUStencilStateFaceDescriptor stencilBack;

    u32 stencilReadMask = 0xFFFFFFFF;
    u32 stencilWriteMask = 0xFFFFFFFF;
};

// Vertex Input

enum GPUIndexFormat {
    "uint16",
    "uint32"
};

// Vertex formats
// The name of the format specifies the data type of the component, the number of
// values, and whether the data is normalized.
//     uchar = unsigned 8-bit value
//     char = signed 8-bit value
//     ushort = unsigned 16-bit value
//     short = signed 16-bit value
//     half = half-precision 16-bit floating point value
//     float = 32-bit floating point value
//     uint = unsigned 32-bit integer value
//     int = signed 32-bit integer value
// If no number of values is given in the name, a single value is provided.
// If the format has the "-bgra" suffix, it means the values are arranged as
// blue, green, red and alpha values.

enum GPUVertexFormat {
    "uchar2",
    "uchar4",
    "char2",
    "char4",
    "uchar2norm",
    "uchar4norm",
    "char2norm",
    "char4norm",
    "ushort2",
    "ushort4",
    "short2",
    "short4",
    "ushort2norm",
    "ushort4norm",
    "short2norm",
    "short4norm",
    "half2",
    "half4",
    "float",
    "float2",
    "float3",
    "float4",
    "uint",
    "uint2",
    "uint3",
    "uint4",
    "int",
    "int2",
    "int3",
    "int4"
};

enum GPUInputStepMode {
    "vertex",
    "instance"
};

dictionary GPUVertexAttributeDescriptor {
    u64 offset = 0;
    required GPUVertexFormat format;
    required u32 shaderLocation;
};

dictionary GPUVertexBufferDescriptor {
    required u64 stride;
    GPUInputStepMode stepMode = "vertex";
    required sequence<GPUVertexAttributeDescriptor> attributeSet;
};

dictionary GPUVertexInputDescriptor {
    GPUIndexFormat indexFormat = "uint32";
    required sequence<GPUVertexBufferDescriptor?> vertexBuffers;
};

dictionary GPUPipelineStageDescriptor {
    required GPUShaderModule module;
    required DOMString entryPoint;
    // TODO other stuff like specialization constants?
};

dictionary GPUPipelineDescriptorBase {
    required GPUPipelineLayout layout;
};

dictionary GPUComputePipelineDescriptor : GPUPipelineDescriptorBase {
    required GPUPipelineStageDescriptor computeStage;
};

interface GPUComputePipeline {
};

enum GPUPrimitiveTopology {
    "point-list",
    "line-list",
    "line-strip",
    "triangle-list",
    "triangle-strip"
};

dictionary GPURenderPipelineDescriptor : GPUPipelineDescriptorBase {
    required GPUPipelineStageDescriptor vertexStage;
    GPUPipelineStageDescriptor? fragmentStage = null;

    required GPUPrimitiveTopology primitiveTopology;
    required GPURasterizationStateDescriptor rasterizationState;
    required sequence<GPUColorStateDescriptor> colorStates;
    GPUDepthStencilStateDescriptor? depthStencilState = null;
    required GPUVertexInputDescriptor vertexInput;

    // Number of MSAA samples
    u32 sampleCount = 1;
    // TODO other properties
};

interface GPURenderPipeline {
};

interface GPUProgrammablePassEncoder {
    void endPass();

    // Allowed in both compute and render passes
    void setBindGroup(u32 index, GPUBindGroup bindGroup, optional sequence<u64> dynamicOffsets);
};

interface GPURenderPassEncoder : GPUProgrammablePassEncoder {
    void setPipeline(GPURenderPipeline pipeline);
    void setBlendColor(GPUColor color);
    void setStencilReference(u32 reference);

    // The default viewport is (0.0, 0.0, w, h, 0.0, 1.0), where w and h are the dimensions of back buffer
    void setViewport(float x, float y, float width, float height, float minDepth, float maxDepth);

    // The default scissor rectangle is (0, 0, w, h), where w and h are the dimensions of back buffer.
    // Width and height must be greater than 0. Otherwise, an error will be generated.
    void setScissorRect(u32 x, u32 y, u32 width, u32 height);

    void setIndexBuffer(GPUBuffer buffer, u64 offset);
    void setVertexBuffers(u32 startSlot, sequence<GPUBuffer> buffers, sequence<u64> offsets);

    void draw(u32 vertexCount, u32 instanceCount, u32 firstVertex, u32 firstInstance);
    void drawIndexed(u32 indexCount, u32 instanceCount, u32 firstIndex, i32 baseVertex, u32 firstInstance);

    // TODO add missing commands
};

interface GPUComputePassEncoder : GPUProgrammablePassEncoder {
    void setPipeline(GPUComputePipeline pipeline);
    void dispatch(u32 x, u32 y, u32 z);

    // TODO add missing commands
};


enum GPULoadOp {
    "clear",
    "load"
};

enum GPUStoreOp {
    "store"
};

dictionary GPURenderPassColorAttachmentDescriptor {
    required GPUTextureView attachment;
    GPUTextureView? resolveTarget = null;

    required GPULoadOp loadOp;
    required GPUStoreOp storeOp;
    GPUColor clearColor = {r: 0.0, g: 0.0, b: 0.0, a: 1.0};
};

dictionary GPURenderPassDepthStencilAttachmentDescriptor {
    required GPUTextureView attachment;

    required GPULoadOp depthLoadOp;
    required GPUStoreOp depthStoreOp;
    required float clearDepth;

    required GPULoadOp stencilLoadOp;
    required GPUStoreOp stencilStoreOp;
    u32 clearStencil = 0;
};

dictionary GPURenderPassDescriptor {
    required sequence<GPURenderPassColorAttachmentDescriptor> colorAttachments;
    GPURenderPassDepthStencilAttachmentDescriptor? depthStencilAttachment = null;
};

dictionary GPUBufferCopyView {
    required GPUBuffer buffer;
    u64 offset = 0;
    required u32 rowPitch;
    required u32 imageHeight;
};

dictionary GPUTextureCopyView {
    required GPUTexture texture;
    u32 mipLevel = 0;
    u32 arrayLayer = 0;
    GPUOrigin3D origin = {x: 0, y: 0, z: 0};
};

interface GPUCommandBuffer {
};

interface GPUCommandEncoder {
    GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor);
    GPUComputePassEncoder beginComputePass();

    // Commands allowed outside of "passes"
    void copyBufferToBuffer(
        GPUBuffer src,
        u64 srcOffset,
        GPUBuffer dst,
        u64 dstOffset,
        u64 size);

    void copyBufferToTexture(
        GPUBufferCopyView source,
        GPUTextureCopyView destination,
        GPUExtent3D copySize);

    void copyTextureToBuffer(
        GPUTextureCopyView source,
        GPUBufferCopyView destination,
        GPUExtent3D copySize);

    void copyTextureToTexture(
        GPUTextureCopyView source,
        GPUTextureCopyView destination,
        GPUExtent3D copySize);

    GPUCommandBuffer finish();
};

dictionary GPUCommandEncoderDescriptor {
    //TODO: reusability flag?
};

dictionary GPUFenceDescriptor {
    u64 initialValue = 0;
};

interface GPUFence {
    u64 getCompletedValue();
    Promise<void> onCompletion(u64 completionValue);
};

interface GPUQueue {
    void submit(sequence<GPUCommandBuffer> buffers);

    GPUFence createFence(GPUFenceDescriptor descriptor);
    void signal(GPUFence fence, u64 signalValue);
};

interface GPUCanvasContext {
    // Calling configureSwapChain a second time invalidates the previous one,
    // and all of the textures it’s produced.
    GPUSwapChain configureSwapChain(GPUSwapChainDescriptor descriptor);

    Promise<GPUTextureFormat> getSwapChainPreferredFormat(GPUDevice device);
}

dictionary GPUSwapChainDescriptor {
    required GPUDevice device;
    required GPUTextureFormat format;
    GPUTextureUsageFlags usage = GPUTextureUsage.OUTPUT_ATTACHMENT;
};

interface GPUSwapChain {
    GPUTexture getCurrentTexture();
};

dictionary GPUExtensions {
    boolean anisotropicFiltering = false;
};

dictionary GPULimits {
    u32 maxBindGroups = 4;
};

// Device
interface GPUDevice {
    readonly attribute GPUExtensions extensions;
    readonly attribute GPULimits limits;
    readonly attribute GPUAdapter adapter;

    GPUBuffer createBuffer(GPUBufferDescriptor descriptor);
    (GPUBuffer, ArrayBuffer) createBufferMapped(GPUBufferDescriptor descriptor);
    Promise<(GPUBuffer, ArrayBuffer)> createBufferMappedAsync(GPUBufferDescriptor descriptor);
    GPUTexture createTexture(GPUTextureDescriptor descriptor);
    GPUSampler createSampler(GPUSamplerDescriptor descriptor);

    GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor);
    GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor);
    GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor);

    GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor);
    GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor);
    GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor);

    GPUCommandEncoder createCommandEncoder(GPUCommandEncoderDescriptor descriptor);

    GPUQueue getQueue();
};

dictionary GPUDeviceDescriptor {
    GPUExtensions extensions;
    GPULimits limits;

    // TODO are other things configurable like queues?
};

interface GPUAdapter {
    readonly attribute DOMString name;
    readonly attribute GPUExtensions extensions;
    //readonly attribute GPULimits limits; Don’t expose higher limits for now.

    // May reject with DOMException  // TODO: DOMException("OperationError")?
    Promise<GPUDevice> requestDevice(GPUDeviceDescriptor descriptor);
};

enum GPUPowerPreference {
    "low-power",
    "high-performance"
};

dictionary GPURequestAdapterOptions {
    GPUPowerPreference? powerPreference;
};

[Exposed=Window]
interface GPU {
    // May reject with DOMException  // TODO: DOMException("OperationError")?
    Promise<GPUAdapter> requestAdapter(optional GPURequestAdapterOptions options);
};

[Exposed=Window]
partial interface Navigator {
    [SameObject] readonly attribute GPU gpu;
};

[Exposed=DedicatedWorker]
partial interface WorkerNavigator {
    [SameObject] readonly attribute GPU gpu;
};

interface GPUDeviceLostInfo {
    readonly attribute DOMString message;
};

partial interface GPUDevice {
    readonly attribute Promise<GPUDeviceLostInfo> lost;
};

enum GPUErrorFilter {
    "none",
    "out-of-memory",
    "validation"
};

[
    Constructor()
]
interface GPUOutOfMemoryError {};

[
    Constructor(DOMString message)
]
interface GPUValidationError {
    readonly attribute DOMString message;
};

typedef (GPUOutOfMemoryError or GPUValidationError) GPUError;

partial interface GPUDevice {
    void pushErrorScope(GPUErrorFilter filter);
    Promise<GPUError?> popErrorScope();
};

[
    Constructor(DOMString type, GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict),
    Exposed=Window
]
interface GPUUncapturedErrorEvent : Event {
    readonly attribute GPUError error;
};

dictionary GPUUncapturedErrorEventInit : EventInit {
    required GPUError error;
};

// TODO: is it possible to expose the EventTarget only on the main thread?
partial interface GPUDevice : EventTarget {
    [Exposed=Window]
    attribute EventHandler onuncapturederror;
};

partial interface GPUProgrammablePassEncoder {
    void pushDebugGroup(DOMString groupLabel);
    void popDebugGroup();
    void insertDebugMarker(DOMString markerLabel);
};

interface mixin GPUDebugLabel {
    attribute DOMString label;
};

GPUBindGroup includes GPUDebugLabel;
GPUBindGroupLayout includes GPUDebugLabel;
GPUBuffer includes GPUDebugLabel;
GPUCommandBuffer includes GPUDebugLabel;
GPUCommandEncoder includes GPUDebugLabel;
GPUComputePipeline includes GPUDebugLabel;
GPUFence includes GPUDebugLabel;
GPUPipelineLayout includes GPUDebugLabel;
GPUProgrammablePassEncoder includes GPUDebugLabel;
GPUQueue includes GPUDebugLabel;
GPURenderPipeline includes GPUDebugLabel;
GPUSampler includes GPUDebugLabel;
GPUShaderModule includes GPUDebugLabel;
GPUTexture includes GPUDebugLabel;
GPUTextureView includes GPUDebugLabel;

partial dictionary GPUCommandEncoderDescriptor {
    DOMString label;
};

partial dictionary GPUFenceDescriptor {
    DOMString label;
};

partial dictionary GPUPipelineDescriptorBase {
    DOMString label;
};

partial dictionary GPUShaderModuleDescriptor {
    DOMString label;
};

上述所有接口的汇总目录。

统一结尾:以上均为个人理解和一家之言,有任何错漏之处欢迎留言讨论,共同进步,一经发现错漏,必立刻更新,且会在修改处指明reporter。谢谢

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值