Merge pull request #284 from antiagainst/gtest-hlsl
Tests: Add support for testing file-based HLSL source code in GTest.
This commit is contained in:
commit
e0a24778dd
@ -44,7 +44,8 @@ using CompileToAstTest = GlslangTest<::testing::TestWithParam<std::string>>;
|
||||
TEST_P(CompileToAstTest, FromFile)
|
||||
{
|
||||
loadFileCompileAndCheck(GLSLANG_TEST_DIRECTORY, GetParam(),
|
||||
Semantics::OpenGL, Target::AST);
|
||||
Source::GLSL, Semantics::OpenGL,
|
||||
Target::AST);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@ -183,7 +184,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
"nonVulkan.frag",
|
||||
"spv.atomic.comp",
|
||||
})),
|
||||
FileNameAsCustomTestName
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
|
@ -13,6 +13,7 @@ if (TARGET gmock)
|
||||
# Test related source files
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/AST.FromFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/BuiltInResource.FromFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Hlsl.FromFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Pp.FromFile.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Spv.FromFile.cpp
|
||||
)
|
||||
|
81
gtests/Hlsl.FromFile.cpp
Normal file
81
gtests/Hlsl.FromFile.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
//
|
||||
// Copyright (C) 2016 Google, Inc.
|
||||
//
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
//
|
||||
// Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
//
|
||||
// Redistributions in binary form must reproduce the above
|
||||
// copyright notice, this list of conditions and the following
|
||||
// disclaimer in the documentation and/or other materials provided
|
||||
// with the distribution.
|
||||
//
|
||||
// Neither the name of Google Inc. nor the names of its
|
||||
// contributors may be used to endorse or promote products derived
|
||||
// from this software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "TestFixture.h"
|
||||
|
||||
namespace glslangtest {
|
||||
namespace {
|
||||
|
||||
struct FileNameEntryPointPair {
|
||||
const char* fileName;
|
||||
const char* entryPoint;
|
||||
};
|
||||
|
||||
// We are using FileNameEntryPointPair objects as parameters for instantiating
|
||||
// the template, so the global FileNameAsCustomTestSuffix() won't work since
|
||||
// it assumes std::string as parameters. Thus, an overriding one here.
|
||||
std::string FileNameAsCustomTestSuffix(
|
||||
const ::testing::TestParamInfo<FileNameEntryPointPair>& info) {
|
||||
std::string name = info.param.fileName;
|
||||
// A valid test case suffix cannot have '.' and '-' inside.
|
||||
std::replace(name.begin(), name.end(), '.', '_');
|
||||
std::replace(name.begin(), name.end(), '-', '_');
|
||||
return name;
|
||||
}
|
||||
|
||||
using HlslCompileTest = GlslangTest<::testing::TestWithParam<FileNameEntryPointPair>>;
|
||||
|
||||
// Compiling HLSL to SPIR-V under Vulkan semantics. Expected to successfully
|
||||
// generate SPIR-V.
|
||||
TEST_P(HlslCompileTest, FromFile)
|
||||
{
|
||||
loadFileCompileAndCheck(GLSLANG_TEST_DIRECTORY, GetParam().fileName,
|
||||
Source::HLSL, Semantics::Vulkan,
|
||||
Target::BothASTAndSpv, GetParam().entryPoint);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
ToSpirv, HlslCompileTest,
|
||||
::testing::ValuesIn(std::vector<FileNameEntryPointPair>{
|
||||
{"hlsl.frag", "PixelShaderFunction"},
|
||||
}),
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
} // anonymous namespace
|
||||
} // namespace glslangtest
|
@ -69,7 +69,7 @@ public:
|
||||
InitializationToken acquire(EShMessages new_messages)
|
||||
{
|
||||
if ((lastMessages ^ new_messages) &
|
||||
(EShMsgVulkanRules | EShMsgSpvRules)) {
|
||||
(EShMsgVulkanRules | EShMsgSpvRules | EShMsgReadHlsl)) {
|
||||
glslang::FinalizeProcess();
|
||||
glslang::InitializeProcess();
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
"preprocessor.defined.vert",
|
||||
"preprocessor.many.endif.vert",
|
||||
})),
|
||||
FileNameAsCustomTestName
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
|
@ -49,7 +49,8 @@ using VulkanSemantics = GlslangTest<::testing::TestWithParam<std::string>>;
|
||||
TEST_P(CompileToSpirvTest, FromFile)
|
||||
{
|
||||
loadFileCompileAndCheck(GLSLANG_TEST_DIRECTORY, GetParam(),
|
||||
Semantics::Vulkan, Target::Spirv);
|
||||
Source::GLSL, Semantics::Vulkan,
|
||||
Target::Spv);
|
||||
}
|
||||
|
||||
// GLSL-level Vulkan semantics test. Expected to error out before generating
|
||||
@ -57,7 +58,8 @@ TEST_P(CompileToSpirvTest, FromFile)
|
||||
TEST_P(VulkanSemantics, FromFile)
|
||||
{
|
||||
loadFileCompileAndCheck(GLSLANG_TEST_DIRECTORY, GetParam(),
|
||||
Semantics::Vulkan, Target::Spirv);
|
||||
Source::GLSL, Semantics::Vulkan,
|
||||
Target::Spv);
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
@ -173,7 +175,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
"spv.specConstant.comp",
|
||||
"spv.specConstantComposite.vert",
|
||||
})),
|
||||
FileNameAsCustomTestName
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
@ -183,7 +185,7 @@ INSTANTIATE_TEST_CASE_P(
|
||||
"vulkan.vert",
|
||||
"vulkan.comp",
|
||||
})),
|
||||
FileNameAsCustomTestName
|
||||
FileNameAsCustomTestSuffix
|
||||
);
|
||||
// clang-format on
|
||||
|
||||
|
@ -36,7 +36,7 @@
|
||||
|
||||
namespace glslangtest {
|
||||
|
||||
std::string FileNameAsCustomTestName(
|
||||
std::string FileNameAsCustomTestSuffix(
|
||||
const ::testing::TestParamInfo<std::string>& info)
|
||||
{
|
||||
std::string name = info.param;
|
||||
@ -46,7 +46,7 @@ std::string FileNameAsCustomTestName(
|
||||
return name;
|
||||
}
|
||||
|
||||
EShLanguage GetGlslLanguageForStage(const std::string& stage)
|
||||
EShLanguage GetShaderStage(const std::string& stage)
|
||||
{
|
||||
if (stage == "vert") {
|
||||
return EShLangVertex;
|
||||
@ -66,17 +66,27 @@ EShLanguage GetGlslLanguageForStage(const std::string& stage)
|
||||
}
|
||||
}
|
||||
|
||||
EShMessages GetSpirvMessageOptionsForSemanticsAndTarget(Semantics semantics,
|
||||
Target target)
|
||||
EShMessages DeriveOptions(Source source, Semantics semantics, Target target)
|
||||
{
|
||||
EShMessages result = EShMsgDefault;
|
||||
|
||||
switch (source) {
|
||||
case Source::GLSL:
|
||||
break;
|
||||
case Source::HLSL:
|
||||
result = EShMsgReadHlsl;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (target) {
|
||||
case Target::AST:
|
||||
result = EShMsgAST;
|
||||
result = static_cast<EShMessages>(result | EShMsgAST);
|
||||
break;
|
||||
case Target::Spirv:
|
||||
result = EShMsgSpvRules;
|
||||
case Target::Spv:
|
||||
result = static_cast<EShMessages>(result | EShMsgSpvRules);
|
||||
break;
|
||||
case Target::BothASTAndSpv:
|
||||
result = static_cast<EShMessages>(result | EShMsgSpvRules | EShMsgAST);
|
||||
break;
|
||||
};
|
||||
|
||||
|
@ -65,9 +65,14 @@ namespace glslangtest {
|
||||
// This function is used to provide custom test name suffixes based on the
|
||||
// shader source file names. Otherwise, the test name suffixes will just be
|
||||
// numbers, which are not quite obvious.
|
||||
std::string FileNameAsCustomTestName(
|
||||
std::string FileNameAsCustomTestSuffix(
|
||||
const ::testing::TestParamInfo<std::string>& info);
|
||||
|
||||
enum class Source {
|
||||
GLSL,
|
||||
HLSL,
|
||||
};
|
||||
|
||||
// Enum for shader compilation semantics.
|
||||
enum class Semantics {
|
||||
OpenGL,
|
||||
@ -77,13 +82,13 @@ enum class Semantics {
|
||||
// Enum for compilation target.
|
||||
enum class Target {
|
||||
AST,
|
||||
Spirv,
|
||||
Spv,
|
||||
BothASTAndSpv,
|
||||
};
|
||||
|
||||
EShLanguage GetGlslLanguageForStage(const std::string& stage);
|
||||
EShLanguage GetShaderStage(const std::string& stage);
|
||||
|
||||
EShMessages GetSpirvMessageOptionsForSemanticsAndTarget(Semantics semantics,
|
||||
Target target);
|
||||
EShMessages DeriveOptions(Source, Semantics, Target);
|
||||
|
||||
// Reads the content of the file at the given |path|. On success, returns true
|
||||
// and the contents; otherwise, returns false and an empty string.
|
||||
@ -160,23 +165,23 @@ public:
|
||||
const std::string spirv; // Optional SPIR-V disassembly text.
|
||||
};
|
||||
|
||||
// Compiles and linkes the given GLSL |source| code of the given shader
|
||||
// Compiles and linkes the given source |code| of the given shader
|
||||
// |stage| into the given |target| under the given |semantics|. Returns
|
||||
// a GlslangResult instance containing all the information generated
|
||||
// during the process. If |target| is Target::Spirv, also disassembles
|
||||
// the result and returns disassembly text.
|
||||
GlslangResult compileGlsl(const std::string& source,
|
||||
GlslangResult compile(const std::string& code, Source source,
|
||||
const std::string& stage, Semantics semantics,
|
||||
Target target)
|
||||
Target target, const std::string& entryPointName)
|
||||
{
|
||||
const char* shaderStrings = source.data();
|
||||
const int shaderLengths = static_cast<int>(source.size());
|
||||
const EShLanguage language = GetGlslLanguageForStage(stage);
|
||||
const char* shaderStrings = code.data();
|
||||
const int shaderLengths = static_cast<int>(code.size());
|
||||
const EShLanguage kind = GetShaderStage(stage);
|
||||
|
||||
glslang::TShader shader(language);
|
||||
glslang::TShader shader(kind);
|
||||
shader.setStringsWithLengths(&shaderStrings, &shaderLengths, 1);
|
||||
const EShMessages messages =
|
||||
GetSpirvMessageOptionsForSemanticsAndTarget(semantics, target);
|
||||
if (!entryPointName.empty()) shader.setEntryPoint(entryPointName.c_str());
|
||||
const EShMessages messages = DeriveOptions(source, semantics, target);
|
||||
// Reinitialize glslang if the semantics change.
|
||||
GlslangInitializer::InitializationToken token =
|
||||
GlobalTestSettings.initializer->acquire(messages);
|
||||
@ -190,9 +195,9 @@ public:
|
||||
|
||||
spv::SpvBuildLogger logger;
|
||||
|
||||
if (success && target == Target::Spirv) {
|
||||
if (success && (target == Target::Spv || target == Target::BothASTAndSpv)) {
|
||||
std::vector<uint32_t> spirv_binary;
|
||||
glslang::GlslangToSpv(*program.getIntermediate(language),
|
||||
glslang::GlslangToSpv(*program.getIntermediate(kind),
|
||||
spirv_binary, &logger);
|
||||
|
||||
std::ostringstream disassembly_stream;
|
||||
@ -210,7 +215,10 @@ public:
|
||||
|
||||
void loadFileCompileAndCheck(const std::string& testDir,
|
||||
const std::string& testName,
|
||||
Semantics semantics, Target target)
|
||||
Source source,
|
||||
Semantics semantics,
|
||||
Target target,
|
||||
const std::string& entryPointName="")
|
||||
{
|
||||
const std::string inputFname = testDir + "/" + testName;
|
||||
const std::string expectedOutputFname =
|
||||
@ -221,7 +229,8 @@ public:
|
||||
tryLoadFile(expectedOutputFname, "expected output", &expectedOutput);
|
||||
|
||||
GlslangResult result =
|
||||
compileGlsl(input, GetSuffix(testName), semantics, target);
|
||||
compile(input, source, GetSuffix(testName),
|
||||
semantics, target, entryPointName);
|
||||
|
||||
// Generate the hybrid output in the way of glslangValidator.
|
||||
std::ostringstream stream;
|
||||
@ -236,7 +245,7 @@ public:
|
||||
outputIfNotEmpty(result.linkingOutput);
|
||||
outputIfNotEmpty(result.linkingError);
|
||||
stream << result.spirvWarningsErrors;
|
||||
if (target == Target::Spirv) {
|
||||
if (target == Target::Spv || target == Target::BothASTAndSpv) {
|
||||
stream
|
||||
<< (result.spirv.empty()
|
||||
? "SPIR-V is not generated for failed compile or link\n"
|
||||
@ -247,10 +256,10 @@ public:
|
||||
expectedOutputFname);
|
||||
}
|
||||
|
||||
// Preprocesses the given GLSL |source| code. On success, returns true, the
|
||||
// Preprocesses the given |source| code. On success, returns true, the
|
||||
// preprocessed shader, and warning messages. Otherwise, returns false, an
|
||||
// empty string, and error messages.
|
||||
std::tuple<bool, std::string, std::string> preprocessGlsl(
|
||||
std::tuple<bool, std::string, std::string> preprocess(
|
||||
const std::string& source)
|
||||
{
|
||||
const char* shaderStrings = source.data();
|
||||
@ -290,7 +299,7 @@ public:
|
||||
|
||||
bool ppOk;
|
||||
std::string output, error;
|
||||
std::tie(ppOk, output, error) = preprocessGlsl(input);
|
||||
std::tie(ppOk, output, error) = preprocess(input);
|
||||
if (!output.empty()) output += '\n';
|
||||
if (!error.empty()) error += '\n';
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user