From 790d1a0b17376ac3c53c123f7a1b117220d71f95 Mon Sep 17 00:00:00 2001 From: Alexandros Frantzis Date: Tue, 25 Jun 2019 13:56:52 +0300 Subject: [PATCH] virgl: Add tests for virgl_staging_mgr Signed-off-by: Alexandros Frantzis Reviewed-by: Chia-I Wu --- src/gallium/drivers/virgl/meson.build | 4 + src/gallium/drivers/virgl/tests/meson.build | 31 ++ .../drivers/virgl/tests/virgl_staging_mgr_test.cpp | 389 +++++++++++++++++++++ 3 files changed, 424 insertions(+) create mode 100644 src/gallium/drivers/virgl/tests/meson.build create mode 100644 src/gallium/drivers/virgl/tests/virgl_staging_mgr_test.cpp diff --git a/src/gallium/drivers/virgl/meson.build b/src/gallium/drivers/virgl/meson.build index e3c67a2..0c04370 100644 --- a/src/gallium/drivers/virgl/meson.build +++ b/src/gallium/drivers/virgl/meson.build @@ -56,3 +56,7 @@ driver_virgl = declare_dependency( compile_args : '-DGALLIUM_VIRGL', link_with : [libvirgl, libvirgldrm, libvirglvtest], ) + +if with_tests + subdir('tests') +endif diff --git a/src/gallium/drivers/virgl/tests/meson.build b/src/gallium/drivers/virgl/tests/meson.build new file mode 100644 index 0000000..86cb27d --- /dev/null +++ b/src/gallium/drivers/virgl/tests/meson.build @@ -0,0 +1,31 @@ +# Copyright © 2019 Collabora Ltd. + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. + +test( + 'virgl_staging_mgr', + executable( + 'virgl_staging_mgr_test', + files('virgl_staging_mgr_test.cpp'), + dependencies : [dep_thread, idep_gtest], + include_directories : [inc_common, include_directories('..')], + link_with : [libvirgl, libgallium], + ), + suite : ['virgl'], +) diff --git a/src/gallium/drivers/virgl/tests/virgl_staging_mgr_test.cpp b/src/gallium/drivers/virgl/tests/virgl_staging_mgr_test.cpp new file mode 100644 index 0000000..b241f39 --- /dev/null +++ b/src/gallium/drivers/virgl/tests/virgl_staging_mgr_test.cpp @@ -0,0 +1,389 @@ +/* + * Copyright 2019 Collabora Ltd. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include + +#include "virgl_context.h" +#include "virgl_resource.h" +#include "virgl_screen.h" +#include "virgl_staging_mgr.h" +#include "virgl_winsys.h" + +#include "util/u_inlines.h" +#include "util/u_memory.h" + +struct virgl_hw_res { + struct pipe_reference reference; + uint32_t target; + uint32_t bind; + uint32_t size; + void *data; +}; + +static struct virgl_hw_res * +fake_resource_create(struct virgl_winsys *vws, + enum pipe_texture_target target, + 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 size) +{ + struct virgl_hw_res *hw_res = CALLOC_STRUCT(virgl_hw_res); + + pipe_reference_init(&hw_res->reference, 1); + + hw_res->target = target; + hw_res->bind = bind; + hw_res->size = size; + hw_res->data = CALLOC(size, 1); + + return hw_res; +} + +static void +fake_resource_reference(struct virgl_winsys *vws, + struct virgl_hw_res **dres, + struct virgl_hw_res *sres) +{ + struct virgl_hw_res *old = *dres; + + if (pipe_reference(&(*dres)->reference, &sres->reference)) { + FREE(old->data); + FREE(old); + } + + *dres = sres; +} + +static void * +fake_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res) +{ + return hw_res->data; +} + +static struct pipe_context * +fake_virgl_context_create() +{ + struct virgl_context *vctx = CALLOC_STRUCT(virgl_context); + struct virgl_screen *vs = CALLOC_STRUCT(virgl_screen); + struct virgl_winsys *vws = CALLOC_STRUCT(virgl_winsys); + + vctx->base.screen = &vs->base; + vs->vws = vws; + + vs->vws->resource_create = fake_resource_create; + vs->vws->resource_reference = fake_resource_reference; + vs->vws->resource_map = fake_resource_map; + + return &vctx->base; +} + +static void +fake_virgl_context_destroy(struct pipe_context *ctx) +{ + struct virgl_context *vctx = virgl_context(ctx); + struct virgl_screen *vs = virgl_screen(ctx->screen); + + FREE(vs->vws); + FREE(vs); + FREE(vctx); +} + +static void * +resource_map(struct virgl_hw_res *hw_res) +{ + return hw_res->data; +} + +static void +release_resources(struct virgl_hw_res *resources[], unsigned len) +{ + for (unsigned i = 0; i < len; ++i) + fake_resource_reference(NULL, &resources[i], NULL); +} + +class VirglStagingMgr : public ::testing::Test +{ +protected: + VirglStagingMgr() : ctx(fake_virgl_context_create()) + { + virgl_staging_init(&staging, ctx, staging_size); + } + + ~VirglStagingMgr() + { + virgl_staging_destroy(&staging); + fake_virgl_context_destroy(ctx); + } + + static const unsigned staging_size; + struct pipe_context * const ctx; + struct virgl_staging_mgr staging; +}; + +const unsigned VirglStagingMgr::staging_size = 4096; + +class VirglStagingMgrWithAlignment : public VirglStagingMgr, + public ::testing::WithParamInterface +{ +protected: + VirglStagingMgrWithAlignment() : alignment(GetParam()) {} + const unsigned alignment; +}; + +TEST_P(VirglStagingMgrWithAlignment, + suballocations_are_non_overlapping_in_same_resource) +{ + const unsigned alloc_sizes[] = {16, 450, 79, 240, 128, 1001}; + const unsigned num_resources = sizeof(alloc_sizes) / sizeof(alloc_sizes[0]); + struct virgl_hw_res *out_resource[num_resources] = {0}; + unsigned expected_offset = 0; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + for (unsigned i = 0; i < num_resources; ++i) { + alloc_succeeded = + virgl_staging_alloc(&staging, alloc_sizes[i], alignment, &out_offset, + &out_resource[i], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, expected_offset); + ASSERT_NE(out_resource[i], nullptr); + if (i > 0) { + EXPECT_EQ(out_resource[i], out_resource[i - 1]); + } + EXPECT_EQ(map_ptr, + (uint8_t*)resource_map(out_resource[i]) + expected_offset); + + expected_offset += alloc_sizes[i]; + expected_offset = align(expected_offset, alignment); + } + + release_resources(out_resource, num_resources); +} + +INSTANTIATE_TEST_CASE_P(WithAlignment, + VirglStagingMgrWithAlignment, + ::testing::Values(1, 16), + testing::PrintToStringParamName()); + +TEST_F(VirglStagingMgr, + non_fitting_allocation_reallocates_resource) +{ + struct virgl_hw_res *out_resource[2] = {0}; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + alloc_succeeded = + virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset, + &out_resource[0], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[0], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[0])); + + alloc_succeeded = + virgl_staging_alloc(&staging, 2, 1, &out_offset, + &out_resource[1], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[1], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[1])); + /* New resource with same size as old resource. */ + EXPECT_NE(out_resource[1], out_resource[0]); + EXPECT_EQ(out_resource[1]->size, out_resource[0]->size); + + release_resources(out_resource, 2); +} + +TEST_F(VirglStagingMgr, + non_fitting_aligned_allocation_reallocates_resource) +{ + struct virgl_hw_res *out_resource[2] = {0}; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + alloc_succeeded = + virgl_staging_alloc(&staging, staging_size - 1, 1, &out_offset, + &out_resource[0], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[0], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[0])); + + alloc_succeeded = + virgl_staging_alloc(&staging, 1, 16, &out_offset, + &out_resource[1], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[1], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[1])); + /* New resource with same size as old resource. */ + EXPECT_NE(out_resource[1], out_resource[0]); + EXPECT_EQ(out_resource[1]->size, out_resource[0]->size); + + release_resources(out_resource, 2); +} + +TEST_F(VirglStagingMgr, + large_non_fitting_allocation_reallocates_large_resource) +{ + struct virgl_hw_res *out_resource[2] = {0}; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + ASSERT_LT(staging_size, 5123); + + alloc_succeeded = + virgl_staging_alloc(&staging, 5123, 1, &out_offset, + &out_resource[0], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[0], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[0])); + EXPECT_GE(out_resource[0]->size, 5123); + + alloc_succeeded = + virgl_staging_alloc(&staging, 19345, 1, &out_offset, + &out_resource[1], &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + EXPECT_EQ(out_offset, 0); + ASSERT_NE(out_resource[1], nullptr); + EXPECT_EQ(map_ptr, resource_map(out_resource[1])); + /* New resource */ + EXPECT_NE(out_resource[1], out_resource[0]); + EXPECT_GE(out_resource[1]->size, 19345); + + release_resources(out_resource, 2); +} + +TEST_F(VirglStagingMgr, releases_resource_on_destruction) +{ + struct virgl_hw_res *out_resource = NULL; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + alloc_succeeded = + virgl_staging_alloc(&staging, 128, 1, &out_offset, + &out_resource, &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + ASSERT_NE(out_resource, nullptr); + /* The resource is referenced both by staging internally, + * and out_resource. + */ + EXPECT_EQ(out_resource->reference.count, 2); + + /* Destroying staging releases the internal reference. */ + virgl_staging_destroy(&staging); + EXPECT_EQ(out_resource->reference.count, 1); + + release_resources(&out_resource, 1); +} + +static struct virgl_hw_res * +failing_resource_create(struct virgl_winsys *vws, + enum pipe_texture_target target, + 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 size) +{ + return NULL; +} + +TEST_F(VirglStagingMgr, fails_gracefully_if_resource_create_fails) +{ + struct virgl_screen *vs = virgl_screen(ctx->screen); + struct virgl_hw_res *out_resource = NULL; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + vs->vws->resource_create = failing_resource_create; + + alloc_succeeded = + virgl_staging_alloc(&staging, 128, 1, &out_offset, + &out_resource, &map_ptr); + + EXPECT_FALSE(alloc_succeeded); + EXPECT_EQ(out_resource, nullptr); + EXPECT_EQ(map_ptr, nullptr); +} + +static void * +failing_resource_map(struct virgl_winsys *vws, struct virgl_hw_res *hw_res) +{ + return NULL; +} + +TEST_F(VirglStagingMgr, fails_gracefully_if_map_fails) +{ + struct virgl_screen *vs = virgl_screen(ctx->screen); + struct virgl_hw_res *out_resource = NULL; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + vs->vws->resource_map = failing_resource_map; + + alloc_succeeded = + virgl_staging_alloc(&staging, 128, 1, &out_offset, + &out_resource, &map_ptr); + + EXPECT_FALSE(alloc_succeeded); + EXPECT_EQ(out_resource, nullptr); + EXPECT_EQ(map_ptr, nullptr); +} + +TEST_F(VirglStagingMgr, uses_staging_buffer_resource) +{ + struct virgl_hw_res *out_resource = NULL; + unsigned out_offset; + void *map_ptr; + bool alloc_succeeded; + + alloc_succeeded = + virgl_staging_alloc(&staging, 128, 1, &out_offset, + &out_resource, &map_ptr); + + EXPECT_TRUE(alloc_succeeded); + ASSERT_NE(out_resource, nullptr); + EXPECT_EQ(out_resource->target, PIPE_BUFFER); + EXPECT_EQ(out_resource->bind, VIRGL_BIND_STAGING); + + release_resources(&out_resource, 1); +} -- 2.7.4