virgl: Add tests for virgl_staging_mgr
authorAlexandros Frantzis <alexandros.frantzis@collabora.com>
Tue, 25 Jun 2019 10:56:52 +0000 (13:56 +0300)
committerChia-I Wu <olvaffe@gmail.com>
Fri, 28 Jun 2019 04:30:02 +0000 (04:30 +0000)
Signed-off-by: Alexandros Frantzis <alexandros.frantzis@collabora.com>
Reviewed-by: Chia-I Wu <olvaffe@gmail.com>
src/gallium/drivers/virgl/meson.build
src/gallium/drivers/virgl/tests/meson.build [new file with mode: 0644]
src/gallium/drivers/virgl/tests/virgl_staging_mgr_test.cpp [new file with mode: 0644]

index e3c67a29a1048723e47eb5078391cad26a5afa6b..0c04370d4ec734a124d77d1eaabe4f79eb45e09b 100644 (file)
@@ -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 (file)
index 0000000..86cb27d
--- /dev/null
@@ -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 (file)
index 0000000..b241f39
--- /dev/null
@@ -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 <gtest/gtest.h>
+
+#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<unsigned>
+{
+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);
+}