xref: /aosp_15_r20/external/skia/src/gpu/graphite/dawn/DawnBuffer.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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