summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-06-09 11:34:21 -0700
committerTim Foley <tfoley@nvidia.com>2017-06-09 13:44:59 -0700
commitfcf83dbf9effab3bd98bad2b83b2468b7eb05cfd (patch)
tree41047c94883b86ec085a81597391ce3ef557cd43 /tools
parent52e8d4b9a27ab0060f874c3a63ab531847be35c0 (diff)
Initial import of code.
Diffstat (limited to 'tools')
-rw-r--r--tools/glslang/glslang.cpp175
-rw-r--r--tools/glslang/glslang.h23
-rw-r--r--tools/glslang/glslang.vcxproj237
-rw-r--r--tools/glslang/glslang.vcxproj.filters253
-rw-r--r--tools/render-test/README.md4
-rw-r--r--tools/render-test/main.cpp369
-rw-r--r--tools/render-test/options.cpp95
-rw-r--r--tools/render-test/options.h41
-rw-r--r--tools/render-test/render-d3d11.cpp900
-rw-r--r--tools/render-test/render-d3d11.h10
-rw-r--r--tools/render-test/render-gl.cpp242
-rw-r--r--tools/render-test/render-gl.h10
-rw-r--r--tools/render-test/render-test.vcxproj170
-rw-r--r--tools/render-test/render-test.vcxproj.filters54
-rw-r--r--tools/render-test/render.h118
-rw-r--r--tools/render-test/slang-support.cpp84
-rw-r--r--tools/render-test/slang-support.h12
-rw-r--r--tools/render-test/window.h10
-rw-r--r--tools/slang-test/main.cpp1030
-rw-r--r--tools/slang-test/os.cpp370
-rw-r--r--tools/slang-test/os.h160
-rw-r--r--tools/slang-test/slang-test.vcxproj277
-rw-r--r--tools/slang-test/slang-test.vcxproj.filters30
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