From 047daae9300c8a94d28383cf992ce00e3ad2da1e Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Mon, 9 Sep 2019 13:54:31 -0400 Subject: CPU compute testing on non windows targets (#1045) * WIP: Refactor of CPUCompute and stand alone cpu-render-test * Fix compilation on CygWin. * Make CPU compute tests run on non windows targets. * Check that C/C++ compiler is available for CPU compute. * Fix some tabbing issues. * Add -fPIC on gfx * Use dxcompiler_47.dll from slang-binaries on windows. * make https for git module slang-binaries * Fix comment in premake5.lua around d3dcompiler_47.dll * Add resources to the CPUComputeUtil::Context to keep in scope. * Fixes problem compiling on cygwin where dx12 is included in build of gfx lib. --- tools/render-test/cpu-compute-util.cpp | 365 +++++++++++++++++ tools/render-test/cpu-compute-util.h | 40 ++ tools/render-test/cpu-memory-binding.cpp | 1 + tools/render-test/cpu-render-test-main.cpp | 99 +++++ tools/render-test/options.cpp | 2 +- tools/render-test/render-test-main.cpp | 453 +-------------------- tools/render-test/render-test-tool.vcxproj | 2 + tools/render-test/render-test-tool.vcxproj.filters | 6 + tools/render-test/shader-input-layout.cpp | 34 +- tools/render-test/slang-support.cpp | 89 +++- tools/render-test/slang-support.h | 19 + 11 files changed, 651 insertions(+), 459 deletions(-) create mode 100644 tools/render-test/cpu-compute-util.cpp create mode 100644 tools/render-test/cpu-compute-util.h create mode 100644 tools/render-test/cpu-render-test-main.cpp (limited to 'tools/render-test') diff --git a/tools/render-test/cpu-compute-util.cpp b/tools/render-test/cpu-compute-util.cpp new file mode 100644 index 000000000..85a8fb1b0 --- /dev/null +++ b/tools/render-test/cpu-compute-util.cpp @@ -0,0 +1,365 @@ +#define _CRT_SECURE_NO_WARNINGS 1 + +#include "cpu-compute-util.h" + +#include "../../slang-com-helper.h" + +#include "../../source/core/slang-std-writers.h" +#include "../../source/core/slang-token-reader.h" + +#define SLANG_PRELUDE_NAMESPACE CPPPrelude +#include "../../prelude/slang-cpp-types.h" + +namespace renderer_test { +using namespace Slang; + +/* static */SlangResult CPUComputeUtil::writeBindings(const ShaderInputLayout& layout, const List& buffers, const String& fileName) +{ + FILE * f = fopen(fileName.getBuffer(), "wb"); + if (!f) + { + return SLANG_FAIL; + } + + const auto& entries = layout.entries; + + for (int i = 0; i < entries.getCount(); ++i) + { + const auto& entry = entries[i]; + if (entry.isOutput) + { + const auto& buffer = buffers[i]; + + unsigned int* ptr = (unsigned int*)buffer.m_data; + + const int size = int(entry.bufferData.getCount()); + // Must be the same size or less than allocated buffer + SLANG_ASSERT(size * sizeof(unsigned int) <= buffer.m_sizeInBytes); + + for (int i = 0; i < size; ++i) + { + unsigned int v = ptr[i]; + + fprintf(f, "%X\n", v); + } + } + } + fclose(f); + return SLANG_OK; +} + + +template +struct OneTexture2D : public CPUComputeUtil::Resource, public CPPPrelude::ITexture2D +{ + void setOne(void* out) + { + float* dst = (float*)out; + for (int i = 0; i < COUNT; ++i) + { + dst[i] = 1.0f; + } + } + + virtual void Load(const CPPPrelude::int3& v, void* out) SLANG_OVERRIDE + { + setOne(out); + } + virtual void Sample(CPPPrelude::SamplerState samplerState, const CPPPrelude::float2& loc, void* out) SLANG_OVERRIDE + { + setOne(out); + } + virtual void SampleLevel(CPPPrelude::SamplerState samplerState, const CPPPrelude::float2& loc, float level, void* out) SLANG_OVERRIDE + { + setOne(out); + } + + OneTexture2D() + { + m_interface = static_cast(this); + } +}; + +static CPUComputeUtil::Resource* _newOneTexture2D(int elemCount) +{ + switch (elemCount) + { + case 1: return new OneTexture2D<1>(); + case 2: return new OneTexture2D<2>(); + case 3: return new OneTexture2D<3>(); + case 4: return new OneTexture2D<4>(); + default: return nullptr; + } +} + +/* static */SlangResult CPUComputeUtil::calcBindings(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext) +{ + auto request = compilationAndLayout.output.request; + auto reflection = (slang::ShaderReflection*) spGetReflection(request); + + const auto& sourcePath = compilationAndLayout.sourcePath; + + auto& binding = outContext.binding; + + binding.init(reflection, 0); + + auto& buffers = outContext.buffers; + buffers.clear(); + + // Okay we need to find all of the bindings and match up to those in the layout + const ShaderInputLayout& layout = compilationAndLayout.layout; + + { + auto outStream = StdWriters::getOut(); + auto& entries = layout.entries; + buffers.setCount(entries.getCount()); + + for (int entryIndex = 0; entryIndex < entries.getCount(); ++entryIndex) + { + auto& entry = entries[entryIndex]; + + if (entry.name.getLength() == 0) + { + outStream.print("No 'name' specified for resources in '%s'\n", sourcePath.getBuffer()); + return SLANG_FAIL; + } + + // We will parse the 'name' as may be path to a resource + TokenReader parser(entry.name); + + CPUMemoryBinding::Location location; + + { + Token nameToken = parser.ReadToken(); + if (nameToken.Type != TokenType::Identifier) + { + outStream.print("Invalid input syntax at line %d", int(parser.NextToken().Position.Line)); + return SLANG_FAIL; + } + location = binding.find(nameToken.Content.getBuffer()); + if (location.isInvalid()) + { + outStream.print("Unable to find entry in '%s' for '%s' (for CPU name must be specified) \n", sourcePath.getBuffer(), entry.name.getBuffer()); + return SLANG_FAIL; + } + } + + while (!parser.IsEnd()) + { + Token token = parser.NextToken(0); + + if (token.Type == TokenType::LBracket) + { + parser.ReadToken(); + int index = parser.ReadInt(); + SLANG_ASSERT(index >= 0); + + location = location.toIndex(index); + if (location.isInvalid()) + { + outStream.print("Unable to find entry in '%d' in '%s'\n", index, entry.name.getBuffer()); + return SLANG_FAIL; + } + parser.ReadMatchingToken(TokenType::RBracket); + } + else if (token.Type == TokenType::Dot) + { + parser.ReadToken(); + Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier); + + location = location.toField(identifierToken.Content.getBuffer()); + if (location.isInvalid()) + { + outStream.print("Unable to find field '%s' in '%s'\n", identifierToken.Content.getBuffer(), entry.name.getBuffer()); + return SLANG_FAIL; + } + } + else if (token.Type == TokenType::Comma) + { + // Break out + break; + } + else + { + throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line); + } + } + + auto& srcEntry = layout.entries[entryIndex]; + + auto typeLayout = location.getTypeLayout(); + const auto kind = typeLayout->getKind(); + switch (kind) + { + case slang::TypeReflection::Kind::Vector: + case slang::TypeReflection::Kind::Matrix: + case slang::TypeReflection::Kind::Array: + case slang::TypeReflection::Kind::Scalar: + case slang::TypeReflection::Kind::Struct: + { + SLANG_RETURN_ON_FAIL(binding.setInplace(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); + break; + } + default: + break; + case slang::TypeReflection::Kind::ConstantBuffer: + { + SLANG_RETURN_ON_FAIL(binding.setBufferContents(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); + break; + } + case slang::TypeReflection::Kind::ParameterBlock: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::TextureBuffer: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::ShaderStorageBuffer: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + break; + } + case slang::TypeReflection::Kind::GenericTypeParameter: + { + const char* name = typeLayout->getName(); + SLANG_UNUSED(name); + break; + } + case slang::TypeReflection::Kind::Interface: + { + const char* name = typeLayout->getName(); + SLANG_UNUSED(name); + break; + } + case slang::TypeReflection::Kind::Resource: + { + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); + + //auto access = type->getResourceAccess(); + + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) + { + default: + assert(!"unhandled case"); + break; + case SLANG_TEXTURE_2D: + { + slang::TypeReflection* typeReflection = location.getTypeLayout()->getResourceResultType(); + + int count = 1; + if (typeReflection->getKind() == slang::TypeReflection::Kind::Vector) + { + count = int(typeReflection->getElementCount()); + } + + RefPtr resource = _newOneTexture2D(count); + outContext.m_resources.add(resource); + + SLANG_RETURN_ON_FAIL(binding.setObject(location, resource->getInterface())); + break; + } + case SLANG_TEXTURE_1D: + case SLANG_TEXTURE_3D: + case SLANG_TEXTURE_CUBE: + case SLANG_TEXTURE_BUFFER: + { + // Just set to null for now + SLANG_RETURN_ON_FAIL(binding.setObject(location, nullptr)); + break; + } + case SLANG_BYTE_ADDRESS_BUFFER: + case SLANG_STRUCTURED_BUFFER: + { + CPUMemoryBinding::Buffer buffer; + SLANG_RETURN_ON_FAIL(binding.setNewBuffer(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int), buffer)); + buffers[entryIndex] = buffer; + break; + } + } + if (shape & SLANG_TEXTURE_ARRAY_FLAG) + { + + } + if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) + { + + } + + break; + } + } + } + } + + return SLANG_OK; +} + +/* static */SlangResult CPUComputeUtil::execute(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& context) +{ + auto request = compilationAndLayout.output.request; + auto reflection = (slang::ShaderReflection*) spGetReflection(request); + + ComPtr sharedLibrary; + SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(request, 0, 0, sharedLibrary.writeRef())); + + // Use reflection to find the entry point name + + struct UniformState; + typedef void(*Func)(CPPPrelude::ComputeVaryingInput* varyingInput, CPPPrelude::UniformEntryPointParams* uniformEntryPointParams, UniformState* uniformState); + + slang::EntryPointReflection* entryPoint = nullptr; + Func func = nullptr; + { + auto entryPointCount = reflection->getEntryPointCount(); + SLANG_ASSERT(entryPointCount == 1); + + entryPoint = reflection->getEntryPointByIndex(0); + + const char* entryPointName = entryPoint->getName(); + func = (Func)sharedLibrary->findFuncByName(entryPointName); + + if (!func) + { + return SLANG_FAIL; + } + } + + SlangUInt numThreadsPerAxis[3]; + entryPoint->getComputeThreadGroupSize(3, numThreadsPerAxis); + + { + UniformState* uniformState = (UniformState*)context.binding.m_rootBuffer.m_data; + CPPPrelude::UniformEntryPointParams* uniformEntryPointParams = (CPPPrelude::UniformEntryPointParams*)context.binding.m_entryPointBuffer.m_data; + + CPPPrelude::ComputeVaryingInput varying; + varying.groupID = {}; + + for (int z = 0; z < int(numThreadsPerAxis[2]); ++z) + { + varying.groupThreadID.z = z; + for (int y = 0; y < int(numThreadsPerAxis[1]); ++y) + { + varying.groupThreadID.y = y; + for (int x = 0; x < int(numThreadsPerAxis[0]); ++x) + { + varying.groupThreadID.x = x; + + func(&varying, uniformEntryPointParams, uniformState); + } + } + } + } + + return SLANG_OK; +} + + + +} // renderer_test diff --git a/tools/render-test/cpu-compute-util.h b/tools/render-test/cpu-compute-util.h new file mode 100644 index 000000000..cbc4e6e58 --- /dev/null +++ b/tools/render-test/cpu-compute-util.h @@ -0,0 +1,40 @@ +#ifndef CPU_COMPUTE_UTIL_H +#define CPU_COMPUTE_UTIL_H + +#include "cpu-memory-binding.h" +#include "slang-support.h" +#include "options.h" + +#include "../../source/core/slang-smart-pointer.h" + +namespace renderer_test { + +struct CPUComputeUtil +{ + struct Resource : public RefObject + { + void* getInterface() const { return m_interface; } + void* m_interface; + }; + + struct Context + { + /// Holds the binding information + CPUMemoryBinding binding; + /// Buffers are held in same order as entries in layout (useful for dumping out bindings) + List buffers; + /// Holds the resources created (in calcBindings) + List > m_resources; + }; + + static SlangResult calcBindings(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext); + + static SlangResult execute(const ShaderCompilerUtil::OutputAndLayout& compilationAndLayout, Context& outContext); + + static SlangResult writeBindings(const ShaderInputLayout& layout, const List& buffers, const Slang::String& fileName); +}; + + +} // renderer_test + +#endif //CPU_MEMORY_BINDING_H diff --git a/tools/render-test/cpu-memory-binding.cpp b/tools/render-test/cpu-memory-binding.cpp index 7760c5919..612c2fc57 100644 --- a/tools/render-test/cpu-memory-binding.cpp +++ b/tools/render-test/cpu-memory-binding.cpp @@ -46,6 +46,7 @@ SlangResult CPUMemoryBinding::init(slang::ShaderReflection* reflection, int entr { m_reflection = reflection; m_rootBuffer = Buffer(); + m_entryPointBuffer = Buffer(); m_allBuffers.clear(); m_arena.deallocateAll(); diff --git a/tools/render-test/cpu-render-test-main.cpp b/tools/render-test/cpu-render-test-main.cpp new file mode 100644 index 000000000..d754f9359 --- /dev/null +++ b/tools/render-test/cpu-render-test-main.cpp @@ -0,0 +1,99 @@ +// cpu-render-test-main.cpp + +#include "options.h" + +#include "slang-support.h" + +#include "../source/core/slang-io.h" + +#include "shader-input-layout.h" +#include +#include + +#include "../../source/core/slang-test-tool-util.h" + +#include "cpu-compute-util.h" + +SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSession* session, int argcIn, const char*const* argvIn) +{ + using namespace renderer_test; + using namespace Slang; + + StdWriters::setSingleton(stdWriters); + + // Parse command-line options + SLANG_RETURN_ON_FAIL(parseOptions(argcIn, argvIn, StdWriters::getError())); + + // Declare window pointer before renderer, such that window is released after renderer + RefPtr window; + // Renderer is constructed (later) using the window + Slang::RefPtr renderer; + + ShaderCompilerUtil::Input input; + + input.profile = ""; + input.target = SLANG_TARGET_NONE; + input.args = &gOptions.slangArgs[0]; + input.argCount = gOptions.slangArgCount; + + SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; + SlangPassThrough slangPassThrough = SLANG_PASS_THROUGH_NONE; + char const* profileName = ""; + switch (gOptions.rendererType) + { + case RendererType::CPU: + input.target = SLANG_HOST_CALLABLE; + input.profile = ""; + nativeLanguage = SLANG_SOURCE_LANGUAGE_CPP; + slangPassThrough = SLANG_PASS_THROUGH_GENERIC_C_CPP; + break; + default: + fprintf(stderr, "error: unexpected\n"); + return SLANG_FAIL; + } + + switch (gOptions.inputLanguageID) + { + case Options::InputLanguageID::Slang: + input.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG; + input.passThrough = SLANG_PASS_THROUGH_NONE; + break; + + case Options::InputLanguageID::Native: + input.sourceLanguage = nativeLanguage; + input.passThrough = slangPassThrough; + break; + + default: + break; + } + + // Use the profile name set on options if set + input.profile = gOptions.profileName ? gOptions.profileName : input.profile; + + { + ShaderCompilerUtil::OutputAndLayout compilationAndLayout; + SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.shaderType, input, compilationAndLayout)); + + CPUComputeUtil::Context context; + SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context)); + SLANG_RETURN_ON_FAIL(CPUComputeUtil::execute(compilationAndLayout, context)); + + // Dump everything out that was written + return CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath); + } +} + +int main(int argc, char** argv) +{ + using namespace Slang; + SlangSession* session = spCreateSession(nullptr); + + TestToolUtil::setSessionDefaultPrelude(argv[0], session); + auto stdWriters = StdWriters::initDefaultSingleton(); + SlangResult res = innerMain(stdWriters, session, argc, argv); + spDestroySession(session); + + return (int)TestToolUtil::getReturnCode(res); +} + diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index f8c2c1d12..a9e51b8e0 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -15,7 +15,7 @@ namespace renderer_test { using namespace Slang; -static const Options gDefaultOptions; +static const Options gDefaultOptions = Options(); Options gOptions; diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index 440ba9c82..5b9621142 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -21,13 +21,9 @@ #include #include -#define SLANG_PRELUDE_NAMESPACE CPPPrelude -#include "../../prelude/slang-cpp-types.h" - #include "../../source/core/slang-test-tool-util.h" -#include "../../source/core/slang-memory-arena.h" -#include "cpu-memory-binding.h" +#include "cpu-compute-util.h" #define WIN32_LEAN_AND_MEAN #define NOMINMAX @@ -241,436 +237,6 @@ class RenderTestApp int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. }; -// Entry point name to use for vertex/fragment shader -static const char vertexEntryPointName[] = "vertexMain"; -static const char fragmentEntryPointName[] = "fragmentMain"; -static const char computeEntryPointName[] = "computeMain"; - -static SlangResult _readSource(const String& inSourcePath, List& outSourceText) -{ - // Read in the source code - FILE* sourceFile = fopen(inSourcePath.getBuffer(), "rb"); - if (!sourceFile) - { - fprintf(stderr, "error: failed to open '%s' for reading\n", inSourcePath.getBuffer()); - return SLANG_FAIL; - } - fseek(sourceFile, 0, SEEK_END); - size_t sourceSize = ftell(sourceFile); - fseek(sourceFile, 0, SEEK_SET); - - outSourceText.setCount(sourceSize + 1); - fread(outSourceText.getBuffer(), sourceSize, 1, sourceFile); - fclose(sourceFile); - outSourceText[sourceSize] = 0; - - return SLANG_OK; -} - -struct CompileOutput -{ - ShaderCompilerUtil::Output compileOutput; - ShaderInputLayout layout; -}; - -static SlangResult _compile(SlangSession* session, const String& sourcePath, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input, CompileOutput& output) -{ - List sourceText; - SLANG_RETURN_ON_FAIL(_readSource(sourcePath, sourceText)); - - auto& layout = output.layout; - - // Default the amount of renderTargets based on shader type - switch (shaderType) - { - default: - layout.numRenderTargets = 1; - break; - - case Options::ShaderProgramType::Compute: - layout.numRenderTargets = 0; - break; - } - - // Parse the layout - layout.parse(sourceText.getBuffer()); - layout.updateForTarget(input.target); - - // Setup SourceInfo - ShaderCompileRequest::SourceInfo sourceInfo; - sourceInfo.path = sourcePath.getBuffer(); - sourceInfo.dataBegin = sourceText.getBuffer(); - // Subtract 1 because it's zero terminated - sourceInfo.dataEnd = sourceText.getBuffer() + sourceText.getCount() - 1; - - ShaderCompileRequest compileRequest; - compileRequest.source = sourceInfo; - if (shaderType == Options::ShaderProgramType::Graphics || shaderType == Options::ShaderProgramType::GraphicsCompute) - { - compileRequest.vertexShader.source = sourceInfo; - compileRequest.vertexShader.name = vertexEntryPointName; - compileRequest.fragmentShader.source = sourceInfo; - compileRequest.fragmentShader.name = fragmentEntryPointName; - } - else - { - compileRequest.computeShader.source = sourceInfo; - compileRequest.computeShader.name = computeEntryPointName; - } - compileRequest.globalGenericTypeArguments = layout.globalGenericTypeArguments; - compileRequest.entryPointGenericTypeArguments = layout.entryPointGenericTypeArguments; - compileRequest.globalExistentialTypeArguments = layout.globalExistentialTypeArguments; - compileRequest.entryPointExistentialTypeArguments = layout.entryPointExistentialTypeArguments; - - return ShaderCompilerUtil::compileProgram(session, input, compileRequest, output.compileOutput); -} - -static SlangResult _writeBindings(const ShaderInputLayout& layout, const List& buffers, const String& fileName) -{ - FILE * f = fopen(fileName.getBuffer(), "wb"); - if (!f) - { - return SLANG_FAIL; - } - - const auto& entries = layout.entries; - - for (int i = 0; i < entries.getCount(); ++i) - { - const auto& entry = entries[i]; - if (entry.isOutput) - { - const auto& buffer = buffers[i]; - - unsigned int* ptr = (unsigned int*)buffer.m_data; - - const int size = int(entry.bufferData.getCount()); - // Must be the same size or less thatn allocated buffer - SLANG_ASSERT(size * sizeof(unsigned int) <= buffer.m_sizeInBytes); - - for (int i = 0; i < size; ++i) - { - unsigned int v = ptr[i]; - - fprintf(f, "%X\n", v); - } - } - } - fclose(f); - return SLANG_OK; -} - -struct CPUResource: public RefObject -{ - void* getInterface() const { return m_interface; } - void* m_interface; -}; - -template -struct OneTexture2D: public CPUResource, public CPPPrelude::ITexture2D -{ - void setOne(void* out) - { - float* dst = (float*)out; - for (int i = 0; i < COUNT; ++i) - { - dst[i] = 1.0f; - } - } - - virtual void Load(const CPPPrelude::int3& v, void* out) SLANG_OVERRIDE - { - setOne(out); - } - virtual void Sample(CPPPrelude::SamplerState samplerState, const CPPPrelude::float2& loc, void* out) SLANG_OVERRIDE - { - setOne(out); - } - virtual void SampleLevel(CPPPrelude::SamplerState samplerState, const CPPPrelude::float2& loc, float level, void* out) SLANG_OVERRIDE - { - setOne(out); - } - - OneTexture2D() - { - m_interface = static_cast(this); - } -}; - -static CPUResource* _newOneTexture2D(int elemCount) -{ - switch (elemCount) - { - case 1: return new OneTexture2D<1>(); - case 2: return new OneTexture2D<2>(); - case 3: return new OneTexture2D<3>(); - case 4: return new OneTexture2D<4>(); - default: return nullptr; - } -} - -static SlangResult _doCPUCompute(SlangSession* session, const String& sourcePath, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) -{ - CompileOutput output; - SLANG_RETURN_ON_FAIL(_compile(session, sourcePath, shaderType, input, output)); - - ComPtr sharedLibrary; - SLANG_RETURN_ON_FAIL(spGetEntryPointHostCallable(output.compileOutput.request, 0, 0, sharedLibrary.writeRef())); - - // Use reflection to find the entry point name - auto request = output.compileOutput.request; - - struct UniformState; - typedef void(*Func)(CPPPrelude::ComputeVaryingInput* varyingInput, CPPPrelude::UniformEntryPointParams* params, UniformState* uniformState); - - auto reflection = (slang::ShaderReflection*) spGetReflection(request); - - slang::EntryPointReflection* entryPoint = nullptr; - Func func = nullptr; - { - auto entryPointCount = reflection->getEntryPointCount(); - SLANG_ASSERT(entryPointCount == 1); - - entryPoint = reflection->getEntryPointByIndex(0); - - const char* entryPointName = entryPoint->getName(); - func = (Func) sharedLibrary->findFuncByName(entryPointName); - - if (!func) - { - return SLANG_FAIL; - } - } - - CPUMemoryBinding binding; - SLANG_RETURN_ON_FAIL(binding.init(reflection, 0)); - - List buffers; - - // Okay we need to find all of the bindings and match up to those in the layout - ShaderInputLayout& layout = output.layout; - - List > resources; - - { - auto& outStream = StdWriters::getOut(); - auto& entries = layout.entries; - buffers.setCount(entries.getCount()); - - for (int entryIndex = 0; entryIndex < entries.getCount(); ++entryIndex) - { - auto& entry = entries[entryIndex]; - - if (entry.name.getLength() == 0) - { - outStream.print("No 'name' specified for resources in '%s'\n", sourcePath.getBuffer()); - return SLANG_FAIL; - } - - // We will parse the 'name' as may be path to a resource - TokenReader parser(entry.name); - - CPUMemoryBinding::Location location; - - { - Token nameToken = parser.ReadToken(); - if (nameToken.Type != TokenType::Identifier) - { - outStream.print("Invalid input syntax at line %d", int(parser.NextToken().Position.Line)); - return SLANG_FAIL; - } - location = binding.find(nameToken.Content.getBuffer()); - if (location.isInvalid()) - { - outStream.print("Unable to find entry in '%s' for '%s' (for CPU name must be specified) \n", sourcePath.getBuffer(), entry.name.getBuffer()); - return SLANG_FAIL; - } - } - - while (!parser.IsEnd()) - { - Token token = parser.NextToken(0); - - if (token.Type == TokenType::LBracket) - { - parser.ReadToken(); - int index = parser.ReadInt(); - SLANG_ASSERT(index >= 0); - - location = location.toIndex(index); - if (location.isInvalid()) - { - outStream.print("Unable to find entry in '%d' in '%s'\n", index, entry.name.getBuffer()); - return SLANG_FAIL; - } - parser.ReadMatchingToken(TokenType::RBracket); - } - else if (token.Type == TokenType::Dot) - { - parser.ReadToken(); - Token identifierToken = parser.ReadMatchingToken(TokenType::Identifier); - - location = location.toField(identifierToken.Content.getBuffer()); - if (location.isInvalid()) - { - outStream.print("Unable to find field '%s' in '%s'\n", identifierToken.Content.getBuffer(), entry.name.getBuffer()); - return SLANG_FAIL; - } - } - else if (token.Type == TokenType::Comma) - { - // Break out - break; - } - else - { - throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line); - } - } - - auto& srcEntry = layout.entries[entryIndex]; - - auto typeLayout = location.getTypeLayout(); - const auto kind = typeLayout->getKind(); - switch (kind) - { - case slang::TypeReflection::Kind::Vector: - case slang::TypeReflection::Kind::Matrix: - case slang::TypeReflection::Kind::Array: - case slang::TypeReflection::Kind::Scalar: - case slang::TypeReflection::Kind::Struct: - { - SLANG_RETURN_ON_FAIL(binding.setInplace(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int))); - break; - } - default: - break; - case slang::TypeReflection::Kind::ConstantBuffer: - { - SLANG_RETURN_ON_FAIL(binding.setBufferContents(location, srcEntry.bufferData.getBuffer(),srcEntry.bufferData.getCount() * sizeof(unsigned int) )); - break; - } - case slang::TypeReflection::Kind::ParameterBlock: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::TextureBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::ShaderStorageBuffer: - { - auto elementTypeLayout = typeLayout->getElementTypeLayout(); - SLANG_UNUSED(elementTypeLayout); - break; - } - case slang::TypeReflection::Kind::GenericTypeParameter: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Interface: - { - const char* name = typeLayout->getName(); - SLANG_UNUSED(name); - break; - } - case slang::TypeReflection::Kind::Resource: - { - auto type = typeLayout->getType(); - auto shape = type->getResourceShape(); - - //auto access = type->getResourceAccess(); - - switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) - { - default: - assert(!"unhandled case"); - break; - case SLANG_TEXTURE_2D: - { - slang::TypeReflection* typeReflection = location.getTypeLayout()->getResourceResultType(); - - int count = 1; - if (typeReflection->getKind() == slang::TypeReflection::Kind::Vector) - { - count = int(typeReflection->getElementCount()); - } - - RefPtr resource = _newOneTexture2D(count); - resources.add(resource); - - SLANG_RETURN_ON_FAIL(binding.setObject(location, resource->getInterface())); - break; - } - case SLANG_TEXTURE_1D: - case SLANG_TEXTURE_3D: - case SLANG_TEXTURE_CUBE: - case SLANG_TEXTURE_BUFFER: - { - // Just set to null for now - SLANG_RETURN_ON_FAIL(binding.setObject(location, nullptr)); - break; - } - case SLANG_BYTE_ADDRESS_BUFFER: - case SLANG_STRUCTURED_BUFFER: - { - CPUMemoryBinding::Buffer buffer; - SLANG_RETURN_ON_FAIL(binding.setNewBuffer(location, srcEntry.bufferData.getBuffer(), srcEntry.bufferData.getCount() * sizeof(unsigned int), buffer )); - buffers[entryIndex] = buffer; - break; - } - } - if (shape & SLANG_TEXTURE_ARRAY_FLAG) - { - - } - if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) - { - - } - - break; - } - } - } - } - - SlangUInt numThreadsPerAxis[3]; - entryPoint->getComputeThreadGroupSize(3, numThreadsPerAxis); - - { - UniformState* uniformState = (UniformState*)binding.m_rootBuffer.m_data; - CPPPrelude::UniformEntryPointParams* params = (CPPPrelude::UniformEntryPointParams*)binding.m_entryPointBuffer.m_data; - - CPPPrelude::ComputeVaryingInput varying; - varying.groupID = {}; - - for (int z = 0; z < int(numThreadsPerAxis[2]); ++z) - { - varying.groupThreadID.z = z; - for (int y = 0; y < int(numThreadsPerAxis[1]); ++y) - { - varying.groupThreadID.y = y; - for (int x = 0; x < int(numThreadsPerAxis[0]); ++x) - { - varying.groupThreadID.x = x; - - func(&varying, params, uniformState); - } - } - } - } - - // Dump everything out that was write (we wrote in place!) - return _writeBindings(layout, buffers, gOptions.outputPath); -} - SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) { SLANG_RETURN_ON_FAIL(_initializeShaders(session, renderer, shaderType, input)); @@ -769,10 +335,10 @@ SlangResult RenderTestApp::initialize(SlangSession* session, Renderer* renderer, Result RenderTestApp::_initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) { - CompileOutput output; - SLANG_RETURN_ON_FAIL(_compile(session, gOptions.sourcePath, shaderType, input, output)); + ShaderCompilerUtil::OutputAndLayout output; + SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, shaderType, input, output)); m_shaderInputLayout = output.layout; - m_shaderProgram = renderer->createProgram(output.compileOutput.desc); + m_shaderProgram = renderer->createProgram(output.output.desc); return m_shaderProgram ? SLANG_OK : SLANG_FAIL; } @@ -1024,8 +590,15 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe if (!renderer) { - SLANG_RETURN_ON_FAIL(_doCPUCompute(session, gOptions.sourcePath, gOptions.shaderType, input)); - return SLANG_OK; + ShaderCompilerUtil::OutputAndLayout compilationAndLayout; + SLANG_RETURN_ON_FAIL(ShaderCompilerUtil::compileWithLayout(session, gOptions.sourcePath, gOptions.shaderType, input, compilationAndLayout)); + + CPUComputeUtil::Context context; + SLANG_RETURN_ON_FAIL(CPUComputeUtil::calcBindings(compilationAndLayout, context)); + SLANG_RETURN_ON_FAIL(CPUComputeUtil::execute(compilationAndLayout, context)); + + // Dump everything out that was written + return CPUComputeUtil::writeBindings(compilationAndLayout.layout, context.buffers, gOptions.outputPath); } { diff --git a/tools/render-test/render-test-tool.vcxproj b/tools/render-test/render-test-tool.vcxproj index 0399d67e0..3e0c0ae56 100644 --- a/tools/render-test/render-test-tool.vcxproj +++ b/tools/render-test/render-test-tool.vcxproj @@ -179,6 +179,7 @@ + @@ -187,6 +188,7 @@ + diff --git a/tools/render-test/render-test-tool.vcxproj.filters b/tools/render-test/render-test-tool.vcxproj.filters index 43767fc8d..769d53aed 100644 --- a/tools/render-test/render-test-tool.vcxproj.filters +++ b/tools/render-test/render-test-tool.vcxproj.filters @@ -9,6 +9,9 @@ + + Header Files + Header Files @@ -29,6 +32,9 @@ + + Source Files + Source Files diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index 810e31c2c..7f75805e9 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -440,6 +440,23 @@ namespace renderer_test } } + template + void _iteratePixels(int dimension, int size, unsigned int * buffer, F f) + { + if (dimension == 1) + for (int i = 0; i < size; i++) + buffer[i] = f(i, 0, 0); + else if (dimension == 2) + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + buffer[i*size + j] = f(j, i, 0); + else if (dimension == 3) + for (int i = 0; i < size; i++) + for (int j = 0; j < size; j++) + for (int k = 0; k < size; k++) + buffer[i*size*size + j * size + k] = f(k, j, i); + }; + void generateTextureDataRGB8(TextureData& output, const InputTextureDesc& inputDesc) { int arrLen = inputDesc.arrayLength; @@ -453,21 +470,6 @@ namespace renderer_test output.textureSize = inputDesc.size; output.mipLevels = Math::Log2Floor(output.textureSize) + 1; output.dataBuffer.setCount(output.mipLevels * output.arraySize); - auto iteratePixels = [&](int dimension, int size, unsigned int * buffer, auto f) - { - if (dimension == 1) - for (int i = 0; i < size; i++) - buffer[i] = f(i, 0, 0); - else if (dimension == 2) - for (int i = 0; i < size; i++) - for (int j = 0; j < size; j++) - buffer[i*size + j] = f(j, i, 0); - else if (dimension == 3) - for (int i = 0; i < size; i++) - for (int j = 0; j < size; j++) - for (int k = 0; k < size; k++) - buffer[i*size*size + j*size + k] = f(k, j, i); - }; int slice = 0; for (int i = 0; i < arraySize; i++) @@ -482,7 +484,7 @@ namespace renderer_test bufferLen *= size*size; dataBuffer[slice].setCount(bufferLen); - iteratePixels(inputDesc.dimension, size, dataBuffer[slice].getBuffer(), [&](int x, int y, int z) -> unsigned int + _iteratePixels(inputDesc.dimension, size, dataBuffer[slice].getBuffer(), [&](int x, int y, int z) -> unsigned int { if (inputDesc.content == InputTextureContent::Zero) { diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 230f78453..df7e91df8 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -10,6 +10,12 @@ #include namespace renderer_test { +using namespace Slang; + +// Entry point name to use for vertex/fragment shader +static const char vertexEntryPointName[] = "vertexMain"; +static const char fragmentEntryPointName[] = "fragmentMain"; +static const char computeEntryPointName[] = "computeMain"; /* static */ SlangResult ShaderCompilerUtil::compileProgram(SlangSession* session, const Input& input, const ShaderCompileRequest& request, Output& out) { @@ -138,7 +144,9 @@ namespace renderer_test { { fprintf(stderr, "%s", diagnostics); } - if (SLANG_SUCCEEDED(res)) + + SLANG_RETURN_ON_FAIL(res); + { size_t codeSize = 0; char const* code = (char const*) spGetEntryPointCode(slangRequest, computeEntryPoint, &codeSize); @@ -166,7 +174,9 @@ namespace renderer_test { // OutputDebugStringA(diagnostics); fprintf(stderr, "%s", diagnostics); } - if (SLANG_SUCCEEDED(res)) + + SLANG_RETURN_ON_FAIL(res); + { size_t vertexCodeSize = 0; char const* vertexCode = (char const*) spGetEntryPointCode(slangRequest, vertexEntryPoint, &vertexCodeSize); @@ -193,4 +203,79 @@ namespace renderer_test { return SLANG_OK; } +/* static */SlangResult ShaderCompilerUtil::readSource(const String& inSourcePath, List& outSourceText) +{ + // Read in the source code + FILE* sourceFile = fopen(inSourcePath.getBuffer(), "rb"); + if (!sourceFile) + { + fprintf(stderr, "error: failed to open '%s' for reading\n", inSourcePath.getBuffer()); + return SLANG_FAIL; + } + fseek(sourceFile, 0, SEEK_END); + size_t sourceSize = ftell(sourceFile); + fseek(sourceFile, 0, SEEK_SET); + + outSourceText.setCount(sourceSize + 1); + fread(outSourceText.getBuffer(), sourceSize, 1, sourceFile); + fclose(sourceFile); + outSourceText[sourceSize] = 0; + + return SLANG_OK; +} + +/* static */SlangResult ShaderCompilerUtil::compileWithLayout(SlangSession* session, const String& sourcePath, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input, OutputAndLayout& output) +{ + List sourceText; + SLANG_RETURN_ON_FAIL(readSource(sourcePath, sourceText)); + + output.sourcePath = sourcePath; + + auto& layout = output.layout; + + // Default the amount of renderTargets based on shader type + switch (shaderType) + { + default: + layout.numRenderTargets = 1; + break; + + case Options::ShaderProgramType::Compute: + layout.numRenderTargets = 0; + break; + } + + // Parse the layout + layout.parse(sourceText.getBuffer()); + layout.updateForTarget(input.target); + + // Setup SourceInfo + ShaderCompileRequest::SourceInfo sourceInfo; + sourceInfo.path = sourcePath.getBuffer(); + sourceInfo.dataBegin = sourceText.getBuffer(); + // Subtract 1 because it's zero terminated + sourceInfo.dataEnd = sourceText.getBuffer() + sourceText.getCount() - 1; + + ShaderCompileRequest compileRequest; + compileRequest.source = sourceInfo; + if (shaderType == Options::ShaderProgramType::Graphics || shaderType == Options::ShaderProgramType::GraphicsCompute) + { + compileRequest.vertexShader.source = sourceInfo; + compileRequest.vertexShader.name = vertexEntryPointName; + compileRequest.fragmentShader.source = sourceInfo; + compileRequest.fragmentShader.name = fragmentEntryPointName; + } + else + { + compileRequest.computeShader.source = sourceInfo; + compileRequest.computeShader.name = computeEntryPointName; + } + compileRequest.globalGenericTypeArguments = layout.globalGenericTypeArguments; + compileRequest.entryPointGenericTypeArguments = layout.entryPointGenericTypeArguments; + compileRequest.globalExistentialTypeArguments = layout.globalExistentialTypeArguments; + compileRequest.entryPointExistentialTypeArguments = layout.entryPointExistentialTypeArguments; + + return ShaderCompilerUtil::compileProgram(session, input, compileRequest, output.output); +} + } // renderer_test diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h index 662098546..543ab3258 100644 --- a/tools/render-test/slang-support.h +++ b/tools/render-test/slang-support.h @@ -6,6 +6,7 @@ #include #include "shader-input-layout.h" +#include "options.h" namespace renderer_test { @@ -33,6 +34,12 @@ struct ShaderCompilerUtil } void reset() { + { + desc.pipelineType = PipelineType::Unknown; + desc.kernels = nullptr; + desc.kernelCount = 0; + } + kernelDescs.clear(); if (request && session) { @@ -52,8 +59,20 @@ struct ShaderCompilerUtil ShaderProgram::Desc desc; SlangCompileRequest* request = nullptr; SlangSession* session = nullptr; + }; + struct OutputAndLayout + { + Output output; + ShaderInputLayout layout; + Slang::String sourcePath; + }; + + static SlangResult compileWithLayout(SlangSession* session, const Slang::String& sourcePath, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input, OutputAndLayout& output); + + static SlangResult readSource(const Slang::String& inSourcePath, List& outSourceText); + static SlangResult compileProgram(SlangSession* session, const Input& input, const ShaderCompileRequest& request, Output& out); }; -- cgit v1.2.3