Add support for RenderDoc in Vulkan tests
authorCygan, Slawomir <slawomir.cygan@intel.com>
Thu, 5 Jul 2018 16:54:09 +0000 (18:54 +0200)
committerAlexander Galazin <Alexander.Galazin@arm.com>
Fri, 7 Dec 2018 11:56:33 +0000 (06:56 -0500)
This add --deqp-renderdoc=enable commandline, which adds
start frame / end frame signalization using RenderDoc 'in-application' API.

Components: Vulkan, Framework

VK-GL-CTS Issue: 1444

Change-Id: Ib9e8a978cecb96784405ec32495fcb2b156540be

12 files changed:
AndroidGen.mk
CMakeLists.txt
external/fetch_sources.py
external/renderdoc/.gitignore [new file with mode: 0644]
external/vulkancts/README.md
external/vulkancts/framework/vulkan/CMakeLists.txt
external/vulkancts/framework/vulkan/vkRenderDocUtil.cpp [new file with mode: 0644]
external/vulkancts/framework/vulkan/vkRenderDocUtil.hpp [new file with mode: 0644]
external/vulkancts/modules/vulkan/vktTestCase.cpp
external/vulkancts/modules/vulkan/vktTestPackage.cpp
framework/common/tcuCommandLine.cpp
framework/common/tcuCommandLine.hpp

index be11280..7628e3b 100644 (file)
@@ -45,6 +45,7 @@ LOCAL_SRC_FILES := \
        external/vulkancts/framework/vulkan/vkQueryUtil.cpp \
        external/vulkancts/framework/vulkan/vkRef.cpp \
        external/vulkancts/framework/vulkan/vkRefUtil.cpp \
+       external/vulkancts/framework/vulkan/vkRenderDocUtil.cpp \
        external/vulkancts/framework/vulkan/vkShaderProgram.cpp \
        external/vulkancts/framework/vulkan/vkShaderToSpirV.cpp \
        external/vulkancts/framework/vulkan/vkSpirVAsm.cpp \
index e1b2449..791a2f5 100644 (file)
@@ -95,6 +95,9 @@ if (NOT EXISTS ${SPIRV_INCLUDE_PATH})
 endif()
 include_directories(${SPIRV_INCLUDE_PATH})
 
+# RenderDoc API
+include_directories(external/renderdoc/src)
+
 include_directories(${PNG_INCLUDE_PATH})
 
 message(STATUS "DEQP_TARGET_NAME        = ${DEQP_TARGET_NAME}")
index 98fdbce..bdef993 100755 (executable)
@@ -175,6 +175,58 @@ class SourcePackage (Source):
                if self.postExtract != None:
                        self.postExtract(dstPath)
 
+class SourceFile (Source):
+       def __init__(self, url, filename, checksum, baseDir, extractDir = "src"):
+               Source.__init__(self, baseDir, extractDir)
+               self.url                        = url
+               self.filename           = filename
+               self.checksum           = checksum
+
+       def update (self, cmdProtocol = None):
+               if not self.isFileUpToDate():
+                       Source.clean(self)
+                       self.fetchAndVerifyFile()
+
+       def isFileUpToDate (self):
+               file = os.path.join(EXTERNAL_DIR, pkg.baseDir, pkg.extractDir, pkg.filename)
+               if os.path.exists(file):
+                       return computeChecksum(readFile(file)) == self.checksum
+               else:
+                       return False
+
+       def connectToUrl (self, url):
+               result = None
+
+               if sys.version_info < (3, 0):
+                       from urllib2 import urlopen
+               else:
+                       from urllib.request import urlopen
+
+               if args.insecure:
+                       print("Ignoring certificate checks")
+                       ssl_context = ssl._create_unverified_context()
+                       result = urlopen(url, context=ssl_context)
+               else:
+                       result = urlopen(url)
+
+               return result
+
+       def fetchAndVerifyFile (self):
+               print("Fetching %s" % self.url)
+
+               req                     = self.connectToUrl(self.url)
+               data            = req.read()
+               checksum        = computeChecksum(data)
+               dstPath         = os.path.join(EXTERNAL_DIR, self.baseDir, self.extractDir, self.filename)
+
+               if checksum != self.checksum:
+                       raise Exception("Checksum mismatch for %s, expected %s, got %s" % (self.filename, self.checksum, checksum))
+
+               if not os.path.exists(os.path.dirname(dstPath)):
+                       os.mkdir(os.path.dirname(dstPath))
+
+               writeFile(dstPath, data)
+
 class GitRepo (Source):
        def __init__(self, httpsUrl, sshUrl, revision, baseDir, extractDir = "src", removeTags = []):
                Source.__init__(self, baseDir, extractDir)
