summaryrefslogtreecommitdiffstats
path: root/gpu/command_buffer/service/sync_point_manager.h
blob: eea2dfc8d71f77fbe50573e8a71b2d06a2a2f4bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_
#define GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_

#include <stdint.h>

#include <functional>
#include <queue>
#include <unordered_map>
#include <vector>

#include "base/atomic_sequence_num.h"
#include "base/callback.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/condition_variable.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_checker.h"
#include "gpu/command_buffer/common/command_buffer_id.h"
#include "gpu/command_buffer/common/constants.h"
#include "gpu/gpu_export.h"

namespace base {
class SingleThreadTaskRunner;
}  // namespace base

namespace gpu {

class SyncPointClient;
class SyncPointClientState;
class SyncPointManager;

class GPU_EXPORT SyncPointOrderData
    : public base::RefCountedThreadSafe<SyncPointOrderData> {
 public:
  static scoped_refptr<SyncPointOrderData> Create();
  void Destroy();

  uint32_t GenerateUnprocessedOrderNumber(SyncPointManager* sync_point_manager);
  void BeginProcessingOrderNumber(uint32_t order_num);
  void PauseProcessingOrderNumber(uint32_t order_num);
  void FinishProcessingOrderNumber(uint32_t order_num);

  uint32_t processed_order_num() const {
    base::AutoLock auto_lock(lock_);
    return processed_order_num_;
  }

  uint32_t unprocessed_order_num() const {
    base::AutoLock auto_lock(lock_);
    return unprocessed_order_num_;
  }

  uint32_t current_order_num() const {
    DCHECK(processing_thread_checker_.CalledOnValidThread());
    return current_order_num_;
  }

  bool IsProcessingOrderNumber() {
    DCHECK(processing_thread_checker_.CalledOnValidThread());
    return !paused_ && current_order_num_ > processed_order_num();
  }

 private:
  friend class base::RefCountedThreadSafe<SyncPointOrderData>;
  friend class SyncPointClientState;

  struct OrderFence {
    uint32_t order_num;
    uint64_t fence_release;
    scoped_refptr<SyncPointClientState> client_state;

    OrderFence(uint32_t order,
               uint64_t release,
               scoped_refptr<SyncPointClientState> state);
    ~OrderFence();

    bool operator>(const OrderFence& rhs) const {
      return (order_num > rhs.order_num) ||
             ((order_num == rhs.order_num) &&
              (fence_release > rhs.fence_release));
    }
  };
  typedef std::priority_queue<OrderFence,
                              std::vector<OrderFence>,
                              std::greater<OrderFence>> OrderFenceQueue;

  SyncPointOrderData();
  ~SyncPointOrderData();

  bool ValidateReleaseOrderNumber(
      scoped_refptr<SyncPointClientState> client_state,
      uint32_t wait_order_num,
      uint64_t fence_release);

  // Non thread-safe functions need to be called from a single thread.
  base::ThreadChecker processing_thread_checker_;

  // Current IPC order number being processed (only used on processing thread).
  uint32_t current_order_num_;

  // Whether or not the current order number is being processed or paused.
  bool paused_;

  // This lock protects destroyed_, processed_order_num_,
  // unprocessed_order_num_, and order_fence_queue_. All order numbers (n) in
  // order_fence_queue_ must follow the invariant:
  //   processed_order_num_ < n <= unprocessed_order_num_.
  mutable base::Lock lock_;

  bool destroyed_;

  // Last finished IPC order number.
  uint32_t processed_order_num_;

  // Unprocessed order number expected to be processed under normal execution.
  uint32_t unprocessed_order_num_;

  // In situations where we are waiting on fence syncs that do not exist, we
  // validate by making sure the order number does not pass the order number
  // which the wait command was issued. If the order number reaches the
  // wait command's, we should automatically release up to the expected
  // release count. Note that this also releases other lower release counts,
  // so a single misbehaved fence sync is enough to invalidate/signal all
  // previous fence syncs.
  OrderFenceQueue order_fence_queue_;

  DISALLOW_COPY_AND_ASSIGN(SyncPointOrderData);
};

class GPU_EXPORT SyncPointClientState
    : public base::RefCountedThreadSafe<SyncPointClientState> {
 public:
  scoped_refptr<SyncPointOrderData> order_data() { return order_data_; }

  bool IsFenceSyncReleased(uint64_t release) {
    return release <= fence_sync_release();
  }

  uint64_t fence_sync_release() {
    base::AutoLock auto_lock(fence_sync_lock_);
    return fence_sync_release_;
  }

 private:
  friend class base::RefCountedThreadSafe<SyncPointClientState>;
  friend class SyncPointClient;
  friend class SyncPointOrderData;

  struct ReleaseCallback {
    uint64_t release_count;
    base::Closure callback_closure;

    ReleaseCallback(uint64_t release, const base::Closure& callback);
    ~ReleaseCallback();

    bool operator>(const ReleaseCallback& rhs) const {
      return release_count > rhs.release_count;
    }
  };
  typedef std::priority_queue<ReleaseCallback,
                              std::vector<ReleaseCallback>,
                              std::greater<ReleaseCallback>>
      ReleaseCallbackQueue;

  SyncPointClientState(scoped_refptr<SyncPointOrderData> order_data);
  ~SyncPointClientState();

  // Queues the callback to be called if the release is valid. If the release
  // is invalid this function will return False and the callback will never
  // be called.
  bool WaitForRelease(uint32_t wait_order_num,
                      uint64_t release,
                      const base::Closure& callback);

  void ReleaseFenceSync(uint64_t release);
  void EnsureReleased(uint64_t release);
  void ReleaseFenceSyncLocked(uint64_t release,
                              std::vector<base::Closure>* callback_list);

  // Global order data where releases will originate from.
  scoped_refptr<SyncPointOrderData> order_data_;

  // Protects fence_sync_release_, fence_callback_queue_.
  base::Lock fence_sync_lock_;

  // Current fence sync release that has been signaled.
  uint64_t fence_sync_release_;

  // In well defined fence sync operations, fence syncs are released in order
  // so simply having a priority queue for callbacks is enough.
  ReleaseCallbackQueue release_callback_queue_;

  DISALLOW_COPY_AND_ASSIGN(SyncPointClientState);
};

class GPU_EXPORT SyncPointClient {
 public:
  ~SyncPointClient();

