2
* Copyright 2019 Collabora Ltd.
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:
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
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.
24
#include <gtest/gtest.h>
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"
32
#include "util/u_inlines.h"
33
#include "util/u_memory.h"
36
struct pipe_reference reference;
43
static struct virgl_hw_res *
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)
53
struct virgl_hw_res *hw_res = CALLOC_STRUCT(virgl_hw_res);
55
pipe_reference_init(&hw_res->reference, 1);
57
hw_res->target = target;
60
hw_res->data = CALLOC(size, 1);
66
fake_resource_reference(struct virgl_winsys *vws,
67
struct virgl_hw_res **dres,
68
struct virgl_hw_res *sres)
70
struct virgl_hw_res *old = *dres;
72
if (pipe_reference(&(*dres)->reference, &sres->reference)) {
81
fake_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res)
86
static struct pipe_context *
87
fake_virgl_context_create()
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);
93
vctx->base.screen = &vs->base;
96
vs->vws->resource_create = fake_resource_create;
97
vs->vws->resource_reference = fake_resource_reference;
98
vs->vws->resource_map = fake_resource_map;
104
fake_virgl_context_destroy(struct pipe_context *ctx)
106
struct virgl_context *vctx = virgl_context(ctx);
107
struct virgl_screen *vs = virgl_screen(ctx->screen);
115
resource_map(struct virgl_hw_res *hw_res)
121
release_resources(struct virgl_hw_res *resources[], unsigned len)
123
for (unsigned i = 0; i < len; ++i)
124
fake_resource_reference(NULL, &resources[i], NULL);
127
class VirglStagingMgr : public ::testing::Test
130
VirglStagingMgr() : ctx(fake_virgl_context_create())
132
virgl_staging_init(&staging, ctx, staging_size);
137
virgl_staging_destroy(&staging);
138
fake_virgl_context_destroy(ctx);
141
static const unsigned staging_size;
142
struct pipe_context * const ctx;
143
struct virgl_staging_mgr staging;
146
const unsigned VirglStagingMgr::staging_size = 4096;
148
class VirglStagingMgrWithAlignment : public VirglStagingMgr,
149
public ::testing::WithParamInterface<unsigned>
152
VirglStagingMgrWithAlignment() : alignment(GetParam()) {}
153
const unsigned alignment;
156
TEST_P(VirglStagingMgrWithAlignment,
157
suballocations_are_non_overlapping_in_same_resource)
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;
165
bool alloc_succeeded;
167
for (unsigned i = 0; i < num_resources; ++i) {
169
virgl_staging_alloc(&staging, alloc_sizes[i], alignment, &out_offset,
170
&out_resource[i], &map_ptr);
172
EXPECT_TRUE(alloc_succeeded);
173
EXPECT_EQ(out_offset, expected_offset);
174
ASSERT_NE(out_resource[i], nullptr);
176
EXPECT_EQ(out_resource[i], out_resource[i - 1]);
179
(uint8_t*)resource_map(out_resource[i]) + expected_offset);
181
expected_offset += alloc_sizes[i];
182
expected_offset = align(expected_offset, alignment);
185
release_resources(out_resource, num_resources);
188
INSTANTIATE_TEST_CASE_P(WithAlignment,
189
VirglStagingMgrWithAlignment,
190
::testing::Values(1, 16),
191
testing::PrintToStringParamName());
193
TEST_F(VirglStagingMgr,
194
non_fitting_allocation_reallocates_resource)
196
struct virgl_hw_res *out_resource[2] = {0};
199
bool alloc_succeeded;
202
virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset,
203
&out_resource[0], &map_ptr);
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]));
211
virgl_staging_alloc(&staging, 2, 1, &out_offset,
212
&out_resource[1], &map_ptr);
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);
222
release_resources(out_resource, 2);
225
TEST_F(VirglStagingMgr,
226
non_fitting_aligned_allocation_reallocates_resource)
228
struct virgl_hw_res *out_resource[2] = {0};
231
bool alloc_succeeded;
234
virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset,
235
&out_resource[0], &map_ptr);
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]));
243
virgl_staging_alloc(&staging, 1, 16, &out_offset,
244
&out_resource[1], &map_ptr);
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);
254
release_resources(out_resource, 2);
257
TEST_F(VirglStagingMgr,
258
large_non_fitting_allocation_reallocates_large_resource)
260
struct virgl_hw_res *out_resource[2] = {0};
263
bool alloc_succeeded;
265
ASSERT_LT(staging_size, 5123);
268
virgl_staging_alloc(&staging, 5123, 1, &out_offset,
269
&out_resource[0], &map_ptr);
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);
278
virgl_staging_alloc(&staging, 19345, 1, &out_offset,
279
&out_resource[1], &map_ptr);
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]));
286
EXPECT_NE(out_resource[1], out_resource[0]);
287
EXPECT_GE(out_resource[1]->size, 19345);
289
release_resources(out_resource, 2);
292
TEST_F(VirglStagingMgr, releases_resource_on_destruction)
294
struct virgl_hw_res *out_resource = NULL;
297
bool alloc_succeeded;
300
virgl_staging_alloc(&staging, 128, 1, &out_offset,
301
&out_resource, &map_ptr);
303
EXPECT_TRUE(alloc_succeeded);
304
ASSERT_NE(out_resource, nullptr);
305
/* The resource is referenced both by staging internally,
308
EXPECT_EQ(out_resource->reference.count, 2);
310
/* Destroying staging releases the internal reference. */
311
virgl_staging_destroy(&staging);
312
EXPECT_EQ(out_resource->reference.count, 1);
314
release_resources(&out_resource, 1);
317
static struct virgl_hw_res *
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)
330
TEST_F(VirglStagingMgr, fails_gracefully_if_resource_create_fails)
332
struct virgl_screen *vs = virgl_screen(ctx->screen);
333
struct virgl_hw_res *out_resource = NULL;
336
bool alloc_succeeded;
338
vs->vws->resource_create = failing_resource_create;
341
virgl_staging_alloc(&staging, 128, 1, &out_offset,
342
&out_resource, &map_ptr);
344
EXPECT_FALSE(alloc_succeeded);
345
EXPECT_EQ(out_resource, nullptr);
346
EXPECT_EQ(map_ptr, nullptr);
350
failing_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res)
355
TEST_F(VirglStagingMgr, fails_gracefully_if_map_fails)
357
struct virgl_screen *vs = virgl_screen(ctx->screen);
358
struct virgl_hw_res *out_resource = NULL;
361
bool alloc_succeeded;
363
vs->vws->resource_map = failing_resource_map;
366
virgl_staging_alloc(&staging, 128, 1, &out_offset,
367
&out_resource, &map_ptr);
369
EXPECT_FALSE(alloc_succeeded);
370
EXPECT_EQ(out_resource, nullptr);
371
EXPECT_EQ(map_ptr, nullptr);
374
TEST_F(VirglStagingMgr, uses_staging_buffer_resource)
376
struct virgl_hw_res *out_resource = NULL;
379
bool alloc_succeeded;
382
virgl_staging_alloc(&staging, 128, 1, &out_offset,
383
&out_resource, &map_ptr);
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);
390
release_resources(&out_resource, 1);