Log GLSL sources and SPIR-V disassembly in Vulkan tests
authorPyry Haulos <phaulos@google.com>
Tue, 14 Jul 2015 22:40:41 +0000 (15:40 -0700)
committerPyry Haulos <phaulos@google.com>
Wed, 15 Jul 2015 20:48:55 +0000 (13:48 -0700)
Change-Id: I9671014509f0eb178c77d05b4251474e69496ae9

external/vulkancts/framework/vulkan/CMakeLists.txt
external/vulkancts/framework/vulkan/vkGlslToSpirV.cpp
external/vulkancts/framework/vulkan/vkGlslToSpirV.hpp
external/vulkancts/framework/vulkan/vkPrograms.cpp
external/vulkancts/framework/vulkan/vkPrograms.hpp
external/vulkancts/modules/vulkan/CMakeLists.txt
external/vulkancts/modules/vulkan/vktBuildPrograms.cpp
external/vulkancts/modules/vulkan/vktTestPackage.cpp

index 158a06b..071cdda 100644 (file)
@@ -26,6 +26,7 @@ set(VKUTIL_SRCS
        )
 
 set(VKUTIL_LIBS
+       glutil
        tcutil
        )
 
index f09e36d..31f8910 100644 (file)
@@ -35,6 +35,7 @@
 #include "vkGlslToSpirV.hpp"
 #include "deArrayUtil.hpp"
 #include "deMemory.h"
+#include "deClock.h"
 #include "qpDebugOut.h"
 
 #if defined(DEQP_HAVE_GLSLANG)
 #      include "deMutex.hpp"
 
 #      include "SPIRV/GlslangToSpv.h"
+#      include "SPIRV/disassemble.h"
+#      include "SPIRV/GLSL450Lib.h"
+#      include "SPIRV/doc.h"
 #      include "glslang/Include/InfoSink.h"
 #      include "glslang/Include/ShHandle.h"
 #      include "glslang/MachineIndependent/localintermediate.h"
 #      include "glslang/Public/ShaderLang.h"
+
+// Required by SPIR-V disassembler
+const char* GlslStd450DebugNames[GLSL_STD_450::Count];
+
 #endif
 
 namespace vk
@@ -78,7 +86,12 @@ static de::Mutex                                     s_glslangLock;
 
 void initGlslang (void*)
 {
+       // Main compiler
        ShInitialize();
+
+       // SPIR-V disassembly
+       spv::Parameterize();
+       GLSL_STD_450::GetDebugNames(GlslStd450DebugNames);
 }
 
 void prepareGlslang (void)
@@ -220,7 +233,7 @@ void getDefaultBuiltInResources (TBuiltInResource* builtin)
 
 } // anonymous
 
-void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>& dst)
+void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>* dst, glu::ShaderProgramInfo* buildInfo)
 {
        TBuiltInResource        builtinRes;
 
@@ -232,27 +245,42 @@ void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>& dst)
        {
                if (!program.sources[shaderType].empty())
                {
-                       de::ScopedLock          compileLock     (s_glslangLock);
-                       const char* const       srcText         = program.sources[shaderType][0].c_str();
-                       const int                       srcLen          = (int)program.sources[shaderType][0].size();
+                       de::ScopedLock          compileLock                     (s_glslangLock);
+                       const std::string&      srcText                         = program.sources[shaderType][0];
+                       const char*                     srcPtrs[]                       = { srcText.c_str() };
+                       int                                     srcLengths[]            = { (int)srcText.size() };
                        vector<deUint32>        spvBlob;
                        TInfoSink                       infoSink;
-                       SpvGenerator            compiler        (getGlslangStage(glu::ShaderType(shaderType)), spvBlob, infoSink);
-                       const int                       compileOk       = ShCompile(static_cast<ShHandle>(&compiler), &srcText, 1, &srcLen, EShOptNone, &builtinRes, 0);
+                       SpvGenerator            compiler                        (getGlslangStage(glu::ShaderType(shaderType)), spvBlob, infoSink);
+                       const deUint64          compileStartTime        = deGetMicroseconds();
+                       const int                       compileOk                       = ShCompile(static_cast<ShHandle>(&compiler), srcPtrs, DE_LENGTH_OF_ARRAY(srcPtrs), srcLengths, EShOptNone, &builtinRes, 0);
 
-                       if (compileOk == 0)
                        {
-                               // \todo [2015-06-19 pyry] Create special CompileException and pass error messages through that
-                               qpPrint(infoSink.info.c_str());
-                               TCU_FAIL("Failed to compile shader");
+                               glu::ShaderInfo shaderBuildInfo;
+
+                               shaderBuildInfo.type                    = (glu::ShaderType)shaderType;
+                               shaderBuildInfo.source                  = srcText;
+                               shaderBuildInfo.infoLog                 = infoSink.info.c_str(); // \todo [2015-07-13 pyry] Include debug log?
+                               shaderBuildInfo.compileTimeUs   = deGetMicroseconds()-compileStartTime;
+                               shaderBuildInfo.compileOk               = (compileOk != 0);
+
+                               buildInfo->shaders.push_back(shaderBuildInfo);
                        }
 
-                       dst.resize(spvBlob.size() * sizeof(deUint32));
+                       buildInfo->program.infoLog              = "(No linking performed)";
+                       buildInfo->program.linkOk               = (compileOk != 0);
+                       buildInfo->program.linkTimeUs   = 0;
+
+                       if (compileOk == 0)
+                               TCU_FAIL("Failed to compile shader");
+
+                       dst->resize(spvBlob.size() * sizeof(deUint32));
 #if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
-                       deMemcpy(&dst[0], &spvBlob[0], dst.size());
+                       deMemcpy(&(*dst)[0], &spvBlob[0], dst->size());
 #else
 #      error "Big-endian not supported"
 #endif
+
                        return;
                }
        }
