• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Collabora Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * on the rights to use, copy, modify, merge, publish, distribute, sub
8  * license, and/or sell copies of the Software, and to permit persons to whom
9  * the Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice (including the next
12  * paragraph) shall be included in all copies or substantial portions of the
13  * Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17  * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
18  * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
21  * USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23 
24 #include <gtest/gtest.h>
25 
26 #include "virgl_context.h"
27 #include "virgl_resource.h"
28 #include "virgl_screen.h"
29 #include "virgl_staging_mgr.h"
30 #include "virgl_winsys.h"
31 
32 #include "util/u_inlines.h"
33 #include "util/u_memory.h"
34 
35 struct virgl_hw_res {
36     struct pipe_reference reference;
37     uint32_t target;
38     uint32_t bind;
39     uint32_t size;
40     void *data;
41 };
42 
43 static struct virgl_hw_res *
fake_resource_create(struct virgl_winsys * vws,enum pipe_texture_target target,const void * map_front_private,uint32_t format,uint32_t bind,uint32_t width,uint32_t height,uint32_t depth,uint32_t array_size,uint32_t last_level,uint32_t nr_samples,uint32_t flags,uint32_t size)44 fake_resource_create(struct virgl_winsys *vws,
45                      enum pipe_texture_target target,
46                      const void *map_front_private,
47                      uint32_t format, uint32_t bind,
48                      uint32_t width, uint32_t height,
49                      uint32_t depth, uint32_t array_size,
50                      uint32_t last_level, uint32_t nr_samples,
51                      uint32_t flags, uint32_t size)
52 {
53    struct virgl_hw_res *hw_res = CALLOC_STRUCT(virgl_hw_res);
54 
55    pipe_reference_init(&hw_res->reference, 1);
56 
57    hw_res->target = target;
58    hw_res->bind = bind;
59    hw_res->size = size;
60    hw_res->data = CALLOC(size, 1);
61 
62    return hw_res;
63 }
64 
65 static void
fake_resource_reference(struct virgl_winsys * vws,struct virgl_hw_res ** dres,struct virgl_hw_res * sres)66 fake_resource_reference(struct virgl_winsys *vws,
67                         struct virgl_hw_res **dres,
68                         struct virgl_hw_res *sres)
69 {
70    struct virgl_hw_res *old = *dres;
71 
72    if (pipe_reference(&(*dres)->reference, &sres->reference)) {
73       FREE(old->data);
74       FREE(old);
75    }
76 
77    *dres = sres;
78 }
79 
80 static void *
fake_resource_map(struct virgl_winsys * vws,struct virgl_hw_res * hw_res)81 fake_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res)
82 {
83    return hw_res->data;
84 }
85 
86 static struct pipe_context *
fake_virgl_context_create()87 fake_virgl_context_create()
88 {
89    struct virgl_context *vctx = CALLOC_STRUCT(virgl_context);
90    struct virgl_screen *vs = CALLOC_STRUCT(virgl_screen);
91    struct virgl_winsys *vws = CALLOC_STRUCT(virgl_winsys);
92 
93    vctx->base.screen = &vs->base;
94    vs->vws = vws;
95 
96    vs->vws->resource_create = fake_resource_create;
97    vs->vws->resource_reference = fake_resource_reference;
98    vs->vws->resource_map = fake_resource_map;
99 
100    return &vctx->base;
101 }
102 
103 static void
fake_virgl_context_destroy(struct pipe_context * ctx)104 fake_virgl_context_destroy(struct pipe_context *ctx)
105 {
106    struct virgl_context *vctx = virgl_context(ctx);
107    struct virgl_screen *vs = virgl_screen(ctx->screen);
108 
109    FREE(vs->vws);
110    FREE(vs);
111    FREE(vctx);
112 }
113 
114 static void *
resource_map(struct virgl_hw_res * hw_res)115 resource_map(struct virgl_hw_res *hw_res)
116 {
117    return hw_res->data;
118 }
119 
120 static void
release_resources(struct virgl_hw_res * resources[],unsigned len)121 release_resources(struct virgl_hw_res *resources[], unsigned len)
122 {
123    for (unsigned i = 0; i < len; ++i)
124       fake_resource_reference(NULL, &resources[i], NULL);
125 }
126 
127 class VirglStagingMgr : public ::testing::Test
128 {
129 protected:
VirglStagingMgr()130    VirglStagingMgr() : ctx(fake_virgl_context_create())
131    {
132       virgl_staging_init(&staging, ctx, staging_size);
133    }
134 
~VirglStagingMgr()135    ~VirglStagingMgr()
136    {
137       virgl_staging_destroy(&staging);
138       fake_virgl_context_destroy(ctx);
139    }
140 
141    static const unsigned staging_size;
142    struct pipe_context * const ctx;
143    struct virgl_staging_mgr staging;
144 };
145 
146 const unsigned VirglStagingMgr::staging_size = 4096;
147 
148 class VirglStagingMgrWithAlignment : public VirglStagingMgr,
149                                      public ::testing::WithParamInterface<unsigned>
150 {
151 protected:
VirglStagingMgrWithAlignment()152    VirglStagingMgrWithAlignment() : alignment(GetParam()) {}
153    const unsigned alignment;
154 };
155 
TEST_P(VirglStagingMgrWithAlignment,suballocations_are_non_overlapping_in_same_resource)156 TEST_P(VirglStagingMgrWithAlignment,
157        suballocations_are_non_overlapping_in_same_resource)
158 {
159    const unsigned alloc_sizes[] = {16, 450, 79, 240, 128, 1001};
160    const unsigned num_resources = sizeof(alloc_sizes) / sizeof(alloc_sizes[0]);
161    struct virgl_hw_res *out_resource[num_resources] = {0};
162    unsigned expected_offset = 0;
163    unsigned out_offset;
164    void *map_ptr;
165    bool alloc_succeeded;
166 
167    for (unsigned i = 0; i < num_resources; ++i) {
168       alloc_succeeded =
169          virgl_staging_alloc(&staging, alloc_sizes[i], alignment, &out_offset,
170                            &out_resource[i], &map_ptr);
171 
172       EXPECT_TRUE(alloc_succeeded);
173       EXPECT_EQ(out_offset, expected_offset);
174       ASSERT_NE(out_resource[i], nullptr);
175       if (i > 0) {
176          EXPECT_EQ(out_resource[i], out_resource[i - 1]);
177       }
178       EXPECT_EQ(map_ptr,
179             (uint8_t*)resource_map(out_resource[i]) + expected_offset);
180 
181       expected_offset += alloc_sizes[i];
182       expected_offset = align(expected_offset, alignment);
183    }
184 
185    release_resources(out_resource, num_resources);
186 }
187 
188 INSTANTIATE_TEST_CASE_P(WithAlignment,
189                         VirglStagingMgrWithAlignment,
190                         ::testing::Values(1, 16),
191                         testing::PrintToStringParamName());
192 
TEST_F(VirglStagingMgr,non_fitting_allocation_reallocates_resource)193 TEST_F(VirglStagingMgr,
194        non_fitting_allocation_reallocates_resource)
195 {
196    struct virgl_hw_res *out_resource[2] = {0};
197    unsigned out_offset;
198    void *map_ptr;
199    bool alloc_succeeded;
200 
201    alloc_succeeded =
202       virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset,
203                           &out_resource[0], &map_ptr);
204 
205    EXPECT_TRUE(alloc_succeeded);
206    EXPECT_EQ(out_offset, 0);
207    ASSERT_NE(out_resource[0], nullptr);
208    EXPECT_EQ(map_ptr, resource_map(out_resource[0]));
209 
210    alloc_succeeded =
211       virgl_staging_alloc(&staging, 2, 1, &out_offset,
212                           &out_resource[1], &map_ptr);
213 
214    EXPECT_TRUE(alloc_succeeded);
215    EXPECT_EQ(out_offset, 0);
216    ASSERT_NE(out_resource[1], nullptr);
217    EXPECT_EQ(map_ptr, resource_map(out_resource[1]));
218    /* New resource with same size as old resource. */
219    EXPECT_NE(out_resource[1], out_resource[0]);
220    EXPECT_EQ(out_resource[1]->size, out_resource[0]->size);
221 
222    release_resources(out_resource, 2);
223 }
224 
TEST_F(VirglStagingMgr,non_fitting_aligned_allocation_reallocates_resource)225 TEST_F(VirglStagingMgr,
226        non_fitting_aligned_allocation_reallocates_resource)
227 {
228    struct virgl_hw_res *out_resource[2] = {0};
229    unsigned out_offset;
230    void *map_ptr;
231    bool alloc_succeeded;
232 
233    alloc_succeeded =
234       virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset,
235                           &out_resource[0], &map_ptr);
236 
237    EXPECT_TRUE(alloc_succeeded);
238    EXPECT_EQ(out_offset, 0);
239    ASSERT_NE(out_resource[0], nullptr);
240    EXPECT_EQ(map_ptr, resource_map(out_resource[0]));
241 
242    alloc_succeeded =
243       virgl_staging_alloc(&staging, 1, 16, &out_offset,
244                           &out_resource[1], &map_ptr);
245 
246    EXPECT_TRUE(alloc_succeeded);
247    EXPECT_EQ(out_offset, 0);
248    ASSERT_NE(out_resource[1], nullptr);
249    EXPECT_EQ(map_ptr, resource_map(out_resource[1]));
250    /* New resource with same size as old resource. */
251    EXPECT_NE(out_resource[1], out_resource[0]);
252    EXPECT_EQ(out_resource[1]->size, out_resource[0]->size);
253 
254    release_resources(out_resource, 2);
255 }
256 
TEST_F(VirglStagingMgr,large_non_fitting_allocation_reallocates_large_resource)257 TEST_F(VirglStagingMgr,
258        large_non_fitting_allocation_reallocates_large_resource)
259 {
260    struct virgl_hw_res *out_resource[2] = {0};
261    unsigned out_offset;
262    void *map_ptr;
263    bool alloc_succeeded;
264 
265    ASSERT_LT(staging_size, 5123);
266 
267    alloc_succeeded =
268       virgl_staging_alloc(&staging, 5123, 1, &out_offset,
269                           &out_resource[0], &map_ptr);
270 
271    EXPECT_TRUE(alloc_succeeded);
272    EXPECT_EQ(out_offset, 0);
273    ASSERT_NE(out_resource[0], nullptr);
274    EXPECT_EQ(map_ptr, resource_map(out_resource[0]));
275    EXPECT_GE(out_resource[0]->size, 5123);
276 
277    alloc_succeeded =
278       virgl_staging_alloc(&staging, 19345, 1, &out_offset,
279                           &out_resource[1], &map_ptr);
280 
281    EXPECT_TRUE(alloc_succeeded);
282    EXPECT_EQ(out_offset, 0);
283    ASSERT_NE(out_resource[1], nullptr);
284    EXPECT_EQ(map_ptr, resource_map(out_resource[1]));
285    /* New resource */
286    EXPECT_NE(out_resource[1], out_resource[0]);
287    EXPECT_GE(out_resource[1]->size, 19345);
288 
289    release_resources(out_resource, 2);
290 }
291 
TEST_F(VirglStagingMgr,releases_resource_on_destruction)292 TEST_F(VirglStagingMgr, releases_resource_on_destruction)
293 {
294    struct virgl_hw_res *out_resource = NULL;
295    unsigned out_offset;
296    void *map_ptr;
297    bool alloc_succeeded;
298 
299    alloc_succeeded =
300       virgl_staging_alloc(&staging, 128, 1, &out_offset,
301                           &out_resource, &map_ptr);
302 
303    EXPECT_TRUE(alloc_succeeded);
304    ASSERT_NE(out_resource, nullptr);
305    /* The resource is referenced both by staging internally,
306     * and out_resource.
307     */
308    EXPECT_EQ(out_resource->reference.count, 2);
309 
310    /* Destroying staging releases the internal reference. */
311    virgl_staging_destroy(&staging);
312    EXPECT_EQ(out_resource->reference.count, 1);
313 
314    release_resources(&out_resource, 1);
315 }
316 
317 static struct virgl_hw_res *
failing_resource_create(struct virgl_winsys * vws,enum pipe_texture_target target,const void * map_front_private,uint32_t format,uint32_t bind,uint32_t width,uint32_t height,uint32_t depth,uint32_t array_size,uint32_t last_level,uint32_t nr_samples,uint32_t flags,uint32_t size)318 failing_resource_create(struct virgl_winsys *vws,
319                         enum pipe_texture_target target,
320                         const void *map_front_private,
321                         uint32_t format, uint32_t bind,
322                         uint32_t width, uint32_t height,
323                         uint32_t depth, uint32_t array_size,
324                         uint32_t last_level, uint32_t nr_samples,
325                         uint32_t flags, uint32_t size)
326 {
327    return NULL;
328 }
329 
TEST_F(VirglStagingMgr,fails_gracefully_if_resource_create_fails)330 TEST_F(VirglStagingMgr, fails_gracefully_if_resource_create_fails)
331 {
332    struct virgl_screen *vs = virgl_screen(ctx->screen);
333    struct virgl_hw_res *out_resource = NULL;
334    unsigned out_offset;
335    void *map_ptr;
336    bool alloc_succeeded;
337 
338    vs->vws->resource_create = failing_resource_create;
339 
340    alloc_succeeded =
341       virgl_staging_alloc(&staging, 128, 1, &out_offset,
342                           &out_resource, &map_ptr);
343 
344    EXPECT_FALSE(alloc_succeeded);
345    EXPECT_EQ(out_resource, nullptr);
346    EXPECT_EQ(map_ptr, nullptr);
347 }
348 
349 static void *
failing_resource_map(struct virgl_winsys * vws,struct virgl_hw_res * hw_res)350 failing_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res)
351 {
352    return NULL;
353 }
354 
TEST_F(VirglStagingMgr,fails_gracefully_if_map_fails)355 TEST_F(VirglStagingMgr, fails_gracefully_if_map_fails)
356 {
357    struct virgl_screen *vs = virgl_screen(ctx->screen);
358    struct virgl_hw_res *out_resource = NULL;
359    unsigned out_offset;
360    void *map_ptr;
361    bool alloc_succeeded;
362 
363    vs->vws->resource_map = failing_resource_map;
364 
365    alloc_succeeded =
366       virgl_staging_alloc(&staging, 128, 1, &out_offset,
367                           &out_resource, &map_ptr);
368 
369    EXPECT_FALSE(alloc_succeeded);
370    EXPECT_EQ(out_resource, nullptr);
371    EXPECT_EQ(map_ptr, nullptr);
372 }
373 
TEST_F(VirglStagingMgr,uses_staging_buffer_resource)374 TEST_F(VirglStagingMgr, uses_staging_buffer_resource)
375 {
376    struct virgl_hw_res *out_resource = NULL;
377    unsigned out_offset;
378    void *map_ptr;
379    bool alloc_succeeded;
380 
381    alloc_succeeded =
382       virgl_staging_alloc(&staging, 128, 1, &out_offset,
383                           &out_resource, &map_ptr);
384 
385    EXPECT_TRUE(alloc_succeeded);
386    ASSERT_NE(out_resource, nullptr);
387    EXPECT_EQ(out_resource->target, PIPE_BUFFER);
388    EXPECT_EQ(out_resource->bind, VIRGL_BIND_STAGING);
389 
390    release_resources(&out_resource, 1);
391 }
392