@@ -255,6 +307,11 @@ PACKAGES = [
                "c9d164ec247f426a525a7b89936694aefbc91fb7a50182b198898b8fc91174b4",
                "libpng",
                postExtract = postExtractLibpng),
+       SourceFile(
+               "https://raw.githubusercontent.com/baldurk/renderdoc/v1.1/renderdoc/api/app/renderdoc_app.h",
+               "renderdoc_app.h",
+               "e7b5f0aa5b1b0eadc63a1c624c0ca7f5af133aa857d6a4271b0ef3d0bdb6868e",
+               "renderdoc"),
        GitRepo(
                "https://github.com/KhronosGroup/SPIRV-Tools.git",
                None,
diff --git a/external/renderdoc/.gitignore b/external/renderdoc/.gitignore
new file mode 100644 (file)
index 0000000..85de9cf
--- /dev/null
@@ -0,0 +1 @@
+src
index eae0e74..224aeeb 100644 (file)
@@ -467,3 +467,18 @@ targets.
 
 Do not truncate the shader cache file at startup. No shader compilation will
 occur on repeated runs of the CTS.
+
+
+RenderDoc
+---------
+The RenderDoc (https://renderdoc.org/) graphics debugger may be used to debug
+Vulkan tests.
+
+Following command line option should be used when launching tests from
+RenderDoc UI:
+
+       --deqp-renderdoc=enable
+
+This causes the framework to interface with the debugger and mark each dEQP
+test case as a separate 'frame', just for the purpose of capturing. The frames
+are added using RenderDoc 'In-Application API', instead of swapchain operations.
index 66ba8d7..3fc871c 100644 (file)
@@ -56,6 +56,8 @@ set(VKUTILNOSHADER_SRCS
        vkYCbCrImageWithMemory.hpp
        vkObjUtil.cpp
        vkObjUtil.hpp
+       vkRenderDocUtil.hpp
+       vkRenderDocUtil.cpp
        )
 
 set(VKUTIL_SRCS
diff --git a/external/vulkancts/framework/vulkan/vkRenderDocUtil.cpp b/external/vulkancts/framework/vulkan/vkRenderDocUtil.cpp
new file mode 100644 (file)
index 0000000..ca5d1ee
--- /dev/null
@@ -0,0 +1,108 @@
+/*-------------------------------------------------------------------------
+ * Vulkan CTS Framework
+ * --------------------
+ *
+ * Copyright (c) 2018 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief RenderDoc utility
+ *//*--------------------------------------------------------------------*/
+
+#include "vkRenderDocUtil.hpp"
+
+#include "deDynamicLibrary.hpp"
+#include "deUniquePtr.hpp"
+#include "tcuDefs.hpp"
+
+#include "renderdoc_app.h"
+#include <stdexcept>
+
+#if (DE_OS == DE_OS_WIN32)
+#      define RENDERDOC_LIBRARY_NAME "renderdoc.dll"
+#else
+#      define RENDERDOC_LIBRARY_NAME "librenderdoc.so"
+#endif
+
+namespace vk
+{
+
+struct RenderDocPrivate
+{
+                                                                               RenderDocPrivate        (void)  : m_api(DE_NULL), m_valid(false) {}
+
+       de::MovePtr<de::DynamicLibrary>         m_library;
+       ::RENDERDOC_API_1_1_2*                          m_api;
+       bool                                                            m_valid;
+};
+
+RenderDocUtil::RenderDocUtil (void)
+       : m_priv        (new RenderDocPrivate)
+{
+       try
+       {
+               m_priv->m_library        = de::MovePtr<de::DynamicLibrary>(new de::DynamicLibrary(RENDERDOC_LIBRARY_NAME));
+       }
+       catch (const std::runtime_error& e)
+       {
+               tcu::print("Library %s not loaded: %s, RenderDoc API not available", e.what(), RENDERDOC_LIBRARY_NAME);
+       }
+
+       if (m_priv->m_library)
+       {
+               ::pRENDERDOC_GetAPI pGetApi = (::pRENDERDOC_GetAPI)m_priv->m_library->getFunction("RENDERDOC_GetAPI");
+               const int                       ret             = pGetApi(eRENDERDOC_API_Version_1_1_2, (void **)&m_priv->m_api);
+
+               if (ret == 1)
+               {
+                       m_priv->m_api->TriggerCapture();
+
+                       m_priv->m_valid = true;
+               }
+               else
+               {
+                       tcu::print("RENDERDOC_GetAPI returned %d status, RenderDoc API not available", ret);
+               }
+       }
+}
+
+RenderDocUtil::~RenderDocUtil (void)
+{
+       if (m_priv)
+       {
+               delete m_priv;
+       }
+}
+
+bool RenderDocUtil::isValid (void)
+{
+       return m_priv != DE_NULL && m_priv->m_valid;
+}
+
+void RenderDocUtil::startFrame (vk::VkInstance instance)
+{
+       if (!isValid()) return;
+
+       m_priv->m_api->StartFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), DE_NULL);
+}
+
+void RenderDocUtil::endFrame (vk::VkInstance instance)
+{
+       if (!isValid()) return;
+
+       m_priv->m_api->EndFrameCapture(RENDERDOC_DEVICEPOINTER_FROM_VKINSTANCE(instance), DE_NULL);
+}
+
+} // vk
diff --git a/external/vulkancts/framework/vulkan/vkRenderDocUtil.hpp b/external/vulkancts/framework/vulkan/vkRenderDocUtil.hpp
new file mode 100644 (file)
index 0000000..1ae8cca
--- /dev/null
@@ -0,0 +1,51 @@
+#ifndef _VKRENDERDOCUTIL_HPP
+#define _VKRENDERDOCUTIL_HPP
+/*-------------------------------------------------------------------------
+ * Vulkan CTS Framework
+ * --------------------
+ *
+ * Copyright (c) 2018 The Khronos Group Inc.
+ * Copyright (c) 2018 Intel Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *//*!
+ * \file
+ * \brief VK_EXT_debug_report utilities
+ *//*--------------------------------------------------------------------*/
+
+#include <vkDefs.hpp>
+
+namespace vk
+{
+
+struct RenderDocPrivate;
+
+class RenderDocUtil
+{
+public:
+                                                                                       RenderDocUtil           (void);
+                                                                                       ~RenderDocUtil          (void);
+
+       bool                                                                    isValid                         (void);
+
+       void                                                                    startFrame                      (vk::VkInstance);
+       void                                                                    endFrame                        (vk::VkInstance);
+
+private:
+       RenderDocPrivate *                                              m_priv;
+};
+
+} // vk
+
+#endif // _VKRENDERDOCUTIL_HPP
index c158c6b..f307e75 100644 (file)
@@ -158,6 +158,7 @@ vector<string> addCoreDeviceExtensions(const vector<string>& extensions, deUint3
 deUint32 getTargetInstanceVersion (const PlatformInterface& vkp)
 {
        deUint32 version = pack(ApiVersion(1, 0, 0));
+
        if (vkp.enumerateInstanceVersion(&version) != VK_SUCCESS)
                TCU_THROW(InternalError, "Enumerate instance version error");
        return version;
index 03dd141..f03dbec 100644 (file)
@@ -35,6 +35,7 @@
 #include "vkDebugReportUtil.hpp"
 #include "vkQueryUtil.hpp"
 #include "vkApiVersion.hpp"
+#include "vkRenderDocUtil.hpp"
 
 #include "deUniquePtr.hpp"
 
@@ -198,6 +199,7 @@ private:
        Context                                                                         m_context;
 
        const UniquePtr<vk::DebugReportRecorder>        m_debugReportRecorder;
+       const UniquePtr<vk::RenderDocUtil>                      m_renderDoc;
 
        TestInstance*                                                           m_instance;                     //!< Current test case instance
 };
@@ -216,6 +218,9 @@ TestCaseExecutor::TestCaseExecutor (tcu::TestContext& testCtx)
                                                                                                                 m_context.getInstanceInterface(),
                                                                                                                 m_context.getInstance())
                                                         : MovePtr<vk::DebugReportRecorder>(DE_NULL))
