@@ -112,12 +112,18 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
112
112
return sk_sp<GrGpu>(new GrMtlGpu (direct, options, device, queue, featureSet));
113
113
}
114
114
115
+ // This constant determines how many OutstandingCommandBuffers are allocated together as a block in
116
+ // the deque. As such it needs to balance allocating too much memory vs. incurring
117
+ // allocation/deallocation thrashing. It should roughly correspond to the max number of outstanding
118
+ // command buffers we expect to see.
119
+ static const int kDefaultOutstandingAllocCnt = 8 ;
120
+
115
121
GrMtlGpu::GrMtlGpu (GrDirectContext* direct, const GrContextOptions& options,
116
122
id <MTLDevice > device, id <MTLCommandQueue > queue, MTLFeatureSet featureSet)
117
123
: INHERITED(direct)
118
124
, fDevice(device)
119
125
, fQueue(queue)
120
- , fCmdBuffer( nullptr )
126
+ , fOutstandingCommandBuffers( sizeof (OutstandingCommandBuffer), kDefaultOutstandingAllocCnt )
121
127
, fCompiler(new SkSL::Compiler())
122
128
, fResourceProvider(this )
123
129
, fDisconnected(false )
@@ -135,24 +141,25 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
135
141
void GrMtlGpu::disconnect (DisconnectType type) {
136
142
INHERITED::disconnect (type);
137
143
138
- if (DisconnectType:: kCleanup == type ) {
144
+ if (! fDisconnected ) {
139
145
this ->destroyResources ();
140
- } else {
141
- delete fCmdBuffer ;
142
- fCmdBuffer = nullptr ;
143
-
144
- fResourceProvider .destroyResources ();
145
-
146
- fQueue = nil ;
147
- fDevice = nil ;
148
-
149
146
fDisconnected = true ;
150
147
}
151
148
}
152
149
153
150
void GrMtlGpu::destroyResources () {
154
- // Will implicitly delete the command buffer
155
151
this ->submitCommandBuffer (SyncQueue::kForce_SyncQueue );
152
+
153
+ // We used a placement new for each object in fOutstandingCommandBuffers, so we're responsible
154
+ // for calling the destructor on each of them as well.
155
+ while (!fOutstandingCommandBuffers .empty ()) {
156
+ OutstandingCommandBuffer* buffer =
157
+ (OutstandingCommandBuffer*)fOutstandingCommandBuffers .front ();
158
+ this ->deleteFence (buffer->fFence );
159
+ buffer->~OutstandingCommandBuffer ();
160
+ fOutstandingCommandBuffers .pop_front ();
161
+ }
162
+
156
163
fResourceProvider .destroyResources ();
157
164
158
165
fQueue = nil ;
@@ -175,18 +182,44 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
175
182
}
176
183
177
184
GrMtlCommandBuffer* GrMtlGpu::commandBuffer () {
178
- if (!fCmdBuffer ) {
179
- fCmdBuffer = GrMtlCommandBuffer::Create (fQueue );
185
+ if (!fCurrentCmdBuffer ) {
186
+ fCurrentCmdBuffer = GrMtlCommandBuffer::Make (fQueue );
187
+
188
+ // This should be done after we have a new command buffer in case the freeing of any
189
+ // resources held by a finished command buffer causes us to send a new command to the gpu
190
+ // (like changing the resource state).
191
+ this ->checkForFinishedCommandBuffers ();
180
192
}
181
- return fCmdBuffer ;
193
+ return fCurrentCmdBuffer . get () ;
182
194
}
183
195
184
196
void GrMtlGpu::submitCommandBuffer (SyncQueue sync) {
185
- if (fCmdBuffer ) {
186
- fResourceProvider .addBufferCompletionHandler (fCmdBuffer );
187
- fCmdBuffer ->commit (SyncQueue::kForce_SyncQueue == sync);
188
- delete fCmdBuffer ;
189
- fCmdBuffer = nullptr ;
197
+ // TODO: handle sync with empty command buffer
198
+ if (fCurrentCmdBuffer ) {
199
+ fResourceProvider .addBufferCompletionHandler (fCurrentCmdBuffer .get ());
200
+
201
+ GrFence fence = this ->insertFence ();
202
+ new (fOutstandingCommandBuffers .push_back ()) OutstandingCommandBuffer (
203
+ fCurrentCmdBuffer , fence);
204
+
205
+ fCurrentCmdBuffer ->commit (SyncQueue::kForce_SyncQueue == sync);
206
+ fCurrentCmdBuffer .reset ();
207
+ }
208
+ }
209
+
210
+ void GrMtlGpu::checkForFinishedCommandBuffers () {
211
+ // Iterate over all the outstanding command buffers to see if any have finished. The command
212
+ // buffers are in order from oldest to newest, so we start at the front to check if their fence
213
+ // has signaled. If so we pop it off and move onto the next.
214
+ // Repeat till we find a command list that has not finished yet (and all others afterwards are
215
+ // also guaranteed to not have finished).
216
+ OutstandingCommandBuffer* front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers .front ();
217
+ while (front && this ->waitFence (front->fFence )) {
218
+ // Since we used placement new we are responsible for calling the destructor manually.
219
+ this ->deleteFence (front->fFence );
220
+ front->~OutstandingCommandBuffer ();
221
+ fOutstandingCommandBuffers .pop_front ();
222
+ front = (OutstandingCommandBuffer*)fOutstandingCommandBuffers .front ();
190
223
}
191
224
}
192
225
@@ -1254,7 +1287,6 @@ static int get_surface_sample_cnt(GrSurface* surf) {
1254
1287
}
1255
1288
1256
1289
GrMtlBuffer* grMtlBuffer = static_cast <GrMtlBuffer*>(transferBuffer);
1257
- grMtlBuffer->bind ();
1258
1290
1259
1291
size_t transBufferRowBytes = bpp * width;
1260
1292
size_t transBufferImageBytes = transBufferRowBytes * height;
0 commit comments