1 // Copyright 2014 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.
4
5 #include "ui/ozone/platform/drm/gpu/mock_drm_device.h"
6
7 #include <xf86drm.h>
8 #include <memory>
9 #include <utility>
10
11 #include "base/check.h"
12 #include "base/stl_util.h"
13 #include "skia/ext/legacy_display_globals.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "third_party/skia/include/core/SkCanvas.h"
16 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_atomic.h"
17 #include "ui/ozone/platform/drm/gpu/hardware_display_plane_manager_legacy.h"
18
19 // Private types defined in libdrm. Define them here so we can peek at the
20 // commit and ensure the expected state has been set correctly.
21 struct drmModeAtomicReqItem {
22 uint32_t object_id;
23 uint32_t property_id;
24 uint64_t value;
25 };
26
27 typedef drmModeAtomicReqItem* drmModeAtomicReqItemPtr;
28
29 struct _drmModeAtomicReq {
30 uint32_t cursor;
31 uint32_t size_items;
32 drmModeAtomicReqItemPtr items;
33 };
34
35 namespace ui {
36
37 namespace {
38
39 constexpr uint32_t kTestModesetFlags =
40 DRM_MODE_ATOMIC_TEST_ONLY | DRM_MODE_ATOMIC_ALLOW_MODESET;
41
42 constexpr uint32_t kCommitModesetFlags = DRM_MODE_ATOMIC_ALLOW_MODESET;
43
44 template <class Object>
DrmAllocator()45 Object* DrmAllocator() {
46 return static_cast<Object*>(drmMalloc(sizeof(Object)));
47 }
48
CreatePropertyObject(const std::vector<DrmDevice::Property> & properties)49 ScopedDrmObjectPropertyPtr CreatePropertyObject(
50 const std::vector<DrmDevice::Property>& properties) {
51 ScopedDrmObjectPropertyPtr drm_properties(
52 DrmAllocator<drmModeObjectProperties>());
53 drm_properties->count_props = properties.size();
54 drm_properties->props = static_cast<uint32_t*>(
55 drmMalloc(sizeof(uint32_t) * drm_properties->count_props));
56 drm_properties->prop_values = static_cast<uint64_t*>(
57 drmMalloc(sizeof(uint64_t) * drm_properties->count_props));
58 for (size_t i = 0; i < properties.size(); ++i) {
59 drm_properties->props[i] = properties[i].id;
60 drm_properties->prop_values[i] = properties[i].value;
61 }
62
63 return drm_properties;
64 }
65
66 template <class Type>
FindObjectById(uint32_t id,std::vector<Type> & properties)67 Type* FindObjectById(uint32_t id, std::vector<Type>& properties) {
68 auto it = std::find_if(properties.begin(), properties.end(),
69 [id](const Type& p) { return p.id == id; });
70 return it != properties.end() ? &(*it) : nullptr;
71 }
72
73 } // namespace
74
75 MockDrmDevice::CrtcProperties::CrtcProperties() = default;
76 MockDrmDevice::CrtcProperties::CrtcProperties(const CrtcProperties&) = default;
77 MockDrmDevice::CrtcProperties::~CrtcProperties() = default;
78
79 MockDrmDevice::ConnectorProperties::ConnectorProperties() = default;
80 MockDrmDevice::ConnectorProperties::ConnectorProperties(
81 const ConnectorProperties&) = default;
82 MockDrmDevice::ConnectorProperties::~ConnectorProperties() = default;
83
84 MockDrmDevice::PlaneProperties::PlaneProperties() = default;
85 MockDrmDevice::PlaneProperties::PlaneProperties(const PlaneProperties&) =
86 default;
87 MockDrmDevice::PlaneProperties::~PlaneProperties() = default;
88
MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device)89 MockDrmDevice::MockDrmDevice(std::unique_ptr<GbmDevice> gbm_device)
90 : DrmDevice(base::FilePath(),
91 base::File(),
92 true /* is_primary_device */,
93 std::move(gbm_device)),
94 get_crtc_call_count_(0),
95 set_crtc_call_count_(0),
96 add_framebuffer_call_count_(0),
97 remove_framebuffer_call_count_(0),
98 page_flip_call_count_(0),
99 overlay_clear_call_count_(0),
100 allocate_buffer_count_(0),
101 set_crtc_expectation_(true),
102 add_framebuffer_expectation_(true),
103 page_flip_expectation_(true),
104 create_dumb_buffer_expectation_(true),
105 current_framebuffer_(0) {
106 plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
107 }
108
109 MockDrmDevice::~MockDrmDevice() = default;
110
111 // static
AllocateInFormatsBlob(uint32_t id,const std::vector<uint32_t> & supported_formats,const std::vector<drm_format_modifier> & supported_format_modifiers)112 ScopedDrmPropertyBlobPtr MockDrmDevice::AllocateInFormatsBlob(
113 uint32_t id,
114 const std::vector<uint32_t>& supported_formats,
115 const std::vector<drm_format_modifier>& supported_format_modifiers) {
116 drm_format_modifier_blob header;
117 header.count_formats = supported_formats.size();
118 header.formats_offset = sizeof(header);
119 header.count_modifiers = supported_format_modifiers.size();
120 header.modifiers_offset =
121 header.formats_offset + sizeof(uint32_t) * header.count_formats;
122
123 ScopedDrmPropertyBlobPtr blob(DrmAllocator<drmModePropertyBlobRes>());
124 blob->id = id;
125 blob->length = header.modifiers_offset +
126 sizeof(drm_format_modifier) * header.count_modifiers;
127 blob->data = drmMalloc(blob->length);
128
129 memcpy(blob->data, &header, sizeof(header));
130 memcpy(static_cast<uint8_t*>(blob->data) + header.formats_offset,
131 supported_formats.data(), sizeof(uint32_t) * header.count_formats);
132 memcpy(static_cast<uint8_t*>(blob->data) + header.modifiers_offset,
133 supported_format_modifiers.data(),
134 sizeof(drm_format_modifier) * header.count_modifiers);
135
136 return blob;
137 }
138
InitializeState(const std::vector<CrtcProperties> & crtc_properties,const std::vector<ConnectorProperties> & connector_properties,const std::vector<PlaneProperties> & plane_properties,const std::map<uint32_t,std::string> & property_names,bool use_atomic)139 void MockDrmDevice::InitializeState(
140 const std::vector<CrtcProperties>& crtc_properties,
141 const std::vector<ConnectorProperties>& connector_properties,
142 const std::vector<PlaneProperties>& plane_properties,
143 const std::map<uint32_t, std::string>& property_names,
144 bool use_atomic) {
145 CHECK(InitializeStateWithResult(crtc_properties, connector_properties,
146 plane_properties, property_names,
147 use_atomic));
148 }
149
InitializeStateWithResult(const std::vector<CrtcProperties> & crtc_properties,const std::vector<ConnectorProperties> & connector_properties,const std::vector<PlaneProperties> & plane_properties,const std::map<uint32_t,std::string> & property_names,bool use_atomic)150 bool MockDrmDevice::InitializeStateWithResult(
151 const std::vector<CrtcProperties>& crtc_properties,
152 const std::vector<ConnectorProperties>& connector_properties,
153 const std::vector<PlaneProperties>& plane_properties,
154 const std::map<uint32_t, std::string>& property_names,
155 bool use_atomic) {
156 UpdateState(crtc_properties, connector_properties, plane_properties,
157 property_names);
158 if (use_atomic) {
159 plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerAtomic>(this);
160 } else {
161 plane_manager_ = std::make_unique<HardwareDisplayPlaneManagerLegacy>(this);
162 }
163
164 return plane_manager_->Initialize();
165 }
166
UpdateState(const std::vector<CrtcProperties> & crtc_properties,const std::vector<ConnectorProperties> & connector_properties,const std::vector<PlaneProperties> & plane_properties,const std::map<uint32_t,std::string> & property_names)167 void MockDrmDevice::UpdateState(
168 const std::vector<CrtcProperties>& crtc_properties,
169 const std::vector<ConnectorProperties>& connector_properties,
170 const std::vector<PlaneProperties>& plane_properties,
171 const std::map<uint32_t, std::string>& property_names) {
172 crtc_properties_ = crtc_properties;
173 connector_properties_ = connector_properties;
174 plane_properties_ = plane_properties;
175 property_names_ = property_names;
176 }
177
GetResources()178 ScopedDrmResourcesPtr MockDrmDevice::GetResources() {
179 ScopedDrmResourcesPtr resources(DrmAllocator<drmModeRes>());
180 resources->count_crtcs = crtc_properties_.size();
181 resources->crtcs = static_cast<uint32_t*>(
182 drmMalloc(sizeof(uint32_t) * resources->count_crtcs));
183 for (size_t i = 0; i < crtc_properties_.size(); ++i)
184 resources->crtcs[i] = crtc_properties_[i].id;
185
186 resources->count_connectors = connector_properties_.size();
187 resources->connectors = static_cast<uint32_t*>(
188 drmMalloc(sizeof(uint32_t) * resources->count_connectors));
189 for (size_t i = 0; i < connector_properties_.size(); ++i)
190 resources->connectors[i] = connector_properties_[i].id;
191
192 return resources;
193 }
194
GetPlaneResources()195 ScopedDrmPlaneResPtr MockDrmDevice::GetPlaneResources() {
196 ScopedDrmPlaneResPtr resources(DrmAllocator<drmModePlaneRes>());
197 resources->count_planes = plane_properties_.size();
198 resources->planes = static_cast<uint32_t*>(
199 drmMalloc(sizeof(uint32_t) * resources->count_planes));
200 for (size_t i = 0; i < plane_properties_.size(); ++i)
201 resources->planes[i] = plane_properties_[i].id;
202
203 return resources;
204 }
205
GetObjectProperties(uint32_t object_id,uint32_t object_type)206 ScopedDrmObjectPropertyPtr MockDrmDevice::GetObjectProperties(
207 uint32_t object_id,
208 uint32_t object_type) {
209 if (object_type == DRM_MODE_OBJECT_PLANE) {
210 PlaneProperties* properties = FindObjectById(object_id, plane_properties_);
211 if (properties)
212 return CreatePropertyObject(properties->properties);
213 } else if (object_type == DRM_MODE_OBJECT_CRTC) {
214 CrtcProperties* properties = FindObjectById(object_id, crtc_properties_);
215 if (properties)
216 return CreatePropertyObject(properties->properties);
217 } else if (object_type == DRM_MODE_OBJECT_CONNECTOR) {
218 ConnectorProperties* properties =
219 FindObjectById(object_id, connector_properties_);
220 if (properties)
221 return CreatePropertyObject(properties->properties);
222 }
223
224 return nullptr;
225 }
226
GetCrtc(uint32_t crtc_id)227 ScopedDrmCrtcPtr MockDrmDevice::GetCrtc(uint32_t crtc_id) {
228 get_crtc_call_count_++;
229 return ScopedDrmCrtcPtr(DrmAllocator<drmModeCrtc>());
230 }
231
SetCrtc(uint32_t crtc_id,uint32_t framebuffer,std::vector<uint32_t> connectors,const drmModeModeInfo & mode)232 bool MockDrmDevice::SetCrtc(uint32_t crtc_id,
233 uint32_t framebuffer,
234 std::vector<uint32_t> connectors,
235 const drmModeModeInfo& mode) {
236 crtc_fb_[crtc_id] = framebuffer;
237 current_framebuffer_ = framebuffer;
238 set_crtc_call_count_++;
239 return set_crtc_expectation_;
240 }
241
DisableCrtc(uint32_t crtc_id)242 bool MockDrmDevice::DisableCrtc(uint32_t crtc_id) {
243 current_framebuffer_ = 0;
244 return true;
245 }
246
GetConnector(uint32_t connector_id)247 ScopedDrmConnectorPtr MockDrmDevice::GetConnector(uint32_t connector_id) {
248 return ScopedDrmConnectorPtr(DrmAllocator<drmModeConnector>());
249 }
250
AddFramebuffer2(uint32_t width,uint32_t height,uint32_t format,uint32_t handles[4],uint32_t strides[4],uint32_t offsets[4],uint64_t modifiers[4],uint32_t * framebuffer,uint32_t flags)251 bool MockDrmDevice::AddFramebuffer2(uint32_t width,
252 uint32_t height,
253 uint32_t format,
254 uint32_t handles[4],
255 uint32_t strides[4],
256 uint32_t offsets[4],
257 uint64_t modifiers[4],
258 uint32_t* framebuffer,
259 uint32_t flags) {
260 add_framebuffer_call_count_++;
261 *framebuffer = add_framebuffer_call_count_;
262 framebuffer_ids_.insert(*framebuffer);
263 return add_framebuffer_expectation_;
264 }
265
RemoveFramebuffer(uint32_t framebuffer)266 bool MockDrmDevice::RemoveFramebuffer(uint32_t framebuffer) {
267 {
268 auto it = framebuffer_ids_.find(framebuffer);
269 CHECK(it != framebuffer_ids_.end());
270 framebuffer_ids_.erase(it);
271 }
272 remove_framebuffer_call_count_++;
273 std::vector<uint32_t> crtcs_to_clear;
274 for (auto crtc_fb : crtc_fb_) {
275 if (crtc_fb.second == framebuffer)
276 crtcs_to_clear.push_back(crtc_fb.first);
277 }
278 for (auto crtc : crtcs_to_clear)
279 crtc_fb_[crtc] = 0;
280 return true;
281 }
282
GetFramebuffer(uint32_t framebuffer)283 ScopedDrmFramebufferPtr MockDrmDevice::GetFramebuffer(uint32_t framebuffer) {
284 return ScopedDrmFramebufferPtr();
285 }
286
PageFlip(uint32_t crtc_id,uint32_t framebuffer,scoped_refptr<PageFlipRequest> page_flip_request)287 bool MockDrmDevice::PageFlip(uint32_t crtc_id,
288 uint32_t framebuffer,
289 scoped_refptr<PageFlipRequest> page_flip_request) {
290 page_flip_call_count_++;
291 DCHECK(page_flip_request);
292 crtc_fb_[crtc_id] = framebuffer;
293 current_framebuffer_ = framebuffer;
294 if (page_flip_expectation_)
295 callbacks_.push(page_flip_request->AddPageFlip());
296 return page_flip_expectation_;
297 }
298
GetPlane(uint32_t plane_id)299 ScopedDrmPlanePtr MockDrmDevice::GetPlane(uint32_t plane_id) {
300 PlaneProperties* properties = FindObjectById(plane_id, plane_properties_);
301 if (!properties)
302 return nullptr;
303
304 ScopedDrmPlanePtr plane(DrmAllocator<drmModePlane>());
305 plane->possible_crtcs = properties->crtc_mask;
306 return plane;
307 }
308
GetProperty(drmModeConnector * connector,const char * name)309 ScopedDrmPropertyPtr MockDrmDevice::GetProperty(drmModeConnector* connector,
310 const char* name) {
311 return ScopedDrmPropertyPtr(DrmAllocator<drmModePropertyRes>());
312 }
313
GetProperty(uint32_t id)314 ScopedDrmPropertyPtr MockDrmDevice::GetProperty(uint32_t id) {
315 auto it = property_names_.find(id);
316 if (it == property_names_.end())
317 return nullptr;
318
319 ScopedDrmPropertyPtr property(DrmAllocator<drmModePropertyRes>());
320 property->prop_id = id;
321 strcpy(property->name, it->second.c_str());
322 return property;
323 }
324
SetProperty(uint32_t connector_id,uint32_t property_id,uint64_t value)325 bool MockDrmDevice::SetProperty(uint32_t connector_id,
326 uint32_t property_id,
327 uint64_t value) {
328 return true;
329 }
330
CreatePropertyBlob(const void * blob,size_t size)331 ScopedDrmPropertyBlob MockDrmDevice::CreatePropertyBlob(const void* blob,
332 size_t size) {
333 uint32_t id = ++property_id_generator_;
334 allocated_property_blobs_.insert(id);
335 return std::make_unique<DrmPropertyBlobMetadata>(this, id);
336 }
337
DestroyPropertyBlob(uint32_t id)338 void MockDrmDevice::DestroyPropertyBlob(uint32_t id) {
339 EXPECT_TRUE(allocated_property_blobs_.erase(id));
340 }
341
GetCapability(uint64_t capability,uint64_t * value)342 bool MockDrmDevice::GetCapability(uint64_t capability, uint64_t* value) {
343 return true;
344 }
345
GetPropertyBlob(uint32_t property_id)346 ScopedDrmPropertyBlobPtr MockDrmDevice::GetPropertyBlob(uint32_t property_id) {
347 auto it = blob_property_map_.find(property_id);
348 if (it == blob_property_map_.end())
349 return nullptr;
350
351 ScopedDrmPropertyBlobPtr blob(DrmAllocator<drmModePropertyBlobRes>());
352 blob->id = property_id;
353 blob->length = it->second->length;
354 blob->data = drmMalloc(blob->length);
355 memcpy(blob->data, it->second->data, blob->length);
356
357 return blob;
358 }
359
GetPropertyBlob(drmModeConnector * connector,const char * name)360 ScopedDrmPropertyBlobPtr MockDrmDevice::GetPropertyBlob(
361 drmModeConnector* connector,
362 const char* name) {
363 return ScopedDrmPropertyBlobPtr(DrmAllocator<drmModePropertyBlobRes>());
364 }
365
SetObjectProperty(uint32_t object_id,uint32_t object_type,uint32_t property_id,uint32_t property_value)366 bool MockDrmDevice::SetObjectProperty(uint32_t object_id,
367 uint32_t object_type,
368 uint32_t property_id,
369 uint32_t property_value) {
370 set_object_property_count_++;
371 return true;
372 }
373
SetCursor(uint32_t crtc_id,uint32_t handle,const gfx::Size & size)374 bool MockDrmDevice::SetCursor(uint32_t crtc_id,
375 uint32_t handle,
376 const gfx::Size& size) {
377 crtc_cursor_map_[crtc_id] = handle;
378 return true;
379 }
380
MoveCursor(uint32_t crtc_id,const gfx::Point & point)381 bool MockDrmDevice::MoveCursor(uint32_t crtc_id, const gfx::Point& point) {
382 return true;
383 }
384
CreateDumbBuffer(const SkImageInfo & info,uint32_t * handle,uint32_t * stride)385 bool MockDrmDevice::CreateDumbBuffer(const SkImageInfo& info,
386 uint32_t* handle,
387 uint32_t* stride) {
388 if (!create_dumb_buffer_expectation_)
389 return false;
390
391 *handle = allocate_buffer_count_++;
392 *stride = info.minRowBytes();
393 void* pixels = new char[info.computeByteSize(*stride)];
394 SkSurfaceProps props = skia::LegacyDisplayGlobals::GetSkSurfaceProps();
395 buffers_.push_back(SkSurface::MakeRasterDirectReleaseProc(
396 info, pixels, *stride,
397 [](void* pixels, void* context) { delete[] static_cast<char*>(pixels); },
398 /*context=*/nullptr, &props));
399 buffers_[*handle]->getCanvas()->clear(SK_ColorBLACK);
400
401 return true;
402 }
403
DestroyDumbBuffer(uint32_t handle)404 bool MockDrmDevice::DestroyDumbBuffer(uint32_t handle) {
405 if (handle >= buffers_.size() || !buffers_[handle])
406 return false;
407
408 buffers_[handle].reset();
409 return true;
410 }
411
MapDumbBuffer(uint32_t handle,size_t size,void ** pixels)412 bool MockDrmDevice::MapDumbBuffer(uint32_t handle, size_t size, void** pixels) {
413 if (handle >= buffers_.size() || !buffers_[handle])
414 return false;
415
416 SkPixmap pixmap;
417 buffers_[handle]->peekPixels(&pixmap);
418 *pixels = const_cast<void*>(pixmap.addr());
419 return true;
420 }
421
UnmapDumbBuffer(void * pixels,size_t size)422 bool MockDrmDevice::UnmapDumbBuffer(void* pixels, size_t size) {
423 return true;
424 }
425
CloseBufferHandle(uint32_t handle)426 bool MockDrmDevice::CloseBufferHandle(uint32_t handle) {
427 return true;
428 }
429
CommitProperties(drmModeAtomicReq * request,uint32_t flags,uint32_t crtc_count,scoped_refptr<PageFlipRequest> page_flip_request)430 bool MockDrmDevice::CommitProperties(
431 drmModeAtomicReq* request,
432 uint32_t flags,
433 uint32_t crtc_count,
434 scoped_refptr<PageFlipRequest> page_flip_request) {
435 if (flags == kTestModesetFlags)
436 ++test_modeset_count_;
437 else if (flags == kCommitModesetFlags)
438 ++commit_modeset_count_;
439
440 commit_count_++;
441 if (!commit_expectation_)
442 return false;
443
444 for (uint32_t i = 0; i < request->cursor; ++i) {
445 bool res = ValidatePropertyValue(request->items[i].property_id,
446 request->items[i].value);
447 if (!res)
448 return false;
449 }
450
451 if (page_flip_request)
452 callbacks_.push(page_flip_request->AddPageFlip());
453
454 if (flags & DRM_MODE_ATOMIC_TEST_ONLY)
455 return true;
456
457 // Only update values if not testing.
458 for (uint32_t i = 0; i < request->cursor; ++i) {
459 bool res =
460 UpdateProperty(request->items[i].object_id,
461 request->items[i].property_id, request->items[i].value);
462 if (!res)
463 return false;
464 }
465
466 return true;
467 }
468
SetGammaRamp(uint32_t crtc_id,const std::vector<display::GammaRampRGBEntry> & lut)469 bool MockDrmDevice::SetGammaRamp(
470 uint32_t crtc_id,
471 const std::vector<display::GammaRampRGBEntry>& lut) {
472 set_gamma_ramp_count_++;
473 return legacy_gamma_ramp_expectation_;
474 }
475
SetCapability(uint64_t capability,uint64_t value)476 bool MockDrmDevice::SetCapability(uint64_t capability, uint64_t value) {
477 return true;
478 }
479
GetFramebufferForCrtc(uint32_t crtc_id) const480 uint32_t MockDrmDevice::GetFramebufferForCrtc(uint32_t crtc_id) const {
481 auto it = crtc_fb_.find(crtc_id);
482 return it != crtc_fb_.end() ? it->second : 0u;
483 }
484
RunCallbacks()485 void MockDrmDevice::RunCallbacks() {
486 while (!callbacks_.empty()) {
487 PageFlipCallback callback = std::move(callbacks_.front());
488 callbacks_.pop();
489 std::move(callback).Run(0, base::TimeTicks());
490 }
491 }
492
SetPropertyBlob(ScopedDrmPropertyBlobPtr blob)493 void MockDrmDevice::SetPropertyBlob(ScopedDrmPropertyBlobPtr blob) {
494 blob_property_map_[blob->id] = std::move(blob);
495 }
496
UpdateProperty(uint32_t id,uint64_t value,std::vector<DrmDevice::Property> * properties)497 bool MockDrmDevice::UpdateProperty(
498 uint32_t id,
499 uint64_t value,
500 std::vector<DrmDevice::Property>* properties) {
501 DrmDevice::Property* property = FindObjectById(id, *properties);
502 if (!property)
503 return false;
504
505 property->value = value;
506 return true;
507 }
508
UpdateProperty(uint32_t object_id,uint32_t property_id,uint64_t value)509 bool MockDrmDevice::UpdateProperty(uint32_t object_id,
510 uint32_t property_id,
511 uint64_t value) {
512 PlaneProperties* plane_properties =
513 FindObjectById(object_id, plane_properties_);
514 if (plane_properties)
515 return UpdateProperty(property_id, value, &plane_properties->properties);
516
517 CrtcProperties* crtc_properties = FindObjectById(object_id, crtc_properties_);
518 if (crtc_properties)
519 return UpdateProperty(property_id, value, &crtc_properties->properties);
520
521 ConnectorProperties* connector_properties =
522 FindObjectById(object_id, connector_properties_);
523 if (connector_properties) {
524 return UpdateProperty(property_id, value,
525 &connector_properties->properties);
526 }
527
528 return false;
529 }
530
ValidatePropertyValue(uint32_t id,uint64_t value)531 bool MockDrmDevice::ValidatePropertyValue(uint32_t id, uint64_t value) {
532 auto it = property_names_.find(id);
533 if (it == property_names_.end())
534 return false;
535
536 if (value == 0)
537 return true;
538
539 std::vector<std::string> blob_properties = {"CTM", "DEGAMMA_LUT", "GAMMA_LUT",
540 "PLANE_CTM"};
541 if (base::Contains(blob_properties, it->second))
542 return base::Contains(allocated_property_blobs_, value);
543
544 return true;
545 }
546
547 } // namespace ui
548