+       , m_renderDoc                   (testCtx.getCommandLine().isRenderDocEnabled()
+                                                        ? MovePtr<vk::RenderDocUtil>(new vk::RenderDocUtil())
+                                                        : MovePtr<vk::RenderDocUtil>(DE_NULL))
        , m_instance                    (DE_NULL)
 {
 }
@@ -304,6 +309,8 @@ void TestCaseExecutor::init (tcu::TestCase* testCase, const std::string& casePat
                buildProgram<vk::SpirVProgramInfo, vk::SpirVAsmCollection::Iterator>(casePath, asmIterator, m_prebuiltBinRegistry, log, &m_progCollection, commandLine);
        }
 
+       if (m_renderDoc) m_renderDoc->startFrame(m_context.getInstance());
+
        DE_ASSERT(!m_instance);
        m_instance = vktCase->createInstance(m_context);
 }
@@ -313,6 +320,8 @@ void TestCaseExecutor::deinit (tcu::TestCase*)
        delete m_instance;
        m_instance = DE_NULL;
 
+       if (m_renderDoc) m_renderDoc->endFrame(m_context.getInstance());
+
        // Collect and report any debug messages
        if (m_debugReportRecorder)
        {
index 2024159..0dd6613 100644 (file)
@@ -94,6 +94,7 @@ DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheFilename,              std::string);
 DE_DECLARE_COMMAND_LINE_OPT(Optimization,                              int);
 DE_DECLARE_COMMAND_LINE_OPT(OptimizeSpirv,                             bool);
 DE_DECLARE_COMMAND_LINE_OPT(ShaderCacheTruncate,               bool);