@@ -260,13 +288,33 @@ void glslToSpirV (const glu::ProgramSources& program, std::vector<deUint8>& dst)
        TCU_THROW(InternalError, "Can't compile empty program");
 }
 
+void disassembleSpirV (size_t binarySize, const deUint8* binary, std::ostream* dst)
+{
+       std::vector<deUint32>   binForDisasm    (binarySize/4);
+
+       DE_ASSERT(binarySize%4 == 0);
+
+#if (DE_ENDIANNESS == DE_LITTLE_ENDIAN)
+       deMemcpy(&binForDisasm[0], binary, binarySize);
+#else
+#      error "Big-endian not supported"
+#endif
+
+       spv::Disassemble(*dst, binForDisasm);
+}
+
 #else // defined(DEQP_HAVE_GLSLANG)
 
-void glslToSpirV (const glu::ProgramSources&, std::vector<deUint8>&)
+void glslToSpirV (const glu::ProgramSources&, std::vector<deUint8>*, glu::ShaderProgramInfo*)
 {
        TCU_THROW(NotSupportedError, "GLSL to SPIR-V compilation not supported (DEQP_HAVE_GLSLANG not defined)");
 }
 
+void disassembleSpirV (size_t, const deUint8*, std::ostream*)
+{
+       TCU_THROW(NotSupportedError, "SPIR-V disassembling not supported (DEQP_HAVE_GLSLANG not defined)");
+}
+
 #endif
 
 } // vk
index 794334c..df4513b 100644 (file)
  *//*--------------------------------------------------------------------*/
 
 #include "vkDefs.hpp"
+#include "vkPrograms.hpp"
 #include "gluShaderProgram.hpp"
 
+#include <ostream>
+
 namespace vk
 {
 
 //! Compile GLSL program to SPIR-V. Will fail with NotSupportedError if compiler is not available.
-void   glslToSpirV             (const glu::ProgramSources& src, std::vector<deUint8>& dst);
+void   glslToSpirV                     (const glu::ProgramSources& src, std::vector<deUint8>* dst, glu::ShaderProgramInfo* buildInfo);
+
+//! Disassemble SPIR-V binary
+void   disassembleSpirV        (size_t binarySize, const deUint8* binary, std::ostream* dst);
 
 } // vk
 
index 471bc0e..40e19ff 100644 (file)
@@ -34,6 +34,9 @@
 
 #include "vkPrograms.hpp"
 #include "vkGlslToSpirV.hpp"
+
+#include "tcuTestLog.hpp"
+
 #include "deArrayUtil.hpp"
 #include "deMemory.h"
 
@@ -42,6 +45,7 @@ namespace vk
 
 using std::string;
 using std::vector;
+using tcu::TestLog;
 
 // ProgramBinary
 
