From dc6d0417b137c8ecdcb3b99b7624358bba7fefa8 Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Mon, 19 Aug 2019 14:08:57 -0400 Subject: WIP: Compute test running on CPU (#1023) * * Simplify some of test code around CPPCompiler * Test using 'callable' with pass-through * Small cpu doc improvements * Improvements to Clang output parsing. * Remove temporary file (base filename) . * Improve handling of external errors - handle severity. * On error dumping out to 'actual' file for runCPPCompilerCompile. * Small fixes. Set the source language type correctly for pass thru. * Remove warning for test for clang backend c * Preliminary work around making render-test compute potentiall work with CPU. Made ShaderCompiler -> a stateless ShaderCompilerUtil. Means we don't require a Renderer interface to do shader compilation. * Refactor such that CPU test can take place in without Window or Renderer. * Hack to look for prelude in source file directory. Fix bug returning the SharedLibrary for HostCallable. * Compute test running on CPU. * Need the prelude currently in same directly as test. * Hack to remove warning - that then produces an error on appveyor build. Disable running render CPU test on non-windows. * Improve handling of disabling CPU tests on linux. * Added bit-cast.slang working on CPU. --- tools/render-test/options.cpp | 1 + tools/render-test/render-test-main.cpp | 643 +++++++++++++++++++++++------- tools/render-test/shader-input-layout.cpp | 30 +- tools/render-test/shader-input-layout.h | 7 +- tools/render-test/slang-support.cpp | 49 +-- tools/render-test/slang-support.h | 55 ++- 6 files changed, 596 insertions(+), 189 deletions(-) (limited to 'tools/render-test') diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index 9423b5b6e..f8c2c1d12 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -28,6 +28,7 @@ static gfx::RendererType _toRenderType(Slang::RenderApiType apiType) case RenderApiType::D3D12: return gfx::RendererType::DirectX12; case RenderApiType::OpenGl: return gfx::RendererType::OpenGl; case RenderApiType::Vulkan: return gfx::RendererType::Vulkan; + case RenderApiType::CPU: return gfx::RendererType::CPU; default: return gfx::RendererType::Unknown; } } diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp index f82e03ad5..91230b9d0 100644 --- a/tools/render-test/render-test-main.cpp +++ b/tools/render-test/render-test-main.cpp @@ -17,7 +17,12 @@ #include #include +// TODO(JS): We need to put the prelude into a better place +#define SLANG_PRELUDE_NAMESPACE CPPPrelude +#include "../../tests/cross-compile/slang-cpp-prelude.h" + #include "../../source/core/slang-test-tool-util.h" +#include "../../source/core/slang-memory-arena.h" #define WIN32_LEAN_AND_MEAN #define NOMINMAX @@ -200,7 +205,7 @@ class RenderTestApp // At initialization time, we are going to load and compile our Slang shader // code, and then create the API objects we need for rendering. - Result initialize(Renderer* renderer, ShaderCompiler* shaderCompiler); + Result initialize(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input); void runCompute(); void renderFrame(); void finalize(); @@ -213,7 +218,7 @@ class RenderTestApp protected: /// Called in initialize - Result initializeShaders(ShaderCompiler* shaderCompiler); + Result _initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input); // variables for state to be used for rendering... uintptr_t m_constantBufferSize, m_computeResultBufferSize; @@ -236,49 +241,432 @@ static const char vertexEntryPointName[] = "vertexMain"; static const char fragmentEntryPointName[] = "fragmentMain"; static const char computeEntryPointName[] = "computeMain"; -SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shaderCompiler) +static SlangResult _readSource(const String& inSourcePath, List& outSourceText) { - SLANG_RETURN_ON_FAIL(initializeShaders(shaderCompiler)); + // 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); - m_numAddedConstantBuffers = 0; - m_renderer = renderer; + outSourceText.setCount(sourceSize + 1); + fread(outSourceText.getBuffer(), sourceSize, 1, sourceFile); + fclose(sourceFile); + outSourceText[sourceSize] = 0; - // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + 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) { - m_constantBufferSize = 16 * sizeof(float); + default: + layout.numRenderTargets = 1; + break; + + case Options::ShaderProgramType::Compute: + layout.numRenderTargets = 0; + break; + } - BufferResource::Desc constantBufferDesc; - constantBufferDesc.init(m_constantBufferSize); - constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + // Parse the layout + layout.parse(sourceText.getBuffer()); - m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); - if (!m_constantBuffer) - return SLANG_FAIL; + // 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 _handleResource(slang::TypeReflection* type, ShaderInputLayoutEntry& src, void* dst) +{ + auto shape = type->getResourceShape(); + auto access = type->getResourceAccess(); + switch (shape & SLANG_RESOURCE_BASE_SHAPE_MASK) { - //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer - // - // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified - // in the test file - RefPtr addedConstantBuffer; - switch(gOptions.shaderType) - { default: + assert(!"unhandled case"); break; + case SLANG_TEXTURE_1D: + case SLANG_TEXTURE_2D: + case SLANG_TEXTURE_3D: + case SLANG_TEXTURE_CUBE: + case SLANG_TEXTURE_BUFFER: + { + return SLANG_FAIL; + } + case SLANG_STRUCTURED_BUFFER: + { + // TODO(JS): I guess this is questionable - because sizeof(unsigned int) != sizeof(uint32_t) necessarily + CPPPrelude::StructuredBuffer& dstBuf = *(CPPPrelude::StructuredBuffer*)dst; + dstBuf.data = src.bufferData.getBuffer(); + dstBuf.count = src.bufferData.getCount(); + break; + } + case SLANG_BYTE_ADDRESS_BUFFER: + { + CPPPrelude::ByteAddressBuffer& dstBuf = *(CPPPrelude::ByteAddressBuffer*)dst; + dstBuf.data = src.bufferData.getBuffer(); + dstBuf.sizeInBytes = src.bufferData.getCount() * sizeof(unsigned int); - case Options::ShaderProgramType::Graphics: - case Options::ShaderProgramType::GraphicsCompute: - addedConstantBuffer = m_constantBuffer; - m_numAddedConstantBuffers++; break; } + } + if (shape & SLANG_TEXTURE_ARRAY_FLAG) + { + + } + if (shape & SLANG_TEXTURE_MULTISAMPLE_FLAG) + { + + } + + if (access != SLANG_RESOURCE_ACCESS_READ) + { + switch (access) + { + default: + assert(!"unhandled case"); + break; + + case SLANG_RESOURCE_ACCESS_READ: + break; + + case SLANG_RESOURCE_ACCESS_READ_WRITE: break; + case SLANG_RESOURCE_ACCESS_RASTER_ORDERED: break; + case SLANG_RESOURCE_ACCESS_APPEND: break; + case SLANG_RESOURCE_ACCESS_CONSUME: break; + } + + } + return SLANG_OK; +} - BindingStateImpl* bindingState = nullptr; - SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); - m_bindingState = bindingState; +static SlangResult _writeBindings(const ShaderInputLayout& layout, const String& fileName) +{ + FILE * f = fopen(fileName.getBuffer(), "wb"); + if (!f) + { + return SLANG_FAIL; } + for (auto entry : layout.entries) + { + if (entry.isOutput) + { + auto ptr = entry.bufferData.getBuffer(); + const int size = int(entry.bufferData.getCount()); + for (int i = 0; i < size; ++i) + { + fprintf(f, "%X\n", ptr[i]); + } + } + } + fclose(f); + return SLANG_OK; +} + +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, 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; + } + } + // Okay we need to find all of the bindings and match up to those in the layout + + ShaderInputLayout& layout = output.layout; + + // For general storage + MemoryArena arena; + arena.init(1024); + List uniformState; + + { + int parameterCount = reflection->getParameterCount(); + + for (int i = 0; i < parameterCount; ++i) + { + auto parameter = reflection->getParameterByIndex(i); + + const char* paramName = parameter->getName(); + + const Index entryIndex = layout.findEntryIndexByName(paramName); + if (entryIndex < 0) + { + auto& outStream = StdWriters::getOut(); + + int numNamed = 0; + for (const auto& entry : layout.entries) + { + numNamed += int(entry.name.getLength() > 0); + } + + if (layout.entries.getCount() > 0 && numNamed == 0) + { + outStream.print("No 'name' specified for resources in '%s'\n", sourcePath.getBuffer()); + } + else + { + outStream.print("Unable to find entry in '%s' for '%s' (for CPU name must be specified) \n", sourcePath.getBuffer(), paramName); + } + return SLANG_FAIL; + } + + auto stage = parameter->getStage(); + + auto typeLayout = parameter->getTypeLayout(); + auto categoryCount = parameter->getCategoryCount(); + + SLANG_ASSERT(categoryCount == 1); + + // Only dealing one category per item right now + auto category = parameter->getCategoryByIndex(0); + + auto offset = parameter->getOffset(category); + auto space = parameter->getBindingSpace(category); + auto count = typeLayout->getSize(category); + + size_t end = offset + count; + if (uniformState.getCount() * sizeof(void*) < end) + { + uniformState.setCount(end / sizeof(void*)); + } + + void* dstEntry = ((uint8_t*)uniformState.getBuffer()) + offset; + auto& srcEntry = layout.entries[entryIndex]; + + switch (typeLayout->getKind()) + { + default: + break; + + case slang::TypeReflection::Kind::Array: + { + auto arrayTypeLayout = typeLayout; + auto elementTypeLayout = arrayTypeLayout->getElementTypeLayout(); + auto elementCount = int(arrayTypeLayout->getElementCount()); + + if (arrayTypeLayout->getSize(SLANG_PARAMETER_CATEGORY_UNIFORM) != 0) + { + int elementStride = int(arrayTypeLayout->getElementStride(SLANG_PARAMETER_CATEGORY_UNIFORM)); + SLANG_UNUSED(elementStride); + } + SLANG_UNUSED(elementTypeLayout); + SLANG_UNUSED(elementCount); + break; + } + case slang::TypeReflection::Kind::Struct: + { + auto structTypeLayout = typeLayout; + auto name = structTypeLayout->getName(); + SLANG_UNUSED(name); + + auto fieldCount = structTypeLayout->getFieldCount(); + for (uint32_t ff = 0; ff < fieldCount; ++ff) + { + auto field = structTypeLayout->getFieldByIndex(ff); + SLANG_UNUSED(field); + } + auto type = structTypeLayout->getType(); + SLANG_UNUSED(type); + break; + } + case slang::TypeReflection::Kind::ConstantBuffer: + { + auto elementTypeLayout = typeLayout->getElementTypeLayout(); + SLANG_UNUSED(elementTypeLayout); + 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: + { + // Some resource types (notably structured buffers) + // encode layout information for their result/element + // type, but others don't. We need to check for + // the relevant cases here. + // + auto type = typeLayout->getType(); + auto shape = type->getResourceShape(); + + if ((shape & SLANG_RESOURCE_BASE_SHAPE_MASK) == SLANG_STRUCTURED_BUFFER) + { + _handleResource(typeLayout->getType(), srcEntry, dstEntry); + + } + else + { + //emitReflectionTypeInfoJSON(writer, typeLayout->getType()); + } + break; + } + } + } + } + + SlangUInt numThreadsPerAxis[3]; + entryPoint->getComputeThreadGroupSize(3, numThreadsPerAxis); + + { + CPPPrelude::ComputeVaryingInput varying; + varying.groupID = {}; + + for (int z = 0; z < numThreadsPerAxis[2]; ++z) + { + varying.groupThreadID.z = z; + for (int y = 0; y < numThreadsPerAxis[1]; ++y) + { + varying.groupThreadID.y = y; + for (int x = 0; x < numThreadsPerAxis[0]; ++x) + { + varying.groupThreadID.x = x; + + func(&varying, (UniformState*)uniformState.getBuffer()); + } + } + } + } + + // Dump everything out that was write (we wrote in place!) + return _writeBindings(layout, 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)); + + m_numAddedConstantBuffers = 0; + m_renderer = renderer; + + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + m_constantBufferSize = 16 * sizeof(float); + + BufferResource::Desc constantBufferDesc; + constantBufferDesc.init(m_constantBufferSize); + constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + + m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); + if (!m_constantBuffer) + return SLANG_FAIL; + + //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer + // + // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified + // in the test file + RefPtr addedConstantBuffer; + switch(shaderType) + { + default: + break; + + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + addedConstantBuffer = m_constantBuffer; + m_numAddedConstantBuffers++; + break; + } + + BindingStateImpl* bindingState = nullptr; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); + m_bindingState = bindingState; + // Do other initialization that doesn't depend on the source language. // Input Assembler (IA) @@ -301,7 +689,7 @@ SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shader return SLANG_FAIL; { - switch(gOptions.shaderType) + switch(shaderType) { default: assert(!"unexpected test shader type"); @@ -336,63 +724,12 @@ SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shader return m_pipelineState ? SLANG_OK : SLANG_FAIL; } -Result RenderTestApp::initializeShaders(ShaderCompiler* shaderCompiler) +Result RenderTestApp::_initializeShaders(SlangSession* session, Renderer* renderer, Options::ShaderProgramType shaderType, const ShaderCompilerUtil::Input& input) { - // Read in the source code - char const* sourcePath = gOptions.sourcePath; - FILE* sourceFile = fopen(sourcePath, "rb"); - if (!sourceFile) - { - fprintf(stderr, "error: failed to open '%s' for reading\n", sourcePath); - return SLANG_FAIL; - } - fseek(sourceFile, 0, SEEK_END); - size_t sourceSize = ftell(sourceFile); - fseek(sourceFile, 0, SEEK_SET); - - List sourceText; - sourceText.setCount(sourceSize + 1); - fread(sourceText.getBuffer(), sourceSize, 1, sourceFile); - fclose(sourceFile); - sourceText[sourceSize] = 0; - - switch( gOptions.shaderType ) - { - default: - m_shaderInputLayout.numRenderTargets = 1; - break; - - case Options::ShaderProgramType::Compute: - m_shaderInputLayout.numRenderTargets = 0; - break; - } - m_shaderInputLayout.Parse(sourceText.getBuffer()); - - ShaderCompileRequest::SourceInfo sourceInfo; - sourceInfo.path = sourcePath; - sourceInfo.dataBegin = sourceText.getBuffer(); - sourceInfo.dataEnd = sourceText.getBuffer() + sourceSize; - - ShaderCompileRequest compileRequest; - compileRequest.source = sourceInfo; - if (gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.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 = m_shaderInputLayout.globalGenericTypeArguments; - compileRequest.entryPointGenericTypeArguments = m_shaderInputLayout.entryPointGenericTypeArguments; - compileRequest.globalExistentialTypeArguments = m_shaderInputLayout.globalExistentialTypeArguments; - compileRequest.entryPointExistentialTypeArguments = m_shaderInputLayout.entryPointExistentialTypeArguments; - m_shaderProgram = shaderCompiler->compileProgram(compileRequest); - + CompileOutput output; + SLANG_RETURN_ON_FAIL(_compile(session, gOptions.sourcePath, shaderType, input, output)); + m_shaderInputLayout = output.layout; + m_shaderProgram = renderer->createProgram(output.compileOutput.desc); return m_shaderProgram ? SLANG_OK : SLANG_FAIL; } @@ -501,61 +838,90 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe // Parse command-line options SLANG_RETURN_ON_FAIL(parseOptions(argcIn, argvIn, StdWriters::getError())); - RefPtr window(new renderer_test::Window); - SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); 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; - SlangCompileTarget slangTarget = SLANG_TARGET_NONE; - SlangPassThrough slangPassThrough = SLANG_PASS_THROUGH_NONE; + SlangPassThrough slangPassThrough = SLANG_PASS_THROUGH_NONE; char const* profileName = ""; switch (gOptions.rendererType) { case RendererType::DirectX11: renderer = createD3D11Renderer(); - slangTarget = SLANG_DXBC; + input.target = SLANG_DXBC; + input.profile = "sm_5_0"; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; slangPassThrough = SLANG_PASS_THROUGH_FXC; - profileName = "sm_5_0"; + break; case RendererType::DirectX12: renderer = createD3D12Renderer(); - slangTarget = SLANG_DXBC; + input.target = SLANG_DXBC; + input.profile = "sm_5_0"; nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; slangPassThrough = SLANG_PASS_THROUGH_FXC; - profileName = "sm_5_0"; + if( gOptions.useDXIL ) { - slangTarget = SLANG_DXIL; + input.target = SLANG_DXIL; + input.profile = "sm_6_0"; slangPassThrough = SLANG_PASS_THROUGH_DXC; - profileName = "sm_6_0"; } break; case RendererType::OpenGl: renderer = createGLRenderer(); - slangTarget = SLANG_GLSL; + input.target = SLANG_GLSL; + input.profile = "glsl_430"; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; - profileName = "glsl_430"; break; case RendererType::Vulkan: renderer = createVKRenderer(); - slangTarget = SLANG_SPIRV; + input.target = SLANG_SPIRV; + input.profile = "glsl_430"; nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; - profileName = "glsl_430"; break; - + 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; + StringBuilder rendererName; rendererName << "[" << RendererUtil::toText(gOptions.rendererType) << "] "; if (gOptions.adapter.getLength()) @@ -563,22 +929,18 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe rendererName << "'" << gOptions.adapter << "'"; } + RefPtr window; - if (!renderer) + if (renderer) { - if (!gOptions.onlyStartup) - { - fprintf(stderr, "Unable to create renderer %s\n", rendererName.getBuffer()); - } - return SLANG_FAIL; - } + Renderer::Desc desc; + desc.width = gWindowWidth; + desc.height = gWindowHeight; + desc.adapter = gOptions.adapter; - Renderer::Desc desc; - desc.width = gWindowWidth; - desc.height = gWindowHeight; - desc.adapter = gOptions.adapter; + window = new renderer_test::Window; + SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); - { SlangResult res = renderer->initialize(desc, (HWND)window->getHandle()); if (SLANG_FAILED(res)) { @@ -588,15 +950,7 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe } return res; } - } - - // If the only test is we can startup, then we are done - if (gOptions.onlyStartup) - { - return SLANG_OK; - } - { for (const auto& feature : gOptions.renderFeatures) { // If doesn't have required feature... we have to give up @@ -606,36 +960,33 @@ SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSe } } } + else + { + if (gOptions.rendererType != RendererType::CPU) + { + if (!gOptions.onlyStartup) + { + fprintf(stderr, "Unable to create renderer %s\n", rendererName.getBuffer()); + } + return SLANG_FAIL; + } + } - // Use the profile name set on options if set - profileName = gOptions.profileName ? gOptions.profileName : profileName; - - ShaderCompiler shaderCompiler; - shaderCompiler.renderer = renderer; - shaderCompiler.target = slangTarget; - shaderCompiler.profile = profileName; - shaderCompiler.slangSession = session; - - switch (gOptions.inputLanguageID) - { - case Options::InputLanguageID::Slang: - shaderCompiler.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG; - shaderCompiler.passThrough = SLANG_PASS_THROUGH_NONE; - break; - - case Options::InputLanguageID::Native: - shaderCompiler.sourceLanguage = nativeLanguage; - shaderCompiler.passThrough = slangPassThrough; - break; + // If the only test is we can startup, then we are done + if (gOptions.onlyStartup) + { + return SLANG_OK; + } - default: - break; - } + if (!renderer) + { + SLANG_RETURN_ON_FAIL(_doCPUCompute(session, gOptions.sourcePath, gOptions.shaderType, input)); + return SLANG_OK; + } { RenderTestApp app; - - SLANG_RETURN_ON_FAIL(app.initialize(renderer, &shaderCompiler)); + SLANG_RETURN_ON_FAIL(app.initialize(session, renderer, gOptions.shaderType, input)); window->show(); diff --git a/tools/render-test/shader-input-layout.cpp b/tools/render-test/shader-input-layout.cpp index 8205c979e..e555ee6ca 100644 --- a/tools/render-test/shader-input-layout.cpp +++ b/tools/render-test/shader-input-layout.cpp @@ -6,7 +6,23 @@ namespace renderer_test { using namespace Slang; - void ShaderInputLayout::Parse(const char * source) + + + Index ShaderInputLayout::findEntryIndexByName(const String& name) const + { + const Index count = Index(entries.getCount()); + for (Index i = 0; i < count; ++i) + { + const auto& entry = entries[i]; + if (entry.name == name) + { + return Index(i); + } + } + return -1; + } + + void ShaderInputLayout::parse(const char * source) { entries.clear(); globalGenericTypeArguments.clear(); @@ -267,6 +283,18 @@ namespace renderer_test parser.ReadToken(); entry.isOutput = true; } + else if (parser.LookAhead("name")) + { + parser.ReadToken(); + Token nameToken = parser.ReadToken(); + + if (nameToken.Type != TokenType::Identifier) + { + throw TextFormatException("Invalid input syntax at line " + parser.NextToken().Position.Line); + } + entry.name = nameToken.Content; + } + if (parser.LookAhead(",")) parser.Read(","); } diff --git a/tools/render-test/shader-input-layout.h b/tools/render-test/shader-input-layout.h index d5a1b6fd5..0f11c4ad7 100644 --- a/tools/render-test/shader-input-layout.h +++ b/tools/render-test/shader-input-layout.h @@ -61,6 +61,8 @@ public: bool isOutput = false; int hlslBinding = -1; Slang::List glslBinding; + + Slang::String name; ///< Optional name. Useful for binding through reflection. }; struct TextureData @@ -80,7 +82,10 @@ public: Slang::List globalExistentialTypeArguments; Slang::List entryPointExistentialTypeArguments; int numRenderTargets = 1; - void Parse(const char * source); + + Slang::Index findEntryIndexByName(const Slang::String& name) const; + + void parse(const char * source); }; void generateTextureDataRGB8(TextureData& output, const InputTextureDesc& desc); diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 77f1436b1..230f78453 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -11,19 +11,21 @@ namespace renderer_test { -RefPtr ShaderCompiler::compileProgram( - ShaderCompileRequest const& request) +/* static */ SlangResult ShaderCompilerUtil::compileProgram(SlangSession* session, const Input& input, const ShaderCompileRequest& request, Output& out) { - SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); + out.reset(); - spSetCodeGenTarget(slangRequest, target); - spSetTargetProfile(slangRequest, 0, - spFindProfile(slangSession, profile)); + SlangCompileRequest* slangRequest = spCreateCompileRequest(session); + out.request = slangRequest; + out.session = session; + + spSetCodeGenTarget(slangRequest, input.target); + spSetTargetProfile(slangRequest, 0, spFindProfile(session, input.profile)); // Define a macro so that shader code in a test can detect what language we // are nominally working with. char const* langDefine = nullptr; - switch (sourceLanguage) + switch (input.sourceLanguage) { case SLANG_SOURCE_LANGUAGE_GLSL: spAddPreprocessorDefine(slangRequest, "__GLSL__", "1"); @@ -41,14 +43,14 @@ RefPtr ShaderCompiler::compileProgram( break; } - if (passThrough != SLANG_PASS_THROUGH_NONE) + if (input.passThrough != SLANG_PASS_THROUGH_NONE) { - spSetPassThrough(slangRequest, passThrough); + spSetPassThrough(slangRequest, input.passThrough); } // Process any additional command-line options specified for Slang using // the `-xslang ` option to `render-test`. - SLANG_RETURN_NULL_ON_FAIL(spProcessCommandLineArguments(slangRequest, &gOptions.slangArgs[0], gOptions.slangArgCount)); + SLANG_RETURN_ON_FAIL(spProcessCommandLineArguments(slangRequest, input.args, input.argCount)); int computeTranslationUnit = 0; int vertexTranslationUnit = 0; @@ -57,6 +59,8 @@ RefPtr ShaderCompiler::compileProgram( char const* fragmentEntryPointName = request.fragmentShader.name; char const* computeEntryPointName = request.computeShader.name; + const auto sourceLanguage = input.sourceLanguage; + if (sourceLanguage == SLANG_SOURCE_LANGUAGE_GLSL) { // GLSL presents unique challenges because, frankly, it got the whole @@ -91,8 +95,6 @@ RefPtr ShaderCompiler::compileProgram( } - RefPtr shaderProgram; - Slang::List rawGlobalTypeNames; for (auto typeName : request.globalGenericTypeArguments) rawGlobalTypeNames.add(typeName.getBuffer()); @@ -146,12 +148,7 @@ RefPtr ShaderCompiler::compileProgram( kernelDesc.codeBegin = code; kernelDesc.codeEnd = code + codeSize; - ShaderProgram::Desc desc; - desc.pipelineType = PipelineType::Compute; - desc.kernels = &kernelDesc; - desc.kernelCount = 1; - - shaderProgram = renderer->createProgram(desc); + out.set(PipelineType::Compute, &kernelDesc, 1); } } else @@ -189,21 +186,11 @@ RefPtr ShaderCompiler::compileProgram( kernelDescs[1].codeBegin = fragmentCode; kernelDescs[1].codeEnd = fragmentCode + fragmentCodeSize; - ShaderProgram::Desc desc; - desc.pipelineType = PipelineType::Graphics; - desc.kernels = &kernelDescs[0]; - desc.kernelCount = kDescCount; - - shaderProgram = renderer->createProgram(desc); + out.set(PipelineType::Graphics, kernelDescs, kDescCount); } } - // We clean up the Slang compilation context and result *after* - // we have run the downstream compiler, because Slang - // owns the memory allocation for the generated text, and will - // free it when we destroy the compilation result. - spDestroyCompileRequest(slangRequest); - - return shaderProgram; + + return SLANG_OK; } } // renderer_test diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h index a9b8c8871..662098546 100644 --- a/tools/render-test/slang-support.h +++ b/tools/render-test/slang-support.h @@ -9,17 +9,52 @@ namespace renderer_test { -struct ShaderCompiler +struct ShaderCompilerUtil { - RefPtr renderer; - SlangCompileTarget target; - SlangSourceLanguage sourceLanguage; - SlangPassThrough passThrough; - char const* profile; - SlangSession* slangSession; - - RefPtr compileProgram( - ShaderCompileRequest const& request); + struct Input + { + SlangCompileTarget target; + SlangSourceLanguage sourceLanguage; + SlangPassThrough passThrough; + char const* profile; + const char** args; + int argCount; + }; + + struct Output + { + void set(PipelineType pipelineType, const ShaderProgram::KernelDesc* inKernelDescs, int kernelDescCount) + { + kernelDescs.clear(); + kernelDescs.addRange(inKernelDescs, kernelDescCount); + desc.pipelineType = pipelineType; + desc.kernels = kernelDescs.getBuffer(); + desc.kernelCount = kernelDescCount; + } + void reset() + { + kernelDescs.clear(); + if (request && session) + { + spDestroyCompileRequest(request); + } + session = nullptr; + request = nullptr; + } + ~Output() + { + if (request && session) + { + spDestroyCompileRequest(request); + } + } + List kernelDescs; + ShaderProgram::Desc desc; + SlangCompileRequest* request = nullptr; + SlangSession* session = nullptr; + }; + + static SlangResult compileProgram(SlangSession* session, const Input& input, const ShaderCompileRequest& request, Output& out); }; -- cgit v1.2.3