diff options
| author | Tim Foley <tfoley@nvidia.com> | 2017-06-09 11:34:21 -0700 |
|---|---|---|
| committer | Tim Foley <tfoley@nvidia.com> | 2017-06-09 13:44:59 -0700 |
| commit | fcf83dbf9effab3bd98bad2b83b2468b7eb05cfd (patch) | |
| tree | 41047c94883b86ec085a81597391ce3ef557cd43 /tools | |
| parent | 52e8d4b9a27ab0060f874c3a63ab531847be35c0 (diff) | |
Initial import of code.
Diffstat (limited to 'tools')
23 files changed, 4674 insertions, 0 deletions
diff --git a/tools/glslang/glslang.cpp b/tools/glslang/glslang.cpp new file mode 100644 index 000000000..cf563faff --- /dev/null +++ b/tools/glslang/glslang.cpp @@ -0,0 +1,175 @@ +// glslang.cpp +#include "glslang.h" + + +#include "StandAlone/ResourceLimits.h" +#include "StandAlone/Worklist.h" +#include "glslang/Include/ShHandle.h" +#include "glslang/Include/revision.h" +#include "glslang/Public/ShaderLang.h" +#include "SPIRV/GlslangToSpv.h" +#include "SPIRV/GLSL.std.450.h" +#include "SPIRV/doc.h" +#include "SPIRV/disassemble.h" + +#include "../../Slang.h" + +#if 0 +#include <cstring> +#include <cstdlib> +#include <cctype> +#include <cmath> +#include <array> +#include <memory> +#include <thread> +#endif + +#ifdef _WIN32 +#include <Windows.h> +#endif + +#include <sstream> + +// This is a wrapper to allow us to run the `glslang` compiler +// in a controlled fashion. + +#define UNLIMITED 9999 + +static TBuiltInResource gResources = +{ + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, UNLIMITED, + UNLIMITED, UNLIMITED, UNLIMITED, + + { true, true, true, true, true, true, true, true, true, } +}; + +static void dump( + std::string const& text, + glslang_OutputFunc outputFunc, + void* outputUserData, + FILE* fallbackStream) +{ + if( outputFunc ) + { + outputFunc(text.c_str(), outputUserData); + } + else + { + fprintf(fallbackStream, "%s", text.c_str()); + + // also output it for debug purposes + OutputDebugStringA(text.c_str()); + } +} + +static void dumpDiagnostics( + glslang_CompileRequest* request, + std::string const& log) +{ + dump(log, request->diagnosticFunc, request->diagnosticUserData, stderr); +} + +extern "C" +_declspec(dllexport) +int glslang_compile(glslang_CompileRequest* request) +{ + glslang::InitializeProcess(); + + EShLanguage glslangStage; + switch( request->slangStage ) + { +#define CASE(SP, GL) case SLANG_STAGE_##SP: glslangStage = EShLang##GL; break + CASE(VERTEX, Vertex); + CASE(FRAGMENT, Fragment); + CASE(GEOMETRY, Geometry); + CASE(HULL, TessControl); + CASE(DOMAIN, TessEvaluation); + CASE(COMPUTE, Compute); + +#undef CASE + + default: + return 1; + } + + // TODO: compute glslang stage to use + + glslang::TShader* shader = new glslang::TShader(glslangStage); + auto shaderPtr = std::unique_ptr<glslang::TShader>(shader); + + glslang::TProgram* program = new glslang::TProgram(); + auto programPtr = std::unique_ptr<glslang::TProgram>(program); + + int sourceTextLength = (int) strlen(request->sourceText); + + shader->setPreamble("#extension GL_GOOGLE_cpp_style_line_directive : require\n"); + + shader->setStringsWithLengthsAndNames( + &request->sourceText, + &sourceTextLength, + &request->sourcePath, + 1); + + // Note: this seems required to get past a bug where + // glslang complains about a declaration of `out gl_PerVertex` + // that it (seemingly) *should* allow according to the GLSL-for-Vulkan + // extension. + shader->setAutoMapLocations(true); + + // Let's auto-map the bindings too, just because we can + shader->setAutoMapBindings(true); + + EShMessages messages = EShMessages(EShMsgSpvRules | EShMsgVulkanRules); + + if( !shader->parse(&gResources, 110, false, messages) ) + { + dumpDiagnostics(request, shader->getInfoLog()); + return 1; + } + + program->addShader(shader); + + if( !program->link(messages) ) + { + dumpDiagnostics(request, program->getInfoLog()); + return 1; + } + + if( !program->mapIO() ) + { + dumpDiagnostics(request, program->getInfoLog()); + return 1; + } + + for(int stage = 0; stage < EShLangCount; ++stage) + { + auto stageIntermediate = program->getIntermediate((EShLanguage)stage); + if(!stageIntermediate) + continue; + + std::vector<unsigned int> spirv; + std::string warningsErrors; + spv::SpvBuildLogger logger; + glslang::GlslangToSpv(*stageIntermediate, spirv, &logger); + + dumpDiagnostics(request, logger.getAllMessages()); + + std::stringstream spirvAsmStream; + + spv::Disassemble(spirvAsmStream, spirv); + + dump(spirvAsmStream.str(), request->outputFunc, request->outputUserData, stdout); + } + + + glslang::FinalizeProcess(); + + return 0; +} diff --git a/tools/glslang/glslang.h b/tools/glslang/glslang.h new file mode 100644 index 000000000..73c8a978e --- /dev/null +++ b/tools/glslang/glslang.h @@ -0,0 +1,23 @@ +// glslang.h +#ifndef GLSLANG_H_INCLUDED +#define GLSLANG_H_INCLUDED + +typedef void (*glslang_OutputFunc)(char const* text, void* userData); + +struct glslang_CompileRequest +{ + char const* sourcePath; + char const* sourceText; + + glslang_OutputFunc diagnosticFunc; + void* diagnosticUserData; + + glslang_OutputFunc outputFunc; + void* outputUserData; + + int slangStage; +}; + +typedef int (*glslang_CompileFunc)(glslang_CompileRequest* request); + +#endif diff --git a/tools/glslang/glslang.vcxproj b/tools/glslang/glslang.vcxproj new file mode 100644 index 000000000..b8034e0bc --- /dev/null +++ b/tools/glslang/glslang.vcxproj @@ -0,0 +1,237 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{C495878A-832C-485B-B347-0998A90CC936}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>glslang</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>DynamicLibrary</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir)external\glslang\;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir)external\glslang\;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir)external\glslang\;$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir)external\glslang\;$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;GLSLANG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;_WINDOWS;_USRDLL;GLSLANG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;GLSLANG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_WINDOWS;_USRDLL;GLSLANG_EXPORTS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Windows</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="..\..\external\glslang\glslang\GenericCodeGen\CodeGen.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\GenericCodeGen\Link.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Constant.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\glslang_tab.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\InfoSink.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Initialize.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Intermediate.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\intermOut.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\IntermTraverse.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\iomapper.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\limits.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\linkValidate.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\parseConst.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ParseContextBase.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ParseHelper.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\PoolAlloc.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\Pp.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpAtom.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpContext.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpMemory.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpScanner.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpSymbols.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpTokens.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\propagateNoContraction.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\reflection.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\RemoveTree.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Scan.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ShaderLang.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\SymbolTable.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Versions.cpp" /> + <ClCompile Include="..\..\external\glslang\glslang\OSDependent\Windows\ossource.cpp" /> + <ClCompile Include="..\..\external\glslang\OGLCompilersDLL\InitializeDll.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\disassemble.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\doc.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\GlslangToSpv.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\InReadableOrder.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\Logger.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\SpvBuilder.cpp" /> + <ClCompile Include="..\..\external\glslang\SPIRV\SPVRemapper.cpp" /> + <ClCompile Include="..\..\external\glslang\StandAlone\ResourceLimits.cpp" /> + <ClCompile Include="..\..\external\glslang\StandAlone\StandAlone.cpp" /> + <ClCompile Include="glslang.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\glslang_tab.cpp.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\gl_types.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Initialize.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\iomapper.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\LiveTraverser.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\localintermediate.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\ParseHelper.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\parseVersions.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpContext.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpTokens.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\propagateNoContraction.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\reflection.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\RemoveTree.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Scan.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\ScanContext.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\SymbolTable.h" /> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Versions.h" /> + <ClInclude Include="..\..\external\glslang\glslang\OSDependent\osinclude.h" /> + <ClInclude Include="..\..\external\glslang\OGLCompilersDLL\InitializeDll.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\bitutils.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\disassemble.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\doc.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.AMD.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.KHR.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.NV.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.std.450.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\GlslangToSpv.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\hex_float.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\Logger.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\spirv.hpp" /> + <ClInclude Include="..\..\external\glslang\SPIRV\SpvBuilder.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\spvIR.h" /> + <ClInclude Include="..\..\external\glslang\SPIRV\SPVRemapper.h" /> + <ClInclude Include="..\..\external\glslang\StandAlone\ResourceLimits.h" /> + <ClInclude Include="..\..\external\glslang\StandAlone\Worklist.h" /> + <ClInclude Include="glslang.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/tools/glslang/glslang.vcxproj.filters b/tools/glslang/glslang.vcxproj.filters new file mode 100644 index 000000000..79a7657ce --- /dev/null +++ b/tools/glslang/glslang.vcxproj.filters @@ -0,0 +1,253 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Constant.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\glslang_tab.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\InfoSink.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Initialize.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Intermediate.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\intermOut.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\IntermTraverse.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\iomapper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\limits.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\linkValidate.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\parseConst.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ParseContextBase.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ParseHelper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\PoolAlloc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\propagateNoContraction.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\reflection.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\RemoveTree.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Scan.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\ShaderLang.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\SymbolTable.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\Versions.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\Pp.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpAtom.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpContext.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpMemory.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpScanner.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpSymbols.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpTokens.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\OSDependent\Windows\ossource.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\StandAlone\ResourceLimits.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\StandAlone\StandAlone.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\GenericCodeGen\CodeGen.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\glslang\GenericCodeGen\Link.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\OGLCompilersDLL\InitializeDll.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\disassemble.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\doc.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\GlslangToSpv.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\InReadableOrder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\Logger.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\SpvBuilder.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="..\..\external\glslang\SPIRV\SPVRemapper.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="glslang.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\gl_types.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\glslang_tab.cpp.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Initialize.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\iomapper.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\LiveTraverser.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\localintermediate.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\ParseHelper.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\parseVersions.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\propagateNoContraction.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\reflection.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\RemoveTree.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Scan.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\ScanContext.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\SymbolTable.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\Versions.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpContext.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\MachineIndependent\preprocessor\PpTokens.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\glslang\OSDependent\osinclude.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\StandAlone\ResourceLimits.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\StandAlone\Worklist.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\OGLCompilersDLL\InitializeDll.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\bitutils.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\disassemble.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\doc.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.AMD.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.KHR.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.ext.NV.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\GLSL.std.450.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\GlslangToSpv.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\hex_float.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\Logger.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\spirv.hpp"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\SpvBuilder.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\spvIR.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="..\..\external\glslang\SPIRV\SPVRemapper.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="glslang.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/tools/render-test/README.md b/tools/render-test/README.md new file mode 100644 index 000000000..6d0d9111f --- /dev/null +++ b/tools/render-test/README.md @@ -0,0 +1,4 @@ +Render Test +=========== + +This is a simple tool for running end-to-end tests that render with Spire, so that we can validate that it generates correct code. diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp new file mode 100644 index 000000000..e444a8387 --- /dev/null +++ b/tools/render-test/main.cpp @@ -0,0 +1,369 @@ +// main.cpp + +#include "options.h" +#include "render.h" +#include "render-d3d11.h" +#include "render-gl.h" +#include "slang-support.h" + +#include <stdio.h> +#include <stdlib.h> + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +namespace renderer_test { + +// + + +int gWindowWidth = 1024; +int gWindowHeight = 768; + +// +// For the purposes of a small example, we will define the vertex data for a +// single triangle directly in the source file. It should be easy to extend +// this example to load data from an external source, if desired. +// + +struct Vertex +{ + float position[3]; + float color[3]; +}; + +static const int kVertexCount = 3; +static const Vertex kVertexData[kVertexCount] = { + { { 0, 0, 0.5 }, {1, 0, 0} }, + { { 0, 1, 0.5 }, {0, 0, 1} }, + { { 1, 0, 0.5 }, {0, 1, 0} }, +}; + + +// Global variables for state to be used for rendering... + +uintptr_t gConstantBufferSize; + +Buffer* gConstantBuffer; +InputLayout* gInputLayout; +Buffer* gVertexBuffer; +ShaderProgram* gShaderProgram; + +// Entry point name to use for vertex/fragment shader +static char const* vertexEntryPointName = "vertexMain"; +static char const* fragmentEntryPointName = "fragmentMain"; + +// "Profile" to use when compiling for HLSL targets +// TODO: does this belong here? +static char const* vertexProfileName = "vs_4_0"; +static char const* fragmentProfileName = "ps_4_0"; + +Error initializeShaders( + ShaderCompiler* shaderCompiler) +{ + // 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); + exit(1); + } + fseek(sourceFile, 0, SEEK_END); + size_t sourceSize = ftell(sourceFile); + fseek(sourceFile, 0, SEEK_SET); + char* sourceText = (char*) malloc(sourceSize + 1); + if( !sourceText ) + { + fprintf(stderr, "error: out of memory"); + exit(1); + } + fread(sourceText, sourceSize, 1, sourceFile); + fclose(sourceFile); + sourceText[sourceSize] = 0; + + ShaderCompileRequest::SourceInfo sourceInfo; + sourceInfo.path = sourcePath; + sourceInfo.text = sourceText; + + ShaderCompileRequest compileRequest; + compileRequest.source = sourceInfo; + compileRequest.vertexShader.source = sourceInfo; + compileRequest.vertexShader.name = vertexEntryPointName; + compileRequest.vertexShader.profile = vertexProfileName; + compileRequest.fragmentShader.source = sourceInfo; + compileRequest.fragmentShader.name = fragmentEntryPointName; + compileRequest.fragmentShader.profile = fragmentProfileName; + + gShaderProgram = shaderCompiler->compileProgram(compileRequest); + if( !gShaderProgram ) + { + return Error::Unexpected; + } + + return Error::None; +} + +// +// At initialization time, we are going to load and compile our Slang shader +// code, and then create the D3D11 API objects we need for rendering. +// +Error initializeInner( + Renderer* renderer, + ShaderCompiler* shaderCompiler) +{ + Error err = Error::None; + + err = initializeShaders(shaderCompiler); + if(err != Error::None) return err; + + + // Do other initialization that doesn't depend on the source language. + + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + gConstantBufferSize = 16 * sizeof(float); + + BufferDesc constantBufferDesc; + constantBufferDesc.size = gConstantBufferSize; + constantBufferDesc.flavor = BufferFlavor::Constant; + + gConstantBuffer = renderer->createBuffer(constantBufferDesc); + if(!gConstantBuffer) + return Error::Unexpected; + + // Input Assembler (IA) + + InputElementDesc inputElements[] = { + { "A", 0, Format::RGB_Float32, offsetof(Vertex, position) }, + { "A", 1, Format::RGB_Float32, offsetof(Vertex, color) }, + }; + + gInputLayout = renderer->createInputLayout(&inputElements[0], 2); + if(!gInputLayout) + return Error::Unexpected; + + BufferDesc vertexBufferDesc; + vertexBufferDesc.size = kVertexCount * sizeof(Vertex); + vertexBufferDesc.flavor = BufferFlavor::Vertex; + vertexBufferDesc.initData = &kVertexData[0]; + + gVertexBuffer = renderer->createBuffer(vertexBufferDesc); + if(!gVertexBuffer) + return Error::Unexpected; + + return Error::None; +} + +void renderFrameInner( + Renderer* renderer) +{ + auto mappedData = renderer->map(gConstantBuffer, MapFlavor::WriteDiscard); + if(mappedData) + { + static const float kIdentity[] = + { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + memcpy(mappedData, kIdentity, sizeof(kIdentity)); + + renderer->unmap(gConstantBuffer); + } + + // Input Assembler (IA) + + renderer->setInputLayout(gInputLayout); + renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); + + renderer->setVertexBuffer(0, gVertexBuffer, sizeof(Vertex)); + + // Vertex Shader (VS) + // Pixel Shader (PS) + + renderer->setShaderProgram(gShaderProgram); + renderer->setConstantBuffer(0, gConstantBuffer); + + // + + renderer->draw(3); +} + +void finalize() +{ +} + + + +// +// We use a bare-minimum window procedure to get things up and running. +// + +static LRESULT CALLBACK windowProc( + HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + + +} // renderer_test + + +// + +int main( + int argc, + char** argv) +{ + using namespace renderer_test; + + // Parse command-line options + parseOptions(&argc, argv); + + + // Do initial window-creation stuff here, rather than in the renderer-specific files + + HINSTANCE instance = GetModuleHandleA(0); + int showCommand = SW_SHOW; + + // First we register a window class. + + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = instance; + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"HelloWorld"; + windowClassDesc.hIconSm = 0; + ATOM windowClassAtom = RegisterClassExW(&windowClassDesc); + if(!windowClassAtom) + { + fprintf(stderr, "error: failed to register window class\n"); + return 1; + } + + // Next, we create a window using that window class. + + DWORD windowExtendedStyle = 0; + DWORD windowStyle = 0; + LPWSTR windowName = L"Slang Hello World"; + HWND windowHandle = CreateWindowExW( + windowExtendedStyle, + (LPWSTR)windowClassAtom, + windowName, + windowStyle, + 0, 0, // x, y + gWindowWidth, gWindowHeight, + NULL, // parent + NULL, // menu + instance, + NULL); + if(!windowHandle) + { + fprintf(stderr, "error: failed to create window\n"); + return 1; + } + + + Renderer* renderer = nullptr; + switch( gOptions.mode ) + { + case Mode::Slang: + case Mode::HLSL: + renderer = createD3D11Renderer(); + break; + + case Mode::GLSLCrossCompile: + renderer = createGLRenderer(); + break; + + default: + fprintf(stderr, "error: unexpected\n"); + exit(1); + break; + } + + renderer->initialize(windowHandle); + + auto shaderCompiler = renderer->getShaderCompiler(); + switch( gOptions.mode ) + { + case Mode::Slang: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_HLSL); + break; + + case Mode::GLSLCrossCompile: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_GLSL); + break; + + default: + break; + } + + Error err = initializeInner(renderer, shaderCompiler); + if( err != Error::None ) + { + exit(1); + } + + + // Once initialization is all complete, we show the window... + ShowWindow(windowHandle, showCommand); + + // ... and enter the event loop: + for(;;) + { + MSG message; + + int result = PeekMessageW(&message, NULL, 0, 0, PM_REMOVE); + if (result != 0) + { + if (message.message == WM_QUIT) + { + return (int)message.wParam; + } + + TranslateMessage(&message); + DispatchMessageW(&message); + } + else + { + // Whenver we don't have Windows events to process, + // we render a frame. + + renderer->clearFrame(); + + renderFrameInner(renderer); + + // If we are in a mode where output is requested, we need to snapshot the back buffer here + if( gOptions.outputPath ) + { + renderer->captureScreenShot(gOptions.outputPath); + return 0; + } + + renderer->presentFrame(); + } + } + + return 0; +} + diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp new file mode 100644 index 000000000..9cfbb81fb --- /dev/null +++ b/tools/render-test/options.cpp @@ -0,0 +1,95 @@ +// options.cpp + +#include "options.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +namespace renderer_test { + +Options gOptions; + +void parseOptions(int* argc, char** argv) +{ + int argCount = *argc; + char const* const* argCursor = argv; + char const* const* argEnd = argCursor + argCount; + + char const** writeCursor = (char const**) argv; + + // first argument is the application name + if( argCursor != argEnd ) + { + gOptions.appName = *argCursor++; + } + + // now iterate over arguments to collect options + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + if( arg[0] != '-' ) + { + *writeCursor++ = arg; + continue; + } + + if( strcmp(arg, "--") == 0 ) + { + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + *writeCursor++ = arg; + } + break; + } + else if( strcmp(arg, "-o") == 0 ) + { + if( argCursor == argEnd ) + { + fprintf(stderr, "expected argument for '%s' option\n", arg); + exit(1); + } + gOptions.outputPath = *argCursor++; + } + else if( strcmp(arg, "-hlsl") == 0 ) + { + gOptions.mode = Mode::HLSL; + } + else if( strcmp(arg, "-slang") == 0 ) + { + gOptions.mode = Mode::Slang; + } + else if( strcmp(arg, "-glsl-cross") == 0 ) + { + gOptions.mode = Mode::GLSLCrossCompile; + } + else + { + fprintf(stderr, "unknown option '%s'\n", arg); + exit(1); + } + } + + // any arguments left over were positional arguments + argCount = (int)(writeCursor - argv); + argCursor = argv; + argEnd = argCursor + argCount; + + // first positional argument is source shader path + if( argCursor != argEnd ) + { + gOptions.sourcePath = *argCursor++; + } + + // any remaining arguments represent an error + if(argCursor != argEnd) + { + fprintf(stderr, "unexpected arguments\n"); + exit(1); + } + + *argc = 0; +} + +} // renderer_test diff --git a/tools/render-test/options.h b/tools/render-test/options.h new file mode 100644 index 000000000..ffb07bc93 --- /dev/null +++ b/tools/render-test/options.h @@ -0,0 +1,41 @@ +// options.h +#pragma once + +#include <stdint.h> + +namespace renderer_test { + +typedef intptr_t Int; +typedef uintptr_t UInt; + +enum class Mode +{ + Slang, + HLSL, + GLSLCrossCompile, +}; + +struct Options +{ + char const* appName = "render-test"; + char const* sourcePath = nullptr; + char const* outputPath = nullptr; + Mode mode = Mode::Slang; +}; + +extern Options gOptions; + +extern int gWindowWidth; +extern int gWindowHeight; + + +void parseOptions(int* argc, char** argv); + +enum class Error +{ + None = 0, + InvalidParam, + Unexpected, +}; + +} // renderer_test diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp new file mode 100644 index 000000000..ab981bd45 --- /dev/null +++ b/tools/render-test/render-d3d11.cpp @@ -0,0 +1,900 @@ +// render-d3d11.cpp +#include "render-d3d11.h" + +#include "options.h" +#include "render.h" + +// In order to use the Slang API, we need to include its header + +#include <Slang.h> + +#define STB_IMAGE_WRITE_IMPLEMENTATION +#include "external/stb/stb_image_write.h" + +// We will be rendering with Direct3D 11, so we need to include +// the Windows and D3D11 headers + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#include <d3d11_2.h> +#include <d3dcompiler.h> + +// We will use the C standard library just for printing error messages. +#include <stdio.h> + +#ifdef _MSC_VER +#include <stddef.h> +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif +// + +namespace renderer_test { + +// + + +// + +// Global variabels for the various D3D11 API objects to be used for rendering +ID3D11Buffer* dxConstantBuffer; +ID3D11InputLayout* dxInputLayout; +ID3D11Buffer* dxVertexBuffer; +ID3D11VertexShader* dxVertexShader; +ID3D11PixelShader* dxPixelShader; + +// The Slang compiler currently generates HLSL source, so we'll need a utility +// routine (defined later) to translate that into D3D11 shader bytecode. +ID3DBlob* compileHLSLShader( + char const* sourcePath, + char const* source, + char const* entryPointName, + char const* dxProfileName); + +static char const* vertexEntryPointName = "vertexMain"; +static char const* fragmentEntryPointName = "fragmentMain"; + +static char const* vertexProfileName = "vs_4_0"; +static char const* fragmentProfileName = "ps_4_0"; + +ID3DBlob* gVertexShaderBlob; +ID3DBlob* gPixelShaderBlob; + +// Initialization when using HLSL for shaders +HRESULT initializeHLSLInner(ID3D11Device* dxDevice, char const* sourcePath, char const* sourceText) +{ + // Compile the generated HLSL code + gVertexShaderBlob = compileHLSLShader(sourcePath, sourceText, vertexEntryPointName, vertexProfileName); + if(!gVertexShaderBlob) return E_FAIL; + + gPixelShaderBlob = compileHLSLShader(sourcePath, sourceText, fragmentEntryPointName, fragmentProfileName); + if(!gPixelShaderBlob) return E_FAIL; + + + return S_OK; +} + +// Initialization when using HLSL for shaders +HRESULT initializeHLSL(ID3D11Device* dxDevice, char const* sourceText) +{ + HRESULT hr = initializeHLSLInner(dxDevice, gOptions.sourcePath, sourceText); + if(FAILED(hr)) + return hr; + + // TODO: any reflection stuff to do here? + + return S_OK; +} + +// Initialization when using Slang for shaders +HRESULT initializeSlang(ID3D11Device* dxDevice, char const* sourceText) +{ + // + // First, we will load and compile our Slang source code. + // + + // The argument here is an optional directory where the Slang compiler + // can cache files to speed up compilation of many kernels. + SlangSession* slangSession = spCreateSession(NULL); + + // A compile request represents a single invocation of the compiler, + // to process some inputs and produce outputs (or errors). + SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); + + // Instruct Slang to generate code as HLSL + spSetCodeGenTarget(slangRequest, SLANG_HLSL); + + int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr); + + spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, gOptions.sourcePath, sourceText); + + spAddTranslationUnitEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, spFindProfile(slangSession, vertexProfileName)); + spAddTranslationUnitEntryPoint(slangRequest, translationUnitIndex, fragmentEntryPointName, spFindProfile(slangSession, fragmentProfileName)); + + int compileErr = spCompile(slangRequest); + if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + OutputDebugStringA(diagnostics); + fprintf(stderr, "%s", diagnostics); + } + if(compileErr) + { + return E_FAIL; + } + + char const* translatedCode = spGetTranslationUnitSource(slangRequest, translationUnitIndex); + + // Compile the generated HLSL code + HRESULT hr = initializeHLSLInner(dxDevice, "slangGeneratedCode", translatedCode); + if(FAILED(hr)) + return hr; + + // We clean up the Slang compilation context and result *after* + // we have done the HLSL-to-bytecode compilation, because Slang + // owns the memory allocation for the generated HLSL, and will + // free it when we destroy the compilation result. + spDestroyCompileRequest(slangRequest); + spDestroySession(slangSession); + + return S_OK; +} + +#if 0 + +// +// At initialization time, we are going to load and compile our Slang shader +// code, and then create the D3D11 API objects we need for rendering. +// +HRESULT initializeInner( ID3D11Device* dxDevice ) +{ + HRESULT hr = S_OK; + + // 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); + exit(1); + } + fseek(sourceFile, 0, SEEK_END); + size_t sourceSize = ftell(sourceFile); + fseek(sourceFile, 0, SEEK_SET); + char* sourceText = (char*) malloc(sourceSize + 1); + if( !sourceText ) + { + fprintf(stderr, "error: out of memory"); + exit(1); + } + fread(sourceText, sourceSize, 1, sourceFile); + fclose(sourceFile); + sourceText[sourceSize] = 0; + + switch( gOptions.mode ) + { + case Mode::HLSL: + hr = initializeHLSL(dxDevice, sourceText); + break; + + case Mode::Slang: + hr = initializeSlang(dxDevice, sourceText); + break; + + default: + hr = E_FAIL; + break; + } + if( FAILED(hr) ) + { + return hr; + } + + // Do other initialization that doesn't depend on the source language. + + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + gConstantBufferSize = 16 * sizeof(float); + + + D3D11_BUFFER_DESC dxConstantBufferDesc = { 0 }; + dxConstantBufferDesc.ByteWidth = gConstantBufferSize; + dxConstantBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + dxConstantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + dxConstantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + + hr = dxDevice->CreateBuffer( + &dxConstantBufferDesc, + NULL, + &dxConstantBuffer); + if(FAILED(hr)) return hr; + + + // Input Assembler (IA) + + // In Slang-generated HLSL, all vertex shader inputs have a semantic + // like: `A0`, `A1`, `A2`, etc., rather than trying to do by-name + // matching. The user is thus responsibile for ensuring that the + // order of their "input element descs" here matches the order + // in which inputs are declared in the shader code. + D3D11_INPUT_ELEMENT_DESC dxInputElements[] = { + {"A", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + {"A", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0 }, + }; + hr = dxDevice->CreateInputLayout( + &dxInputElements[0], + 2, + gVertexShaderBlob->GetBufferPointer(), + gVertexShaderBlob->GetBufferSize(), + &dxInputLayout); + if(FAILED(hr)) return hr; + + D3D11_BUFFER_DESC dxVertexBufferDesc = { 0 }; + dxVertexBufferDesc.ByteWidth = kVertexCount * sizeof(Vertex); + dxVertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; + dxVertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; + + D3D11_SUBRESOURCE_DATA dxVertexBufferInitData = { 0 }; + dxVertexBufferInitData.pSysMem = &kVertexData[0]; + + hr = dxDevice->CreateBuffer( + &dxVertexBufferDesc, + &dxVertexBufferInitData, + &dxVertexBuffer); + if(FAILED(hr)) return hr; + + // Vertex Shader (VS) + + hr = dxDevice->CreateVertexShader( + gVertexShaderBlob->GetBufferPointer(), + gVertexShaderBlob->GetBufferSize(), + NULL, + &dxVertexShader); + gVertexShaderBlob->Release(); + if(FAILED(hr)) return hr; + + // Pixel Shader (PS) + + hr = dxDevice->CreatePixelShader( + gPixelShaderBlob->GetBufferPointer(), + gPixelShaderBlob->GetBufferSize(), + NULL, + &dxPixelShader); + gPixelShaderBlob->Release(); + if(FAILED(hr)) return hr; + + return S_OK; +} + +void renderFrameInner(ID3D11DeviceContext* dxContext) +{ + // We update our constant buffer per-frame, just for the purposes + // of the example, but we don't actually load different data + // per-frame (we always use an identity projection). + D3D11_MAPPED_SUBRESOURCE mapped; + HRESULT hr = dxContext->Map(dxConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); + if(!FAILED(hr)) + { + float* data = (float*) mapped.pData; + + static const float kIdentity[] = + { 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; + memcpy(data, kIdentity, sizeof(kIdentity)); + + dxContext->Unmap(dxConstantBuffer, 0); + } + + // Input Assembler (IA) + + dxContext->IASetInputLayout(dxInputLayout); + dxContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + + UINT dxVertexStride = sizeof(Vertex); + UINT dxVertexBufferOffset = 0; + dxContext->IASetVertexBuffers(0, 1, &dxVertexBuffer, &dxVertexStride, &dxVertexBufferOffset); + + // Vertex Shader (VS) + + dxContext->VSSetShader(dxVertexShader, NULL, 0); + dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); + + // Pixel Shader (PS) + + dxContext->PSSetShader(dxPixelShader, NULL, 0); + dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); + + // + + dxContext->Draw(3, 0); +} + +void finalize() +{ +} + +#endif + +// +// Definition of the HLSL-to-bytecode compilation logic. +// +ID3DBlob* compileHLSLShader( + char const* sourcePath, + char const* source, + char const* entryPointName, + char const* dxProfileName ) +{ + // Rather than statically link against the `d3dcompile` library, we + // dynamically load it. + // + // Note: A more realistic application would compile from HLSL text to D3D + // shader bytecode as part of an offline process, rather than doing it + // on-the-fly like this + // + static pD3DCompile D3DCompile_ = nullptr; + if( !D3DCompile_ ) + { + // TODO(tfoley): maybe want to search for one of a few versions of the DLL + HMODULE d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); + if(!d3dcompiler) + { + fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); + exit(1); + } + + D3DCompile_ = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile"); + if( !D3DCompile_ ) + { + fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); + exit(1); + } + } + + // For this example, we turn on debug output, and turn off all + // optimization. A real application would only use these flags + // when shader debugging is needed. + UINT flags = 0; + flags |= D3DCOMPILE_DEBUG; + flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; + + // The `D3DCompile` entry point takes a bunch of parameters, but we + // don't really need most of them for Slang-generated code. + ID3DBlob* dxShaderBlob = nullptr; + ID3DBlob* dxErrorBlob = nullptr; + HRESULT hr = D3DCompile_( + source, + strlen(source), + sourcePath, + nullptr, + nullptr, + entryPointName, + dxProfileName, + flags, + 0, + &dxShaderBlob, + &dxErrorBlob); + + // If the HLSL-to-bytecode compilation produced any diagnostic messages + // then we will print them out (whether or not the compilation failed). + if( dxErrorBlob ) + { + OutputDebugStringA( + (char const*)dxErrorBlob->GetBufferPointer()); + dxErrorBlob->Release(); + } + + if( FAILED(hr) ) + { + return nullptr; + } + + return dxShaderBlob; +} + + + + +// Capture a texture to a file + +static HRESULT captureTextureToFile( + ID3D11Device* dxDevice, + ID3D11DeviceContext* dxContext, + ID3D11Texture2D* dxTexture, + char const* outputPath) +{ + if(!dxContext) return E_INVALIDARG; + if(!dxTexture) return E_INVALIDARG; + + D3D11_TEXTURE2D_DESC dxTextureDesc; + dxTexture->GetDesc(&dxTextureDesc); + + // Don't bother supporing MSAA for right now + if(dxTextureDesc.SampleDesc.Count > 1) + return E_INVALIDARG; + + HRESULT hr = S_OK; + ID3D11Texture2D* dxStagingTexture = nullptr; + + if( dxTextureDesc.Usage == D3D11_USAGE_STAGING && (dxTextureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) ) + { + dxStagingTexture = dxTexture; + dxStagingTexture->AddRef(); + } + else + { + // Modify the descriptor to give us a staging texture + dxTextureDesc.BindFlags = 0; + dxTextureDesc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE; + dxTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + dxTextureDesc.Usage = D3D11_USAGE_STAGING; + + hr = dxDevice->CreateTexture2D(&dxTextureDesc, 0, &dxStagingTexture); + if(FAILED(hr)) + return hr; + + dxContext->CopyResource(dxStagingTexture, dxTexture); + } + + // Now just read back texels from the staging textures + + D3D11_MAPPED_SUBRESOURCE dxMappedResource; + hr = dxContext->Map(dxStagingTexture, 0, D3D11_MAP_READ, 0, &dxMappedResource); + if(FAILED(hr)) + return hr; + + int stbResult = stbi_write_png( + outputPath, + dxTextureDesc.Width, + dxTextureDesc.Height, + 4, + dxMappedResource.pData, + dxMappedResource.RowPitch); + if( !stbResult ) + { + return E_UNEXPECTED; + } + + dxContext->Unmap(dxStagingTexture, 0); + + dxStagingTexture->Release(); + + return S_OK; +} + +// + +class D3D11Renderer : public Renderer, public ShaderCompiler +{ +public: + IDXGISwapChain* dxSwapChain = NULL; + ID3D11Device* dxDevice = NULL; + ID3D11DeviceContext* dxImmediateContext = NULL; + ID3D11Texture2D* dxBackBufferTexture = NULL; + ID3D11RenderTargetView* dxBackBufferRTV = NULL; + + virtual void initialize(void* inWindowHandle) override + { + auto windowHandle = (HWND) inWindowHandle; + + // Rather than statically link against D3D, we load it dynamically. + + HMODULE d3d11 = LoadLibraryA("d3d11.dll"); + if(!d3d11) + { + fprintf(stderr, "error: failed load 'd3d11.dll'\n"); + exit(1); + } + + PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = + (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress( + d3d11, + "D3D11CreateDeviceAndSwapChain"); + if(!D3D11CreateDeviceAndSwapChain_) + { + fprintf(stderr, + "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); + exit(1); + } + + // We create our device in debug mode, just so that we can check that the + // example doesn't trigger warnings. + UINT deviceFlags = 0; + deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; + + // We will ask for the highest feature level that can be supported. + + D3D_FEATURE_LEVEL featureLevels[] = { + D3D_FEATURE_LEVEL_11_1, + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + D3D_FEATURE_LEVEL_9_3, + D3D_FEATURE_LEVEL_9_2, + D3D_FEATURE_LEVEL_9_1, + }; + D3D_FEATURE_LEVEL dxFeatureLevel = D3D_FEATURE_LEVEL_9_1; + + // Our swap chain uses RGBA8 with sRGB, with double buffering. + + DXGI_SWAP_CHAIN_DESC dxSwapChainDesc = { 0 }; + dxSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; + dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; + dxSwapChainDesc.SampleDesc.Count = 1; + dxSwapChainDesc.SampleDesc.Quality = 0; + dxSwapChainDesc.BufferCount = 2; + dxSwapChainDesc.OutputWindow = windowHandle; + dxSwapChainDesc.Windowed = TRUE; + dxSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; + dxSwapChainDesc.Flags = 0; + + // On a machine that does not have an up-to-date version of D3D installed, + // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` + // if you ask for featuer level 11_1. The workaround is to call + // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 + // at the start of the list of requested feature levels, and the second + // time without it. + + HRESULT hr = S_OK; + for( int ii = 0; ii < 2; ++ii ) + { + hr = D3D11CreateDeviceAndSwapChain_( + NULL, // adapter (use default) + D3D_DRIVER_TYPE_HARDWARE, + NULL, // software + deviceFlags, + &featureLevels[ii], + (sizeof(featureLevels) / sizeof(featureLevels[0])) - 1, + D3D11_SDK_VERSION, + &dxSwapChainDesc, + &dxSwapChain, + &dxDevice, + &dxFeatureLevel, + &dxImmediateContext); + + // Failures with `E_INVALIDARG` might be due to feature level 11_1 + // not being supported. Other failures are real, though. + if( hr != E_INVALIDARG ) + break; + } + if( FAILED(hr) ) + { + exit(1); + } + + // After we've created the swap chain, we can request a pointer to the + // back buffer as a D3D11 texture, and create a render-target view from it. + + static const IID kIID_ID3D11Texture2D = { + 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, + 0x95, 0x35, 0xd3, 0x4f, 0x9c }; + dxSwapChain->GetBuffer( + 0, + kIID_ID3D11Texture2D, + (void**)&dxBackBufferTexture); + + dxDevice->CreateRenderTargetView( + dxBackBufferTexture, + NULL, + &dxBackBufferRTV); + + // We immediately bind the back-buffer render target view, and we aren't + // going to switch. We don't bother with a depth buffer. + dxImmediateContext->OMSetRenderTargets( + 1, + &dxBackBufferRTV, + NULL); + + // Similarly, we are going to set up a viewport once, and then never + // switch, since this is a simple test app. + D3D11_VIEWPORT dxViewport; + dxViewport.TopLeftX = 0; + dxViewport.TopLeftY = 0; + dxViewport.Width = (float) gWindowWidth; + dxViewport.Height = (float) gWindowHeight; + dxViewport.MaxDepth = 1; // TODO(tfoley): use reversed depth + dxViewport.MinDepth = 0; + dxImmediateContext->RSSetViewports(1, &dxViewport); + } + + virtual void clearFrame() override + { + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + dxImmediateContext->ClearRenderTargetView( + dxBackBufferRTV, + kClearColor); + } + + virtual void presentFrame() override + { + dxSwapChain->Present(0, 0); + } + + virtual void captureScreenShot(char const* outputPath) override + { + HRESULT hr = captureTextureToFile( + dxDevice, + dxImmediateContext, + dxBackBufferTexture, + outputPath); + if( FAILED(hr) ) + { + fprintf(stderr, "error: could not capture screenshot to '%s'\n", outputPath); + exit(1); + } + } + + virtual ShaderCompiler* getShaderCompiler() override + { + return this; + } + + virtual Buffer* createBuffer(BufferDesc const& desc) override + { + D3D11_BUFFER_DESC dxBufferDesc = { 0 }; + dxBufferDesc.ByteWidth = desc.size; + + switch( desc.flavor ) + { + case BufferFlavor::Constant: + dxBufferDesc.Usage = D3D11_USAGE_DYNAMIC; + dxBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; + dxBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + break; + + case BufferFlavor::Vertex: + break; + + default: + return nullptr; + } + + D3D11_SUBRESOURCE_DATA dxInitData = { 0 }; + dxInitData.pSysMem = desc.initData; + + + ID3D11Buffer* dxBuffer = nullptr; + HRESULT hr = dxDevice->CreateBuffer( + &dxBufferDesc, + desc.initData ? &dxInitData : nullptr, + &dxBuffer); + if(FAILED(hr)) return nullptr; + + return (Buffer*) dxBuffer; + } + + static DXGI_FORMAT mapFormat(Format format) + { + switch( format ) + { + case Format::RGB_Float32: + return DXGI_FORMAT_R32G32B32_FLOAT; + + default: + return DXGI_FORMAT_UNKNOWN; + } + } + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override + { + D3D11_INPUT_ELEMENT_DESC dxInputElements[16] = {}; + + char hlslBuffer[1024]; + char* hlslCursor = &hlslBuffer[0]; + + hlslCursor += sprintf(hlslCursor, "float4 main(\n"); + + for( UInt ii = 0; ii < inputElementCount; ++ii ) + { + dxInputElements[ii].SemanticName = inputElements[ii].semanticName; + dxInputElements[ii].SemanticIndex = inputElements[ii].semanticIndex; + dxInputElements[ii].Format = mapFormat(inputElements[ii].format); + dxInputElements[ii].InputSlot = 0; + dxInputElements[ii].AlignedByteOffset = inputElements[ii].offset; + dxInputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + dxInputElements[ii].InstanceDataStepRate = 0; + + if(ii != 0) + { + hlslCursor+= sprintf(hlslCursor, ",\n"); + } + + char const* typeName = "Uknown"; + switch(inputElements[ii].format) + { + case Format::RGB_Float32: + typeName = "float3"; + break; + + default: + return nullptr; + } + + hlslCursor+= sprintf(hlslCursor, "%s a%d : %s%d", typeName, ii, + inputElements[ii].semanticName, + inputElements[ii].semanticIndex); + } + + hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }"); + + auto dxVertexShaderBlob = compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_4_0"); + if(!dxVertexShaderBlob) + return nullptr; + + ID3D11InputLayout* dxInputLayout = nullptr; + HRESULT hr = dxDevice->CreateInputLayout( + &dxInputElements[0], + inputElementCount, + dxVertexShaderBlob->GetBufferPointer(), + dxVertexShaderBlob->GetBufferSize(), + &dxInputLayout); + + dxVertexShaderBlob->Release(); + + if(FAILED(hr)) + return nullptr; + + return (InputLayout*) dxInputLayout; + } + + virtual void* map(Buffer* buffer, MapFlavor flavor) override + { + auto dxContext = dxImmediateContext; + + auto dxBuffer = (ID3D11Buffer*) buffer; + + D3D11_MAP dxMapFlavor; + switch( flavor ) + { + case MapFlavor::WriteDiscard: + dxMapFlavor = D3D11_MAP_WRITE_DISCARD; + break; + + default: + return nullptr; + } + + // We update our constant buffer per-frame, just for the purposes + // of the example, but we don't actually load different data + // per-frame (we always use an identity projection). + D3D11_MAPPED_SUBRESOURCE dxMapped; + HRESULT hr = dxContext->Map(dxBuffer, 0, dxMapFlavor, 0, &dxMapped); + if(FAILED(hr)) + return nullptr; + + return dxMapped.pData; + } + + virtual void unmap(Buffer* buffer) override + { + auto dxContext = dxImmediateContext; + + auto dxBuffer = (ID3D11Buffer*) buffer; + + dxContext->Unmap(dxBuffer, 0); + } + + virtual void setInputLayout(InputLayout* inputLayout) override + { + auto dxContext = dxImmediateContext; + auto dxInputLayout = (ID3D11InputLayout*) inputLayout; + + dxContext->IASetInputLayout(dxInputLayout); + } + + virtual void setPrimitiveTopology(PrimitiveTopology topology) override + { + auto dxContext = dxImmediateContext; + + D3D11_PRIMITIVE_TOPOLOGY dxTopology; + switch( topology ) + { + case PrimitiveTopology::TriangleList: + dxTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + break; + + default: + return; + } + + dxContext->IASetPrimitiveTopology(dxTopology); + } + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override + { + auto dxContext = dxImmediateContext; + + static const int kMaxVertexBuffers = 16; + + UINT dxVertexStrides[kMaxVertexBuffers]; + UINT dxVertexOffsets[kMaxVertexBuffers]; + + for( UInt ii = 0; ii < slotCount; ++ii ) + { + dxVertexStrides[ii] = strides[ii]; + dxVertexOffsets[ii] = offsets[ii]; + } + + auto dxVertexBuffers = (ID3D11Buffer* const*) buffers; + + dxContext->IASetVertexBuffers(startSlot, slotCount, &dxVertexBuffers[0], &dxVertexStrides[0], &dxVertexOffsets[0]); + } + + virtual void setShaderProgram(ShaderProgram* inProgram) override + { + auto dxContext = dxImmediateContext; + + auto program = (D3D11ShaderProgram*) inProgram; + + dxContext->VSSetShader(program->dxVertexShader, NULL, 0); + dxContext->PSSetShader(program->dxPixelShader, NULL, 0); + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + auto dxContext = dxImmediateContext; + + // TODO: actually use those offsets + + auto dxConstantBuffers = (ID3D11Buffer* const*) buffers; + + dxContext->VSSetConstantBuffers(startSlot, slotCount, &dxConstantBuffers[0]); + dxContext->VSSetConstantBuffers(startSlot, slotCount, &dxConstantBuffers[0]); + } + + + virtual void draw(UInt vertexCount, UInt startVertex) override + { + auto dxContext = dxImmediateContext; + + dxContext->Draw(vertexCount, startVertex); + } + + + // ShaderCompiler interface + + struct D3D11ShaderProgram + { + ID3D11VertexShader* dxVertexShader; + ID3D11PixelShader* dxPixelShader; + }; + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + auto dxVertexShaderBlob = compileHLSLShader(request.source.path, request.source.text, request.vertexShader .name, request.vertexShader .profile); + if(!dxVertexShaderBlob) return nullptr; + + auto dxFragmentShaderBlob = compileHLSLShader(request.source.path, request.source.text, request.fragmentShader .name, request.fragmentShader .profile); + if(!dxFragmentShaderBlob) return nullptr; + + ID3D11VertexShader* dxVertexShader; + ID3D11PixelShader* dxPixelShader; + + HRESULT vsResult = dxDevice->CreateVertexShader( dxVertexShaderBlob ->GetBufferPointer(), dxVertexShaderBlob ->GetBufferSize(), nullptr, &dxVertexShader); + HRESULT psResult = dxDevice->CreatePixelShader( dxFragmentShaderBlob->GetBufferPointer(), dxFragmentShaderBlob->GetBufferSize(), nullptr, &dxPixelShader); + + dxVertexShaderBlob ->Release(); + dxFragmentShaderBlob->Release(); + + if(FAILED(vsResult)) return nullptr; + if(FAILED(psResult)) return nullptr; + + D3D11ShaderProgram* shaderProgram = new D3D11ShaderProgram(); + shaderProgram->dxVertexShader = dxVertexShader; + shaderProgram->dxPixelShader = dxPixelShader; + return (ShaderProgram*) shaderProgram; + } +}; + + + +Renderer* createD3D11Renderer() +{ + return new D3D11Renderer(); +} + +} // renderer_test diff --git a/tools/render-test/render-d3d11.h b/tools/render-test/render-d3d11.h new file mode 100644 index 000000000..59142731d --- /dev/null +++ b/tools/render-test/render-d3d11.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace renderer_test { + +class Renderer; + +Renderer* createD3D11Renderer(); + +} // renderer_test diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp new file mode 100644 index 000000000..545dd2e44 --- /dev/null +++ b/tools/render-test/render-gl.cpp @@ -0,0 +1,242 @@ +// render-gl.cpp +#include "render-gl.h" + +#include "options.h" +#include "render.h" + +#include <stdio.h> +#include <stdlib.h> + +// TODO(tfoley): eventually we should be able to run these +// tests on non-Windows targets to confirm that cross-compilation +// at least *works* on those platforms... +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#include <stddef.h> +#if (_MSC_VER < 1900) +#define snprintf sprintf_s +#endif +#endif + +#pragma comment(lib, "opengl32") + +#include <GL/GL.h> +#include "external/glext.h" + +// We define an "X-macro" for mapping over loadable OpenGL +// extension entry point that we will use, so that we can +// easily write generic code to iterate over them. +#define MAP_GL_EXTENSION_FUNCS(F) \ + F(glCreateProgram, PFNGLCREATEPROGRAMPROC) \ + F(glCreateShader, PFNGLCREATESHADERPROC) \ + F(glShaderSource, PFNGLSHADERSOURCEPROC) \ + F(glCompileShader, PFNGLCOMPILESHADERPROC) \ + F(glGetShaderiv, PFNGLGETSHADERIVPROC) \ + F(glDeleteShader, PFNGLDELETESHADERPROC) \ + F(glAttachShader, PFNGLATTACHSHADERPROC) \ + F(glLinkProgram, PFNGLLINKPROGRAMPROC) \ + F(glGetProgramiv, PFNGLGETPROGRAMIVPROC) \ + F(glGetProgramInfoLog, PFNGLGETPROGRAMINFOLOGPROC) \ + F(glDeleteProgram, PFNGLDELETEPROGRAMPROC) \ + F(glGetShaderInfoLog, PFNGLGETSHADERINFOLOGPROC) \ + /* emtty */ + +namespace renderer_test { + +class GLRenderer : public Renderer, public ShaderCompiler +{ +public: + HDC deviceContext; + HGLRC glContext; + + // Declre a function pointer for each OpenGL + // extension function we need to load + +#define DECLARE_GL_EXTENSION_FUNC(NAME, TYPE) TYPE NAME; + MAP_GL_EXTENSION_FUNCS(DECLARE_GL_EXTENSION_FUNC) +#undef DECLARE_GL_EXTENSION_FUNC + + // Renderer interface + + virtual void initialize(void* inWindowHandle) override + { + auto windowHandle = (HWND) inWindowHandle; + + deviceContext = GetDC(windowHandle); + + PIXELFORMATDESCRIPTOR pixelFormatDesc = { sizeof(PIXELFORMATDESCRIPTOR) }; + pixelFormatDesc.nVersion = 1; + pixelFormatDesc.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pixelFormatDesc.iPixelType = PFD_TYPE_RGBA; + pixelFormatDesc.cColorBits = 32; + pixelFormatDesc.cDepthBits = 24; + pixelFormatDesc.cStencilBits = 8; + pixelFormatDesc.iLayerType = PFD_MAIN_PLANE; + + int pixelFormatIndex = ChoosePixelFormat(deviceContext, &pixelFormatDesc); + SetPixelFormat(deviceContext, pixelFormatIndex, &pixelFormatDesc); + + glContext = wglCreateContext(deviceContext); + wglMakeCurrent(deviceContext, glContext); + + auto renderer = glGetString(GL_RENDERER); + auto extensions = glGetString(GL_EXTENSIONS); + + // Load ech of our etension functions by name + + #define LOAD_GL_EXTENSION_FUNC(NAME, TYPE) NAME = (TYPE) wglGetProcAddress(#NAME); + MAP_GL_EXTENSION_FUNCS(LOAD_GL_EXTENSION_FUNC) + #undef LOAD_GL_EXTENSION_FUNC + } + + virtual void clearFrame() override + { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + } + + virtual void presentFrame() override + { + glFlush(); + SwapBuffers(deviceContext); + } + + virtual void captureScreenShot(char const* outputPath) override + { + + } + + virtual ShaderCompiler* getShaderCompiler() override + { + return this; + } + + virtual Buffer* createBuffer(BufferDesc const& desc) override + { + return nullptr; + } + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override + { + return nullptr; + } + + virtual void* map(Buffer* buffer, MapFlavor flavor) override + { + return nullptr; + } + + virtual void unmap(Buffer* buffer) override + { + } + + virtual void setInputLayout(InputLayout* inputLayout) override + { + } + + virtual void setPrimitiveTopology(PrimitiveTopology topology) override + { + } + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override + { + } + + virtual void setShaderProgram(ShaderProgram* program) override + { + } + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override + { + } + + + virtual void draw(UInt vertexCount, UInt startVertex = 0) override + { + } + + // ShaderCompiler interface + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + auto programID = glCreateProgram(); + + auto vertexShaderID = loadShader(GL_VERTEX_SHADER, request.vertexShader .source.text); + auto fragmentShaderID = loadShader(GL_FRAGMENT_SHADER, request.fragmentShader.source.text); + + glAttachShader(programID, vertexShaderID); + glAttachShader(programID, fragmentShaderID); + + glLinkProgram(programID); + + glDeleteShader(vertexShaderID); + glDeleteShader(fragmentShaderID); + + GLint success = GL_FALSE; + glGetProgramiv(programID, GL_LINK_STATUS, &success); + if( !success ) + { + int maxSize = 0; + glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*) malloc(maxSize); + + int infoSize = 0; + glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); + if( infoSize > 0 ) + { + fprintf(stderr, "%s", infoBuffer); + OutputDebugStringA(infoBuffer); + } + + glDeleteProgram(programID); + return 0; + } + + return (ShaderProgram*) (uintptr_t) programID; + } + + GLuint loadShader(GLenum stage, char const* source) + { + auto shaderID = glCreateShader(stage); + + glShaderSource(shaderID, 1, &source, nullptr); + glCompileShader(shaderID); + + GLint success = GL_FALSE; + glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); + if( !success ) + { + int maxSize = 0; + glGetShaderiv(shaderID, GL_INFO_LOG_LENGTH, &maxSize); + + auto infoBuffer = (char*) malloc(maxSize); + + int infoSize = 0; + glGetShaderInfoLog(shaderID, maxSize, &infoSize, infoBuffer); + if( infoSize > 0 ) + { + fprintf(stderr, "%s", infoBuffer); + OutputDebugStringA(infoBuffer); + } + + glDeleteShader(shaderID); + return 0; + } + + return shaderID; + } +}; + + + +Renderer* createGLRenderer() +{ + return new GLRenderer(); +} + +} // renderer_test diff --git a/tools/render-test/render-gl.h b/tools/render-test/render-gl.h new file mode 100644 index 000000000..4e6de970c --- /dev/null +++ b/tools/render-test/render-gl.h @@ -0,0 +1,10 @@ +// render-d3d11.h +#pragma once + +namespace renderer_test { + +class Renderer; + +Renderer* createGLRenderer(); + +} // renderer_test diff --git a/tools/render-test/render-test.vcxproj b/tools/render-test/render-test.vcxproj new file mode 100644 index 000000000..dfe610681 --- /dev/null +++ b/tools/render-test/render-test.vcxproj @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{96610759-07B9-4EEB-A974-5C634A2E742B}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>rendertest</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="options.cpp" /> + <ClCompile Include="render-d3d11.cpp" /> + <ClCompile Include="render-gl.cpp" /> + <ClCompile Include="slang-support.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="options.h" /> + <ClInclude Include="render-d3d11.h" /> + <ClInclude Include="render-gl.h" /> + <ClInclude Include="render.h" /> + <ClInclude Include="slang-support.h" /> + <ClInclude Include="window.h" /> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/tools/render-test/render-test.vcxproj.filters b/tools/render-test/render-test.vcxproj.filters new file mode 100644 index 000000000..6e0ff295a --- /dev/null +++ b/tools/render-test/render-test.vcxproj.filters @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="render-d3d11.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="options.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="render-gl.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="slang-support.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="options.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="render-d3d11.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="render-gl.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="render.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="window.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="slang-support.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file diff --git a/tools/render-test/render.h b/tools/render-test/render.h new file mode 100644 index 000000000..a30bf98f9 --- /dev/null +++ b/tools/render-test/render.h @@ -0,0 +1,118 @@ +// render.h +#pragma once + +#include "options.h" +#include "window.h" + +namespace renderer_test { + +typedef struct Buffer Buffer; +typedef struct InputLayout InputLayout; +typedef struct ShaderProgram ShaderProgram; + +struct ShaderCompileRequest +{ + struct SourceInfo + { + char const* path; + char const* text; + }; + + struct EntryPoint + { + char const* name; + char const* profile; + + SourceInfo source; + }; + + SourceInfo source; + EntryPoint vertexShader; + EntryPoint fragmentShader; +}; + +class ShaderCompiler +{ +public: + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) = 0; +}; + +enum class Format +{ + Unknown, + RGB_Float32, +}; + +enum class BufferFlavor +{ + Constant, + Vertex, +}; + +struct BufferDesc +{ + UInt size = 0; + BufferFlavor flavor = BufferFlavor::Constant; + void const* initData = nullptr; +}; + +struct InputElementDesc +{ + char const* semanticName; + UInt semanticIndex; + Format format; + UInt offset; +}; + +enum class MapFlavor +{ + WriteDiscard, +}; + +enum class PrimitiveTopology +{ + TriangleList, +}; + +class Renderer +{ +public: + virtual void initialize(void* inWindowHandle) = 0; + + virtual void clearFrame() = 0; + virtual void presentFrame() = 0; + + virtual void captureScreenShot(char const* outputPath) = 0; + + virtual Buffer* createBuffer(BufferDesc const& desc) = 0; + + virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) = 0; + + virtual ShaderCompiler* getShaderCompiler() = 0; + + virtual void* map(Buffer* buffer, MapFlavor flavor) = 0; + virtual void unmap(Buffer* buffer) = 0; + + virtual void setInputLayout(InputLayout* inputLayout) = 0; + virtual void setPrimitiveTopology(PrimitiveTopology topology) = 0; + + virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) = 0; + + inline void setVertexBuffer(UInt slot, Buffer* buffer, UInt stride, UInt offset = 0) + { + setVertexBuffers(slot, 1, &buffer, &stride, &offset); + } + + virtual void setShaderProgram(ShaderProgram* program) = 0; + + virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) = 0; + + inline void setConstantBuffer(UInt slot, Buffer* buffer, UInt offset = 0) + { + setConstantBuffers(slot, 1, &buffer, &offset); + } + + virtual void draw(UInt vertexCount, UInt startVertex = 0) = 0; +}; + +} // renderer_test diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp new file mode 100644 index 000000000..5aafc562e --- /dev/null +++ b/tools/render-test/slang-support.cpp @@ -0,0 +1,84 @@ +// slang-support.cpp + +#define SLANG_INCLUDE_IMPLEMENTATION + +#include "slang-support.h" + +#include <stdio.h> + +namespace renderer_test { + +struct SlangShaderCompilerWrapper : public ShaderCompiler +{ + ShaderCompiler* innerCompiler; + SlangCompileTarget target; + + virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override + { + SlangSession* slangSession = spCreateSession(NULL); + SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); + + spSetCodeGenTarget(slangRequest, target); + + int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr); + + spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, request.source.path, request.source.text); + + int vertexEntryPoint = spAddTranslationUnitEntryPoint(slangRequest, translationUnitIndex, request.vertexShader.name, spFindProfile(slangSession, request.vertexShader.profile)); + int fragmentEntryPoint = spAddTranslationUnitEntryPoint(slangRequest, translationUnitIndex, request.fragmentShader.name, spFindProfile(slangSession, request.fragmentShader.profile)); + + int compileErr = spCompile(slangRequest); + if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + // TODO(tfoley): re-enable when I get a logging solution in place +// OutputDebugStringA(diagnostics); + fprintf(stderr, "%s", diagnostics); + } + if(compileErr) + { + return nullptr; + } + + char const* translatedCode = spGetTranslationUnitSource(slangRequest, translationUnitIndex); + char const* vertexCode = spGetEntryPointSource(slangRequest, translationUnitIndex, vertexEntryPoint); + char const* fragmentCode = spGetEntryPointSource(slangRequest, translationUnitIndex, fragmentEntryPoint); + + ShaderCompileRequest innerRequest = request; + innerRequest.source.text = translatedCode; + innerRequest.vertexShader.source.text = vertexCode; + innerRequest.fragmentShader.source.text = fragmentCode; + + + auto result = innerCompiler->compileProgram(innerRequest); + + // 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); + spDestroySession(slangSession); + + return result; + } +}; + +ShaderCompiler* createSlangShaderCompiler(ShaderCompiler* innerCompiler, SlangCompileTarget target) +{ + auto result = new SlangShaderCompilerWrapper(); + result->innerCompiler = innerCompiler; + result->target = target; + + return result; + +} + + +} // renderer_test + +// +// In order to actually use Slang in our application, we need to link in its +// implementation. The easiest way to accomplish this is by directly inlcuding +// the (concatenated) Slang source code into our app. +// + +#include <slang.h> diff --git a/tools/render-test/slang-support.h b/tools/render-test/slang-support.h new file mode 100644 index 000000000..a191fbfef --- /dev/null +++ b/tools/render-test/slang-support.h @@ -0,0 +1,12 @@ +// slang-support.h +#pragma once + +#include "render.h" + +#include <slang.h> + +namespace renderer_test { + +ShaderCompiler* createSlangShaderCompiler(ShaderCompiler* innerCompiler, SlangCompileTarget target); + +} // renderer_test diff --git a/tools/render-test/window.h b/tools/render-test/window.h new file mode 100644 index 000000000..5d0a89ee4 --- /dev/null +++ b/tools/render-test/window.h @@ -0,0 +1,10 @@ +// window.h +#pragma once + +namespace renderer_test { + +typedef struct Window Window; + +Window* createWindow(); + +} // renderer_test diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp new file mode 100644 index 000000000..d8a04a050 --- /dev/null +++ b/tools/slang-test/main.cpp @@ -0,0 +1,1030 @@ +// main.cpp + +#include "../../source/core/slang-io.h" + +using namespace CoreLib::Basic; +using namespace CoreLib::IO; + +#include "os.h" + +#define STB_IMAGE_IMPLEMENTATION +#include "external/stb/stb_image.h" + + +#ifdef _WIN32 +#define SLANG_TEST_SUPPORT_HLSL 1 +#include <d3dcompiler.h> +#endif + +#include <assert.h> +#include <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> + +struct Options +{ + char const* appName = "slang-test"; + + // Directory to use when looking for binaries to run + char const* binDir = ""; + + // only run test cases with names that have this prefix + char const* testPrefix = nullptr; + + // generate extra output (notably: command lines we run) + bool shouldBeVerbose = false; + + // force generation of baselines for HLSL tests + bool generateHLSLBaselines = false; +}; +Options options; + +void parseOptions(int* argc, char** argv) +{ + int argCount = *argc; + char const* const* argCursor = argv; + char const* const* argEnd = argCursor + argCount; + + char const** writeCursor = (char const**) argv; + + // first argument is the application name + if( argCursor != argEnd ) + { + options.appName = *argCursor++; + } + + // now iterate over arguments to collect options + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + if( arg[0] != '-' ) + { + *writeCursor++ = arg; + continue; + } + + if( strcmp(arg, "--") == 0 ) + { + while(argCursor != argEnd) + { + char const* arg = *argCursor++; + *writeCursor++ = arg; + } + break; + } + + if( strcmp(arg, "--bindir") == 0 ) + { + if( argCursor == argEnd ) + { + fprintf(stderr, "error: expected operand for '%s'\n", arg); + exit(1); + } + options.binDir = *argCursor++; + } + else if( strcmp(arg, "-v") == 0 ) + { + options.shouldBeVerbose = true; + } + else if( strcmp(arg, "-generate-hlsl-baselines") == 0 ) + { + options.generateHLSLBaselines = true; + } + else if( strcmp(arg, "-release") == 0 ) + { + // Assumed to be handle by .bat file that called us + } + else if( strcmp(arg, "-debug") == 0 ) + { + // Assumed to be handle by .bat file that called us + } + else + { + fprintf(stderr, "unknown option '%s'\n", arg); + exit(1); + } + } + + // any arguments left over were positional arguments + argCount = (int)(writeCursor - argv); + argCursor = argv; + argEnd = argCursor + argCount; + + // first positional argument is a "filter" to apply + if( argCursor != argEnd ) + { + options.testPrefix = *argCursor++; + } + + // any remaining arguments represent an error + if(argCursor != argEnd) + { + fprintf(stderr, "unexpected arguments\n"); + exit(1); + } + + *argc = 0; +} + +// Called for an error in the test-runner (not for an error involving +// a test itself). +void error(char const* message, ...) +{ + fprintf(stderr, "error: "); + + va_list args; + va_start(args, message); + vfprintf(stderr, message, args); + va_end(args); + + fprintf(stderr, "\n"); +} + +enum TestResult +{ + kTestResult_Fail, + kTestResult_Pass, + kTestResult_Ignored, +}; + +bool match(char const** ioCursor, char const* expected) +{ + char const* cursor = *ioCursor; + while(*expected && *cursor == *expected) + { + cursor++; + expected++; + } + if(*expected != 0) return false; + + *ioCursor = cursor; + return true; +} + +void skipHorizontalSpace(char const** ioCursor) +{ + char const* cursor = *ioCursor; + for( ;;) + { + switch( *cursor ) + { + case ' ': + case '\t': + cursor++; + continue; + + default: + break; + } + + break; + } + *ioCursor = cursor; +} + +void skipToEndOfLine(char const** ioCursor) +{ + char const* cursor = *ioCursor; + for( ;;) + { + int c = *cursor; + switch( c ) + { + default: + cursor++; + continue; + + case '\r': case '\n': + { + cursor++; + int d = *cursor; + if( (c ^ d) == ('\r' ^ '\n') ) + { + cursor++; + } + } + // fall through to: + case 0: + *ioCursor = cursor; + return; + } + } +} + +String getString(char const* textBegin, char const* textEnd) +{ + StringBuilder sb; + sb.Append(textBegin, textEnd - textBegin); + return sb.ProduceString(); +} + +String collectRestOfLine(char const** ioCursor) +{ + char const* cursor = *ioCursor; + + char const* textBegin = cursor; + skipToEndOfLine(&cursor); + char const* textEnd = cursor; + + *ioCursor = cursor; + return getString(textBegin, textEnd); +} + +// Optiosn for a particular test +struct TestOptions +{ + String command; + List<String> args; +}; + +// Information on tests to run for a particular file +struct FileTestList +{ + List<TestOptions> tests; +}; + +TestResult gatherTestOptions( + char const** ioCursor, + FileTestList* testList) +{ + char const* cursor = *ioCursor; + + // Start by scanning for the sub-command name: + char const* commandStart = cursor; + for(;;) + { + switch(*cursor) + { + default: + cursor++; + continue; + + case ':': + break; + + case 0: case '\r': case '\n': + return kTestResult_Fail; + } + + break; + } + char const* commandEnd = cursor; + if(*cursor == ':') + cursor++; + + TestOptions testOptions; + testOptions.command = getString(commandStart, commandEnd); + + // Now scan for arguments. For now we just assume that + // any whitespace separation indicates a new argument + // (we don't support quoting) + for(;;) + { + skipHorizontalSpace(&cursor); + + // End of line? then no more options. + switch( *cursor ) + { + case 0: case '\r': case '\n': + skipToEndOfLine(&cursor); + testList->tests.Add(testOptions); + return kTestResult_Pass; + + default: + break; + } + + // Let's try to read one option + char const* argBegin = cursor; + for(;;) + { + switch( *cursor ) + { + default: + cursor++; + continue; + + case 0: case '\r': case '\n': case ' ': case '\t': + break; + } + + break; + } + char const* argEnd = cursor; + assert(argBegin != argEnd); + + testOptions.args.Add(getString(argBegin, argEnd)); + } +} + +// Try to read command-line options from the test file itself +TestResult gatherTestsForFile( + String filePath, + FileTestList* testList) +{ + String fileContents; + try + { + fileContents = CoreLib::IO::File::ReadAllText(filePath); + } + catch (CoreLib::IO::IOException) + { + return kTestResult_Fail; + } + + + // Walk through the lines of the file, looking for test commands + char const* cursor = fileContents.begin(); + + while(*cursor) + { + // We are at the start of a line of input. + + skipHorizontalSpace(&cursor); + + // Look for a pattern that matches what we want + if(match(&cursor, "//TEST:")) + { + if(gatherTestOptions(&cursor, testList) != kTestResult_Pass) + return kTestResult_Fail; + } + else if(match(&cursor, "//TEST_IGNORE_FILE")) + { + return kTestResult_Ignored; + } + else + { + skipToEndOfLine(&cursor); + } + } + + return kTestResult_Pass; +} + +OSError spawnAndWait(String testPath, OSProcessSpawner& spawner) +{ + if( options.shouldBeVerbose ) + { + fprintf(stderr, "%s\n", spawner.commandLine_.Buffer()); + } + + OSError err = spawner.spawnAndWaitForCompletion(); + if (err != kOSError_None) + { + error("failed to run test '%S'", testPath.ToWString()); + } + return err; +} + +String getOutput(OSProcessSpawner& spawner) +{ + OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); + + String standardOuptut = spawner.getStandardOutput(); + String standardError = spawner.getStandardError(); + + // We construct a single output string that captures the results + StringBuilder actualOutputBuilder; + actualOutputBuilder.Append("result code = "); + actualOutputBuilder.Append(resultCode); + actualOutputBuilder.Append("\nstandard error = {\n"); + actualOutputBuilder.Append(standardError); + actualOutputBuilder.Append("}\nstandard output = {\n"); + actualOutputBuilder.Append(standardOuptut); + actualOutputBuilder.Append("}\n"); + + return actualOutputBuilder.ProduceString(); +} + +struct TestInput +{ + String filePath; + TestOptions const* testOptions; + FileTestList const* testList; +}; + +typedef TestResult (*TestCallback)(TestInput& input); + +TestResult runSimpleTest(TestInput& input) +{ + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + + auto filePath = input.filePath; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + String actualOutput = getOutput(spawner); + + String expectedOutputPath = filePath + ".expected"; + String expectedOutput; + try + { + expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); + } + catch (CoreLib::IO::IOException) + { + } + + // If no expected output file was found, then we + // expect everything to be empty + if (expectedOutput.Length() == 0) + { + expectedOutput = "result code = 0\nstandard error = {\n}\nstandard output = {\n}\n"; + } + + TestResult result = kTestResult_Pass; + + // Otherwise we compare to the expected output + if (actualOutput != expectedOutput) + { + result = kTestResult_Fail; + } + + // If the test failed, then we write the actual output to a file + // so that we can easily diff it from the command line and + // diagnose the problem. + if (result == kTestResult_Fail) + { + String actualOutputPath = filePath + ".actual"; + CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); + } + + return result; +} + +#ifdef SLANG_TEST_SUPPORT_HLSL +TestResult generateHLSLBaseline(TestInput& input) +{ + auto filePath = input.filePath; + + OSProcessSpawner spawner; + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + spawner.pushArgument("-target"); + spawner.pushArgument("dxbc-assembly"); + spawner.pushArgument("-pass-through"); + spawner.pushArgument("fxc"); + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + String expectedOutput = getOutput(spawner); + String expectedOutputPath = filePath + ".expected"; + try + { + CoreLib::IO::File::WriteAllText(expectedOutputPath, expectedOutput); + } + catch (CoreLib::IO::IOException) + { + return kTestResult_Fail; + } + return kTestResult_Pass; +} + +TestResult runHLSLComparisonTest(TestInput& input) +{ + auto filePath = input.filePath; + + // We will use the Microsoft compiler to generate out expected output here + String expectedOutputPath = filePath + ".expected"; + + // Generate the expected output using standard HLSL compiler + generateHLSLBaseline(input); + + // need to execute the stand-alone Slang compiler on the file, and compare its output to what we expect + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + // TODO: The compiler should probably define this automatically... + spawner.pushArgument("-D"); + spawner.pushArgument("__SLANG__"); + + spawner.pushArgument("-target"); + spawner.pushArgument("dxbc-assembly"); + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + // We ignore output to stdout, and only worry about what the compiler + // wrote to stderr. + + OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); + + String standardOuptut = spawner.getStandardOutput(); + String standardError = spawner.getStandardError(); + + // We construct a single output string that captures the results + StringBuilder actualOutputBuilder; + actualOutputBuilder.Append("result code = "); + actualOutputBuilder.Append(resultCode); + actualOutputBuilder.Append("\nstandard error = {\n"); + actualOutputBuilder.Append(standardError); + actualOutputBuilder.Append("}\nstandard output = {\n"); + actualOutputBuilder.Append(standardOuptut); + actualOutputBuilder.Append("}\n"); + + String actualOutput = actualOutputBuilder.ProduceString(); + + String expectedOutput; + try + { + expectedOutput = CoreLib::IO::File::ReadAllText(expectedOutputPath); + } + catch (CoreLib::IO::IOException) + { + } + + TestResult result = kTestResult_Pass; + + // If no expected output file was found, then we + // expect everything to be empty + if (expectedOutput.Length() == 0) + { + if (resultCode != 0) result = kTestResult_Fail; + if (standardError.Length() != 0) result = kTestResult_Fail; + if (standardOuptut.Length() != 0) result = kTestResult_Fail; + } + // Otherwise we compare to the expected output + else if (actualOutput != expectedOutput) + { + result = kTestResult_Fail; + } + + // If the test failed, then we write the actual output to a file + // so that we can easily diff it from the command line and + // diagnose the problem. + if (result == kTestResult_Fail) + { + String actualOutputPath = filePath + ".actual"; + CoreLib::IO::File::WriteAllText(actualOutputPath, actualOutput); + } + + return result; +} +#endif + +TestResult doGLSLComparisonTestRun( + TestInput& input, + char const* langDefine, + char const* passThrough, + char const* outputKind, + String* outOutput) +{ + auto filePath = input.filePath; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "slangc.exe"); + spawner.pushArgument(filePath); + + if( langDefine ) + { + spawner.pushArgument("-D"); + spawner.pushArgument(langDefine); + } + + if( passThrough ) + { + spawner.pushArgument("-pass-through"); + spawner.pushArgument(passThrough); + } + + spawner.pushArgument("-no-checking"); + + spawner.pushArgument("-target"); + spawner.pushArgument("spirv-assembly"); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); + + String standardOuptut = spawner.getStandardOutput(); + String standardError = spawner.getStandardError(); + + // We construct a single output string that captures the results + StringBuilder outputBuilder; + outputBuilder.Append("result code = "); + outputBuilder.Append(resultCode); + outputBuilder.Append("\nstandard error = {\n"); + outputBuilder.Append(standardError); + outputBuilder.Append("}\nstandard output = {\n"); + outputBuilder.Append(standardOuptut); + outputBuilder.Append("}\n"); + + String outputPath = filePath + outputKind; + String output = outputBuilder.ProduceString(); + + *outOutput = output; + + return kTestResult_Pass; +} + +TestResult runGLSLComparisonTest(TestInput& input) +{ + auto filePath = input.filePath; + + String expectedOutput; + String actualOutput; + + TestResult hlslResult = doGLSLComparisonTestRun(input, "__GLSL__", "glslang", ".expected", &expectedOutput); + TestResult slangResult = doGLSLComparisonTestRun(input, "__SLANG__", nullptr, ".actual", &actualOutput); + + CoreLib::IO::File::WriteAllText(filePath + ".expected", expectedOutput); + CoreLib::IO::File::WriteAllText(filePath + ".actual", actualOutput); + + if( hlslResult == kTestResult_Fail ) return kTestResult_Fail; + if( slangResult == kTestResult_Fail ) return kTestResult_Fail; + + if (actualOutput != expectedOutput) + { + return kTestResult_Fail; + } + + return kTestResult_Pass; +} + + + +TestResult doRenderComparisonTestRun(TestInput& input, char const* langOption, char const* outputKind, String* outOutput) +{ + // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass + + auto filePath = input.filePath; + + OSProcessSpawner spawner; + + spawner.pushExecutableName(String(options.binDir) + "render-test.exe"); + spawner.pushArgument(filePath); + + for( auto arg : input.testOptions->args ) + { + spawner.pushArgument(arg); + } + + spawner.pushArgument(langOption); + spawner.pushArgument("-o"); + spawner.pushArgument(filePath + outputKind + ".png"); + + if (spawnAndWait(filePath, spawner) != kOSError_None) + { + return kTestResult_Fail; + } + + OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); + + String standardOuptut = spawner.getStandardOutput(); + String standardError = spawner.getStandardError(); + + // We construct a single output string that captures the results + StringBuilder outputBuilder; + outputBuilder.Append("result code = "); + outputBuilder.Append(resultCode); + outputBuilder.Append("\nstandard error = {\n"); + outputBuilder.Append(standardError); + outputBuilder.Append("}\nstandard output = {\n"); + outputBuilder.Append(standardOuptut); + outputBuilder.Append("}\n"); + + String outputPath = filePath + outputKind; + String output = outputBuilder.ProduceString(); + + *outOutput = output; + + return kTestResult_Pass; +} + +TestResult doImageComparison(String const& filePath) +{ + // Allow a difference in the low bits of the 8-bit result, just to play it safe + static const int kAbsoluteDiffCutoff = 2; + + // Allow a relatie 1% difference + static const float kRelativeDiffCutoff = 0.01f; + + String expectedPath = filePath + ".expected.png"; + String actualPath = filePath + ".actual.png"; + + int expectedX, expectedY, expectedN; + int actualX, actualY, actualN; + + + unsigned char* expectedData = stbi_load(expectedPath.begin(), &expectedX, &expectedY, &expectedN, 0); + unsigned char* actualData = stbi_load(actualPath.begin(), &actualX, &actualY, &actualN, 0); + + if(!expectedData) return kTestResult_Fail; + if(!actualData) return kTestResult_Fail; + + if(expectedX != actualX) return kTestResult_Fail; + if(expectedY != actualY) return kTestResult_Fail; + if(expectedN != actualN) return kTestResult_Fail; + + unsigned char* expectedCursor = expectedData; + unsigned char* actualCursor = actualData; + + for( int y = 0; y < actualY; ++y ) + for( int x = 0; x < actualX; ++x ) + for( int n = 0; n < actualN; ++n ) + { + int expectedVal = *expectedCursor++; + int actualVal = *actualCursor++; + + int absoluteDiff = actualVal - expectedVal; + if(absoluteDiff < 0) absoluteDiff = -absoluteDiff; + + if( absoluteDiff < kAbsoluteDiffCutoff ) + { + // There might be a difference, but we'll consider it to be inside tolerance + continue; + } + + if( expectedVal != 0 ) + { + float relativeDiff = fabsf(float(actualVal) - float(expectedVal)) / float(expectedVal); + + if( relativeDiff < kRelativeDiffCutoff ) + { + // relative difference was small enough + continue; + } + } + + // TODO: may need to do some local search sorts of things, to deal with + // cases where vertex shader results lead to rendering that is off + // by one pixel... + + // There was a difference we couldn't excuse! + return kTestResult_Fail; + } + + return kTestResult_Pass; +} + +TestResult runHLSLRenderComparisonTestImpl( + TestInput& input, + char const* expectedArg, + char const* actualArg) +{ + auto filePath = input.filePath; + + String expectedOutput; + String actualOutput; + + TestResult hlslResult = doRenderComparisonTestRun(input, expectedArg, ".expected", &expectedOutput); + TestResult slangResult = doRenderComparisonTestRun(input, actualArg, ".actual", &actualOutput); + + CoreLib::IO::File::WriteAllText(filePath + ".expected", expectedOutput); + CoreLib::IO::File::WriteAllText(filePath + ".actual", actualOutput); + + if( hlslResult == kTestResult_Fail ) return kTestResult_Fail; + if( slangResult == kTestResult_Fail ) return kTestResult_Fail; + + if (actualOutput != expectedOutput) + { + return kTestResult_Fail; + } + + // Next do an image comparison on the expected output images! + + TestResult imageCompareResult = doImageComparison(filePath); + if(imageCompareResult != kTestResult_Pass) + return imageCompareResult; + + return kTestResult_Pass; +} + +TestResult runHLSLRenderComparisonTest(TestInput& input) +{ + return runHLSLRenderComparisonTestImpl(input, "-hlsl", "-slang"); +} + +TestResult runHLSLCrossCompileRenderComparisonTest(TestInput& input) +{ + return runHLSLRenderComparisonTestImpl(input, "-slang", "-glsl-cross"); +} + +TestResult runTest( + String const& filePath, + TestOptions const& testOptions, + FileTestList const& testList) +{ + // based on command name, dispatch to an appropriate callback + static const struct TestCommands + { + char const* name; + TestCallback callback; + } kTestCommands[] = { + { "SIMPLE", &runSimpleTest }, + { "COMPARE_HLSL", &runHLSLComparisonTest }, + { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, + { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, + { "COMPARE_GLSL", &runGLSLComparisonTest }, + { nullptr, nullptr }, + }; + + for( auto ii = kTestCommands; ii->name; ++ii ) + { + if(testOptions.command != ii->name) + continue; + + TestInput testInput; + testInput.filePath = filePath; + testInput.testOptions = &testOptions; + testInput.testList = &testList; + + return ii->callback(testInput); + } + + // No actual test runner found! + + return kTestResult_Fail; +} + + +struct TestContext +{ + int totalTestCount; + int passedTestCount; + int failedTestCount; +}; + +void runTestsOnFile( + TestContext* context, + String filePath) +{ + // Gather a list of tests to run + FileTestList testList; + + if( gatherTestsForFile(filePath, &testList) == kTestResult_Ignored ) + { + // Test was explicitly ignored + return; + } + + // Note cases where a test file exists, but we found nothing to run + if( testList.tests.Count() == 0 ) + { + context->totalTestCount++; + context->failedTestCount++; + + printf("FAILED test: '%S' (no test commands found)\n", filePath.ToWString()); + return; + } + + // We have found a test to run! + int subTestCount = 0; + for( auto& tt : testList.tests ) + { + context->totalTestCount++; + + int subTestIndex = subTestCount++; + + TestResult result = runTest(filePath, tt, testList); + if(result == kTestResult_Ignored) + return; + + if (result == kTestResult_Pass) + { + printf("passed"); + context->passedTestCount++; + } + else + { + printf("FAILED"); + context->failedTestCount++; + } + + printf(" test: '%S'", filePath.ToWString()); + if( subTestIndex ) + { + printf(" subtest:%d", subTestIndex); + } + printf("\n"); + } +} + + +static bool endsWithAllowedExtension( + TestContext* context, + String filePath) +{ + char const* allowedExtensions[] = { + ".slang", + ".hlsl", + ".fx", + ".glsl", + ".vert", + ".frag", + ".geom", + ".tesc", + ".tese", + ".comp", + nullptr }; + + for( auto ii = allowedExtensions; *ii; ++ii ) + { + if(filePath.EndsWith(*ii)) + return true; + } + + return false; +} + +static bool shouldRunTest( + TestContext* context, + String filePath) +{ + if(!endsWithAllowedExtension(context, filePath)) + return false; + + if( options.testPrefix ) + { + if( strncmp(options.testPrefix, filePath.begin(), strlen(options.testPrefix)) != 0 ) + { + return false; + } + } + + return true; +} + +void runTestsInDirectory( + TestContext* context, + String directoryPath) +{ + for (auto file : osFindFilesInDirectory(directoryPath)) + { + if( shouldRunTest(context, file) ) + { + runTestsOnFile(context, file); + } + } + for (auto subdir : osFindChildDirectories(directoryPath)) + { + runTestsInDirectory(context, subdir); + } +} + +// + +int main( + int argc, + char** argv) +{ + parseOptions(&argc, argv); + + TestContext context = { 0 }; + + // Enumerate test files according to policy + // TODO: add more directories to this list + // TODO: allow for a command-line argument to select a particular directory + runTestsInDirectory(&context, "tests/"); + + if (!context.totalTestCount) + { + printf("no tests run\n"); + return 0; + } + + printf("\n===\n%d%% of tests passed (%d/%d)\n===\n\n", (context.passedTestCount*100) / context.totalTestCount, context.passedTestCount, context.totalTestCount); + return context.passedTestCount == context.totalTestCount ? 0 : 1; +} diff --git a/tools/slang-test/os.cpp b/tools/slang-test/os.cpp new file mode 100644 index 000000000..8f5172ae6 --- /dev/null +++ b/tools/slang-test/os.cpp @@ -0,0 +1,370 @@ +// os.cpp +#include "os.h" + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +using namespace CoreLib::Basic; + +// Platform-specific code follows + +#ifdef _WIN32 + +#include <Windows.h> + +static bool advance(OSFindFilesResult& result) +{ + return FindNextFileW(result.findHandle_, &result.fileData_) != 0; +} + +static bool adjustToValidResult(OSFindFilesResult& result) +{ + for (;;) + { + if ((result.fileData_.dwFileAttributes & result.requiredMask_) != result.requiredMask_) + goto skip; + + if ((result.fileData_.dwFileAttributes & result.disallowedMask_) != 0) + goto skip; + + if (wcscmp(result.fileData_.cFileName, L".") == 0) + goto skip; + + if (wcscmp(result.fileData_.cFileName, L"..") == 0) + goto skip; + + result.filePath_ = result.directoryPath_ + String::FromWString(result.fileData_.cFileName); + if (result.fileData_.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + result.filePath_ = result.filePath_ + "/"; + + return true; + + skip: + if (!advance(result)) + return false; + } +} + + +bool OSFindFilesResult::findNextFile() +{ + if (!advance(*this)) return false; + return adjustToValidResult(*this); +} + +OSFindFilesResult osFindFilesInDirectoryMatchingPattern( + CoreLib::Basic::String directoryPath, + CoreLib::Basic::String pattern) +{ + // TODO: add separator to end of directory path if needed + + String searchPath = directoryPath + pattern; + + OSFindFilesResult result; + HANDLE findHandle = FindFirstFileW( + searchPath.ToWString(), + &result.fileData_); + + result.directoryPath_ = directoryPath; + result.findHandle_ = findHandle; + result.requiredMask_ = 0; + result.disallowedMask_ = FILE_ATTRIBUTE_DIRECTORY; + + if (findHandle == INVALID_HANDLE_VALUE) + { + result.findHandle_ = NULL; + result.error_ = kOSError_FileNotFound; + return result; + } + + result.error_ = kOSError_None; + if (!adjustToValidResult(result)) + { + result.findHandle_ = NULL; + } + return result; +} + +OSFindFilesResult osFindFilesInDirectory( + CoreLib::Basic::String directoryPath) +{ + return osFindFilesInDirectoryMatchingPattern(directoryPath, "*"); +} + +OSFindFilesResult osFindChildDirectories( + CoreLib::Basic::String directoryPath) +{ + // TODO: add separator to end of directory path if needed + + String searchPath = directoryPath + "*"; + + OSFindFilesResult result; + HANDLE findHandle = FindFirstFileW( + searchPath.ToWString(), + &result.fileData_); + + result.directoryPath_ = directoryPath; + result.findHandle_ = findHandle; + result.requiredMask_ = FILE_ATTRIBUTE_DIRECTORY; + result.disallowedMask_ = 0; + + if (findHandle == INVALID_HANDLE_VALUE) + { + result.findHandle_ = NULL; + result.error_ = kOSError_FileNotFound; + return result; + } + + result.error_ = kOSError_None; + if (!adjustToValidResult(result)) + { + result.findHandle_ = NULL; + } + return result; +} + +// OSProcessSpawner + +struct OSProcessSpawner_ReaderThreadInfo +{ + HANDLE file; + String output; +}; + +static DWORD WINAPI osReaderThreadProc(LPVOID threadParam) +{ + OSProcessSpawner_ReaderThreadInfo* info = (OSProcessSpawner_ReaderThreadInfo*)threadParam; + HANDLE file = info->file; + + static const int kChunkSize = 1024; + char buffer[kChunkSize]; + + StringBuilder outputBuilder; + + // We need to re-write the output to deal with line + // endings, so we check for paired '\r' and '\n' + // characters, which may span chunks. + int prevChar = -1; + + for (;;) + { + DWORD bytesRead = 0; + BOOL readResult = ReadFile(file, buffer, kChunkSize, &bytesRead, nullptr); + + if (!readResult || GetLastError() == ERROR_BROKEN_PIPE) + { + break; + } + + // walk the buffer and rewrite to eliminate '\r' '\n' pairs + char* readCursor = buffer; + char const* end = buffer + bytesRead; + char* writeCursor = buffer; + + while (readCursor != end) + { + int p = prevChar; + int c = *readCursor++; + prevChar = c; + switch (c) + { + case '\r': case '\n': + // swallow input if '\r' and '\n' appear in sequence + if ((p ^ c) == ('\r' ^ '\n')) + { + // but don't swallow the next byte + prevChar = -1; + continue; + } + // always replace '\r' with '\n' + c = '\n'; + break; + + default: + break; + } + + *writeCursor++ = c; + } + bytesRead = (DWORD)(writeCursor - buffer); + + // Note: Current Slang CoreLib gives no way to know + // the length of the buffer, so we ultimately have + // to just assume null termination... + outputBuilder.Append(buffer, bytesRead); + } + + info->output = outputBuilder.ProduceString(); + + return 0; +} + +void OSProcessSpawner::pushExecutableName( + CoreLib::Basic::String executableName) +{ + executableName_ = executableName; + commandLine_.Append(executableName); +} + +void OSProcessSpawner::pushArgument( + CoreLib::Basic::String argument) +{ + // TODO(tfoley): handle cases where arguments need some escaping + commandLine_.Append(" "); + commandLine_.Append(argument); +} + +OSError OSProcessSpawner::spawnAndWaitForCompletion() +{ + SECURITY_ATTRIBUTES securityAttributes; + securityAttributes.nLength = sizeof(securityAttributes); + securityAttributes.lpSecurityDescriptor = nullptr; + securityAttributes.bInheritHandle = true; + + // create stdout pipe for child process + HANDLE childStdOutReadTmp = nullptr; + HANDLE childStdOutWrite = nullptr; + if (!CreatePipe(&childStdOutReadTmp, &childStdOutWrite, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + // create stderr pipe for child process + HANDLE childStdErrReadTmp = nullptr; + HANDLE childStdErrWrite = nullptr; + if (!CreatePipe(&childStdErrReadTmp, &childStdErrWrite, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + // create stdin pipe for child process + HANDLE childStdInRead = nullptr; + HANDLE childStdInWriteTmp = nullptr; + if (!CreatePipe(&childStdInRead, &childStdInWriteTmp, &securityAttributes, 0)) + { + return kOSError_OperationFailed; + } + + HANDLE currentProcess = GetCurrentProcess(); + + // create a non-inheritable duplicate of the stdout reader + HANDLE childStdOutRead = nullptr; + if (!DuplicateHandle( + currentProcess, childStdOutReadTmp, + currentProcess, &childStdOutRead, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdOutReadTmp)) + { + return kOSError_OperationFailed; + } + + // create a non-inheritable duplicate of the stderr reader + HANDLE childStdErrRead = nullptr; + if (!DuplicateHandle( + currentProcess, childStdErrReadTmp, + currentProcess, &childStdErrRead, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdErrReadTmp)) + { + return kOSError_OperationFailed; + } + + // create a non-inheritable duplicate of the stdin writer + HANDLE childStdInWrite = nullptr; + if (!DuplicateHandle( + currentProcess, childStdInWriteTmp, + currentProcess, &childStdInWrite, + 0, FALSE, DUPLICATE_SAME_ACCESS)) + { + return kOSError_OperationFailed; + } + if (!CloseHandle(childStdInWriteTmp)) + { + return kOSError_OperationFailed; + } + + // Now we can actually get around to starting a process + PROCESS_INFORMATION processInfo; + ZeroMemory(&processInfo, sizeof(processInfo)); + + // TODO: switch to proper wide-character versions of these... + STARTUPINFOW startupInfo; + ZeroMemory(&startupInfo, sizeof(startupInfo)); + startupInfo.cb = sizeof(startupInfo); + startupInfo.hStdError = childStdErrWrite; + startupInfo.hStdOutput = childStdOutWrite; + startupInfo.hStdInput = childStdInRead; + startupInfo.dwFlags = STARTF_USESTDHANDLES; + + // `CreateProcess` requires write access to this, for some reason... + BOOL success = CreateProcessW( + executableName_.ToWString(), + (LPWSTR)commandLine_.ToString().ToWString(), + nullptr, + nullptr, + true, + CREATE_NO_WINDOW, + nullptr, // TODO: allow specifying environment variables? + nullptr, + &startupInfo, + &processInfo); + if (!success) + { + return kOSError_OperationFailed; + } + + // close handles we are now done with + CloseHandle(processInfo.hThread); + CloseHandle(childStdOutWrite); + CloseHandle(childStdErrWrite); + CloseHandle(childStdInRead); + + // Create a thread to read from the child's stdout. + OSProcessSpawner_ReaderThreadInfo stdOutThreadInfo; + stdOutThreadInfo.file = childStdOutRead; + HANDLE stdOutThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdOutThreadInfo, 0, nullptr); + + // Create a thread to read from the child's stderr. + OSProcessSpawner_ReaderThreadInfo stdErrThreadInfo; + stdErrThreadInfo.file = childStdErrRead; + HANDLE stdErrThread = CreateThread(nullptr, 0, &osReaderThreadProc, (LPVOID)&stdErrThreadInfo, 0, nullptr); + + // wait for the process to exit + // TODO: set a timeout as a safety measure... + WaitForSingleObject(processInfo.hProcess, INFINITE); + + // get exit code for process + DWORD childExitCode = 0; + if (!GetExitCodeProcess(processInfo.hProcess, &childExitCode)) + { + return kOSError_OperationFailed; + } + + // wait for the reader threads + WaitForSingleObject(stdOutThread, INFINITE); + WaitForSingleObject(stdErrThread, INFINITE); + + CloseHandle(processInfo.hProcess); + CloseHandle(childStdOutRead); + CloseHandle(childStdErrRead); + CloseHandle(childStdInWrite); + + standardOutput_ = stdOutThreadInfo.output; + standardError_ = stdErrThreadInfo.output; + resultCode_ = childExitCode; + + return kOSError_None; +} + +#else + +// TODO(tfoley): write a default POSIX implementation + +#endif diff --git a/tools/slang-test/os.h b/tools/slang-test/os.h new file mode 100644 index 000000000..2996001e7 --- /dev/null +++ b/tools/slang-test/os.h @@ -0,0 +1,160 @@ +// os.h + +#include "../../source/core/slang-io.h" + +// This file encapsulates the platform-specific operations needed by the test +// runner that are not already provided by the core Slang libs + +#ifdef _WIN32 + +// Include Windows header in a way that minimized namespace pollution. +// TODO: We could try to avoid including this at all, but it would +// mean trying to hide certain struct layouts, which would add +// more dynamic allocation. +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include <Windows.h> +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#else +#endif + +// A simple set of error codes for possible runtime failures +enum OSError +{ + kOSError_None = 0, + kOSError_InvalidArgument, + kOSError_OperationFailed, + kOSError_FileNotFound, +}; + +// A helper type used during enumeration of files in a directory. +struct OSFindFilesResult +{ + CoreLib::Basic::String directoryPath_; + CoreLib::Basic::String filePath_; +#ifdef WIN32 + HANDLE findHandle_; + WIN32_FIND_DATAW fileData_; + DWORD requiredMask_; + DWORD disallowedMask_; + OSError error_; +#else +#endif + + bool findNextFile(); + + struct Iterator + { + OSFindFilesResult* context_; + + bool operator!=(Iterator other) const { return context_ != other.context_; } + void operator++() + { + if (!context_->findNextFile()) + { + context_ = NULL; + } + } + CoreLib::Basic::String const& operator*() const + { + return context_->filePath_; + } + }; + + Iterator begin() + { +#ifdef WIN32 + Iterator result = { findHandle_ ? this : NULL }; +#else +#endif + return result; + } + + Iterator end() + { + Iterator result = { NULL }; + return result; + } +}; + +// Enumerate subdirectories in the given `directoryPath` and return a logical +// collection of the results that can be iterated with a range-based +// `for` loop: +// +// for( auto subdir : osFindChildDirectories(dir)) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a subdirecotry of the directory. +OSFindFilesResult osFindChildDirectories( + CoreLib::Basic::String directoryPath); + +// Enumerate files in the given `directoryPath` that match the provided +// `pattern` as a simplified regex for files to return (e.g., "*.txt") +// and return a logical collection of the results +// that can be iterated with a range-based `for` loop: +// +// for( auto file : osFindFilesInDirectoryMatchingPattern(dir, "*.txt")) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a file in the directory. +OSFindFilesResult osFindFilesInDirectoryMatchingPattern( + CoreLib::Basic::String directoryPath, + CoreLib::Basic::String pattern); + +// Enumerate files in the given `directoryPath` and return a logical +// collection of the results that can be iterated with a range-based +// `for` loop: +// +// for( auto file : osFindFilesInDirectory(dir)) +// { ... } +// +// Each element in the range is a `CoreLib::Basic::String` representing the +// path to a file in the directory. +OSFindFilesResult osFindFilesInDirectory( + CoreLib::Basic::String directoryPath); + + +// An `OSProcessSpawner` can be used to launch a process, and handles +// putting together the arguments in the form required by the target +// platform, as well as capturing any output from the process (both +// standard output and standard error) as strings. +struct OSProcessSpawner +{ + // Set the executable name for the process to be spawned. + // Note: this call must be made before any arguments are pushed. + void pushExecutableName( + CoreLib::Basic::String executableName); + + // Append an argument for the process to be spawned. + void pushArgument( + CoreLib::Basic::String argument); + + // Attempt to spawn the process, and wait for it to complete. + // Returns an error if the attempt to spawn and/or wait fails, + // but returns `kOSError_None` if the process is run to completion, + // whether or not the process returns "successfully" (with a zero + // result code); + OSError spawnAndWaitForCompletion(); + + // If the process is successfully spawned and completes, then + // the user can query the result code that the process produce + // on exit, along with the output it wrote to stdout and stderr. + typedef int ResultCode; + ResultCode getResultCode() { return resultCode_; } + CoreLib::Basic::String const& getStandardOutput() { return standardOutput_; } + CoreLib::Basic::String const& getStandardError() { return standardError_; } + + // "private" data follows + CoreLib::Basic::String standardOutput_; + CoreLib::Basic::String standardError_; + ResultCode resultCode_; +#ifdef WIN32 + CoreLib::Basic::String executableName_; + CoreLib::Basic::StringBuilder commandLine_; +#else +#endif +}; diff --git a/tools/slang-test/slang-test.vcxproj b/tools/slang-test/slang-test.vcxproj new file mode 100644 index 000000000..b8d23d569 --- /dev/null +++ b/tools/slang-test/slang-test.vcxproj @@ -0,0 +1,277 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug_VS2013|Win32"> + <Configuration>Debug_VS2013</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug_VS2013|x64"> + <Configuration>Debug_VS2013</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release_VS2013|Win32"> + <Configuration>Release_VS2013</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release_VS2013|x64"> + <Configuration>Release_VS2013</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectGuid>{0C768A18-1D25-4000-9F37-DA5FE99E3B64}</ProjectGuid> + <Keyword>Win32Proj</Keyword> + <RootNamespace>SpireTestTool</RootNamespace> + <WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>true</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v140</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'" Label="Configuration"> + <ConfigurationType>Application</ConfigurationType> + <UseDebugLibraries>false</UseDebugLibraries> + <PlatformToolset>v120</PlatformToolset> + <WholeProgramOptimization>true</WholeProgramOptimization> + <CharacterSet>Unicode</CharacterSet> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <PlatformToolset>v140</PlatformToolset> + </PropertyGroup> + <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <PlatformToolset>v120</PlatformToolset> + </PropertyGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" /> + <ImportGroup Label="ExtensionSettings"> + </ImportGroup> + <ImportGroup Label="Shared"> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'" Label="PropertySheets"> + <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" /> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <Import Project="..\..\build\slang-build.props" /> + </ImportGroup> + <PropertyGroup Label="UserMacros" /> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <LinkIncremental>true</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'"> + <LinkIncremental>false</LinkIncremental> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|Win32'"> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <IncludePath>$(SolutionDir);$(IncludePath)</IncludePath> + </PropertyGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> + <ClCompile> + <PrecompiledHeader> + </PrecompiledHeader> + <WarningLevel>Level3</WarningLevel> + <Optimization>Disabled</Optimization> + <PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|Win32'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release_VS2013|x64'"> + <ClCompile> + <WarningLevel>Level3</WarningLevel> + <PrecompiledHeader> + </PrecompiledHeader> + <Optimization>MaxSpeed</Optimization> + <FunctionLevelLinking>true</FunctionLevelLinking> + <IntrinsicFunctions>true</IntrinsicFunctions> + <PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions> + <RuntimeLibrary>MultiThreaded</RuntimeLibrary> + </ClCompile> + <Link> + <SubSystem>Console</SubSystem> + <EnableCOMDATFolding>true</EnableCOMDATFolding> + <OptimizeReferences>true</OptimizeReferences> + <GenerateDebugInformation>true</GenerateDebugInformation> + </Link> + </ItemDefinitionGroup> + <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug_VS2013|x64'"> + <ClCompile> + <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> + <Optimization>Disabled</Optimization> + </ClCompile> + </ItemDefinitionGroup> + <ItemGroup> + <ClCompile Include="main.cpp" /> + <ClCompile Include="os.cpp" /> + </ItemGroup> + <ItemGroup> + <ClInclude Include="os.h" /> + </ItemGroup> + <ItemGroup> + <ProjectReference Include="..\..\source\core\core.vcxproj"> + <Project>{f9be7957-8399-899e-0c49-e714fddd4b65}</Project> + </ProjectReference> + </ItemGroup> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" /> + <ImportGroup Label="ExtensionTargets"> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/tools/slang-test/slang-test.vcxproj.filters b/tools/slang-test/slang-test.vcxproj.filters new file mode 100644 index 000000000..3549e6046 --- /dev/null +++ b/tools/slang-test/slang-test.vcxproj.filters @@ -0,0 +1,30 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup> + <Filter Include="Source Files"> + <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier> + <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions> + </Filter> + <Filter Include="Header Files"> + <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier> + <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions> + </Filter> + <Filter Include="Resource Files"> + <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier> + <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions> + </Filter> + </ItemGroup> + <ItemGroup> + <ClCompile Include="main.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + <ClCompile Include="os.cpp"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="os.h"> + <Filter>Header Files</Filter> + </ClInclude> + </ItemGroup> +</Project>
\ No newline at end of file |