  scoped_refptr<SyncPointClientState> client_state() { return client_state_; }

  // Wait for a release count to be reached on a SyncPointClientState. If this
  // function returns false, that means the wait was invalid. Otherwise if it
  // returns True it means the release was valid. In the case where the release
  // is valid but has happened already, it will still return true. In all cases
  // wait_complete_callback will be called eventually. The callback function
  // may be called on another thread so it should be thread-safe. For
  // convenience, another non-threadsafe version is defined below where you
  // can supply a task runner.
  bool Wait(SyncPointClientState* release_state,
            uint64_t release_count,
            const base::Closure& wait_complete_callback);

  bool WaitNonThreadSafe(SyncPointClientState* release_state,
                         uint64_t release_count,
                         scoped_refptr<base::SingleThreadTaskRunner> runner,
                         const base::Closure& wait_complete_callback);

  // Unordered waits are waits which do not occur within the global order number
  // processing order (IE. Not between the corresponding
  // SyncPointOrderData::BeginProcessingOrderNumber() and
  // SyncPointOrderData::FinishProcessingOrderNumber() calls). Because fence
  // sync releases must occur within a corresponding order number, these waits
  // cannot deadlock because they can never depend on any fence sync releases.
  // This is useful for IPC messages that may be processed out of order with
  // respect to regular command buffer processing.
  bool WaitOutOfOrder(SyncPointClientState* release_state,
                      uint64_t release_count,
                      const base::Closure& wait_complete_callback);

  bool WaitOutOfOrderNonThreadSafe(
      SyncPointClientState* release_state,
      uint64_t release_count,
      scoped_refptr<base::SingleThreadTaskRunner> runner,
      const base::Closure& wait_complete_callback);

  void ReleaseFenceSync(uint64_t release);

 private:
  friend class SyncPointManager;

  SyncPointClient();
  SyncPointClient(SyncPointManager* sync_point_manager,
                  scoped_refptr<SyncPointOrderData> order_data,
                  CommandBufferNamespace namespace_id,
                  CommandBufferId client_id);

  // Sync point manager is guaranteed to exist in the lifetime of the client.
  SyncPointManager* sync_point_manager_;

  // Keep the state that is sharable across multiple threads.
  scoped_refptr<SyncPointClientState> client_state_;

  // Unique namespace/client id pair for this sync point client.
  const CommandBufferNamespace namespace_id_;
  const CommandBufferId client_id_;

  DISALLOW_COPY_AND_ASSIGN(SyncPointClient);
};

// This class manages the sync points, which allow cross-channel
// synchronization.
class GPU_EXPORT SyncPointManager {
 public:
  explicit SyncPointManager(bool allow_threaded_wait);
  ~SyncPointManager();

  // Creates/Destroy a sync point client which message processors should hold.
  scoped_ptr<SyncPointClient> CreateSyncPointClient(
      scoped_refptr<SyncPointOrderData> order_data,
      CommandBufferNamespace namespace_id,
      CommandBufferId client_id);

  // Creates a sync point client which cannot process order numbers but can only
  // Wait out of order.
  scoped_ptr<SyncPointClient> CreateSyncPointClientWaiter();

  // Finds the state of an already created sync point client.
  scoped_refptr<SyncPointClientState> GetSyncPointClientState(
      CommandBufferNamespace namespace_id,
      CommandBufferId client_id);

 private:
  friend class SyncPointClient;
  friend class SyncPointOrderData;

  using ClientMap = std::unordered_map<CommandBufferId,
                                       SyncPointClient*,
                                       CommandBufferId::Hasher>;

  uint32_t GenerateOrderNumber();
  void DestroySyncPointClient(CommandBufferNamespace namespace_id,
                              CommandBufferId client_id);

  // Order number is global for all clients.
  base::AtomicSequenceNumber global_order_num_;

  // Client map holds a map of clients id to client for each namespace.
  base::Lock client_maps_lock_;
  ClientMap client_maps_[NUM_COMMAND_BUFFER_NAMESPACES];

  DISALLOW_COPY_AND_ASSIGN(SyncPointManager);
};

}  // namespace gpu

#endif  // GPU_COMMAND_BUFFER_SERVICE_SYNC_POINT_MANAGER_H_