@@ -112,12 +112,18 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
112112 return sk_sp<GrGpu>(new GrMtlGpu (direct, options, device, queue, featureSet));
113113}
114114
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+
115121GrMtlGpu::GrMtlGpu (GrDirectContext* direct, const GrContextOptions& options,
116122 id <MTLDevice > device, id <MTLCommandQueue > queue, MTLFeatureSet featureSet)
117123 : INHERITED(direct)
118124 , fDevice(device)
119125 , fQueue(queue)
120- , fCmdBuffer( nullptr )
126+ , fOutstandingCommandBuffers( sizeof (OutstandingCommandBuffer), kDefaultOutstandingAllocCnt )
121127 , fCompiler(new SkSL::Compiler())
122128 , fResourceProvider(this )
123129 , fDisconnected(false )
@@ -135,24 +141,25 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
135141void GrMtlGpu::disconnect (DisconnectType type) {
136142 INHERITED::disconnect (type);
137143
138- if (DisconnectType:: kCleanup == type ) {
144+ if (! fDisconnected ) {
139145 this ->destroyResources ();
140- } else {
141- delete fCmdBuffer ;
142- fCmdBuffer = nullptr ;
143-
144- fResourceProvider .destroyResources ();
145-
146- fQueue = nil ;
147- fDevice = nil ;
148-
149146 fDisconnected = true ;
150147 }
151148}
152149
153150void GrMtlGpu::destroyResources () {
154- // Will implicitly delete the command buffer
155151 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+
156163 fResourceProvider .destroyResources ();
157164
158165 fQueue = nil ;
@@ -175,18 +182,44 @@ static bool get_feature_set(id<MTLDevice> device, MTLFeatureSet* featureSet) {
175182}
176183
177184GrMtlCommandBuffer* 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 ();
180192 }
181- return fCmdBuffer ;
193+ return fCurrentCmdBuffer . get () ;
182194}
183195
184196void 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 ();
190223 }
191224}
192225
@@ -1254,7 +1287,6 @@ static int get_surface_sample_cnt(GrSurface* surf) {
12541287 }
12551288
12561289 GrMtlBuffer* grMtlBuffer = static_cast <GrMtlBuffer*>(transferBuffer);
1257- grMtlBuffer->bind ();
12581290
12591291 size_t transBufferRowBytes = bpp * width;
12601292 size_t transBufferImageBytes = transBufferRowBytes * height;
0 commit comments