@@ -53,12 +57,12 @@ ProgramBinary::ProgramBinary (ProgramFormat format, size_t binarySize, const deU
 
 // Utils
 
-ProgramBinary* buildProgram (const glu::ProgramSources& program, ProgramFormat binaryFormat)
+ProgramBinary* buildProgram (const glu::ProgramSources& program, ProgramFormat binaryFormat, glu::ShaderProgramInfo* buildInfo)
 {
        if (binaryFormat == PROGRAM_FORMAT_SPIRV)
        {
                vector<deUint8> binary;
-               glslToSpirV(program, binary);
+               glslToSpirV(program, &binary, buildInfo);
                return new ProgramBinary(binaryFormat, binary.size(), &binary[0]);
        }
        else
index eb14933..3e111a1 100644 (file)
 #include "deUniquePtr.hpp"
 #include "deSTLUtil.hpp"
 
+#include <vector>
 #include <map>
 
+namespace tcu
+{
+class TestLog;
+} // tcu
+
 namespace vk
 {
 
@@ -166,7 +172,7 @@ typedef ProgramCollection<glu::ProgramSources>      SourceCollection;
 typedef ProgramCollection<ProgramBinary>               BinaryCollection;
 
 // \todo [2015-03-13 pyry] Likely need BinaryBuilder abstraction for this
-ProgramBinary*                 buildProgram            (const glu::ProgramSources& program, ProgramFormat binaryFormat);
+ProgramBinary*                 buildProgram            (const glu::ProgramSources& program, ProgramFormat binaryFormat, glu::ShaderProgramInfo* buildInfo);
 Move<VkShaderModule>   createShaderModule      (const DeviceInterface& deviceInterface, VkDevice device, const ProgramBinary& binary, VkShaderModuleCreateFlags flags);
 
 } // vk
index a683732..9b79ced 100644 (file)
@@ -20,6 +20,7 @@ set(DEQP_VK_COMMON_SRCS
 set(DEQP_VK_COMMON_LIBS
        tcutil
        vkutil
+       glutil
        deqp-vk-api
        )
 
index bcf1baa..c8699be 100644 (file)
@@ -110,7 +110,8 @@ BuildStats buildPrograms (tcu::TestContext& testCtx, const std::string& dstPath,
                                try
                                {
                                        const vk::ProgramIdentifier                     progId          (casePath, progIter.getName());
-                                       const UniquePtr<vk::ProgramBinary>      binary          (vk::buildProgram(progIter.getProgram(), vk::PROGRAM_FORMAT_SPIRV));
+                                       glu::ShaderProgramInfo                          buildInfo;
+                                       const UniquePtr<vk::ProgramBinary>      binary          (vk::buildProgram(progIter.getProgram(), vk::PROGRAM_FORMAT_SPIRV, &buildInfo));
 
                                        if (mode == BUILDMODE_BUILD)
                                                writer->storeProgram(progId, *binary);
index ee403c7..4cd4435 100644 (file)
 #include "vktTestPackage.hpp"
 
 #include "tcuPlatform.hpp"
+#include "tcuTestCase.hpp"
+#include "tcuTestLog.hpp"
+
 #include "vkPlatform.hpp"
 #include "vkPrograms.hpp"
 #include "vkBinaryRegistry.hpp"
+#include "vkGlslToSpirV.hpp"
+
 #include "deUniquePtr.hpp"
 
 #include "vktInfo.hpp"
 #include "vktApiTests.hpp"
 
 #include <vector>
+#include <sstream>
 
 namespace vkt
 {
@@ -51,6 +57,7 @@ namespace vkt
 using std::vector;
 using de::UniquePtr;
 using de::MovePtr;
+using tcu::TestLog;
 
 // TestCaseExecutor
 
@@ -93,6 +100,7 @@ TestCaseExecutor::~TestCaseExecutor (void)
 void TestCaseExecutor::init (tcu::TestCase* testCase, const std::string& casePath)
 {
        const TestCase*                 vktCase         = dynamic_cast<TestCase*>(testCase);
+       tcu::TestLog&                   log                     = m_context.getTestContext().getLog();
        vk::SourceCollection    sourceProgs;
 
        DE_UNREF(casePath); // \todo [2015-03-13 pyry] Use this to identify ProgramCollection storage path
@@ -106,24 +114,50 @@ void TestCaseExecutor::init (tcu::TestCase* testCase, const std::string& casePat
        for (vk::SourceCollection::Iterator progIter = sourceProgs.begin(); progIter != sourceProgs.end(); ++progIter)
        {
                const vk::ProgramIdentifier             progId          (casePath, progIter.getName());
+               const tcu::ScopedLogSection             progSection     (log, progIter.getName(), "Program: " + progIter.getName());
                de::MovePtr<vk::ProgramBinary>  binProg;
+               glu::ShaderProgramInfo                  buildInfo;
 
                // \todo [2015-07-01 pyry] Command line parameter to control cache vs. build order?
 
                try
                {
-                       binProg = de::MovePtr<vk::ProgramBinary>(vk::buildProgram(progIter.getProgram(), vk::PROGRAM_FORMAT_SPIRV));
+                       binProg = de::MovePtr<vk::ProgramBinary>(vk::buildProgram(progIter.getProgram(), vk::PROGRAM_FORMAT_SPIRV, &buildInfo));
+                       log << buildInfo;
                }
-               catch (const tcu::NotSupportedError&)
+               catch (const tcu::NotSupportedError& err)
                {
                        // Try to load from cache
                        const vk::BinaryRegistryReader  registry        (m_context.getTestContext().getArchive(), "vulkan/prebuilt");
 
+                       log << err << TestLog::Message << "Building from source not supported, loading stored binary instead" << TestLog::EndMessage;
+
                        binProg = de::MovePtr<vk::ProgramBinary>(registry.loadProgram(progId));
+
+                       log << progIter.getProgram();
+               }
+               catch (const tcu::Exception&)
+               {
+                       // Build failed for other reason
+                       log << buildInfo;
+                       throw;
                }
 
                TCU_CHECK_INTERNAL(binProg);
 
+               try
+               {
+                       std::ostringstream disasm;
+
+                       vk::disassembleSpirV(binProg->getSize(), binProg->getBinary(), &disasm);
+
+                       log << TestLog::KernelSource(disasm.str());
+               }
+               catch (const tcu::NotSupportedError& err)
+               {
+                       log << err;
+               }
+
                m_progCollection.add(progId.programName, binProg);
        }