+DE_DECLARE_COMMAND_LINE_OPT(RenderDoc,                                 bool);
 
 static void parseIntList (const char* src, std::vector<int>* dst)
 {
@@ -187,7 +188,8 @@ void registerOptions (de::cmdline::Parser& parser)
                << Option<OptimizeSpirv>                (DE_NULL,       "deqp-optimize-spirv",                  "Apply optimization to spir-v shaders as well",         s_enableNames,          "disable")
                << Option<ShaderCache>                  (DE_NULL,       "deqp-shadercache",                             "Enable or disable shader cache",                                       s_enableNames,          "enable")
                << Option<ShaderCacheFilename>  (DE_NULL,       "deqp-shadercache-filename",    "Write shader cache to given file",                                                                             "shadercache.bin")
-               << Option<ShaderCacheTruncate>  (DE_NULL,       "deqp-shadercache-truncate",    "Truncate shader cache before running tests",           s_enableNames,          "enable");
+               << Option<ShaderCacheTruncate>  (DE_NULL,       "deqp-shadercache-truncate",    "Truncate shader cache before running tests",           s_enableNames,          "enable")
+               << Option<RenderDoc>                    (DE_NULL,       "deqp-renderdoc",                               "Enable RenderDoc frame markers and workarounds",       s_enableNames,          "disable");
 }
 
 void registerLegacyOptions (de::cmdline::Parser& parser)
@@ -806,6 +808,7 @@ const char*                         CommandLine::getShaderCacheFilename                     (void) const    { return m_cmd
 bool                                   CommandLine::isShaderCacheTruncateEnabled       (void) const    { return m_cmdLine.getOption<opt::ShaderCacheTruncate>();                       }
 int                                            CommandLine::getOptimizationRecipe                      (void) const    { return m_cmdLine.getOption<opt::Optimization>();                                      }
 bool                                   CommandLine::isSpirvOptimizationEnabled         (void) const    { return m_cmdLine.getOption<opt::OptimizeSpirv>();                                     }
+bool                                   CommandLine::isRenderDocEnabled                         (void) const    { return m_cmdLine.getOption<opt::RenderDoc>();                                         }
 
 const char* CommandLine::getGLContextType (void) const
 {
index 85b24f5..0425537 100644 (file)
@@ -226,6 +226,9 @@ public:
        //! Enable optimizing of spir-v (--deqp-optimize-spirv)
        bool                                                    isSpirvOptimizationEnabled      (void) const;
 
+       //! Should we mark frames and enable WAs for RenderDoc (--deqp-renderdoc)
+       bool                                                    isRenderDocEnabled                      (void) const;
+
        /*--------------------------------------------------------------------*//*!
         * \brief Creates case list filter
         * \param archive Resources