diff --git a/spec/index.bs b/spec/index.bs index d9d7b971f2..5ebdab257c 100644 --- a/spec/index.bs +++ b/spec/index.bs @@ -318,7 +318,7 @@ They are represented by callbacks and promises in JavaScript.
-{{GPUBuffer/mapReadAsync()|GPUBuffer.mapReadAsync()}}: +{{GPUBuffer/mapAsync()|GPUBuffer.mapAsync()}}: 1. User requests to map a {{GPUBuffer}} on the [=Content timeline=] and gets a promise in return. @@ -1035,19 +1035,15 @@ restrictions depending on the operation. Some {{GPUBuffer|GPUBuffers}} can be mapped which makes the block of memory accessible via an {{ArrayBuffer}} called its mapping. -{{GPUBuffer|GPUBuffers}} can be created via the following functions: - - - {{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer(descriptor)}} - that returns a new buffer in the [=buffer state/unmapped=] state. - - {{GPUDevice/createBufferMapped(descriptor)|GPUDevice.createBufferMapped(descriptor)}} - that returns a new buffer in the [=buffer state/mapped for writing=] state and its - mapping. +{{GPUBuffer|GPUBuffers}} are created via +{{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer(descriptor)}} +that returns a new buffer in the [=buffer state/mapped=] or [=buffer state/unmapped=] state. - -
validating GPUBufferDescriptor(device, descriptor) 1. If device is lost return false. @@ -1135,50 +1144,78 @@ dictionary GPUBufferDescriptor : GPUObjectDescriptorBase { 1. Return true.
-### {{GPUDevice/createBuffer(descriptor)|GPUDevice.createBuffer(descriptor)}} ### {#GPUDevice-createBuffer} +## Buffer Usage ## {#buffer-usage} -
- : createBuffer(descriptor) - :: -
- 1. If the result of [$validating GPUBufferDescriptor$](this, descriptor) is false: - - 1. Record a validation error in the current scope. - - 1. Create an [=invalid=] {{GPUBuffer}} and return the result. - - 1. Let |b| be a new {{GPUBuffer}} object. - 1. Set the {{GPUBuffer/[[size]]}} slot of |b| to the value of the {{GPUBufferDescriptor/size}} attribute of |descriptor|. - 1. Set the {{GPUBuffer/[[usage]]}} slot of |b| to the value of the {{GPUBufferDescriptor/usage}} attribute of |descriptor|. - 1. Set the {{GPUBuffer/[[state]]}} internal slot of |b| to [=buffer state/unmapped=]. - 1. Set the {{GPUBuffer/[[mapping]]}} internal slot of |b| to `null`. - 1. Set each byte of |b|'s allocation to zero. - 1. Return |b|. -
-
+ -### {{GPUDevice/createBufferMapped(descriptor)|GPUDevice.createBufferMapped(descriptor)}} ### {#GPUDevice-createBufferMapped} +### createBuffer(descriptor) ### {#GPUDevice-createBuffer} -
- : createBufferMapped(descriptor) - :: -
- 1. If the result of [$validating GPUBufferDescriptor$](this, descriptor) is false: - - 1. Record a validation error in the current scope. - - 1. Create an invalid {{GPUBuffer}} and return the result. - - 1. Let |b| be a new {{GPUBuffer}} object. - 1. Set the {{GPUBuffer/[[size]]}} slot of |b| to the value of the {{GPUBufferDescriptor/size}} attribute of |descriptor|. - 1. Let |m| be a zero-filled {{ArrayBuffer}} of size the {{GPUBuffer/[[size]]}} slot of |b|. - 1. Set the {{GPUBuffer/[[usage]]}} slot of |b| to the value of the {{GPUBufferDescriptor/usage}} attribute of |descriptor|. - 1. Set the {{GPUBuffer/[[state]]}} internal slot of |b| to [=buffer state/mapped for writing=]. - 1. Set the {{GPUBuffer/[[mapping]]}} internal slot of |b| to |m|. - 1. Set each byte of |b|'s allocation to zero. - 1. Return a sequence containing |b| and |m| in that order. -
-
+
+ **Arguments:** + - {{GPUBufferDescriptor}} |descriptor| + + **Returns:** {{GPUBuffer}} + + 1. If this call doesn't follow the [$createBuffer Valid Usage$]: + + 1. Retun an error buffer. + + Issue(gpuweb/gpuweb#605): Explain that the resulting error buffer can still be mapped at creation. + + 1. Let |b| be a new {{GPUBuffer}} object. + 1. Set |b|.{{GPUBuffer/[[size]]}} to |descriptor|.{{GPUBufferDescriptor/size}}. + 1. Set |b|.{{GPUBuffer/[[usage]]}} to |descriptor|.{{GPUBufferDescriptor/usage}}. + 1. If |descriptor|.{{GPUBufferDescriptor/mappedAtCreation}} is true: + + 1. Set |b|.{{GPUBuffer/[[mapping]]}} to a new {{ArrayBuffer}} of size |b|.{{GPUBuffer/[[size]]}}. + 1. Set |b|.{{GPUBuffer/[[mapping_range]]}} to `[0, descriptor.size]`. + 1. Set |b|.{{GPUBuffer/[[mapped_ranges]]}} to `[]`. + 1. Set |b|.{{GPUBuffer/[[state]]}} to [=buffer state/mapped at creation=]. + + 1. Else: + + 1. Set |b|.{{GPUBuffer/[[mapping]]}} to `null`. + 1. Set |b|.{{GPUBuffer/[[mapping_range]]}} to `null`. + 1. Set |b|.{{GPUBuffer/[[mapped_ranges]]}} to `null`. + 1. Set |b|.{{GPUBuffer/[[state]]}} to [=buffer state/unmapped=]. + + 1. Set each byte of |b|'s allocation to zero. + 1. Return |b|. + + Note: it is valid to set {{GPUBufferDescriptor/mappedAtCreation}} to true without {{GPUBufferUsage/MAP_READ}} + or {{GPUBufferUsage/MAP_WRITE}} in {{GPUBufferDescriptor/usage}}. This can be used to set the buffer's + initial data. + +
+ createBuffer Valid Usage + Given a {{GPUDevice}} |this| and a {{GPUBufferDescriptor}} |descriptor| + the following validation rules apply: + + 1. |this| must be a [=valid=] {{GPUDevice}}. + 1. |descriptor|.{{GPUBufferDescriptor/usage}} must be a subset of |this|.[[allowed buffer usages]]. + 1. If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_READ}} then + the only other usage it may contain is {{GPUBufferUsage/COPY_DST}}. + 1. If |descriptor|.{{GPUBufferDescriptor/usage}} contains {{GPUBufferUsage/MAP_WRITE}} then + the only other usage it may contain is {{GPUBufferUsage/COPY_SRC}}. + + Issue(gpuweb/gpuweb#605): Explain what are a {{GPUDevice}}'s `[[allowed buffer usages]]` + +
+
## Buffer Destruction ## {#buffer-destruction} @@ -1188,131 +1225,189 @@ access to it before garbage collection by calling {{GPUBuffer/destroy()}}. Note: This allows the user agent to reclaim the GPU memory associated with the {{GPUBuffer}} once all previously submitted operations using it are complete. -### destroy() ### {#buffer-destroy} +### destroy() ### {#GPUBuffer-destroy}
- - 1. If the {{[[state]]}} slot of |this| is [=buffer state/mapped for reading=] or [=buffer state/mapped for writing=]: + |this|: of type {{GPUBuffer}}. - 1. Run the steps to unmap `"this"` + 1. If the |this|.{{[[state]]}} is [=buffer state/mapped=] or [=buffer state/mapped at creation=]: - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/destroyed=] -
+ 1. Run the steps to unmap |this| -## Buffer Usage ## {#buffer-usage} + 1. Set |this|.{{[[state]]}} to [=buffer state/destroyed=] - + Issue: Handle error buffers once we have a description of the error monad. +
## Buffer Mapping ## {#buffer-mapping} -An application can request to map a {{GPUBuffer}} to get its mapping which is -an {{ArrayBuffer}} representing the {{GPUBuffer}}'s allocation. Mappings are -requested asynchronously so that the user agent can ensure the GPU finished -using the {{GPUBuffer}} before the application gets its mapping. Mappings can -be requested for reading with {{GPUBuffer/mapReadAsync}} or writing with -{{GPUBuffer/mapWriteAsync}}. A mapped {{GPUBuffer}} cannot be used by the GPU -and must be unmapped using {{GPUBuffer/unmap}} before it can be used on the -[=Queue timeline=]. +An application can request to map a {{GPUBuffer}} so that they can access its +content via {{ArrayBuffer}}s that represent part of the {{GPUBuffer}}'s +allocations. Mapping a {{GPUBuffer}} is requested asynchronously with +{{GPUBuffer/mapAsync}} so that the user agent can ensure the GPU +finished using the {{GPUBuffer}} before the application can access its content. +Once the {{GPUBuffer}} is mapped the application can synchronously ask for access +to ranges of its content with {{GPUBuffer/getMappedRange}}. A mapped {{GPUBuffer}} +cannot be used by the GPU and must be unmapped using {{GPUBuffer/unmap}} before +work using it can be submitted to the [=Queue timeline=]. + +Issue(gpuweb/gpuweb#605): Add client-side validation that a mapped buffer can + only be unmapped and destroyed on the worker on which it was mapped. Likewise + {{GPUBuffer/getMappedRange}} can only be called on that worker. + +### mapAsync(start, offset) ### {#GPUBuffer-mapAsync} + +
-Issue: Add client-side validation that a mapped buffer can only be unmapped and destroyed on the worker on which it was mapped. + Issue(gpuweb/gpuweb#605): There is concern that it should be clearer at a {{GPUBuffer/mapAsync}} + call point if it is meant for reading or writing because the semantics are very different. + Alternatives suggested include splitting into `mapReadAsync` vs. `mapWriteAsync`, or + adding a `GPUMapFlags` as an argument to the call that can later be used to extend the method. -### {{GPUBuffer/mapReadAsync|GPUDevice.mapReadAsync}} ### {#GPUBuffer-mapReadAsync} + |this|: of type {{GPUBuffer}}. -
- Issue: Handle error buffers once we have a description of the error monad. + **Arguments:** + - {{GPUSize64}} |offset| + - {{GPUSize64}} |size| + + **Returns:** {{Promise}} + + Issue(gpuweb/gpuweb#605): Handle error buffers once we have a description of the error monad. + + 1. If |size| is 0 and |offset| is less than |this|.{{[[size]]}}: + + 1. Set |size| to |this|.{{[[size]]}} - |offset| - 1. If the {{[[usage]]}} slot of |this| doesn't contain the {{GPUBufferUsage/MAP_READ}} bit or - if {{[[state]]}} isn't [=buffer state/unmapped=]: + 1. If this call doesn't follow [$mapAsync Valid Usage$]: - 1. Record a validation error on the current scope. - 1. Return [=a promise rejected with=] an {{AbortError}}. + 1. Record a validation error on the current scope. + 1. Return [=a promise rejected with=] an {{AbortError}} on the [=Device timeline=]. - Issue: Specify that the rejection happens on the device timeline. + 1. Let |p| be a new {{Promise}}. + 1. Set |this|.{{[[mapping]]}} to |p|. + 1. Set |this|.{{[[state]]}} to [=buffer state/mapping pending=]. + 1. Enqueue an operation on the default queue's [=Queue timeline=] that will execute the following: - 1. Let |p| be a new {{Promise}}. - 1. Set the {{[[mapping]]}} slot of |this| to |p|. - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/mapping pending for reading=]. - 1. Enqueue an operation on the [=Queue timeline=] that will execute the following: + 1. If |this|.{{[[state]]}} is [=buffer state/mapping pending=]: - 1. Let |m| be a new {{ArrayBuffer}} of size the {{[[size]]}} of |this|. - 1. Set the content of |m| to the content of |this|'s allocation. - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/mapped for reading=]. - 1. If |p| is pending: + 1. Let |m| be a new {{ArrayBuffer}} of size |size|. + 1. Set the content of |m| to the content of |this|'s allocation starting at offset |offset| and for |size| bytes. + 1. Set |this|.{{[[mapping]]}} to |m|. + 1. Set |this|.{{[[state]]}} to [=buffer state/mapped=]. + 1. Set |this|.{{[[mapping_range]]}} to `[start, offset]`. + 1. Set |this|.{{[[mapped_ranges]]}} to `[]`. + 1. Resolve |p|. - 1. Resolve |p| with |m|. + 1. Return |p|. - 1. Return |p|. +
+ mapAsync Valid Usage + + Given a {{GPUBuffer}} |this|, a {{GPUSize64}} |offset| and a {{GPUSize64}} |size| + the following validation rules apply: + + 1. |this| must be a [=valid=] {{GPUBuffer}}. + 1. |offset| must be a multiple of 4. + 1. |size| must be a multiple of 4. + 1. |offset| + |size| must be less or equal to |this|.{{[[size]]}} + 1. |this|.{{[[usage]]}} must contain {{GPUBufferUsage/MAP_READ}} or {{GPUBufferUsage/MAP_WRITE}}. + 1. |this|.{{[[state]]}} must be [=buffer state/unmapped=] + + +
-### {{GPUBuffer/mapWriteAsync|GPUDevice.mapWriteAsync}} ### {#GPUBuffer-mapWriteAsync} +### getMappedRange(start, offset) ### {#GPUBuffer-getMappedRange} -
+
+ |this|: of type {{GPUBuffer}}. - Issue: Handle error buffers once we have a description of the error monad. + **Arguments:** + - {{GPUSize64}} |offset| + - {{GPUSize64}} |size| - 1. If the {{[[usage]]}} slot of |this| doesn't contain the {{GPUBufferUsage/MAP_WRITE}} bit or - if {{[[state]]}} isn't [=buffer state/unmapped=]: + **Returns:** {{ArrayBuffer}} - 1. Record a validation error on the current scope. - 1. Return [=a promise rejected with=] an {{AbortError}}. + 1. If this call doesn't follow the [$getMappedRange Valid Usage$]: - Issue: Specify that the rejection happens on the device timeline. + 1. Throw an {{OperationError}}. - 1. Let |p| be a new {{Promise}}. - 1. Set the {{[[mapping]]}} slot of |this| to |p|. - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/mapping pending for writing=]. - 1. Enqueue an operation on the [=Queue timeline=] that will execute the following: + 1. Let |m| be a new {{ArrayBuffer}} of size |size| pointing at the content of |this|.{{[[mapping]]}} at offset |offset| - |this|.{{[[mapping_range]]}}[0]. + 1. Append |m| to |this|.{{[[mapped_ranges]]}}. + 1. Return |m|. - 1. Let |m| be a new {{ArrayBuffer}} of size the {{[[size]]}} of |this| that is filled with zeroes. - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/mapped for writing=]. - 1. If |p| is pending: +
+ getMappedRange Valid Usage - 1. Resolve |p| with |m|. + Given a {{GPUBuffer}} |this|, a {{GPUSize64}} |offset| and a {{GPUSize64}} |size| + the following validation rules apply: - 1. Return |p|. + 1. |this|.{{[[state]]}} must be [=buffer state/mapped=] or [=buffer state/mapped at creation=]. + 1. |offset| must be a multiple of 8. + 1. |size| must be a multiple of 4. + 1. |offset| must be greater than or equal to |this|.{{[[mapping_range]]}}[0]. + 1. |offset| + |size| must be less than or equal to |this|.{{[[mapping_range]]}}[0] + |this|.{{[[mapping_range]]}}[1]. + 1. [|offset|, |offset| + |size|) must not overlap another range in |this|.{{[[mapped_ranges]]}}. + + Note: It is valid to get mapped ranges of an error {{GPUBuffer}} that is [=buffer state/mapped at creation=] because + the [=Content timeline=] might not know it is an error {{GPUBuffer}}. + + +
-### unmap() ### {#buffer-unmap} +### unmap() ### {#GPUBuffer-unmap}
+ |this|: of type {{GPUBuffer}}. - 1. If the {{[[state]]}} slot of |this| is [=buffer state/unmapped=] or [=buffer state/destroyed=]: + 1. If this call doesn't follow [$unmap Valid Usage$]: - 1. Record a validation error on the current scope. - 1. Return. + 1. Record a validation error on the current scope. + 1. Return. - 1. If the {{[[mapping]]}} slot of |this| is a {{Promise}}: + 1. If |this|.{{[[state]]}} is [=buffer state/mapping pending=]: - 1. [=Reject=] {{[[mapping]]}} with an {{AbortError}}. - 1. Set the {{[[mapping]]}} slot of |this| to null. + 1. [=Reject=] {{[[mapping]]}} with an {{OperationError}}. + 1. Set |this|.{{[[mapping]]}} to null. - 1. If the {{[[mapping]]}} slot of |this| is an {{ArrayBuffer}}: + 1. If |this|.{{[[state]]}} is [=buffer state/mapped=] or [=buffer state/mapped at creation=]: - 1. If the {{[[state]]}} slot of this is [=buffer state/mapped for writing=]: + 1. If one of the two following conditions holds: - 1. Enqueue an operation on the [=Queue timeline=] that updates |this|'s allocation to the content of the {{ArrayBuffer}} in the {{[[mapping]]}} slot of |this|. + 1. |this|.{{[[state]]}} is [=buffer state/mapped at creation=] + 1. |this|.{{[[state]]}} is [=buffer state/mapped=] and |this|.{{[[usage]]}} contains {{GPUBufferUsage/MAP_WRITE}} - 1. Detach |this|.{{[[mapping]]}} from its content. - 1. Set the {{[[mapping]]}} slot of |this| to null. + 1. Then: - 1. Set the {{[[state]]}} slot of |this| to [=buffer state/unmapped=]. + 1. Enqueue an operation on the default queue's [=Queue timeline=] that updates the |this|.{{[[mapping_range]]}} + of |this|'s allocation to the content of |this|.{{[[mapping]]}}. -
+ 1. Detach each {{ArrayBuffer}} in |this|.{{[[mapped_ranges]]}} from its content. + 1. Set |this|.{{[[mapping]]}} to null. + 1. Set |this|.{{[[mapping_range]]}} to null. + 1. Set |this|.{{[[mapped_ranges]]}} to null. + + 1. Set |this|.{{[[state]]}} to [=buffer state/unmapped=]. + + Note: When a {{GPUBufferUsage/MAP_READ}} buffer (not currently mapped at creation) is unmapped, + any local modifications done by the application to the mapped ranges {{ArrayBuffer}} are + discarded and will not affect the content of follow-up mappings. +
+ unmap Valid Usage + + Given a {{GPUBuffer}} the following validation rules apply: + + 1. |this|.{{[[state]]}} must not be [=buffer state/unmapped=] + 1. |this|.{{[[state]]}} must not be [=buffer state/destroyed=] + + Note: It is valid to unmap an error {{GPUBuffer}} that is [=buffer state/mapped at creation=] because + the [=Content timeline=] might not know it is an error {{GPUBuffer}}. + + +
+
# Textures and Texture Views # {#textures}