OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "components/display_compositor/buffer_queue.h" | 5 #include "components/display_compositor/buffer_queue.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <set> | 10 #include <set> |
(...skipping 11 matching lines...) Expand all Loading... |
22 | 22 |
23 using ::testing::_; | 23 using ::testing::_; |
24 using ::testing::Expectation; | 24 using ::testing::Expectation; |
25 using ::testing::Ne; | 25 using ::testing::Ne; |
26 using ::testing::Return; | 26 using ::testing::Return; |
27 | 27 |
28 namespace display_compositor { | 28 namespace display_compositor { |
29 | 29 |
30 class StubGpuMemoryBufferImpl : public gfx::GpuMemoryBuffer { | 30 class StubGpuMemoryBufferImpl : public gfx::GpuMemoryBuffer { |
31 public: | 31 public: |
32 StubGpuMemoryBufferImpl() {} | 32 StubGpuMemoryBufferImpl(size_t* set_color_space_count) |
| 33 : set_color_space_count_(set_color_space_count) {} |
33 | 34 |
34 // Overridden from gfx::GpuMemoryBuffer: | 35 // Overridden from gfx::GpuMemoryBuffer: |
35 bool Map() override { return false; } | 36 bool Map() override { return false; } |
36 void* memory(size_t plane) override { return nullptr; } | 37 void* memory(size_t plane) override { return nullptr; } |
37 void Unmap() override {} | 38 void Unmap() override {} |
38 gfx::Size GetSize() const override { return gfx::Size(); } | 39 gfx::Size GetSize() const override { return gfx::Size(); } |
39 gfx::BufferFormat GetFormat() const override { | 40 gfx::BufferFormat GetFormat() const override { |
40 return gfx::BufferFormat::BGRX_8888; | 41 return gfx::BufferFormat::BGRX_8888; |
41 } | 42 } |
42 int stride(size_t plane) const override { return 0; } | 43 int stride(size_t plane) const override { return 0; } |
43 gfx::GpuMemoryBufferId GetId() const override { | 44 gfx::GpuMemoryBufferId GetId() const override { |
44 return gfx::GpuMemoryBufferId(0); | 45 return gfx::GpuMemoryBufferId(0); |
45 } | 46 } |
| 47 void SetColorSpaceForScanout(const gfx::ColorSpace& color_space) override { |
| 48 *set_color_space_count_ += 1; |
| 49 } |
46 gfx::GpuMemoryBufferHandle GetHandle() const override { | 50 gfx::GpuMemoryBufferHandle GetHandle() const override { |
47 return gfx::GpuMemoryBufferHandle(); | 51 return gfx::GpuMemoryBufferHandle(); |
48 } | 52 } |
49 ClientBuffer AsClientBuffer() override { | 53 ClientBuffer AsClientBuffer() override { |
50 return reinterpret_cast<ClientBuffer>(this); | 54 return reinterpret_cast<ClientBuffer>(this); |
51 } | 55 } |
| 56 |
| 57 size_t* set_color_space_count_; |
52 }; | 58 }; |
53 | 59 |
54 class StubGpuMemoryBufferManager : public cc::TestGpuMemoryBufferManager { | 60 class StubGpuMemoryBufferManager : public cc::TestGpuMemoryBufferManager { |
55 public: | 61 public: |
56 StubGpuMemoryBufferManager() : allocate_succeeds_(true) {} | 62 StubGpuMemoryBufferManager() : allocate_succeeds_(true) {} |
57 | 63 |
| 64 size_t set_color_space_count() const { return set_color_space_count_; } |
| 65 |
58 void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; } | 66 void set_allocate_succeeds(bool value) { allocate_succeeds_ = value; } |
59 | 67 |
60 std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer( | 68 std::unique_ptr<gfx::GpuMemoryBuffer> AllocateGpuMemoryBuffer( |
61 const gfx::Size& size, | 69 const gfx::Size& size, |
62 gfx::BufferFormat format, | 70 gfx::BufferFormat format, |
63 gfx::BufferUsage usage, | 71 gfx::BufferUsage usage, |
64 gpu::SurfaceHandle surface_handle) override { | 72 gpu::SurfaceHandle surface_handle) override { |
65 if (!surface_handle) { | 73 if (!surface_handle) { |
66 return TestGpuMemoryBufferManager::AllocateGpuMemoryBuffer( | 74 return TestGpuMemoryBufferManager::AllocateGpuMemoryBuffer( |
67 size, format, usage, surface_handle); | 75 size, format, usage, surface_handle); |
68 } | 76 } |
69 if (allocate_succeeds_) | 77 if (allocate_succeeds_) |
70 return base::WrapUnique<gfx::GpuMemoryBuffer>( | 78 return base::WrapUnique<gfx::GpuMemoryBuffer>( |
71 new StubGpuMemoryBufferImpl); | 79 new StubGpuMemoryBufferImpl(&set_color_space_count_)); |
72 return nullptr; | 80 return nullptr; |
73 } | 81 } |
74 | 82 |
75 private: | 83 private: |
76 bool allocate_succeeds_; | 84 bool allocate_succeeds_; |
| 85 size_t set_color_space_count_ = 0; |
77 }; | 86 }; |
78 | 87 |
79 #if defined(OS_WIN) | 88 #if defined(OS_WIN) |
80 const gpu::SurfaceHandle kFakeSurfaceHandle = | 89 const gpu::SurfaceHandle kFakeSurfaceHandle = |
81 reinterpret_cast<gpu::SurfaceHandle>(1); | 90 reinterpret_cast<gpu::SurfaceHandle>(1); |
82 #else | 91 #else |
83 const gpu::SurfaceHandle kFakeSurfaceHandle = 1; | 92 const gpu::SurfaceHandle kFakeSurfaceHandle = 1; |
84 #endif | 93 #endif |
85 | 94 |
86 class MockBufferQueue : public BufferQueue { | 95 class MockBufferQueue : public BufferQueue { |
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager( | 275 std::unique_ptr<StubGpuMemoryBufferManager> gpu_memory_buffer_manager( |
267 new StubGpuMemoryBufferManager); | 276 new StubGpuMemoryBufferManager); |
268 std::unique_ptr<BufferQueue> output_surface = | 277 std::unique_ptr<BufferQueue> output_surface = |
269 CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(), | 278 CreateBufferQueue(GL_TEXTURE_2D, context_provider->ContextGL(), |
270 gpu_memory_buffer_manager.get()); | 279 gpu_memory_buffer_manager.get()); |
271 | 280 |
272 EXPECT_CALL(*context, bindFramebuffer(GL_FRAMEBUFFER, Ne(0U))); | 281 EXPECT_CALL(*context, bindFramebuffer(GL_FRAMEBUFFER, Ne(0U))); |
273 ON_CALL(*context, framebufferTexture2D(_, _, _, _, _)) | 282 ON_CALL(*context, framebufferTexture2D(_, _, _, _, _)) |
274 .WillByDefault(Return()); | 283 .WillByDefault(Return()); |
275 | 284 |
276 output_surface->Reshape(gfx::Size(10, 20), 1.0f); | 285 output_surface->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace()); |
277 } | 286 } |
278 | 287 |
279 TEST(BufferQueueStandaloneTest, FboBinding) { | 288 TEST(BufferQueueStandaloneTest, FboBinding) { |
280 GLenum targets[] = {GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB}; | 289 GLenum targets[] = {GL_TEXTURE_2D, GL_TEXTURE_RECTANGLE_ARB}; |
281 for (size_t i = 0; i < 2; ++i) { | 290 for (size_t i = 0; i < 2; ++i) { |
282 GLenum target = targets[i]; | 291 GLenum target = targets[i]; |
283 | 292 |
284 MockedContext* context; | 293 MockedContext* context; |
285 scoped_refptr<cc::TestContextProvider> context_provider = | 294 scoped_refptr<cc::TestContextProvider> context_provider = |
286 CreateMockedContextProvider(&context); | 295 CreateMockedContextProvider(&context); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
318 gpu_memory_buffer_manager.reset(new StubGpuMemoryBufferManager); | 327 gpu_memory_buffer_manager.reset(new StubGpuMemoryBufferManager); |
319 | 328 |
320 std::unique_ptr<GLHelper> gl_helper; | 329 std::unique_ptr<GLHelper> gl_helper; |
321 gl_helper.reset(new GLHelper(context_provider->ContextGL(), | 330 gl_helper.reset(new GLHelper(context_provider->ContextGL(), |
322 context_provider->ContextSupport())); | 331 context_provider->ContextSupport())); |
323 | 332 |
324 output_surface.reset(new BufferQueue( | 333 output_surface.reset(new BufferQueue( |
325 context_provider->ContextGL(), GL_TEXTURE_2D, GL_RGBA, gl_helper.get(), | 334 context_provider->ContextGL(), GL_TEXTURE_2D, GL_RGBA, gl_helper.get(), |
326 gpu_memory_buffer_manager.get(), kFakeSurfaceHandle)); | 335 gpu_memory_buffer_manager.get(), kFakeSurfaceHandle)); |
327 output_surface->Initialize(); | 336 output_surface->Initialize(); |
328 output_surface->Reshape(screen_size, 1.0f); | 337 output_surface->Reshape(screen_size, 1.0f, gfx::ColorSpace()); |
329 // Trigger a sub-buffer copy to exercise all paths. | 338 // Trigger a sub-buffer copy to exercise all paths. |
330 output_surface->BindFramebuffer(); | 339 output_surface->BindFramebuffer(); |
331 output_surface->SwapBuffers(screen_rect); | 340 output_surface->SwapBuffers(screen_rect); |
332 output_surface->PageFlipComplete(); | 341 output_surface->PageFlipComplete(); |
333 output_surface->BindFramebuffer(); | 342 output_surface->BindFramebuffer(); |
334 output_surface->SwapBuffers(small_damage); | 343 output_surface->SwapBuffers(small_damage); |
335 | 344 |
336 int current_fbo = 0; | 345 int current_fbo = 0; |
337 context_provider->ContextGL()->GetIntegerv(GL_FRAMEBUFFER_BINDING, | 346 context_provider->ContextGL()->GetIntegerv(GL_FRAMEBUFFER_BINDING, |
338 ¤t_fbo); | 347 ¤t_fbo); |
339 EXPECT_EQ(static_cast<int>(output_surface->fbo()), current_fbo); | 348 EXPECT_EQ(static_cast<int>(output_surface->fbo()), current_fbo); |
340 } | 349 } |
341 | 350 |
342 TEST_F(BufferQueueTest, PartialSwapReuse) { | 351 TEST_F(BufferQueueTest, PartialSwapReuse) { |
343 output_surface_->Reshape(screen_size, 1.0f); | 352 output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace()); |
344 ASSERT_TRUE(doublebuffering_); | 353 ASSERT_TRUE(doublebuffering_); |
345 EXPECT_CALL(*mock_output_surface_, | 354 EXPECT_CALL(*mock_output_surface_, |
346 CopyBufferDamage(_, _, small_damage, screen_rect)) | 355 CopyBufferDamage(_, _, small_damage, screen_rect)) |
347 .Times(1); | 356 .Times(1); |
348 EXPECT_CALL(*mock_output_surface_, | 357 EXPECT_CALL(*mock_output_surface_, |
349 CopyBufferDamage(_, _, small_damage, small_damage)) | 358 CopyBufferDamage(_, _, small_damage, small_damage)) |
350 .Times(1); | 359 .Times(1); |
351 EXPECT_CALL(*mock_output_surface_, | 360 EXPECT_CALL(*mock_output_surface_, |
352 CopyBufferDamage(_, _, large_damage, small_damage)) | 361 CopyBufferDamage(_, _, large_damage, small_damage)) |
353 .Times(1); | 362 .Times(1); |
354 SendFullFrame(); | 363 SendFullFrame(); |
355 SendDamagedFrame(small_damage); | 364 SendDamagedFrame(small_damage); |
356 SendDamagedFrame(small_damage); | 365 SendDamagedFrame(small_damage); |
357 SendDamagedFrame(large_damage); | 366 SendDamagedFrame(large_damage); |
358 // Verify that the damage has propagated. | 367 // Verify that the damage has propagated. |
359 EXPECT_EQ(next_frame()->damage, large_damage); | 368 EXPECT_EQ(next_frame()->damage, large_damage); |
360 } | 369 } |
361 | 370 |
362 TEST_F(BufferQueueTest, PartialSwapFullFrame) { | 371 TEST_F(BufferQueueTest, PartialSwapFullFrame) { |
363 output_surface_->Reshape(screen_size, 1.0f); | 372 output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace()); |
364 ASSERT_TRUE(doublebuffering_); | 373 ASSERT_TRUE(doublebuffering_); |
365 EXPECT_CALL(*mock_output_surface_, | 374 EXPECT_CALL(*mock_output_surface_, |
366 CopyBufferDamage(_, _, small_damage, screen_rect)) | 375 CopyBufferDamage(_, _, small_damage, screen_rect)) |
367 .Times(1); | 376 .Times(1); |
368 SendFullFrame(); | 377 SendFullFrame(); |
369 SendDamagedFrame(small_damage); | 378 SendDamagedFrame(small_damage); |
370 SendFullFrame(); | 379 SendFullFrame(); |
371 SendFullFrame(); | 380 SendFullFrame(); |
372 EXPECT_EQ(next_frame()->damage, screen_rect); | 381 EXPECT_EQ(next_frame()->damage, screen_rect); |
373 } | 382 } |
374 | 383 |
375 TEST_F(BufferQueueTest, PartialSwapOverlapping) { | 384 TEST_F(BufferQueueTest, PartialSwapOverlapping) { |
376 output_surface_->Reshape(screen_size, 1.0f); | 385 output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace()); |
377 ASSERT_TRUE(doublebuffering_); | 386 ASSERT_TRUE(doublebuffering_); |
378 EXPECT_CALL(*mock_output_surface_, | 387 EXPECT_CALL(*mock_output_surface_, |
379 CopyBufferDamage(_, _, small_damage, screen_rect)) | 388 CopyBufferDamage(_, _, small_damage, screen_rect)) |
380 .Times(1); | 389 .Times(1); |
381 EXPECT_CALL(*mock_output_surface_, | 390 EXPECT_CALL(*mock_output_surface_, |
382 CopyBufferDamage(_, _, overlapping_damage, small_damage)) | 391 CopyBufferDamage(_, _, overlapping_damage, small_damage)) |
383 .Times(1); | 392 .Times(1); |
384 | 393 |
385 SendFullFrame(); | 394 SendFullFrame(); |
386 SendDamagedFrame(small_damage); | 395 SendDamagedFrame(small_damage); |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
476 } | 485 } |
477 } | 486 } |
478 | 487 |
479 TEST_F(BufferQueueTest, ReshapeWithInFlightSurfaces) { | 488 TEST_F(BufferQueueTest, ReshapeWithInFlightSurfaces) { |
480 const size_t kSwapCount = 3; | 489 const size_t kSwapCount = 3; |
481 for (size_t i = 0; i < kSwapCount; ++i) { | 490 for (size_t i = 0; i < kSwapCount; ++i) { |
482 output_surface_->BindFramebuffer(); | 491 output_surface_->BindFramebuffer(); |
483 SwapBuffers(); | 492 SwapBuffers(); |
484 } | 493 } |
485 | 494 |
486 output_surface_->Reshape(gfx::Size(10, 20), 1.0f); | 495 output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace()); |
487 EXPECT_EQ(3u, in_flight_surfaces().size()); | 496 EXPECT_EQ(3u, in_flight_surfaces().size()); |
488 | 497 |
489 for (size_t i = 0; i < kSwapCount; ++i) { | 498 for (size_t i = 0; i < kSwapCount; ++i) { |
490 output_surface_->PageFlipComplete(); | 499 output_surface_->PageFlipComplete(); |
491 EXPECT_FALSE(displayed_frame()); | 500 EXPECT_FALSE(displayed_frame()); |
492 } | 501 } |
493 | 502 |
494 // The dummy surfacess left should be discarded. | 503 // The dummy surfacess left should be discarded. |
495 EXPECT_EQ(0u, available_surfaces().size()); | 504 EXPECT_EQ(0u, available_surfaces().size()); |
496 } | 505 } |
497 | 506 |
498 TEST_F(BufferQueueTest, SwapAfterReshape) { | 507 TEST_F(BufferQueueTest, SwapAfterReshape) { |
| 508 DCHECK_EQ(0u, gpu_memory_buffer_manager_->set_color_space_count()); |
499 const size_t kSwapCount = 3; | 509 const size_t kSwapCount = 3; |
500 for (size_t i = 0; i < kSwapCount; ++i) { | 510 for (size_t i = 0; i < kSwapCount; ++i) { |
501 output_surface_->BindFramebuffer(); | 511 output_surface_->BindFramebuffer(); |
502 SwapBuffers(); | 512 SwapBuffers(); |
503 } | 513 } |
| 514 DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count()); |
504 | 515 |
505 output_surface_->Reshape(gfx::Size(10, 20), 1.0f); | 516 output_surface_->Reshape(gfx::Size(10, 20), 1.0f, gfx::ColorSpace()); |
| 517 DCHECK_EQ(kSwapCount, gpu_memory_buffer_manager_->set_color_space_count()); |
506 | 518 |
507 for (size_t i = 0; i < kSwapCount; ++i) { | 519 for (size_t i = 0; i < kSwapCount; ++i) { |
508 output_surface_->BindFramebuffer(); | 520 output_surface_->BindFramebuffer(); |
509 SwapBuffers(); | 521 SwapBuffers(); |
510 } | 522 } |
511 | 523 DCHECK_EQ(2 * kSwapCount, |
| 524 gpu_memory_buffer_manager_->set_color_space_count()); |
512 EXPECT_EQ(2 * kSwapCount, in_flight_surfaces().size()); | 525 EXPECT_EQ(2 * kSwapCount, in_flight_surfaces().size()); |
513 | 526 |
514 for (size_t i = 0; i < kSwapCount; ++i) { | 527 for (size_t i = 0; i < kSwapCount; ++i) { |
515 output_surface_->PageFlipComplete(); | 528 output_surface_->PageFlipComplete(); |
516 EXPECT_FALSE(displayed_frame()); | 529 EXPECT_FALSE(displayed_frame()); |
517 } | 530 } |
518 | 531 |
519 CheckUnique(); | 532 CheckUnique(); |
520 | 533 |
521 for (size_t i = 0; i < kSwapCount; ++i) { | 534 for (size_t i = 0; i < kSwapCount; ++i) { |
522 unsigned int next_texture_id = in_flight_surfaces().front()->texture; | 535 unsigned int next_texture_id = in_flight_surfaces().front()->texture; |
523 output_surface_->PageFlipComplete(); | 536 output_surface_->PageFlipComplete(); |
524 EXPECT_EQ(displayed_frame()->texture, next_texture_id); | 537 EXPECT_EQ(displayed_frame()->texture, next_texture_id); |
525 EXPECT_TRUE(displayed_frame()); | 538 EXPECT_TRUE(displayed_frame()); |
526 } | 539 } |
| 540 |
| 541 DCHECK_EQ(2 * kSwapCount, |
| 542 gpu_memory_buffer_manager_->set_color_space_count()); |
| 543 for (size_t i = 0; i < kSwapCount; ++i) { |
| 544 output_surface_->BindFramebuffer(); |
| 545 SwapBuffers(); |
| 546 output_surface_->PageFlipComplete(); |
| 547 } |
| 548 DCHECK_EQ(2 * kSwapCount, |
| 549 gpu_memory_buffer_manager_->set_color_space_count()); |
527 } | 550 } |
528 | 551 |
529 TEST_F(BufferQueueMockedContextTest, RecreateBuffers) { | 552 TEST_F(BufferQueueMockedContextTest, RecreateBuffers) { |
530 // This setup is to easily get one frame in each of: | 553 // This setup is to easily get one frame in each of: |
531 // - currently bound for drawing. | 554 // - currently bound for drawing. |
532 // - in flight to GPU. | 555 // - in flight to GPU. |
533 // - currently displayed. | 556 // - currently displayed. |
534 // - free frame. | 557 // - free frame. |
535 // This tests buffers in all states. | 558 // This tests buffers in all states. |
536 // Bind/swap pushes frames into the in flight list, then the PageFlipComplete | 559 // Bind/swap pushes frames into the in flight list, then the PageFlipComplete |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
595 | 618 |
596 // All free buffers should be destroyed, the remaining buffers should all | 619 // All free buffers should be destroyed, the remaining buffers should all |
597 // be replaced but still valid. | 620 // be replaced but still valid. |
598 EXPECT_EQ(1U, in_flight_surfaces().size()); | 621 EXPECT_EQ(1U, in_flight_surfaces().size()); |
599 EXPECT_EQ(0U, available_surfaces().size()); | 622 EXPECT_EQ(0U, available_surfaces().size()); |
600 EXPECT_TRUE(displayed_frame()); | 623 EXPECT_TRUE(displayed_frame()); |
601 EXPECT_TRUE(current_frame()); | 624 EXPECT_TRUE(current_frame()); |
602 } | 625 } |
603 | 626 |
604 TEST_F(BufferQueueTest, AllocateFails) { | 627 TEST_F(BufferQueueTest, AllocateFails) { |
605 output_surface_->Reshape(screen_size, 1.0f); | 628 output_surface_->Reshape(screen_size, 1.0f, gfx::ColorSpace()); |
606 | 629 |
607 // Succeed in the two swaps. | 630 // Succeed in the two swaps. |
608 output_surface_->BindFramebuffer(); | 631 output_surface_->BindFramebuffer(); |
609 EXPECT_TRUE(current_frame()); | 632 EXPECT_TRUE(current_frame()); |
610 output_surface_->SwapBuffers(screen_rect); | 633 output_surface_->SwapBuffers(screen_rect); |
611 | 634 |
612 // Fail the next surface allocation. | 635 // Fail the next surface allocation. |
613 gpu_memory_buffer_manager_->set_allocate_succeeds(false); | 636 gpu_memory_buffer_manager_->set_allocate_succeeds(false); |
614 output_surface_->BindFramebuffer(); | 637 output_surface_->BindFramebuffer(); |
615 EXPECT_FALSE(current_frame()); | 638 EXPECT_FALSE(current_frame()); |
(...skipping 29 matching lines...) Expand all Loading... |
645 testing::Mock::VerifyAndClearExpectations(mock_output_surface_); | 668 testing::Mock::VerifyAndClearExpectations(mock_output_surface_); |
646 EXPECT_CALL(*mock_output_surface_, | 669 EXPECT_CALL(*mock_output_surface_, |
647 CopyBufferDamage(target_texture, source_texture, small_damage, _)) | 670 CopyBufferDamage(target_texture, source_texture, small_damage, _)) |
648 .Times(1); | 671 .Times(1); |
649 output_surface_->SwapBuffers(small_damage); | 672 output_surface_->SwapBuffers(small_damage); |
650 testing::Mock::VerifyAndClearExpectations(mock_output_surface_); | 673 testing::Mock::VerifyAndClearExpectations(mock_output_surface_); |
651 } | 674 } |
652 | 675 |
653 } // namespace | 676 } // namespace |
654 } // namespace display_compositor | 677 } // namespace display_compositor |
OLD | NEW |