diff options
| author | jsmall-nvidia <jsmall@nvidia.com> | 2019-09-23 15:38:25 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2019-09-23 15:38:25 -0400 |
| commit | 05af41d21d74d24871507e6f8f50574ea08c48a2 (patch) | |
| tree | 3197b021ed71c40f6035fdfa7d450b4b3b945422 /tools | |
| parent | ede0792fd9b4c7bc5c2653092ba1d492e67ca190 (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.cpp | 19 | ||||
| -rw-r--r-- | tools/render-test/cpu-compute-util.h | 4 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 50 | ||||
| -rw-r--r-- | tools/render-test/options.h | 3 | ||||
| -rw-r--r-- | tools/render-test/render-test-main.cpp | 122 | ||||
| -rw-r--r-- | tools/render-test/shader-input-layout.cpp | 6 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 25 | ||||
| -rw-r--r-- | tools/slang-test/slang-test-main.cpp | 60 | ||||
| -rw-r--r-- | tools/slang-test/test-reporter.cpp | 69 | ||||
| -rw-r--r-- | tools/slang-test/test-reporter.h | 5 |
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 |
