Home | History | Annotate | Download | only in surfaces
      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 "cc/output/compositor_frame.h"
      6 #include "cc/output/delegated_frame_data.h"
      7 #include "cc/surfaces/surface.h"
      8 #include "cc/surfaces/surface_client.h"
      9 #include "cc/surfaces/surface_manager.h"
     10 #include "testing/gtest/include/gtest/gtest.h"
     11 #include "ui/gfx/size.h"
     12 
     13 namespace cc {
     14 namespace {
     15 
     16 TEST(SurfaceTest, SurfaceLifetime) {
     17   SurfaceManager manager;
     18 
     19   SurfaceId surface_id;
     20   {
     21     Surface surface(&manager, NULL, gfx::Size(5, 5));
     22     surface_id = surface.surface_id();
     23     EXPECT_TRUE(!surface_id.is_null());
     24     EXPECT_EQ(&surface, manager.GetSurfaceForId(surface_id));
     25   }
     26 
     27   EXPECT_EQ(NULL, manager.GetSurfaceForId(surface_id));
     28 }
     29 
     30 class TestSurfaceClient : public SurfaceClient {
     31  public:
     32   TestSurfaceClient() {}
     33   virtual ~TestSurfaceClient() {}
     34 
     35   virtual void ReturnResources(
     36       const ReturnedResourceArray& resources) OVERRIDE {
     37     returned_resources_ = resources;
     38   }
     39 
     40   const ReturnedResourceArray& returned_resources() const {
     41     return returned_resources_;
     42   }
     43 
     44   void clear_returned_resources() { returned_resources_.clear(); }
     45 
     46  private:
     47   ReturnedResourceArray returned_resources_;
     48 
     49   DISALLOW_COPY_AND_ASSIGN(TestSurfaceClient);
     50 };
     51 
     52 void SubmitFrameWithResources(ResourceProvider::ResourceId* resource_ids,
     53                               size_t num_resource_ids,
     54                               Surface* surface) {
     55   scoped_ptr<DelegatedFrameData> frame_data(new DelegatedFrameData);
     56   for (size_t i = 0u; i < num_resource_ids; ++i) {
     57     TransferableResource resource;
     58     resource.id = resource_ids[i];
     59     resource.mailbox_holder.texture_target = GL_TEXTURE_2D;
     60     frame_data->resource_list.push_back(resource);
     61   }
     62   scoped_ptr<CompositorFrame> frame(new CompositorFrame);
     63   frame->delegated_frame_data = frame_data.Pass();
     64   surface->QueueFrame(frame.Pass());
     65 }
     66 
     67 void CheckReturnedResourcesMatchExpected(
     68     ResourceProvider::ResourceId* expected_returned_ids,
     69     int* expected_returned_counts,
     70     size_t expected_resources,
     71     const ReturnedResourceArray& actual_resources) {
     72   ASSERT_EQ(expected_resources, actual_resources.size());
     73   for (size_t i = 0; i < expected_resources; ++i) {
     74     ReturnedResource resource = actual_resources[i];
     75     EXPECT_EQ(expected_returned_ids[i], resource.id);
     76     EXPECT_EQ(expected_returned_counts[i], resource.count);
     77   }
     78 }
     79 
     80 void UnrefResources(ResourceProvider::ResourceId* ids_to_unref,
     81                     int* counts_to_unref,
     82                     size_t num_ids_to_unref,
     83                     Surface* surface) {
     84   ReturnedResourceArray unref_array;
     85   for (size_t i = 0; i < num_ids_to_unref; ++i) {
     86     ReturnedResource resource;
     87     resource.id = ids_to_unref[i];
     88     resource.count = counts_to_unref[i];
     89     unref_array.push_back(resource);
     90   }
     91   surface->UnrefResources(unref_array);
     92 }
     93 
     94 // Tests submitting a frame with resources followed by one with no resources
     95 // with no resource provider action in between.
     96 TEST(SurfaceTest, ResourceLifetimeSimple) {
     97   SurfaceManager manager;
     98   TestSurfaceClient client;
     99   Surface surface(&manager, &client, gfx::Size(5, 5));
    100 
    101   ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3};
    102   SubmitFrameWithResources(
    103       first_frame_ids, arraysize(first_frame_ids), &surface);
    104 
    105   // All of the resources submitted in the first frame are still in use at this
    106   // time by virtue of being in the pending frame, so none can be returned to
    107   // the client yet.
    108   surface.ReturnUnusedResourcesToClient();
    109   EXPECT_EQ(0u, client.returned_resources().size());
    110   client.clear_returned_resources();
    111 
    112   // The second frame references no resources and thus should make all resources
    113   // available to be returned.
    114   SubmitFrameWithResources(NULL, 0, &surface);
    115 
    116   surface.ReturnUnusedResourcesToClient();
    117   ResourceProvider::ResourceId expected_returned_ids[] = {1, 2, 3};
    118   int expected_returned_counts[] = {1, 1, 1};
    119   CheckReturnedResourcesMatchExpected(expected_returned_ids,
    120                                       expected_returned_counts,
    121                                       arraysize(expected_returned_counts),
    122                                       client.returned_resources());
    123 }
    124 
    125 // Tests submitting a frame with resources followed by one with no resources
    126 // with the resource provider holding everything alive.
    127 TEST(SurfaceTest, ResourceLifetimeSimpleWithProviderHoldingAlive) {
    128   SurfaceManager manager;
    129   TestSurfaceClient client;
    130   Surface surface(&manager, &client, gfx::Size(5, 5));
    131 
    132   ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3};
    133   SubmitFrameWithResources(
    134       first_frame_ids, arraysize(first_frame_ids), &surface);
    135 
    136   // All of the resources submitted in the first frame are still in use at this
    137   // time by virtue of being in the pending frame, so none can be returned to
    138   // the client yet.
    139   surface.ReturnUnusedResourcesToClient();
    140   EXPECT_EQ(0u, client.returned_resources().size());
    141   client.clear_returned_resources();
    142 
    143   // Hold on to everything.
    144   surface.RefCurrentFrameResources();
    145 
    146   // The second frame references no resources and thus should make all resources
    147   // available to be returned as soon as the resource provider releases them.
    148   SubmitFrameWithResources(NULL, 0, &surface);
    149 
    150   surface.ReturnUnusedResourcesToClient();
    151   EXPECT_EQ(0u, client.returned_resources().size());
    152   client.clear_returned_resources();
    153 
    154   int release_counts[] = {1, 1, 1};
    155   UnrefResources(
    156       first_frame_ids, release_counts, arraysize(first_frame_ids), &surface);
    157 
    158   surface.ReturnUnusedResourcesToClient();
    159   ResourceProvider::ResourceId expected_returned_ids[] = {1, 2, 3};
    160   int expected_returned_counts[] = {1, 1, 1};
    161   CheckReturnedResourcesMatchExpected(expected_returned_ids,
    162                                       expected_returned_counts,
    163                                       arraysize(expected_returned_counts),
    164                                       client.returned_resources());
    165 }
    166 
    167 // Tests referencing a resource, unref'ing it to zero, then using it again
    168 // before returning it to the client.
    169 TEST(SurfaceTest, ResourceReusedBeforeReturn) {
    170   SurfaceManager manager;
    171   TestSurfaceClient client;
    172   Surface surface(&manager, &client, gfx::Size(5, 5));
    173 
    174   ResourceProvider::ResourceId first_frame_ids[] = {7};
    175   SubmitFrameWithResources(
    176       first_frame_ids, arraysize(first_frame_ids), &surface);
    177 
    178   // This removes all references to resource id 7.
    179   SubmitFrameWithResources(NULL, 0, &surface);
    180 
    181   // This references id 7 again.
    182   SubmitFrameWithResources(
    183       first_frame_ids, arraysize(first_frame_ids), &surface);
    184 
    185   // This removes it again.
    186   SubmitFrameWithResources(NULL, 0, &surface);
    187 
    188   // Now it should be returned.
    189   surface.ReturnUnusedResourcesToClient();
    190 
    191   // We don't care how many entries are in the returned array for 7, so long as
    192   // the total returned count matches the submitted count.
    193   const ReturnedResourceArray& returned = client.returned_resources();
    194   size_t return_count = 0;
    195   for (size_t i = 0; i < returned.size(); ++i) {
    196     EXPECT_EQ(7u, returned[i].id);
    197     return_count += returned[i].count;
    198   }
    199   EXPECT_EQ(2u, return_count);
    200 }
    201 
    202 // Tests having resources referenced multiple times, as if referenced by
    203 // multiple providers.
    204 TEST(SurfaceTest, ResourceRefMultipleTimes) {
    205   SurfaceManager manager;
    206   TestSurfaceClient client;
    207   Surface surface(&manager, &client, gfx::Size(5, 5));
    208 
    209   ResourceProvider::ResourceId first_frame_ids[] = {3, 4};
    210   SubmitFrameWithResources(
    211       first_frame_ids, arraysize(first_frame_ids), &surface);
    212 
    213   // Ref resources from the first frame twice.
    214   surface.RefCurrentFrameResources();
    215   surface.RefCurrentFrameResources();
    216 
    217   ResourceProvider::ResourceId second_frame_ids[] = {4, 5};
    218   SubmitFrameWithResources(
    219       second_frame_ids, arraysize(second_frame_ids), &surface);
    220 
    221   // Ref resources from the second frame 3 times.
    222   surface.RefCurrentFrameResources();
    223   surface.RefCurrentFrameResources();
    224   surface.RefCurrentFrameResources();
    225 
    226   // Submit a frame with no resources to remove all current frame refs from
    227   // submitted resources.
    228   SubmitFrameWithResources(NULL, 0, &surface);
    229 
    230   surface.ReturnUnusedResourcesToClient();
    231   EXPECT_EQ(0u, client.returned_resources().size());
    232   client.clear_returned_resources();
    233 
    234   // Expected current refs:
    235   //  3 -> 2
    236   //  4 -> 2 + 3 = 5
    237   //  5 -> 3
    238   {
    239     SCOPED_TRACE("unref all 3");
    240     ResourceProvider::ResourceId ids_to_unref[] = {3, 4, 5};
    241     int counts[] = {1, 1, 1};
    242     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    243 
    244     surface.ReturnUnusedResourcesToClient();
    245     EXPECT_EQ(0u, client.returned_resources().size());
    246     client.clear_returned_resources();
    247 
    248     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    249 
    250     surface.ReturnUnusedResourcesToClient();
    251     ResourceProvider::ResourceId expected_returned_ids[] = {3};
    252     int expected_returned_counts[] = {1};
    253     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    254                                         expected_returned_counts,
    255                                         arraysize(expected_returned_counts),
    256                                         client.returned_resources());
    257   }
    258 
    259   // Expected refs remaining:
    260   //  4 -> 3
    261   //  5 -> 1
    262   {
    263     SCOPED_TRACE("unref 4 and 5");
    264     ResourceProvider::ResourceId ids_to_unref[] = {4, 5};
    265     int counts[] = {1, 1};
    266     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    267 
    268     surface.ReturnUnusedResourcesToClient();
    269     ResourceProvider::ResourceId expected_returned_ids[] = {5};
    270     int expected_returned_counts[] = {1};
    271     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    272                                         expected_returned_counts,
    273                                         arraysize(expected_returned_counts),
    274                                         client.returned_resources());
    275   }
    276 
    277   // Now, just 2 refs remaining on resource 4. Unref both at once and make sure
    278   // the returned count is correct.
    279   {
    280     SCOPED_TRACE("unref only 4");
    281     ResourceProvider::ResourceId ids_to_unref[] = {4};
    282     int counts[] = {2};
    283     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    284 
    285     surface.ReturnUnusedResourcesToClient();
    286     ResourceProvider::ResourceId expected_returned_ids[] = {4};
    287     int expected_returned_counts[] = {2};
    288     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    289                                         expected_returned_counts,
    290                                         arraysize(expected_returned_counts),
    291                                         client.returned_resources());
    292   }
    293 }
    294 
    295 TEST(SurfaceTest, ResourceLifetime) {
    296   SurfaceManager manager;
    297   TestSurfaceClient client;
    298   Surface surface(&manager, &client, gfx::Size(5, 5));
    299 
    300   ResourceProvider::ResourceId first_frame_ids[] = {1, 2, 3};
    301   SubmitFrameWithResources(
    302       first_frame_ids, arraysize(first_frame_ids), &surface);
    303 
    304   // All of the resources submitted in the first frame are still in use at this
    305   // time by virtue of being in the pending frame, so none can be returned to
    306   // the client yet.
    307   surface.ReturnUnusedResourcesToClient();
    308   EXPECT_EQ(0u, client.returned_resources().size());
    309   client.clear_returned_resources();
    310 
    311   // The second frame references some of the same resources, but some different
    312   // ones. We expect to receive back resource 1 with a count of 1 since it was
    313   // only referenced by the first frame.
    314   ResourceProvider::ResourceId second_frame_ids[] = {2, 3, 4};
    315   SubmitFrameWithResources(
    316       second_frame_ids, arraysize(second_frame_ids), &surface);
    317 
    318   surface.ReturnUnusedResourcesToClient();
    319   {
    320     SCOPED_TRACE("second frame");
    321     ResourceProvider::ResourceId expected_returned_ids[] = {1};
    322     int expected_returned_counts[] = {1};
    323     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    324                                         expected_returned_counts,
    325                                         arraysize(expected_returned_counts),
    326                                         client.returned_resources());
    327   }
    328 
    329   // The third frame references a disjoint set of resources, so we expect to
    330   // receive back all resources from the first and second frames. Resource IDs 2
    331   // and 3 will have counts of 2, since they were used in both frames, and
    332   // resource ID 4 will have a count of 1.
    333   ResourceProvider::ResourceId third_frame_ids[] = {10, 11, 12, 13};
    334   SubmitFrameWithResources(
    335       third_frame_ids, arraysize(third_frame_ids), &surface);
    336 
    337   surface.ReturnUnusedResourcesToClient();
    338   {
    339     SCOPED_TRACE("third frame");
    340     ResourceProvider::ResourceId expected_returned_ids[] = {2, 3, 4};
    341     int expected_returned_counts[] = {2, 2, 1};
    342     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    343                                         expected_returned_counts,
    344                                         arraysize(expected_returned_counts),
    345                                         client.returned_resources());
    346   }
    347 
    348   // Simulate a ResourceProvider taking a ref on all of the resources.
    349   surface.RefCurrentFrameResources();
    350 
    351   ResourceProvider::ResourceId fourth_frame_ids[] = {12, 13};
    352   SubmitFrameWithResources(
    353       fourth_frame_ids, arraysize(fourth_frame_ids), &surface);
    354 
    355   surface.ReturnUnusedResourcesToClient();
    356   EXPECT_EQ(0u, client.returned_resources().size());
    357 
    358   surface.RefCurrentFrameResources();
    359 
    360   // All resources are still being used by the external reference, so none can
    361   // be returned to the client.
    362   surface.ReturnUnusedResourcesToClient();
    363   EXPECT_EQ(0u, client.returned_resources().size());
    364 
    365   // Release resources associated with the first RefCurrentFrameResources() call
    366   // first.
    367   {
    368     ResourceProvider::ResourceId ids_to_unref[] = {10, 11, 12, 13};
    369     int counts[] = {1, 1, 1, 1};
    370     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    371   }
    372 
    373   surface.ReturnUnusedResourcesToClient();
    374   {
    375     SCOPED_TRACE("fourth frame, first unref");
    376     ResourceProvider::ResourceId expected_returned_ids[] = {10, 11};
    377     int expected_returned_counts[] = {1, 1};
    378     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    379                                         expected_returned_counts,
    380                                         arraysize(expected_returned_counts),
    381                                         client.returned_resources());
    382   }
    383 
    384   {
    385     ResourceProvider::ResourceId ids_to_unref[] = {12, 13};
    386     int counts[] = {1, 1};
    387     UnrefResources(ids_to_unref, counts, arraysize(ids_to_unref), &surface);
    388   }
    389 
    390   // Resources 12 and 13 are still in use by the current frame, so they
    391   // shouldn't be available to be returned.
    392   surface.ReturnUnusedResourcesToClient();
    393   EXPECT_EQ(0u, client.returned_resources().size());
    394 
    395   // If we submit an empty frame, however, they should become available.
    396   SubmitFrameWithResources(NULL, 0u, &surface);
    397 
    398   surface.ReturnUnusedResourcesToClient();
    399   {
    400     SCOPED_TRACE("fourth frame, second unref");
    401     ResourceProvider::ResourceId expected_returned_ids[] = {12, 13};
    402     int expected_returned_counts[] = {2, 2};
    403     CheckReturnedResourcesMatchExpected(expected_returned_ids,
    404                                         expected_returned_counts,
    405                                         arraysize(expected_returned_counts),
    406                                         client.returned_resources());
    407   }
    408 }
    409 
    410 }  // namespace
    411 }  // namespace cc
    412