WebKit Bugzilla
New
Browse
Log In
×
Sign in with GitHub
or
Remember my login
Create Account
·
Forgot Password
Forgotten password account recovery
RESOLVED FIXED
Bug 194665
[Web GPU] Buffer updates part 1: async mapping functions, unmap, and destroy
https://bugs.webkit.org/show_bug.cgi?id=194665
Summary
[Web GPU] Buffer updates part 1: async mapping functions, unmap, and destroy
Justin Fan
Reported
2019-02-14 12:56:51 PST
[WebGPU] (WIP) Buffer updates part 1: async mapping functions, unmap, and destroy
Attachments
Patch
(21.93 KB, patch)
2019-02-14 12:57 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews105 for mac-highsierra-wk2
(2.97 MB, application/zip)
2019-02-14 14:22 PST
,
EWS Watchlist
no flags
Details
Patch
(29.82 KB, patch)
2019-02-15 16:57 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch
(29.88 KB, patch)
2019-02-15 17:05 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch
(29.90 KB, patch)
2019-02-15 17:10 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Archive of layout-test-results from ews106 for mac-highsierra-wk2
(3.00 MB, application/zip)
2019-02-15 18:18 PST
,
EWS Watchlist
no flags
Details
Patch
(52.99 KB, patch)
2019-02-20 17:02 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch
(57.10 KB, patch)
2019-02-21 16:24 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch
(60.37 KB, patch)
2019-02-22 16:48 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch
(60.40 KB, patch)
2019-02-22 16:57 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Patch for landing
(60.16 KB, patch)
2019-02-23 14:45 PST
,
Justin Fan
no flags
Details
Formatted Diff
Diff
Show Obsolete
(10)
View All
Add attachment
proposed patch, testcase, etc.
Justin Fan
Comment 1
2019-02-14 12:57:36 PST
Created
attachment 362052
[details]
Patch
Justin Fan
Comment 2
2019-02-14 12:58:11 PST
Draft ready for comments but not real review (most tests haven't been updated)
EWS Watchlist
Comment 3
2019-02-14 12:59:01 PST
Attachment 362052
[details]
did not pass style-queue: ERROR: Source/WebCore/ChangeLog:8: You should remove the 'No new tests' and either add and list tests, or explain why no new tests were possible. [changelog/nonewtests] [5] Total errors found: 1 in 15 files If any of these errors are false positives, please file a bug against check-webkit-style.
Justin Fan
Comment 4
2019-02-14 13:02:36 PST
Comment on
attachment 362052
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362052&action=review
> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:73 > + m_buffer = nullptr;
Is this enough for us to signify destroyed state?
> Source/WebCore/platform/graphics/gpu/GPUBufferUsage.h:38 > + enum Flags : GPUBufferUsageFlags {
Can't bitwise OR enum class values without casting. Perhaps this should be replaced by a bunch of static consts?
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:131 > +// FIXME: MTLStorageModeShared buffers are always mapped? Or should m_mapping not be updated until buffer is unmapped?
BufferOperations.md: "Mappings created with mapWriteAsync are filled with zeros. When they are detached, it is as if buffer.setSubData(0, mapping) was called." Unmap == detach?
Justin Fan
Comment 5
2019-02-14 13:18:48 PST
Comment on
attachment 362052
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362052&action=review
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:84 > + if (descriptor.usage & (GPUBufferUsage::Flags::MapWrite | GPUBufferUsage::Flags::MapRead))
According to the OG proposal back in December MAP_WRITE and MAP_READ are mutually exclusive; is this still true?
EWS Watchlist
Comment 6
2019-02-14 14:22:03 PST
Comment on
attachment 362052
[details]
Patch
Attachment 362052
[details]
did not pass mac-wk2-ews (mac-wk2): Output:
https://webkit-queues.webkit.org/results/11150521
New failing tests: webgpu/buffer-resource-triangles.html webgpu/vertex-buffer-triangle-strip.html webgpu/depth-enabled-triangle-strip.html
EWS Watchlist
Comment 7
2019-02-14 14:22:05 PST
Created
attachment 362062
[details]
Archive of layout-test-results from ews105 for mac-highsierra-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews105 Port: mac-highsierra-wk2 Platform: Mac OS X 10.13.6
Justin Fan
Comment 8
2019-02-14 17:08:08 PST
Comment on
attachment 362052
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362052&action=review
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:98 > GPUBuffer::~GPUBuffer()
Should this invalidate the ArrayBuffer memory before returning? What if this GPUBuffer is being destroyed, but a promise with its ArrayBuffer has resolved but not yet delivered to JavaScript?
Justin Fan
Comment 9
2019-02-15 16:57:30 PST
Created
attachment 362185
[details]
Patch
EWS Watchlist
Comment 10
2019-02-15 16:59:10 PST
Attachment 362185
[details]
did not pass style-queue: ERROR: Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:120: An else should appear on the same line as the preceding } [whitespace/newline] [4] ERROR: Source/WebCore/ChangeLog:8: You should remove the 'No new tests' and either add and list tests, or explain why no new tests were possible. [changelog/nonewtests] [5] Total errors found: 2 in 17 files If any of these errors are false positives, please file a bug against check-webkit-style.
Justin Fan
Comment 11
2019-02-15 17:05:25 PST
Created
attachment 362187
[details]
Patch
Justin Fan
Comment 12
2019-02-15 17:10:20 PST
Created
attachment 362189
[details]
Patch
Radar WebKit Bug Importer
Comment 13
2019-02-15 17:22:24 PST
<
rdar://problem/48130821
>
Justin Fan
Comment 14
2019-02-15 17:24:44 PST
Remaining questions and issues to be solved for this patch: 1) The design guidelines mention that arrayBuffers provided to promises should reference their buffers to prevent their garbage collection. Is this sufficiently accomplished by capturing the Ref of the buffer in the microtask function? 2) Is a map__Async() call considered "complete" upon returning the promise, or settling the promise? 3) Should a queued map__Async() promise reject if it was created while the buffer was unmapped, but is in mapped state when the callback executes? 4) During unmap(), how do I "neuter" the ArrayBuffer without destroying its contents so that later a mapReadAsync() callback can re-map and read its previous contents? 5) How do I prevent the JS developer from writing to the arraybuffer provided to a mapReadAsync() callback? (Just const-qualify the C++ object? Provide a pointer to a separate copy of the data?) 6) How do I tell when the GPU is done with shared memory? According to the design guidelines, mapWriteAsync() should zero-out its arraybuffer before providing it to the callback, but this seems to happen frequently in the middle of draw calls.
EWS Watchlist
Comment 15
2019-02-15 18:18:16 PST
Comment on
attachment 362189
[details]
Patch
Attachment 362189
[details]
did not pass mac-wk2-ews (mac-wk2): Output:
https://webkit-queues.webkit.org/results/11167323
New failing tests: webgpu/buffers.html
EWS Watchlist
Comment 16
2019-02-15 18:18:18 PST
Created
attachment 362198
[details]
Archive of layout-test-results from ews106 for mac-highsierra-wk2 The attached test failures were seen while running run-webkit-tests on the mac-wk2-ews. Bot: ews106 Port: mac-highsierra-wk2 Platform: Mac OS X 10.13.6
Justin Fan
Comment 17
2019-02-20 17:02:17 PST
Created
attachment 362572
[details]
Patch
EWS Watchlist
Comment 18
2019-02-20 17:04:35 PST
Attachment 362572
[details]
did not pass style-queue: ERROR: /Volumes/Data/StyleQueue/WebKit/LayoutTests/platform/mac/TestExpectations:1788: Path does not exist. [test/expectations] [5] Total errors found: 1 in 24 files If any of these errors are false positives, please file a bug against check-webkit-style.
Justin Fan
Comment 19
2019-02-21 16:24:37 PST
Created
attachment 362667
[details]
Patch
Dean Jackson
Comment 20
2019-02-21 16:56:23 PST
Comment on
attachment 362667
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362667&action=review
> Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.h:41 > + Optional<GPUBindGroupDescriptor> validateAndConvertToGPUVersion() const;
I think this should be named asGPUBindGroupDescriptor()
> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:63 > + unmap();
Will it be an error to call unmap on an already unmapped buffer?
> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:68 > +void WebGPUBuffer::rejectOrPendMappingPromise(ArrayBufferPromise&& promise, bool isRead)
I don't like this name. /me looks around for existing examples....
> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:76 > + m_buffer->pendMappingPromise(WTFMove(promise), isRead);
I wonder if it is worth doing more testing here... probably not if GPUBuffer does all the smart stuff (like test if it is already mapped).
> Source/WebCore/platform/graphics/gpu/GPUBuffer.h:48 > +using ArrayBufferPromise = DOMPromiseDeferred<IDLInterface<JSC::ArrayBuffer*>>;
We can't do this. DOMPromiseDeferred lives in bindings/js, and we're inside /platform/. So we can't use Promises at this level. Instead, they'll be held at the WebGPU level. Down here we'll have to implement our own callbacks.
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:131 > + MicrotaskQueue::mainThreadQueue().append(std::make_unique<VoidMicrotask>([this, protectedThis = makeRef(*this)] () mutable {
Similarly, microtasks are part of the DOM. We'll have to use another queue or make our own. I wonder if Media has anything like this... it must.
Justin Fan
Comment 21
2019-02-22 12:05:05 PST
Comment on
attachment 362667
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362667&action=review
>> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:63 >> + unmap(); > > Will it be an error to call unmap on an already unmapped buffer?
According to Corentin's design doc, no.
>> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:68 >> +void WebGPUBuffer::rejectOrPendMappingPromise(ArrayBufferPromise&& promise, bool isRead) > > I don't like this name. /me looks around for existing examples....
registerMappingPromise? ¯\_(ツ)_/¯ We might not need this after I fix the design.
>> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:76 >> + m_buffer->pendMappingPromise(WTFMove(promise), isRead); > > I wonder if it is worth doing more testing here... probably not if GPUBuffer does all the smart stuff (like test if it is already mapped).
Correct, GPUBuffer tracks state and flags so it handles the checking. Should WebGPUBuffer be tracking that state instead?
Justin Fan
Comment 22
2019-02-22 16:48:48 PST
Created
attachment 362786
[details]
Patch
Justin Fan
Comment 23
2019-02-22 16:57:19 PST
Created
attachment 362792
[details]
Patch
Dean Jackson
Comment 24
2019-02-22 17:28:57 PST
Comment on
attachment 362792
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362792&action=review
> Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp:42 > +#if LOG_DISABLED > + UNUSED_PARAM(functionName); > +#endif
You probably don't really need the #if here because the compiler will optimise away the void statement. But whatever.
> Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp:104 > + if (view) > + return static_cast<GPUBindingResource>(view->texture()); > + return WTF::nullopt; > + }, [&layoutBinding, functionName] (const WebGPUBufferBinding& binding) -> Optional<GPUBindingResource> { > + if (binding.buffer && binding.buffer->buffer()) { > + if (!validateBufferBindingType(binding.buffer->buffer().get(), layoutBinding, functionName)) > + return WTF::nullopt; > + > + return static_cast<GPUBindingResource>(GPUBufferBinding { binding.buffer->buffer().releaseNonNull(), binding.offset, binding.size }); > + } > + LOG(WebGPU, "%s: Invalid GPUBindingResource for binding %lu in GPUBindGroupBindings!", functionName, layoutBinding.binding); > + return WTF::nullopt;
I suggest flipping the logic in these two functions so the error case comes first. e.g. if (!view) return WTF::nullopt; return static_cast<GPUBindingResource>(view->texture());
> Source/WebCore/Modules/webgpu/WebGPUBindGroupDescriptor.cpp:111 > + if (bindingResource) > + bindGroupBindings.uncheckedAppend(GPUBindGroupBinding { binding.binding, WTFMove(bindingResource.value()) }); > + else > + return WTF::nullopt;
Same here.
> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:87 > + m_buffer->registerMappingCallback([promise = WTFMove(promise)] (JSC::ArrayBuffer* arrayBuffer) mutable { > + if (!arrayBuffer) { > + promise.reject(); > + return; > + } > + > + promise.resolve(arrayBuffer); > + }, isRead);
Question: Since we are not keeping a reference to the promise anywhere but in this closure, can we guarantee that this callback will always be called? i.e. if we call a combination of mapReadAsync mapWriteAsync unmap destroy, will the buffer always callback?
> Source/WebCore/Modules/webgpu/WebGPUBuffer.h:44 > +using ArrayBufferPromise = DOMPromiseDeferred<IDLInterface<JSC::ArrayBuffer*>>;
Suggestion: maybe this should be BufferReadOrWritePromise? It's not a promise on the ArrayBuffer. Looking through WebCore, most cases like this are named FooPromise, with Foo being related to the call that takes the promise. Also, should the using be inside the class? I think it should.
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:119 > + ASSERT(!m_pendingCallback && !m_mappingCallbackTask.hasPendingTask()); > +
I think we should check here if the m_pendingCallback exists, and if it does.... hmmm... reject it and the new one? Or does the logic above already handle this? Should we also have a generic test for isMappable()?
> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:131 > + if (m_isMapReadable) > + m_pendingCallback->callback(stagingBufferForRead()); > + else > + m_pendingCallback->callback(stagingBufferForWrite());
How about a ternary here? m_pendingCallback->callback(m_isMapReadable ? stagingBufferForRead() : stagingBufferForWrite());
> LayoutTests/webgpu/map-write-buffers.html:70 > + const promise = buffer.mapWriteAsync(); > + buffer.unmap(); > + > + await promise.then(() => {
Don't we have to check if the promise has been resolved instantly? Or does .then() still execute in that case?
Justin Fan
Comment 25
2019-02-23 14:45:42 PST
Comment on
attachment 362792
[details]
Patch View in context:
https://bugs.webkit.org/attachment.cgi?id=362792&action=review
>> Source/WebCore/Modules/webgpu/WebGPUBuffer.cpp:87 >> + }, isRead); > > Question: Since we are not keeping a reference to the promise anywhere but in this closure, can we guarantee that this callback will always be called? i.e. if we call a combination of mapReadAsync mapWriteAsync unmap destroy, will the buffer always callback?
Hmm. The unmap() function calls the closure (to reject the promise) if it exists, and nowhere else in my code do I destroy the closure. unmap is called by destroy() and ~GPUBuffer() as well, so it is robust. I'm not sure exactly what your example means (since a buffer is either map readable or map writeable, not both) but assuming they're both mapReadAsync calls, the second call would immediately reject without registering a callback since the buffer is already mapped by the first. Unmap() first cancels the request's DeferredTask, then calls callback(nullptr) to reject the promise and invalidates the object owning the closure. Destroy() invalidates the MTLBuffer, but at this point the promise has already been rejected.
>> Source/WebCore/platform/graphics/gpu/cocoa/GPUBufferMetal.mm:119 >> + > > I think we should check here if the m_pendingCallback exists, and if it does.... hmmm... reject it and the new one? > > Or does the logic above already handle this? Should we also have a generic test for isMappable()?
The existence of m_pendingCallback indicates that the buffer is in the mapped state. The logic above will prevent additional mapping requests on a buffer that already has one. The checks above reject the new one, but leaves m_pendingCallback alone. the isMap*able() functions actually mean "isUnmapped && wasCreatedWithMap*Flag". I'm renaming m_isMap*able to m_isMap* to match the usage flag that it's based on and to separate it from isMap*able().
>> LayoutTests/webgpu/map-write-buffers.html:70 >> + await promise.then(() => { > > Don't we have to check if the promise has been resolved instantly? Or does .then() still execute in that case?
Yes, (afaik) .then()'s resolve callback is queued on the task queue immediately if called on a resolved promise.
Justin Fan
Comment 26
2019-02-23 14:45:52 PST
Created
attachment 362836
[details]
Patch for landing
WebKit Commit Bot
Comment 27
2019-02-23 15:24:33 PST
Comment on
attachment 362836
[details]
Patch for landing Clearing flags on attachment: 362836 Committed
r241996
: <
https://trac.webkit.org/changeset/241996
>
WebKit Commit Bot
Comment 28
2019-02-23 15:24:35 PST
All reviewed patches have been landed. Closing bug.
Truitt Savell
Comment 29
2019-02-25 13:02:38 PST
It appears that the changes in
https://trac.webkit.org/changeset/241996/webkit
caused these three tests to become flakey: webgpu/vertex-buffer-triangle-strip.html webgpu/depth-enabled-triangle-strip.html webgpu/buffer-resource-triangles.html History:
https://webkit-test-results.webkit.org/dashboards/flakiness_dashboard.html#showAllRuns=true&tests=webgpu%2Fvertex-buffer-triangle-strip.html%20webgpu%2Fdepth-enabled-triangle-strip.html%20webgpu%2Fbuffer-resource-triangles.html
Justin Fan
Comment 30
2019-02-25 14:00:50 PST
(In reply to Truitt Savell from
comment #29
)
> It appears that the changes in >
https://trac.webkit.org/changeset/241996/webkit
> > caused these three tests to become flakey: > > webgpu/vertex-buffer-triangle-strip.html > webgpu/depth-enabled-triangle-strip.html > webgpu/buffer-resource-triangles.html > > History: >
https://webkit-test-results.webkit.org/dashboards/flakiness_dashboard
. > html#showAllRuns=true&tests=webgpu%2Fvertex-buffer-triangle-strip. > html%20webgpu%2Fdepth-enabled-triangle-strip.html%20webgpu%2Fbuffer-resource- > triangles.html
It’s probably a race between the draw call and the mapWriteAsync/unmap calls used to fill the vertex buffers for these tests. For buffer updates part 2 (later today) I’m using a non-racy setSubData call that will be implemented in that patch.
Note
You need to
log in
before you can comment on or make changes to this bug.
Top of Page
Format For Printing
XML
Clone This Bug