summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorjsmall-nvidia <jsmall@nvidia.com>2019-09-23 15:38:25 -0400
committerGitHub <noreply@github.com>2019-09-23 15:38:25 -0400
commit05af41d21d74d24871507e6f8f50574ea08c48a2 (patch)
tree3197b021ed71c40f6035fdfa7d450b4b3b945422 /tools
parentede0792fd9b4c7bc5c2653092ba1d492e67ca190 (diff)
Simple test profiling (#1062)
* First pass support for performance profiling * Test across all elements * Fix bug - sourceContents is not used, should use rawSource. * * Add ability to get prelude from API. * Allow specifying source language for render-test * Made it possible to compile a test input file as C++ * Special handling for reflection * Added C++ impl to performance-profile.slang * Remove some clang warnings. * Output profile timings on appveyor and other TC. * Remove passing around of StdWriters (can use global). Small comment improvements.
Diffstat (limited to 'tools')
-rw-r--r--tools/render-test/cpu-compute-util.cpp19
-rw-r--r--tools/render-test/cpu-compute-util.h4
-rw-r--r--tools/render-test/options.cpp50
-rw-r--r--tools/render-test/options.h3
-rw-r--r--tools/render-test/render-test-main.cpp122
-rw-r--r--tools/render-test/shader-input-layout.cpp6
-rw-r--r--tools/render-test/slang-support.cpp25
-rw-r--r--tools/slang-test/slang-test-main.cpp60
-rw-r--r--tools/slang-test/test-reporter.cpp69
-rw-r--r--tools/slang-test/test-reporter.h5
10 files changed, 322 insertions, 41 deletions
diff --git a/tools/render-test/cpu-compute-util.cpp b/tools/render-test/cpu-compute-util.cpp
index 81325ce80..688e6b254 100644
--- a/tools/render-test/cpu-compute-util.cpp
+++ b/tools/render-test/cpu-compute-util.cpp
@@ -181,7 +181,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
}
else
{
- throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line);
+ throw TextFormatException(String("Invalid input syntax at line ") + parser.NextToken().Position.Line);
}
}
@@ -301,7 +301,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
return SLANG_OK;
}
-/* static */SlangResult CPUComputeUtil::calcExecuteInfo(ExecuteStyle style, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out)
+/* static */SlangResult CPUComputeUtil::calcExecuteInfo(ExecuteStyle style, ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out)
{
auto request = compilationAndLayout.output.request;
auto reflection = (slang::ShaderReflection*) spGetReflection(request);
@@ -314,9 +314,6 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
const char* entryPointName = entryPoint->getName();
- ComPtr<ISlangSharedLibrary> sharedLibrary;
- SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(request, 0, 0, sharedLibrary.writeRef()));
-
// Copy dispatch size
for (int i = 0; i < 3; ++i)
{
@@ -334,7 +331,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
StringBuilder groupEntryPointName;
groupEntryPointName << entryPointName << "_Group";
- CPPPrelude::ComputeFunc groupFunc = (CPPPrelude::ComputeFunc)sharedLibrary->findFuncByName(groupEntryPointName.getBuffer());
+ CPPPrelude::ComputeFunc groupFunc = (CPPPrelude::ComputeFunc)sharedLib->findFuncByName(groupEntryPointName.getBuffer());
if (!groupFunc)
{
return SLANG_FAIL;
@@ -346,7 +343,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
case ExecuteStyle::GroupRange:
{
CPPPrelude::ComputeFunc groupRangeFunc = nullptr;
- groupRangeFunc = (CPPPrelude::ComputeFunc)sharedLibrary->findFuncByName(entryPointName);
+ groupRangeFunc = (CPPPrelude::ComputeFunc)sharedLib->findFuncByName(entryPointName);
if (!groupRangeFunc)
{
return SLANG_FAIL;
@@ -359,7 +356,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
StringBuilder threadEntryPointName;
threadEntryPointName << entryPointName << "_Thread";
- CPPPrelude::ComputeThreadFunc threadFunc = (CPPPrelude::ComputeThreadFunc)sharedLibrary->findFuncByName(threadEntryPointName.getBuffer());
+ CPPPrelude::ComputeThreadFunc threadFunc = (CPPPrelude::ComputeThreadFunc)sharedLib->findFuncByName(threadEntryPointName.getBuffer());
if (!threadFunc)
{
return SLANG_FAIL;
@@ -470,7 +467,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
}
-/* static */ SlangResult CPUComputeUtil::checkStyleConsistency(const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout)
+/* static */ SlangResult CPUComputeUtil::checkStyleConsistency(ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout)
{
Context context;
SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context));
@@ -478,7 +475,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
// Run the thread style to test against
{
ExecuteInfo info;
- SLANG_RETURN_ON_FAIL(calcExecuteInfo(ExecuteStyle::Thread, dispatchSize, compilationAndLayout, context, info));
+ SLANG_RETURN_ON_FAIL(calcExecuteInfo(ExecuteStyle::Thread, sharedLib, dispatchSize, compilationAndLayout, context, info));
SLANG_RETURN_ON_FAIL(execute(info));
}
@@ -489,7 +486,7 @@ static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount)
SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, checkContext));
ExecuteInfo info;
- SLANG_RETURN_ON_FAIL(calcExecuteInfo(style, dispatchSize, compilationAndLayout, checkContext, info));
+ SLANG_RETURN_ON_FAIL(calcExecuteInfo(style, sharedLib, dispatchSize, compilationAndLayout, checkContext, info));
SLANG_RETURN_ON_FAIL(execute(info));
// Make sure the out buffers are all the same
diff --git a/tools/render-test/cpu-compute-util.h b/tools/render-test/cpu-compute-util.h
index 1284735c0..9430eb841 100644
--- a/tools/render-test/cpu-compute-util.h
+++ b/tools/render-test/cpu-compute-util.h
@@ -50,11 +50,11 @@ struct CPUComputeUtil
/// Runs code across run styles and makes sure output buffers match
- static SlangResult checkStyleConsistency(const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout);
+ static SlangResult checkStyleConsistency(ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout);
static SlangResult calcBindings(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext);
- static SlangResult calcExecuteInfo(ExecuteStyle style, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out);
+ static SlangResult calcExecuteInfo(ExecuteStyle style, ISlangSharedLibrary* sharedLib, const uint32_t dispatchSize[3], const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context, ExecuteInfo& out);
static SlangResult execute(const ExecuteInfo& info);
diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp
index e13a2b88f..3d5df6f62 100644
--- a/tools/render-test/options.cpp
+++ b/tools/render-test/options.cpp
@@ -44,6 +44,32 @@ static SlangResult _setRendererType(RendererType type, const char* arg, Slang::W
return SLANG_OK;
}
+static SlangSourceLanguage _findSourceLanguage(const UnownedStringSlice& text)
+{
+ if (text == "c" || text == "C")
+ {
+ return SLANG_SOURCE_LANGUAGE_C;
+ }
+ else if (text == "cpp" || text == "c++" || text == "C++" || text == "cxx")
+ {
+ return SLANG_SOURCE_LANGUAGE_CPP;
+ }
+ else if (text == "slang")
+ {
+ return SLANG_SOURCE_LANGUAGE_SLANG;
+ }
+ else if (text == "glsl")
+ {
+ return SLANG_SOURCE_LANGUAGE_GLSL;
+ }
+ else if (text == "hlsl")
+ {
+ return SLANG_SOURCE_LANGUAGE_HLSL;
+ }
+ return SLANG_SOURCE_LANGUAGE_UNKNOWN;
+}
+
+
SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper stdError)
{
using namespace Slang;
@@ -169,6 +195,10 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s
arg.value = *argCursor++;
gOptions.compileArgs.add(arg);
}
+ else if (strcmp(arg, "-performance-profile") == 0)
+ {
+ gOptions.performanceProfile = true;
+ }
else if (strcmp(arg, "-adapter") == 0)
{
if (argCursor == argEnd)
@@ -207,6 +237,24 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s
gOptions.computeDispatchSize[i] = v;
}
}
+ else if (strcmp(arg, "-source-language") == 0)
+ {
+ if (argCursor == argEnd)
+ {
+ stdError.print("error: expecting a source language name for '%s'\n", arg);
+ return SLANG_FAIL;
+ }
+ UnownedStringSlice sourceLanguageText(*argCursor++);
+
+ SlangSourceLanguage sourceLanguage = _findSourceLanguage(sourceLanguageText);
+ if (sourceLanguage == SLANG_SOURCE_LANGUAGE_UNKNOWN)
+ {
+ stdError.print("error: expecting unknown source language name '%s' for '%s'\n", String(sourceLanguageText).getBuffer(), arg);
+ return SLANG_FAIL;
+ }
+
+ gOptions.sourceLanguage = sourceLanguage;
+ }
else
{
// Lookup
@@ -228,7 +276,7 @@ SlangResult parseOptions(int argc, const char*const* argv, Slang::WriterHelper s
if (languageRenderType != RendererType::Unknown)
{
gOptions.targetLanguageRendererType = languageRenderType;
- gOptions.inputLanguageID = (argName == "hlsl" || argName == "glsl") ? InputLanguageID::Native : InputLanguageID::Slang;
+ gOptions.inputLanguageID = (argName == "hlsl" || argName == "glsl" || argName == "cpp" || argName == "cxx" || argName == "c") ? InputLanguageID::Native : InputLanguageID::Slang;
continue;
}
}
diff --git a/tools/render-test/options.h b/tools/render-test/options.h
index 67eae6603..1bb4af74c 100644
--- a/tools/render-test/options.h
+++ b/tools/render-test/options.h
@@ -49,6 +49,7 @@ struct Options
/// The set render type
RendererType rendererType = RendererType::Unknown;
InputLanguageID inputLanguageID = InputLanguageID::Slang;
+ SlangSourceLanguage sourceLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN;
/// Can be used for overriding the profile
const char* profileName = nullptr;
@@ -59,6 +60,8 @@ struct Options
bool useDXIL = false;
bool onlyStartup = false;
+ bool performanceProfile = false;
+
Slang::List<Slang::String> renderFeatures; /// Required render features for this test to run
Slang::List<Slang::CommandLine::Arg> compileArgs;
diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp
index 3a8871618..13af422a9 100644
--- a/tools/render-test/render-test-main.cpp
+++ b/tools/render-test/render-test-main.cpp
@@ -55,6 +55,13 @@ static const int kVertexCount = SLANG_COUNT_OF(kVertexData);
using namespace Slang;
+static void _outputProfileTime(uint64_t startTicks, uint64_t endTicks)
+{
+ WriterHelper out = StdWriters::getOut();
+ double time = double(endTicks - startTicks) / ProcessUtil::getClockFrequency();
+ out.print("profile-time=%g\n", time);
+}
+
class RenderTestApp : public WindowListener
{
public:
@@ -79,6 +86,8 @@ class RenderTestApp : public WindowListener
/// Called in initialize
Result _initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input);
+ uint64_t m_startTicks;
+
// variables for state to be used for rendering...
uintptr_t m_constantBufferSize, m_computeResultBufferSize;
@@ -232,6 +241,9 @@ void RenderTestApp::runCompute()
auto pipelineType = PipelineType::Compute;
m_renderer->setPipelineState(pipelineType, m_pipelineState);
m_bindingState->apply(m_renderer, pipelineType);
+
+ m_startTicks = ProcessUtil::getClockTick();
+
m_renderer->dispatchCompute(m_options.computeDispatchSize[0], m_options.computeDispatchSize[1], m_options.computeDispatchSize[2]);
}
@@ -314,24 +326,60 @@ Result RenderTestApp::update(Window* window)
}
// If we are in a mode where output is requested, we need to snapshot the back buffer here
- if (m_options.outputPath)
+ if (m_options.outputPath || m_options.performanceProfile)
{
// Submit the work
m_renderer->submitGpuWork();
// Wait until everything is complete
m_renderer->waitForGpu();
- if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute)
+ if (m_options.performanceProfile)
{
- SLANG_RETURN_ON_FAIL(writeBindingOutput(gOptions.outputPath));
+ // It might not be enough on some APIs to 'waitForGpu' to mean the computation has completed. Let's lock an output
+ // buffer to be sure
+ if (m_bindingState->outputBindings.getCount() > 0)
+ {
+ const auto& binding = m_bindingState->outputBindings[0];
+ auto i = binding.entryIndex;
+ const auto& layoutBinding = m_shaderInputLayout.entries[i];
+
+ assert(layoutBinding.isOutput);
+
+ if (binding.resource && binding.resource->isBuffer())
+ {
+ BufferResource* bufferResource = static_cast<BufferResource*>(binding.resource.Ptr());
+ const size_t bufferSize = bufferResource->getDesc().sizeInBytes;
+ unsigned int* ptr = (unsigned int*)m_renderer->map(bufferResource, MapFlavor::HostRead);
+ if (!ptr)
+ {
+ return SLANG_FAIL;
+ }
+ m_renderer->unmap(bufferResource);
+ }
+ }
+
+ // Note we don't do the same with screen rendering -> as that will do a lot of work, which may swamp any computation
+ // so can only really profile compute shaders at the moment
+
+ const uint64_t endTicks = ProcessUtil::getClockTick();
+
+ _outputProfileTime(m_startTicks, endTicks);
}
- else
+
+ if (gOptions.outputPath)
{
- SlangResult res = writeScreen(gOptions.outputPath);
- if (SLANG_FAILED(res))
+ if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute)
{
- fprintf(stderr, "ERROR: failed to write screen capture to file\n");
- return res;
+ SLANG_RETURN_ON_FAIL(writeBindingOutput(gOptions.outputPath));
+ }
+ else
+ {
+ SlangResult res = writeScreen(gOptions.outputPath);
+ if (SLANG_FAILED(res))
+ {
+ fprintf(stderr, "ERROR: failed to write screen capture to file\n");
+ return res;
+ }
}
}
// We are done
@@ -343,6 +391,8 @@ Result RenderTestApp::update(Window* window)
return SLANG_OK;
}
+
+
} // namespace renderer_test
SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSession* session, int argcIn, const char*const* argvIn)
@@ -432,6 +482,16 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe
break;
}
+ if (gOptions.sourceLanguage != SLANG_SOURCE_LANGUAGE_UNKNOWN)
+ {
+ input.sourceLanguage = gOptions.sourceLanguage;
+
+ if (input.sourceLanguage == SLANG_SOURCE_LANGUAGE_C || input.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP)
+ {
+ input.passThrough = SLANG_PASS_THROUGH_GENERIC_C_CPP;
+ }
+ }
+
// Use the profile name set on options if set
input.profile = gOptions.profileName ? gOptions.profileName : input.profile;
@@ -459,24 +519,52 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe
ShaderCompilerUtil::OutputAndLayout compilationAndLayout;
SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.compileArgs, gOptions.shaderType, input, compilationAndLayout));
-
{
+ // Get the shared library -> it contains the executable code, we need to keep around if we recompile
+ ComPtr<ISlangSharedLibrary> sharedLibrary;
+ SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(compilationAndLayout.output.request, 0, 0, sharedLibrary.writeRef()));
+
+ // If we are running c/c++ we still need binding information, so compile again as slang source
+ if (gOptions.sourceLanguage == SLANG_SOURCE_LANGUAGE_C || input.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP)
+ {
+ ShaderCompilerUtil::Input slangInput = input;
+ slangInput.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG;
+ slangInput.passThrough = SLANG_PASS_THROUGH_NONE;
+ // We just want CPP, so we get suitable reflection
+ slangInput.target = SLANG_CPP_SOURCE;
+
+ SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.compileArgs, gOptions.shaderType, slangInput, compilationAndLayout));
+ }
+
+ // calculate binding
CPUComputeUtil::Context context;
SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context));
+ // Get the execution info from the lib
CPUComputeUtil::ExecuteInfo info;
- SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcExecuteInfo(CPUComputeUtil::ExecuteStyle::GroupRange, gOptions.computeDispatchSize, compilationAndLayout, context, info));
+ SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcExecuteInfo(CPUComputeUtil::ExecuteStyle::GroupRange, sharedLibrary, gOptions.computeDispatchSize, compilationAndLayout, context, info));
+
+ const uint64_t startTicks = ProcessUtil::getClockTick();
+
SLANG_RETURN_ON_FAIL(CPUComputeUtil::execute(info));
-
- // Dump everything out that was written
- SLANG_RETURN_ON_FAIL(CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath));
- }
- {
- // Check all execution styles produce the same result
- SLANG_RETURN_ON_FAIL(CPUComputeUtil::checkStyleConsistency(gOptions.computeDispatchSize, compilationAndLayout));
+ if (gOptions.performanceProfile)
+ {
+ const uint64_t endTicks = ProcessUtil::getClockTick();
+ _outputProfileTime(startTicks, endTicks);
+ }
+
+ if (gOptions.outputPath)
+ {
+ // Dump everything out that was written
+ SLANG_RETURN_ON_FAIL(CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath));
+
+ // Check all execution styles produce the same result
+ SLANG_RETURN_ON_FAIL(CPUComputeUtil::checkStyleConsistency(sharedLibrary, gOptions.computeDispatchSize, compilationAndLayout));
+ }
}
+
return SLANG_OK;
}
diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp
index 0d13e5ca1..b5b65f52d 100644
--- a/tools/render-test/shader-input-layout.cpp
+++ b/tools/render-test/shader-input-layout.cpp
@@ -506,7 +506,7 @@ namespace renderer_test
Token nameToken = parser.ReadToken();
if (nameToken.Type != TokenType::Identifier)
{
- throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line);
+ throw TextFormatException(String("Invalid input syntax at line ") + parser.NextToken().Position.Line);
}
builder << nameToken.Content;
@@ -537,7 +537,7 @@ namespace renderer_test
}
else
{
- throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line);
+ throw TextFormatException(String("Invalid input syntax at line ") + parser.NextToken().Position.Line);
}
}
@@ -553,7 +553,7 @@ namespace renderer_test
}
catch (TextFormatException)
{
- throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line);
+ throw TextFormatException(String("Invalid input syntax at line ") + parser.NextToken().Position.Line);
}
}
}
diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp
index 515481d4f..9afddc09b 100644
--- a/tools/render-test/slang-support.cpp
+++ b/tools/render-test/slang-support.cpp
@@ -9,6 +9,8 @@
#include <assert.h>
#include <stdio.h>
+#include "../../source/core/slang-string-util.h"
+
namespace renderer_test {
using namespace Slang;
@@ -54,6 +56,12 @@ static const char computeEntryPointName[] = "computeMain";
case SLANG_SOURCE_LANGUAGE_HLSL:
spAddPreprocessorDefine(slangRequest, "__HLSL__", "1");
break;
+ case SLANG_SOURCE_LANGUAGE_C:
+ spAddPreprocessorDefine(slangRequest, "__C__", "1");
+ break;
+ case SLANG_SOURCE_LANGUAGE_CPP:
+ spAddPreprocessorDefine(slangRequest, "__CPP__", "1");
+ break;
default:
assert(!"unexpected");
@@ -240,6 +248,23 @@ static const char computeEntryPointName[] = "computeMain";
List<char> sourceText;
SLANG_RETURN_ON_FAIL(readSource(sourcePath, sourceText));
+ if (input.sourceLanguage == SLANG_SOURCE_LANGUAGE_CPP || input.sourceLanguage == SLANG_SOURCE_LANGUAGE_C)
+ {
+ // Add an include of the prelude
+ ComPtr<ISlangBlob> prelude;
+ session->getDownstreamCompilerPrelude(SLANG_PASS_THROUGH_GENERIC_C_CPP, prelude.writeRef());
+
+ String preludeString = StringUtil::getString(prelude);
+
+ // Add the prelude
+ StringBuilder builder;
+ builder << preludeString << "\n";
+ builder << UnownedStringSlice(sourceText.getBuffer(), sourceText.getCount());
+
+ sourceText.setCount(builder.getLength());
+ memcpy(sourceText.getBuffer(), builder.getBuffer(), builder.getLength());
+ }
+
output.sourcePath = sourcePath;
auto& layout = output.layout;
diff --git a/tools/slang-test/slang-test-main.cpp b/tools/slang-test/slang-test-main.cpp
index a2d24a54f..e1309d01f 100644
--- a/tools/slang-test/slang-test-main.cpp
+++ b/tools/slang-test/slang-test-main.cpp
@@ -1914,6 +1914,65 @@ static void _addRenderTestOptions(const Options& options, CommandLine& ioCmdLine
}
}
+static SlangResult _extractProfileTime(const UnownedStringSlice& text, double& timeOut)
+{
+ // Need to find the profile figure..
+ LineParser parser(text);
+
+ const auto lineStart = UnownedStringSlice::fromLiteral("profile-time=");
+ for (auto line : parser)
+ {
+ if (line.startsWith(lineStart))
+ {
+ UnownedStringSlice remaining(line.begin() + lineStart.size(), line.end());
+ remaining.trim();
+
+ timeOut = StringToDouble(String(remaining));
+ return SLANG_OK;
+ }
+ }
+
+ return SLANG_FAIL;
+}
+
+TestResult runPerformanceProfile(TestContext* context, TestInput& input)
+{
+ auto outputStem = input.outputStem;
+
+ CommandLine cmdLine;
+
+ cmdLine.setExecutablePath(Path::combine(context->options.binDir, String("render-test") + ProcessUtil::getExecutableSuffix()));
+
+ cmdLine.addArg(input.filePath);
+ cmdLine.addArg("-performance-profile");
+
+ _addRenderTestOptions(context->options, cmdLine);
+
+ for (auto arg : input.testOptions->args)
+ {
+ cmdLine.addArg(arg);
+ }
+
+ ExecuteResult exeRes;
+ TEST_RETURN_ON_DONE(spawnAndWait(context, outputStem, input.spawnType, cmdLine, exeRes));
+ if (context->isCollectingRequirements())
+ {
+ return TestResult::Pass;
+ }
+
+ auto actualOutput = getOutput(exeRes);
+
+ double time;
+ if (SLANG_FAILED(_extractProfileTime(actualOutput.getUnownedSlice(), time)))
+ {
+ return TestResult::Fail;
+ }
+
+ context->reporter->addExecutionTime(time);
+
+ return TestResult::Pass;
+}
+
TestResult runComputeComparisonImpl(TestContext* context, TestInput& input, const char *const* langOpts, size_t numLangOpts)
{
// TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass
@@ -2342,6 +2401,7 @@ static const TestCommandInfo s_testCommandInfos[] =
{ "CPP_COMPILER_EXECUTE", &runCPPCompilerExecute},
{ "CPP_COMPILER_SHARED_LIBRARY", &runCPPCompilerSharedLibrary},
{ "CPP_COMPILER_COMPILE", &runCPPCompilerCompile},
+ { "PERFORMANCE_PROFILE", &runPerformanceProfile},
};
TestResult runTest(
diff --git a/tools/slang-test/test-reporter.cpp b/tools/slang-test/test-reporter.cpp
index f7ab3cf1e..9dd34739b 100644
--- a/tools/slang-test/test-reporter.cpp
+++ b/tools/slang-test/test-reporter.cpp
@@ -142,6 +142,11 @@ void TestReporter::addResult(TestResult result)
m_numCurrentResults++;
}
+void TestReporter::addExecutionTime(double time)
+{
+ m_currentInfo.executionTime = time;
+}
+
void TestReporter::addResultWithLocation(TestResult result, const char* testText, const char* file, int line)
{
assert(m_inTest);
@@ -254,6 +259,31 @@ static void _appendEncodedTeamCityString(const UnownedStringSlice& in, StringBui
}
}
+static void _appendTime(double timeInSec, StringBuilder& out)
+{
+ SLANG_ASSERT(timeInSec >= 0.0);
+ if (timeInSec == 0.0 || timeInSec >= 1.0)
+ {
+ out << timeInSec << "s";
+ return;
+ }
+ timeInSec *= 1000.0f;
+ if (timeInSec > 1.0f)
+ {
+ out << timeInSec << "ms";
+ return;
+ }
+ timeInSec *= 1000.0f;
+ if (timeInSec > 1.0f)
+ {
+ out << timeInSec << "us";
+ return;
+ }
+
+ timeInSec *= 1000.0f;
+ out << timeInSec << "ns";
+}
+
void TestReporter::_addResult(const TestInfo& info)
{
m_totalTestCount++;
@@ -294,7 +324,13 @@ void TestReporter::_addResult(const TestInfo& info)
assert(!"unexpected");
break;
}
- printf("%s test: '%S'\n", resultString, info.name.toWString().begin());
+
+ StringBuilder buffer;
+ if (info.executionTime > 0.0f)
+ {
+ _appendTime(info.executionTime, buffer);
+ }
+ printf("%s test: '%S' %s\n", resultString, info.name.toWString().begin(), buffer.getBuffer());
break;
}
case TestOutputMode::TeamCity:
@@ -303,7 +339,7 @@ void TestReporter::_addResult(const TestInfo& info)
_appendEncodedTeamCityString(info.name.getUnownedSlice(), escapedTestName);
printf("##teamcity[testStarted name='%s']\n", escapedTestName.begin());
-
+
switch (info.testResult)
{
case TestResult::Fail:
@@ -322,12 +358,24 @@ void TestReporter::_addResult(const TestInfo& info)
}
case TestResult::Pass:
{
- if (info.message.getLength())
+ StringBuilder message;
+ message << info.message;
+ // Add execution time if one is set
+ if (info.executionTime > 0.0)
+ {
+ if (message.getLength())
+ {
+ message << " ";
+ }
+ _appendTime(info.executionTime, message);
+ }
+
+ if (message.getLength())
{
StringBuilder escapedMessage;
- _appendEncodedTeamCityString(info.message.getUnownedSlice(), escapedMessage);
+ _appendEncodedTeamCityString(message.getUnownedSlice(), escapedMessage);
printf("##teamcity[testStdOut name='%s' out='%s']\n", escapedTestName.begin(), escapedMessage.begin());
- }
+ }
break;
}
case TestResult::Ignored:
@@ -373,6 +421,8 @@ void TestReporter::_addResult(const TestInfo& info)
break;
}
+ // https://www.appveyor.com/docs/build-worker-api/#add-tests
+
CommandLine cmdLine;
cmdLine.setExecutableFilename("appveyor");
cmdLine.addArg("AddTest");
@@ -385,6 +435,15 @@ void TestReporter::_addResult(const TestInfo& info)
cmdLine.addArg("-Outcome");
cmdLine.addArg(resultString);
+ // If has execution time output it
+ if (info.executionTime > 0.0)
+ {
+ StringBuilder builder;
+ _appendTime(info.executionTime, builder);
+ cmdLine.addArg("-StdOut");
+ cmdLine.addArg(builder);
+ }
+
ExecuteResult exeRes;
SlangResult res = ProcessUtil::execute(cmdLine, exeRes);
diff --git a/tools/slang-test/test-reporter.h b/tools/slang-test/test-reporter.h
index 95f9950e8..cb4d5e985 100644
--- a/tools/slang-test/test-reporter.h
+++ b/tools/slang-test/test-reporter.h
@@ -66,7 +66,8 @@ class TestReporter
{
TestResult testResult = TestResult::Ignored;
Slang::String name;
- Slang::String message; ///< Message that is specific for the testResult
+ Slang::String message; ///< Message that is specific for the testResult
+ double executionTime = 0.0; ///< <= 0.0 if not defined. Time is in seconds.
};
class TestScope
@@ -110,7 +111,7 @@ class TestReporter
void addResult(TestResult result);
void addResultWithLocation(TestResult result, const char* testText, const char* file, int line);
void addResultWithLocation(bool testSucceeded, const char* testText, const char* file, int line);
-
+ void addExecutionTime(double time);
void endTest();
/// Runs start/endTest and outputs the result