summaryrefslogtreecommitdiffstats
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
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.
-rw-r--r--slang.h8
-rw-r--r--source/core/slang-process-util.h4
-rw-r--r--source/core/unix/slang-unix-process-util.cpp15
-rw-r--r--source/core/windows/slang-win-process-util.cpp21
-rw-r--r--source/slang/slang-compiler.cpp5
-rw-r--r--source/slang/slang-compiler.h4
-rw-r--r--source/slang/slang.cpp10
-rw-r--r--tests/compute/performance-profile.slang52
-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
18 files changed, 438 insertions, 44 deletions
diff --git a/slang.h b/slang.h
index c4e62f01e..e31689200 100644
--- a/slang.h
+++ b/slang.h
@@ -2670,6 +2670,14 @@ namespace slang
SlangPassThrough passThrough,
const char* preludeText) = 0;
+ /** Get the 'prelude' for generated code for a 'downstream compiler'.
+ @param passThrough The downstream compiler for generated code that will have the prelude applied to it.
+ @param outPrelude On exit holds a blob that holds the string of the prelude.
+ */
+ virtual SLANG_NO_THROW void SLANG_MCALL getDownstreamCompilerPrelude(
+ SlangPassThrough passThrough,
+ ISlangBlob** outPrelude) = 0;
+
/** Get the build version 'tag' string. The string is the same as produced via `git describe --tags`
for the project. If Slang is built separately from the automated build scripts
the contents will by default be 'unknown'. Any string can be set by changing the
diff --git a/source/core/slang-process-util.h b/source/core/slang-process-util.h
index 89e3400a0..5d8c358e8 100644
--- a/source/core/slang-process-util.h
+++ b/source/core/slang-process-util.h
@@ -91,6 +91,10 @@ struct ProcessUtil
/// Append text escaped for using on a command line
static void appendCommandLineEscaped(const UnownedStringSlice& slice, StringBuilder& out);
+
+ static uint64_t getClockFrequency();
+
+ static uint64_t getClockTick();
};
// -----------------------------------------------------------------------
diff --git a/source/core/unix/slang-unix-process-util.cpp b/source/core/unix/slang-unix-process-util.cpp
index 400f4f773..ab4366b06 100644
--- a/source/core/unix/slang-unix-process-util.cpp
+++ b/source/core/unix/slang-unix-process-util.cpp
@@ -15,6 +15,8 @@
#include <sys/wait.h>
#include <unistd.h>
+#include <time.h>
+
namespace Slang {
@@ -247,4 +249,17 @@ namespace Slang {
return SLANG_FAIL;
}
+
+/* static */uint64_t ProcessUtil::getClockFrequency()
+{
+ return 1000000000;
+}
+
+/* static */uint64_t ProcessUtil::getClockTick()
+{
+ struct timespec now;
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ return now.tv_sec + now.tv_nsec;
+}
+
} // namespace Slang
diff --git a/source/core/windows/slang-win-process-util.cpp b/source/core/windows/slang-win-process-util.cpp
index ac6468109..3a5a01cb3 100644
--- a/source/core/windows/slang-win-process-util.cpp
+++ b/source/core/windows/slang-win-process-util.cpp
@@ -344,4 +344,25 @@ static DWORD WINAPI _readerThreadProc(LPVOID threadParam)
return SLANG_OK;
}
+static uint64_t _getClockFrequency()
+{
+ LARGE_INTEGER timerFrequency;
+ QueryPerformanceFrequency(&timerFrequency);
+ return timerFrequency.QuadPart;
+}
+
+static const uint64_t g_frequency = _getClockFrequency();
+
+/* static */uint64_t ProcessUtil::getClockFrequency()
+{
+ return g_frequency;
+}
+
+/* static */uint64_t ProcessUtil::getClockTick()
+{
+ LARGE_INTEGER counter;
+ QueryPerformanceCounter(&counter);
+ return counter.QuadPart;
+}
+
}
diff --git a/source/slang/slang-compiler.cpp b/source/slang/slang-compiler.cpp
index ce22186ee..0860a339c 100644
--- a/source/slang/slang-compiler.cpp
+++ b/source/slang/slang-compiler.cpp
@@ -1221,8 +1221,7 @@ SlangResult dissassembleDXILUsingDXC(
bool useOriginalFile = false;
String compileSourcePath;
- String sourceContents;
-
+
String rawSource;
SourceLanguage rawSourceLanguage = SourceLanguage::Unknown;
@@ -1313,7 +1312,7 @@ SlangResult dissassembleDXILUsingDXC(
{
_appendCodeWithPath(sourceFile->getPathInfo().foundPath.getUnownedSlice(), sourceFile->getContent(), codeBuilder);
}
- sourceContents = codeBuilder.ProduceString();
+ rawSource = codeBuilder.ProduceString();
}
}
else
diff --git a/source/slang/slang-compiler.h b/source/slang/slang-compiler.h
index c43af9506..ad90bb5ee 100644
--- a/source/slang/slang-compiler.h
+++ b/source/slang/slang-compiler.h
@@ -1824,6 +1824,10 @@ namespace Slang
SlangPassThrough inPassThrough,
char const* prelude) override;
+ SLANG_NO_THROW void SLANG_MCALL getDownstreamCompilerPrelude(
+ SlangPassThrough inPassThrough,
+ ISlangBlob** outPrelude) override;
+
SLANG_NO_THROW const char* SLANG_MCALL getBuildTagString() override;
enum class SharedLibraryFuncType
diff --git a/source/slang/slang.cpp b/source/slang/slang.cpp
index d5adae722..5f22f8a23 100644
--- a/source/slang/slang.cpp
+++ b/source/slang/slang.cpp
@@ -202,6 +202,16 @@ SLANG_NO_THROW void SLANG_MCALL Session::setDownstreamCompilerPrelude(
m_downstreamCompilerPreludes[int(passThrough)] = prelude;
}
+SLANG_NO_THROW void SLANG_MCALL Session::getDownstreamCompilerPrelude(
+ SlangPassThrough inPassThrough,
+ ISlangBlob** outPrelude)
+{
+ PassThroughMode passThrough = PassThroughMode(inPassThrough);
+ SLANG_ASSERT(int(passThrough) > int(PassThroughMode::None) && int(passThrough) < int(PassThroughMode::CountOf));
+
+ *outPrelude = Slang::StringUtil::createStringBlob(m_downstreamCompilerPreludes[int(passThrough)]).detach();
+}
+
SLANG_NO_THROW const char* SLANG_MCALL Session::getBuildTagString()
{
return SLANG_TAG_VERSION;
diff --git a/tests/compute/performance-profile.slang b/tests/compute/performance-profile.slang
new file mode 100644
index 000000000..6ec8ecd94
--- /dev/null
+++ b/tests/compute/performance-profile.slang
@@ -0,0 +1,52 @@
+//TEST(compute):PERFORMANCE_PROFILE:-cpu -compute -compile-arg -O3 -compute-dispatch 256,1,1
+//TEST(compute):PERFORMANCE_PROFILE:-cpu -compute -source-language cpp -compile-arg -O3 -compute-dispatch 256,1,1
+//TEST(compute):PERFORMANCE_PROFILE:-slang -compute -compute-dispatch 256,1,1
+//TEST(compute):PERFORMANCE_PROFILE:-slang -compute -dx12 -compute-dispatch 256,1,1
+//TEST(compute, vulkan):PERFORMANCE_PROFILE:-vk -compute -compute-dispatch 256,1,1
+
+//TEST_INPUT:ubuffer(random(float, 4096, -1, 1), stride=4):dxbinding(0),glbinding(0),out,name outputBuffer
+
+#ifndef __cplusplus
+
+RWStructuredBuffer<float> outputBuffer;
+
+[numthreads(16, 1, 1)]
+void computeMain(uint3 dispatchThreadID : SV_DispatchThreadID)
+{
+ uint i = dispatchThreadID.x;
+ float v = outputBuffer[i];
+ v = v < 0.0f ? (v * v) : (v + v + v);
+ outputBuffer[i] = v;
+}
+
+
+#else
+
+namespace { // anonymous
+
+struct LocalUniformState
+{
+ RWStructuredBuffer<float> outputBuffer_0;
+};
+
+} // anonymous
+
+static void _calc(const RWStructuredBuffer<float>& buf, int start, int end)
+{
+ assert(start >= 0 && end <= buf.count);
+ float* data = buf.data;
+
+ for (int i = start; i < end; ++i)
+ {
+ float v = data[i];
+ data[i] = v < 0.0f ? (v * v) : (v + v + v);
+ }
+}
+
+SLANG_PRELUDE_EXPORT
+void computeMain(ComputeVaryingInput* varyingInput, UniformEntryPointParams* params, LocalUniformState* uniformState)
+{
+ _calc(uniformState->outputBuffer_0, varyingInput->startGroupID.x * 16, varyingInput->endGroupID.x * 16);
+}
+
+#endif \ No newline at end of file
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