1 // Copyright (c) 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "gpu/command_buffer/service/path_manager.h"
9 #include "base/logging.h"
10 #include "gpu/command_buffer/common/gles2_cmd_utils.h"
11 #include "ui/gl/gl_bindings.h"
17 void CallDeletePaths(GLuint first_id
, GLuint range
) {
20 if (range
> static_cast<GLuint
>(std::numeric_limits
<GLsizei
>::max()))
21 irange
= std::numeric_limits
<GLsizei
>::max();
23 irange
= static_cast<GLsizei
>(range
);
25 glDeletePathsNV(first_id
, irange
);
31 template <typename RangeIterator
>
32 static GLuint
RangeSize(const RangeIterator
& it
) {
33 return it
->second
.last_client_id
- it
->first
+ 1;
35 template <typename RangeIterator
>
36 static GLuint
FirstClientId(const RangeIterator
& it
) {
39 template <typename RangeIterator
>
40 static GLuint
FirstServiceId(const RangeIterator
& it
) {
41 return it
->second
.first_service_id
;
43 template <typename RangeIterator
>
44 static GLuint
LastServiceId(const RangeIterator
& it
) {
45 return FirstServiceId(it
) + RangeSize(it
) - 1;
47 static GLuint
LastClientId(PathManager::PathRangeMap::const_iterator
& it
) {
48 return it
->second
.last_client_id
;
50 // Note: this one can be assigned to.
51 static GLuint
& LastClientId(PathManager::PathRangeMap::iterator
& it
) {
52 return it
->second
.last_client_id
;
56 struct IteratorSelector
{
57 typedef typename
T::iterator iterator
;
60 struct IteratorSelector
<const T
> {
61 typedef typename
T::const_iterator iterator
;
64 // Returns the range position that contains |client_id| or
65 // |PathRangeMap::iterator::end()| if |client_id| is not found.
66 template <typename MapType
>
67 static typename IteratorSelector
<MapType
>::iterator
GetContainingRange(
70 auto it
= path_map
.lower_bound(client_id
);
71 if (it
!= path_map
.end() && FirstClientId(it
) == client_id
)
73 if (it
!= path_map
.begin()) {
75 if (LastClientId(it
) >= client_id
)
78 return path_map
.end();
81 // Returns the range position that contains |client_id|. If that is
82 // not available, returns the range that has smallest
83 // |first_client_id| that is bigger than |client_id|. Returns
84 // |PathRangeMap::iterator::end()| if there is no such range.
85 template <typename MapType
>
86 static typename IteratorSelector
<MapType
>::iterator
GetContainingOrNextRange(
89 auto it
= path_map
.lower_bound(client_id
);
90 if (it
!= path_map
.end() && FirstClientId(it
) == client_id
) {
93 if (it
!= path_map
.begin()) {
95 if (LastClientId(it
) >= client_id
)
102 } // anonymous namespace
104 PathManager::PathManager() {
107 PathManager::~PathManager() {
108 DCHECK(path_map_
.empty());
111 void PathManager::Destroy(bool have_context
) {
113 for (PathRangeMap::const_iterator it
= path_map_
.begin();
114 it
!= path_map_
.end(); ++it
)
115 CallDeletePaths(FirstServiceId(it
), RangeSize(it
));
120 void PathManager::CreatePathRange(GLuint first_client_id
,
121 GLuint last_client_id
,
122 GLuint first_service_id
) {
123 DCHECK(first_service_id
> 0u);
124 DCHECK(first_client_id
> 0u);
125 DCHECK(!HasPathsInRange(first_client_id
, last_client_id
));
126 DCHECK(CheckConsistency());
128 PathRangeMap::iterator range
=
129 GetContainingRange(path_map_
, first_client_id
- 1u);
131 if (range
!= path_map_
.end() &&
132 LastServiceId(range
) == first_service_id
- 1u) {
133 DCHECK_EQ(LastClientId(range
), first_client_id
- 1u);
134 LastClientId(range
) = last_client_id
;
136 auto result
= path_map_
.insert(
137 std::make_pair(first_client_id
,
138 PathRangeDescription(last_client_id
, first_service_id
)));
139 DCHECK(result
.second
);
140 range
= result
.first
;
143 PathRangeMap::iterator next_range
= range
;
145 if (next_range
!= path_map_
.end()) {
146 if (LastClientId(range
) == FirstClientId(next_range
) - 1u &&
147 LastServiceId(range
) == FirstServiceId(next_range
) - 1u) {
148 LastClientId(range
) = LastClientId(next_range
);
149 path_map_
.erase(next_range
);
152 DCHECK(CheckConsistency());
155 bool PathManager::HasPathsInRange(GLuint first_client_id
,
156 GLuint last_client_id
) const {
157 PathRangeMap::const_iterator it
=
158 GetContainingOrNextRange(path_map_
, first_client_id
);
159 if (it
== path_map_
.end())
162 return FirstClientId(it
) <= last_client_id
;
165 bool PathManager::GetPath(GLuint client_id
, GLuint
* service_id
) const {
166 PathRangeMap::const_iterator range
= GetContainingRange(path_map_
, client_id
);
167 if (range
== path_map_
.end())
170 *service_id
= FirstServiceId(range
) + client_id
- FirstClientId(range
);
174 void PathManager::RemovePaths(GLuint first_client_id
, GLuint last_client_id
) {
175 DCHECK(CheckConsistency());
176 PathRangeMap::iterator it
=
177 GetContainingOrNextRange(path_map_
, first_client_id
);
179 while (it
!= path_map_
.end() && FirstClientId(it
) <= last_client_id
) {
180 GLuint delete_first_client_id
=
181 std::max(first_client_id
, FirstClientId(it
));
182 GLuint delete_last_client_id
= std::min(last_client_id
, LastClientId(it
));
183 GLuint delete_first_service_id
=
184 FirstServiceId(it
) + delete_first_client_id
- FirstClientId(it
);
185 GLuint delete_range
= delete_last_client_id
- delete_first_client_id
+ 1u;
187 CallDeletePaths(delete_first_service_id
, delete_range
);
189 PathRangeMap::iterator current
= it
;
192 GLuint current_last_client_id
= LastClientId(current
);
194 if (FirstClientId(current
) < delete_first_client_id
)
195 LastClientId(current
) = delete_first_client_id
- 1u;
197 path_map_
.erase(current
);
199 if (current_last_client_id
> delete_last_client_id
) {
200 path_map_
.insert(std::make_pair(
201 delete_last_client_id
+ 1u,
202 PathRangeDescription(current_last_client_id
,
203 delete_first_service_id
+ delete_range
)));
204 DCHECK(delete_last_client_id
== last_client_id
);
205 // This is necessarily the last range to check. Return early due to
206 // consistency. Iterator increment would skip the inserted range. The
207 // algorithm would work ok, but it looks weird.
208 DCHECK(CheckConsistency());
212 DCHECK(CheckConsistency());
215 bool PathManager::CheckConsistency() {
216 GLuint prev_first_client_id
= 0u;
217 GLuint prev_last_client_id
= 0u;
218 GLuint prev_first_service_id
= 0u;
219 for (PathRangeMap::iterator range
= path_map_
.begin();
220 range
!= path_map_
.end(); ++range
) {
221 // Code relies on ranges not starting at 0. Also, the above initialization
224 if (FirstClientId(range
) == 0u || FirstServiceId(range
) == 0u)
227 // Each range is consistent.
228 if (FirstClientId(range
) > LastClientId(range
))
231 if (prev_first_client_id
!= 0u) {
232 // No overlapping ranges. (The iteration is sorted).
233 if (FirstClientId(range
) <= prev_last_client_id
)
236 // No mergeable ranges.
237 bool is_mergeable_client
=
238 FirstClientId(range
) - 1u == prev_last_client_id
;
239 bool is_mergeable_service
=
240 FirstServiceId(range
) - 1u == prev_first_service_id
;
241 if (is_mergeable_client
&& is_mergeable_service
)
244 prev_first_client_id
= FirstClientId(range
);
245 prev_last_client_id
= LastClientId(range
);
246 prev_first_service_id
= FirstServiceId(range
);