1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker * Copyright 2022 Google LLC
3*c8dee2aaSAndroid Build Coastguard Worker *
4*c8dee2aaSAndroid Build Coastguard Worker * Use of this source code is governed by a BSD-style license that can be
5*c8dee2aaSAndroid Build Coastguard Worker * found in the LICENSE file.
6*c8dee2aaSAndroid Build Coastguard Worker */
7*c8dee2aaSAndroid Build Coastguard Worker
8*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/dawn/DawnBuffer.h"
9*c8dee2aaSAndroid Build Coastguard Worker
10*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTraceMemoryDump.h"
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkAlign.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/Log.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/dawn/DawnAsyncWait.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/graphite/dawn/DawnSharedContext.h"
15*c8dee2aaSAndroid Build Coastguard Worker
16*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::graphite {
17*c8dee2aaSAndroid Build Coastguard Worker namespace {
18*c8dee2aaSAndroid Build Coastguard Worker #if defined(__EMSCRIPTEN__)
is_map_succeeded(WGPUBufferMapAsyncStatus status)19*c8dee2aaSAndroid Build Coastguard Worker bool is_map_succeeded(WGPUBufferMapAsyncStatus status) {
20*c8dee2aaSAndroid Build Coastguard Worker return status == WGPUBufferMapAsyncStatus_Success;
21*c8dee2aaSAndroid Build Coastguard Worker }
22*c8dee2aaSAndroid Build Coastguard Worker
23*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]]
log_map_error(WGPUBufferMapAsyncStatus status,const char *)24*c8dee2aaSAndroid Build Coastguard Worker void log_map_error(WGPUBufferMapAsyncStatus status, const char*) {
25*c8dee2aaSAndroid Build Coastguard Worker const char* statusStr;
26*c8dee2aaSAndroid Build Coastguard Worker LogPriority priority = LogPriority::kError;
27*c8dee2aaSAndroid Build Coastguard Worker switch (status) {
28*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_ValidationError:
29*c8dee2aaSAndroid Build Coastguard Worker statusStr = "ValidationError";
30*c8dee2aaSAndroid Build Coastguard Worker break;
31*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_Unknown:
32*c8dee2aaSAndroid Build Coastguard Worker statusStr = "Unknown";
33*c8dee2aaSAndroid Build Coastguard Worker break;
34*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_DeviceLost:
35*c8dee2aaSAndroid Build Coastguard Worker statusStr = "DeviceLost";
36*c8dee2aaSAndroid Build Coastguard Worker break;
37*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_DestroyedBeforeCallback:
38*c8dee2aaSAndroid Build Coastguard Worker statusStr = "DestroyedBeforeCallback";
39*c8dee2aaSAndroid Build Coastguard Worker priority = LogPriority::kDebug;
40*c8dee2aaSAndroid Build Coastguard Worker break;
41*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_UnmappedBeforeCallback:
42*c8dee2aaSAndroid Build Coastguard Worker statusStr = "UnmappedBeforeCallback";
43*c8dee2aaSAndroid Build Coastguard Worker priority = LogPriority::kDebug;
44*c8dee2aaSAndroid Build Coastguard Worker break;
45*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_MappingAlreadyPending:
46*c8dee2aaSAndroid Build Coastguard Worker statusStr = "MappingAlreadyPending";
47*c8dee2aaSAndroid Build Coastguard Worker break;
48*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_OffsetOutOfRange:
49*c8dee2aaSAndroid Build Coastguard Worker statusStr = "OffsetOutOfRange";
50*c8dee2aaSAndroid Build Coastguard Worker break;
51*c8dee2aaSAndroid Build Coastguard Worker case WGPUBufferMapAsyncStatus_SizeOutOfRange:
52*c8dee2aaSAndroid Build Coastguard Worker statusStr = "SizeOutOfRange";
53*c8dee2aaSAndroid Build Coastguard Worker break;
54*c8dee2aaSAndroid Build Coastguard Worker default:
55*c8dee2aaSAndroid Build Coastguard Worker statusStr = "<other>";
56*c8dee2aaSAndroid Build Coastguard Worker break;
57*c8dee2aaSAndroid Build Coastguard Worker }
58*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG(priority, "Buffer async map failed with status %s.", statusStr);
59*c8dee2aaSAndroid Build Coastguard Worker }
60*c8dee2aaSAndroid Build Coastguard Worker
61*c8dee2aaSAndroid Build Coastguard Worker #else
62*c8dee2aaSAndroid Build Coastguard Worker
63*c8dee2aaSAndroid Build Coastguard Worker bool is_map_succeeded(wgpu::MapAsyncStatus status) {
64*c8dee2aaSAndroid Build Coastguard Worker return status == wgpu::MapAsyncStatus::Success;
65*c8dee2aaSAndroid Build Coastguard Worker }
66*c8dee2aaSAndroid Build Coastguard Worker
67*c8dee2aaSAndroid Build Coastguard Worker void log_map_error(wgpu::MapAsyncStatus status, wgpu::StringView message) {
68*c8dee2aaSAndroid Build Coastguard Worker const char* statusStr;
69*c8dee2aaSAndroid Build Coastguard Worker switch (status) {
70*c8dee2aaSAndroid Build Coastguard Worker case wgpu::MapAsyncStatus::InstanceDropped:
71*c8dee2aaSAndroid Build Coastguard Worker statusStr = "InstanceDropped";
72*c8dee2aaSAndroid Build Coastguard Worker break;
73*c8dee2aaSAndroid Build Coastguard Worker case wgpu::MapAsyncStatus::Error:
74*c8dee2aaSAndroid Build Coastguard Worker statusStr = "Error";
75*c8dee2aaSAndroid Build Coastguard Worker break;
76*c8dee2aaSAndroid Build Coastguard Worker case wgpu::MapAsyncStatus::Aborted:
77*c8dee2aaSAndroid Build Coastguard Worker statusStr = "Aborted";
78*c8dee2aaSAndroid Build Coastguard Worker break;
79*c8dee2aaSAndroid Build Coastguard Worker case wgpu::MapAsyncStatus::Unknown:
80*c8dee2aaSAndroid Build Coastguard Worker statusStr = "Unknown";
81*c8dee2aaSAndroid Build Coastguard Worker break;
82*c8dee2aaSAndroid Build Coastguard Worker case wgpu::MapAsyncStatus::Success:
83*c8dee2aaSAndroid Build Coastguard Worker SK_ABORT("This status is not an error");
84*c8dee2aaSAndroid Build Coastguard Worker break;
85*c8dee2aaSAndroid Build Coastguard Worker }
86*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG(LogPriority::kError,
87*c8dee2aaSAndroid Build Coastguard Worker "Buffer async map failed with status %s, message '%.*s'.",
88*c8dee2aaSAndroid Build Coastguard Worker statusStr,
89*c8dee2aaSAndroid Build Coastguard Worker static_cast<int>(message.length),
90*c8dee2aaSAndroid Build Coastguard Worker message.data);
91*c8dee2aaSAndroid Build Coastguard Worker }
92*c8dee2aaSAndroid Build Coastguard Worker #endif // defined(__EMSCRIPTEN__)
93*c8dee2aaSAndroid Build Coastguard Worker } // namespace
94*c8dee2aaSAndroid Build Coastguard Worker
Make(const DawnSharedContext * sharedContext,size_t size,BufferType type,AccessPattern accessPattern)95*c8dee2aaSAndroid Build Coastguard Worker sk_sp<DawnBuffer> DawnBuffer::Make(const DawnSharedContext* sharedContext,
96*c8dee2aaSAndroid Build Coastguard Worker size_t size,
97*c8dee2aaSAndroid Build Coastguard Worker BufferType type,
98*c8dee2aaSAndroid Build Coastguard Worker AccessPattern accessPattern) {
99*c8dee2aaSAndroid Build Coastguard Worker if (size <= 0) {
100*c8dee2aaSAndroid Build Coastguard Worker return nullptr;
101*c8dee2aaSAndroid Build Coastguard Worker }
102*c8dee2aaSAndroid Build Coastguard Worker
103*c8dee2aaSAndroid Build Coastguard Worker wgpu::BufferUsage usage = wgpu::BufferUsage::None;
104*c8dee2aaSAndroid Build Coastguard Worker
105*c8dee2aaSAndroid Build Coastguard Worker switch (type) {
106*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kVertex:
107*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::CopyDst;
108*c8dee2aaSAndroid Build Coastguard Worker break;
109*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kIndex:
110*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Index | wgpu::BufferUsage::CopyDst;
111*c8dee2aaSAndroid Build Coastguard Worker break;
112*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kXferCpuToGpu:
113*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::CopySrc | wgpu::BufferUsage::MapWrite;
114*c8dee2aaSAndroid Build Coastguard Worker break;
115*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kXferGpuToCpu:
116*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::CopyDst | wgpu::BufferUsage::MapRead;
117*c8dee2aaSAndroid Build Coastguard Worker break;
118*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kUniform:
119*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Uniform | wgpu::BufferUsage::CopyDst;
120*c8dee2aaSAndroid Build Coastguard Worker break;
121*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kStorage:
122*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Storage | wgpu::BufferUsage::CopyDst |
123*c8dee2aaSAndroid Build Coastguard Worker wgpu::BufferUsage::CopySrc;
124*c8dee2aaSAndroid Build Coastguard Worker break;
125*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kQuery:
126*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::QueryResolve | wgpu::BufferUsage::CopySrc;
127*c8dee2aaSAndroid Build Coastguard Worker break;
128*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kIndirect:
129*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Indirect | wgpu::BufferUsage::Storage |
130*c8dee2aaSAndroid Build Coastguard Worker wgpu::BufferUsage::CopyDst;
131*c8dee2aaSAndroid Build Coastguard Worker break;
132*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kVertexStorage:
133*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Vertex | wgpu::BufferUsage::Storage;
134*c8dee2aaSAndroid Build Coastguard Worker break;
135*c8dee2aaSAndroid Build Coastguard Worker case BufferType::kIndexStorage:
136*c8dee2aaSAndroid Build Coastguard Worker usage = wgpu::BufferUsage::Index | wgpu::BufferUsage::Storage;
137*c8dee2aaSAndroid Build Coastguard Worker break;
138*c8dee2aaSAndroid Build Coastguard Worker }
139*c8dee2aaSAndroid Build Coastguard Worker
140*c8dee2aaSAndroid Build Coastguard Worker if (sharedContext->caps()->drawBufferCanBeMapped() &&
141*c8dee2aaSAndroid Build Coastguard Worker accessPattern == AccessPattern::kHostVisible && type != BufferType::kXferGpuToCpu) {
142*c8dee2aaSAndroid Build Coastguard Worker if (type == BufferType::kQuery) {
143*c8dee2aaSAndroid Build Coastguard Worker // We can map the query buffer to get the results directly rather than having to copy to
144*c8dee2aaSAndroid Build Coastguard Worker // a transfer buffer.
145*c8dee2aaSAndroid Build Coastguard Worker usage |= wgpu::BufferUsage::MapRead;
146*c8dee2aaSAndroid Build Coastguard Worker } else {
147*c8dee2aaSAndroid Build Coastguard Worker // If the buffer is intended to be mappable, add MapWrite usage and remove
148*c8dee2aaSAndroid Build Coastguard Worker // CopyDst.
149*c8dee2aaSAndroid Build Coastguard Worker // We don't want to allow both CPU and GPU to write to the same buffer.
150*c8dee2aaSAndroid Build Coastguard Worker usage |= wgpu::BufferUsage::MapWrite;
151*c8dee2aaSAndroid Build Coastguard Worker usage &= ~wgpu::BufferUsage::CopyDst;
152*c8dee2aaSAndroid Build Coastguard Worker }
153*c8dee2aaSAndroid Build Coastguard Worker }
154*c8dee2aaSAndroid Build Coastguard Worker
155*c8dee2aaSAndroid Build Coastguard Worker wgpu::BufferDescriptor desc;
156*c8dee2aaSAndroid Build Coastguard Worker desc.usage = usage;
157*c8dee2aaSAndroid Build Coastguard Worker desc.size = size;
158*c8dee2aaSAndroid Build Coastguard Worker // Specifying mappedAtCreation avoids clearing the buffer on the GPU which can cause MapAsync to
159*c8dee2aaSAndroid Build Coastguard Worker // be very slow as it waits for GPU execution to complete.
160*c8dee2aaSAndroid Build Coastguard Worker desc.mappedAtCreation = SkToBool(usage & wgpu::BufferUsage::MapWrite);
161*c8dee2aaSAndroid Build Coastguard Worker
162*c8dee2aaSAndroid Build Coastguard Worker auto buffer = sharedContext->device().CreateBuffer(&desc);
163*c8dee2aaSAndroid Build Coastguard Worker if (!buffer) {
164*c8dee2aaSAndroid Build Coastguard Worker return {};
165*c8dee2aaSAndroid Build Coastguard Worker }
166*c8dee2aaSAndroid Build Coastguard Worker
167*c8dee2aaSAndroid Build Coastguard Worker void* mappedAtCreationPtr = nullptr;
168*c8dee2aaSAndroid Build Coastguard Worker if (desc.mappedAtCreation) {
169*c8dee2aaSAndroid Build Coastguard Worker mappedAtCreationPtr = buffer.GetMappedRange();
170*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(mappedAtCreationPtr);
171*c8dee2aaSAndroid Build Coastguard Worker }
172*c8dee2aaSAndroid Build Coastguard Worker
173*c8dee2aaSAndroid Build Coastguard Worker return sk_sp<DawnBuffer>(
174*c8dee2aaSAndroid Build Coastguard Worker new DawnBuffer(sharedContext, size, std::move(buffer), mappedAtCreationPtr));
175*c8dee2aaSAndroid Build Coastguard Worker }
176*c8dee2aaSAndroid Build Coastguard Worker
DawnBuffer(const DawnSharedContext * sharedContext,size_t size,wgpu::Buffer buffer,void * mappedAtCreationPtr)177*c8dee2aaSAndroid Build Coastguard Worker DawnBuffer::DawnBuffer(const DawnSharedContext* sharedContext,
178*c8dee2aaSAndroid Build Coastguard Worker size_t size,
179*c8dee2aaSAndroid Build Coastguard Worker wgpu::Buffer buffer,
180*c8dee2aaSAndroid Build Coastguard Worker void* mappedAtCreationPtr)
181*c8dee2aaSAndroid Build Coastguard Worker : Buffer(sharedContext,
182*c8dee2aaSAndroid Build Coastguard Worker size,
183*c8dee2aaSAndroid Build Coastguard Worker Protected::kNo, // Dawn doesn't support protected memory
184*c8dee2aaSAndroid Build Coastguard Worker /*commandBufferRefsAsUsageRefs=*/buffer.GetUsage() & wgpu::BufferUsage::MapWrite)
185*c8dee2aaSAndroid Build Coastguard Worker , fBuffer(std::move(buffer)) {
186*c8dee2aaSAndroid Build Coastguard Worker fMapPtr = mappedAtCreationPtr;
187*c8dee2aaSAndroid Build Coastguard Worker }
188*c8dee2aaSAndroid Build Coastguard Worker
189*c8dee2aaSAndroid Build Coastguard Worker #if defined(__EMSCRIPTEN__)
prepareForReturnToCache(const std::function<void ()> & takeRef)190*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::prepareForReturnToCache(const std::function<void()>& takeRef) {
191*c8dee2aaSAndroid Build Coastguard Worker // This function is only useful for Emscripten where we have to pre-map the buffer
192*c8dee2aaSAndroid Build Coastguard Worker // once it is returned to the cache.
193*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->sharedContext()->caps()->bufferMapsAreAsync());
194*c8dee2aaSAndroid Build Coastguard Worker
195*c8dee2aaSAndroid Build Coastguard Worker // This implementation is almost Dawn-agnostic. However, Buffer base class doesn't have any
196*c8dee2aaSAndroid Build Coastguard Worker // way of distinguishing a buffer that is mappable for writing from one mappable for reading.
197*c8dee2aaSAndroid Build Coastguard Worker // We only need to re-map the former.
198*c8dee2aaSAndroid Build Coastguard Worker if (!(fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite)) {
199*c8dee2aaSAndroid Build Coastguard Worker return;
200*c8dee2aaSAndroid Build Coastguard Worker }
201*c8dee2aaSAndroid Build Coastguard Worker // We cannot start an async map while the GPU is still using the buffer. We asked that
202*c8dee2aaSAndroid Build Coastguard Worker // our Resource convert command buffer refs to usage refs. So we should never have any
203*c8dee2aaSAndroid Build Coastguard Worker // command buffer refs.
204*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->debugHasCommandBufferRef());
205*c8dee2aaSAndroid Build Coastguard Worker // Note that the map state cannot change on another thread when we are here. We got here
206*c8dee2aaSAndroid Build Coastguard Worker // because there were no UsageRefs on the buffer but async mapping holds a UsageRef until it
207*c8dee2aaSAndroid Build Coastguard Worker // completes.
208*c8dee2aaSAndroid Build Coastguard Worker if (this->isMapped()) {
209*c8dee2aaSAndroid Build Coastguard Worker return;
210*c8dee2aaSAndroid Build Coastguard Worker }
211*c8dee2aaSAndroid Build Coastguard Worker takeRef();
212*c8dee2aaSAndroid Build Coastguard Worker this->asyncMap([](void* ctx, skgpu::CallbackResult result) {
213*c8dee2aaSAndroid Build Coastguard Worker sk_sp<DawnBuffer> buffer(static_cast<DawnBuffer*>(ctx));
214*c8dee2aaSAndroid Build Coastguard Worker if (result != skgpu::CallbackResult::kSuccess) {
215*c8dee2aaSAndroid Build Coastguard Worker buffer->setDeleteASAP();
216*c8dee2aaSAndroid Build Coastguard Worker }
217*c8dee2aaSAndroid Build Coastguard Worker },
218*c8dee2aaSAndroid Build Coastguard Worker this);
219*c8dee2aaSAndroid Build Coastguard Worker }
220*c8dee2aaSAndroid Build Coastguard Worker
onAsyncMap(GpuFinishedProc proc,GpuFinishedContext ctx)221*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::onAsyncMap(GpuFinishedProc proc, GpuFinishedContext ctx) {
222*c8dee2aaSAndroid Build Coastguard Worker // This function is only useful for Emscripten where we have to use asyncMap().
223*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->sharedContext()->caps()->bufferMapsAreAsync());
224*c8dee2aaSAndroid Build Coastguard Worker
225*c8dee2aaSAndroid Build Coastguard Worker if (proc) {
226*c8dee2aaSAndroid Build Coastguard Worker SkAutoMutexExclusive ex(fAsyncMutex);
227*c8dee2aaSAndroid Build Coastguard Worker if (this->isMapped()) {
228*c8dee2aaSAndroid Build Coastguard Worker proc(ctx, CallbackResult::kSuccess);
229*c8dee2aaSAndroid Build Coastguard Worker return;
230*c8dee2aaSAndroid Build Coastguard Worker }
231*c8dee2aaSAndroid Build Coastguard Worker fAsyncMapCallbacks.push_back(RefCntedCallback::Make(proc, ctx));
232*c8dee2aaSAndroid Build Coastguard Worker }
233*c8dee2aaSAndroid Build Coastguard Worker if (this->isUnmappable()) {
234*c8dee2aaSAndroid Build Coastguard Worker return;
235*c8dee2aaSAndroid Build Coastguard Worker }
236*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fBuffer);
237*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((fBuffer.GetUsage() & wgpu::BufferUsage::MapRead) ||
238*c8dee2aaSAndroid Build Coastguard Worker (fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite));
239*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fBuffer.GetMapState() == wgpu::BufferMapState::Unmapped);
240*c8dee2aaSAndroid Build Coastguard Worker bool isWrite = fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite;
241*c8dee2aaSAndroid Build Coastguard Worker auto buffer = sk_ref_sp(this);
242*c8dee2aaSAndroid Build Coastguard Worker
243*c8dee2aaSAndroid Build Coastguard Worker fBuffer.MapAsync(
244*c8dee2aaSAndroid Build Coastguard Worker isWrite ? wgpu::MapMode::Write : wgpu::MapMode::Read,
245*c8dee2aaSAndroid Build Coastguard Worker 0,
246*c8dee2aaSAndroid Build Coastguard Worker fBuffer.GetSize(),
247*c8dee2aaSAndroid Build Coastguard Worker [](WGPUBufferMapAsyncStatus s, void* userData) {
248*c8dee2aaSAndroid Build Coastguard Worker sk_sp<DawnBuffer> buffer(static_cast<DawnBuffer*>(userData));
249*c8dee2aaSAndroid Build Coastguard Worker buffer->mapCallback(s, /*message=*/nullptr);
250*c8dee2aaSAndroid Build Coastguard Worker },
251*c8dee2aaSAndroid Build Coastguard Worker buffer.release());
252*c8dee2aaSAndroid Build Coastguard Worker }
253*c8dee2aaSAndroid Build Coastguard Worker
onMap()254*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::onMap() {
255*c8dee2aaSAndroid Build Coastguard Worker SKGPU_LOG_W("Synchronous buffer mapping not supported in Dawn. Failing map request.");
256*c8dee2aaSAndroid Build Coastguard Worker }
257*c8dee2aaSAndroid Build Coastguard Worker
258*c8dee2aaSAndroid Build Coastguard Worker #else
259*c8dee2aaSAndroid Build Coastguard Worker
onMap()260*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::onMap() {
261*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(!this->sharedContext()->caps()->bufferMapsAreAsync());
262*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fBuffer);
263*c8dee2aaSAndroid Build Coastguard Worker SkASSERT((fBuffer.GetUsage() & wgpu::BufferUsage::MapRead) ||
264*c8dee2aaSAndroid Build Coastguard Worker (fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite));
265*c8dee2aaSAndroid Build Coastguard Worker bool isWrite = fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite;
266*c8dee2aaSAndroid Build Coastguard Worker
267*c8dee2aaSAndroid Build Coastguard Worker // Use wgpu::Future and WaitAny with timeout=0 to trigger callback immediately.
268*c8dee2aaSAndroid Build Coastguard Worker // This should work because our resource tracking mechanism should make sure that
269*c8dee2aaSAndroid Build Coastguard Worker // the buffer is free of any GPU use at this point.
270*c8dee2aaSAndroid Build Coastguard Worker wgpu::FutureWaitInfo mapWaitInfo{};
271*c8dee2aaSAndroid Build Coastguard Worker
272*c8dee2aaSAndroid Build Coastguard Worker mapWaitInfo.future = fBuffer.MapAsync(
273*c8dee2aaSAndroid Build Coastguard Worker isWrite ? wgpu::MapMode::Write : wgpu::MapMode::Read,
274*c8dee2aaSAndroid Build Coastguard Worker 0,
275*c8dee2aaSAndroid Build Coastguard Worker fBuffer.GetSize(),
276*c8dee2aaSAndroid Build Coastguard Worker wgpu::CallbackMode::WaitAnyOnly,
277*c8dee2aaSAndroid Build Coastguard Worker [this](wgpu::MapAsyncStatus s, wgpu::StringView m) { this->mapCallback(s, m); });
278*c8dee2aaSAndroid Build Coastguard Worker
279*c8dee2aaSAndroid Build Coastguard Worker wgpu::Device device = static_cast<const DawnSharedContext*>(sharedContext())->device();
280*c8dee2aaSAndroid Build Coastguard Worker wgpu::Instance instance = device.GetAdapter().GetInstance();
281*c8dee2aaSAndroid Build Coastguard Worker [[maybe_unused]] auto status = instance.WaitAny(1, &mapWaitInfo, /*timeoutNS=*/0);
282*c8dee2aaSAndroid Build Coastguard Worker
283*c8dee2aaSAndroid Build Coastguard Worker if (status != wgpu::WaitStatus::Success) {
284*c8dee2aaSAndroid Build Coastguard Worker // WaitAny(timeout=0) might fail in this scenario:
285*c8dee2aaSAndroid Build Coastguard Worker // - Allocates a buffer.
286*c8dee2aaSAndroid Build Coastguard Worker // - Encodes a command buffer to copy a texture to this buffer.
287*c8dee2aaSAndroid Build Coastguard Worker // - Submits the command buffer. If OOM happens, this command buffer will fail to
288*c8dee2aaSAndroid Build Coastguard Worker // be submitted.
289*c8dee2aaSAndroid Build Coastguard Worker // - The buffer is *supposed* to be free of any GPU use since the command buffer that would
290*c8dee2aaSAndroid Build Coastguard Worker // have used it wasn't submitted successfully.
291*c8dee2aaSAndroid Build Coastguard Worker // - If we try to map this buffer at this point, internally Dawn will try to use GPU to
292*c8dee2aaSAndroid Build Coastguard Worker // clear this buffer to zeros, since this is its 1st use. WaitAny(timeout=0) won't work
293*c8dee2aaSAndroid Build Coastguard Worker // since the buffer now has a pending GPU clear operation.
294*c8dee2aaSAndroid Build Coastguard Worker //
295*c8dee2aaSAndroid Build Coastguard Worker // To work around this, we need to try again with a blocking WaitAny(), to wait for the
296*c8dee2aaSAndroid Build Coastguard Worker // clear operation to finish.
297*c8dee2aaSAndroid Build Coastguard Worker // Notes:
298*c8dee2aaSAndroid Build Coastguard Worker // - This fallback should be rare since it is caused by an OOM error during buffer
299*c8dee2aaSAndroid Build Coastguard Worker // readbacks.
300*c8dee2aaSAndroid Build Coastguard Worker // - For buffer writing cases, since we use mappedAtCreation, the GPU clear won't happen.
301*c8dee2aaSAndroid Build Coastguard Worker status = instance.WaitAny(
302*c8dee2aaSAndroid Build Coastguard Worker 1, &mapWaitInfo, /*timeoutNS=*/std::numeric_limits<uint64_t>::max());
303*c8dee2aaSAndroid Build Coastguard Worker }
304*c8dee2aaSAndroid Build Coastguard Worker
305*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(status == wgpu::WaitStatus::Success);
306*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(mapWaitInfo.completed);
307*c8dee2aaSAndroid Build Coastguard Worker }
308*c8dee2aaSAndroid Build Coastguard Worker #endif // defined(__EMSCRIPTEN__)
309*c8dee2aaSAndroid Build Coastguard Worker
onUnmap()310*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::onUnmap() {
311*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(fBuffer);
312*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(this->isUnmappable());
313*c8dee2aaSAndroid Build Coastguard Worker
314*c8dee2aaSAndroid Build Coastguard Worker fMapPtr = nullptr;
315*c8dee2aaSAndroid Build Coastguard Worker fBuffer.Unmap();
316*c8dee2aaSAndroid Build Coastguard Worker }
317*c8dee2aaSAndroid Build Coastguard Worker
318*c8dee2aaSAndroid Build Coastguard Worker template <typename StatusT, typename MessageT>
mapCallback(StatusT status,MessageT message)319*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::mapCallback(StatusT status, MessageT message) {
320*c8dee2aaSAndroid Build Coastguard Worker SkAutoMutexExclusive em(this->fAsyncMutex);
321*c8dee2aaSAndroid Build Coastguard Worker if (is_map_succeeded(status)) {
322*c8dee2aaSAndroid Build Coastguard Worker if (this->fBuffer.GetUsage() & wgpu::BufferUsage::MapWrite) {
323*c8dee2aaSAndroid Build Coastguard Worker this->fMapPtr = this->fBuffer.GetMappedRange();
324*c8dee2aaSAndroid Build Coastguard Worker } else {
325*c8dee2aaSAndroid Build Coastguard Worker // If buffer is only created with MapRead usage, Dawn only allows returning
326*c8dee2aaSAndroid Build Coastguard Worker // constant pointer. We need to use const_cast as a workaround here.
327*c8dee2aaSAndroid Build Coastguard Worker this->fMapPtr = const_cast<void*>(this->fBuffer.GetConstMappedRange());
328*c8dee2aaSAndroid Build Coastguard Worker }
329*c8dee2aaSAndroid Build Coastguard Worker } else {
330*c8dee2aaSAndroid Build Coastguard Worker log_map_error(status, message);
331*c8dee2aaSAndroid Build Coastguard Worker for (auto& cb : this->fAsyncMapCallbacks) {
332*c8dee2aaSAndroid Build Coastguard Worker cb->setFailureResult();
333*c8dee2aaSAndroid Build Coastguard Worker }
334*c8dee2aaSAndroid Build Coastguard Worker }
335*c8dee2aaSAndroid Build Coastguard Worker this->fAsyncMapCallbacks.clear();
336*c8dee2aaSAndroid Build Coastguard Worker }
337*c8dee2aaSAndroid Build Coastguard Worker
isUnmappable() const338*c8dee2aaSAndroid Build Coastguard Worker bool DawnBuffer::isUnmappable() const {
339*c8dee2aaSAndroid Build Coastguard Worker return fBuffer.GetMapState() != wgpu::BufferMapState::Unmapped;
340*c8dee2aaSAndroid Build Coastguard Worker }
341*c8dee2aaSAndroid Build Coastguard Worker
freeGpuData()342*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::freeGpuData() {
343*c8dee2aaSAndroid Build Coastguard Worker if (fBuffer) {
344*c8dee2aaSAndroid Build Coastguard Worker // Explicitly destroy the buffer since it might be ref'd by cached bind groups which are
345*c8dee2aaSAndroid Build Coastguard Worker // not immediately cleaned up. Graphite should already guarantee that all command buffers
346*c8dee2aaSAndroid Build Coastguard Worker // using this buffer (indirectly via BindGroups) are already completed.
347*c8dee2aaSAndroid Build Coastguard Worker fBuffer.Destroy();
348*c8dee2aaSAndroid Build Coastguard Worker fBuffer = nullptr;
349*c8dee2aaSAndroid Build Coastguard Worker }
350*c8dee2aaSAndroid Build Coastguard Worker }
351*c8dee2aaSAndroid Build Coastguard Worker
setBackendLabel(char const * label)352*c8dee2aaSAndroid Build Coastguard Worker void DawnBuffer::setBackendLabel(char const* label) {
353*c8dee2aaSAndroid Build Coastguard Worker SkASSERT(label);
354*c8dee2aaSAndroid Build Coastguard Worker if (sharedContext()->caps()->setBackendLabels()) {
355*c8dee2aaSAndroid Build Coastguard Worker fBuffer.SetLabel(label);
356*c8dee2aaSAndroid Build Coastguard Worker }
357*c8dee2aaSAndroid Build Coastguard Worker }
358*c8dee2aaSAndroid Build Coastguard Worker
359*c8dee2aaSAndroid Build Coastguard Worker } // namespace skgpu::graphite
360