diff options
| -rw-r--r-- | source/core/core.vcxproj | 1 | ||||
| -rw-r--r-- | source/core/slang-result.h | 130 | ||||
| -rw-r--r-- | tools/render-test/main.cpp | 293 | ||||
| -rw-r--r-- | tools/render-test/options.cpp | 13 | ||||
| -rw-r--r-- | tools/render-test/options.h | 11 | ||||
| -rw-r--r-- | tools/render-test/render-d3d11.cpp | 109 | ||||
| -rw-r--r-- | tools/render-test/render-d3d12.cpp | 61 | ||||
| -rw-r--r-- | tools/render-test/render-gl.cpp | 22 | ||||
| -rw-r--r-- | tools/render-test/render-vk.cpp | 50 | ||||
| -rw-r--r-- | tools/render-test/render.h | 6 | ||||
| -rw-r--r-- | tools/render-test/slang-support.cpp | 1 | ||||
| -rw-r--r-- | tools/slang-test/README.md | 84 | ||||
| -rw-r--r-- | tools/slang-test/main.cpp | 147 |
13 files changed, 567 insertions, 361 deletions
diff --git a/source/core/core.vcxproj b/source/core/core.vcxproj index c37f2fad2..96f97120e 100644 --- a/source/core/core.vcxproj +++ b/source/core/core.vcxproj @@ -32,6 +32,7 @@ <ClInclude Include="secure-crt.h" /> <ClInclude Include="slang-io.h" /> <ClInclude Include="slang-math.h" /> + <ClInclude Include="slang-result.h" /> <ClInclude Include="slang-string.h" /> <ClInclude Include="smart-pointer.h" /> <ClInclude Include="stream.h" /> diff --git a/source/core/slang-result.h b/source/core/slang-result.h new file mode 100644 index 000000000..da88c18b2 --- /dev/null +++ b/source/core/slang-result.h @@ -0,0 +1,130 @@ +#ifndef SLANG_RESULT_H +#define SLANG_RESULT_H + +#include <cstdint> +#include <assert.h> + +/*! SlangResult is a type that represents the status result after a call to a function/method. It can represent if the call was successful or +not. It can also specify in an extensible manner what facility produced the result (as the integral 'facility') as well as what caused it (as an integral 'code'). +Under the covers SlangResult is represented as a int32_t. A negative value indicates an error, a positive (or 0) value indicates success. + +SlangResult is designed to be compatible with COM HRESULT. + +It's layout in bits is as follows + +Severity | Facility | Code +---------|----------|----- +31 | 30-16 | 15-0 + +Severity - 1 fail, 0 is success. +Facility is where the error originated from +Code is the code specific to the facility. + +The layout is designed such that failure is a negative number, and success is positive due to Result +being represented by an Int32. + +Result codes have the following style + +1. SLANG_name +2. SLANG_s_f_name +3. SLANG_s_name + +where s is severity as a single letter S - success, and E for error +Style 1 is reserved for SLANG_OK and SLANG_FAIL as they are so common and not tied to a facility + +s is S for success, E for error +f is the short version of the facility name + +For the common used SLANG_OK and SLANG_FAIL, the name prefix is dropped. +It is acceptable to expand 'f' to a longer name to differentiate a name +ie for a facility 'DRIVER' it might make sense to have an error of the form SLANG_E_DRIVER_OUT_OF_MEMORY +*/ + +typedef int32_t SlangResult; + +// Make just the identifier for the code +#define SLANG_MAKE_RESULT_ID(fac, code) ((((int32_t)(fac))<<16) | ((int32_t)(code))) + +//! Make a result +#define SLANG_MAKE_RESULT(sev, fac, code) ((((int32_t)(sev))<<31) | SLANG_MAKE_RESULT_ID(fac, code)) + +//! Will be 0 - for ok, 1 for failure +#define SLANG_GET_RESULT_SEVERITY(r) ((int32_t)(((uint32_t(r)) >> 31)) +//! Get the facility the result is associated with +#define SLANG_GET_RESULT_FACILITY(r) ((int32_t)(((r) >> 16) & 0x7fff)) +//! Get the result code for the facility +#define SLANG_GET_RESULT_CODE(r) ((int32_t)((r) & 0xffff)) + +#define SLANG_MAKE_ERROR(fac, code) (SLANG_MAKE_RESULT_ID(SLANG_FACILITY_##fac, code) | 0x80000000) +#define SLANG_MAKE_SUCCESS(fac, code) SLANG_MAKE_RESULT_ID(SLANG_FACILITY_##fac, code) + +/*************************** Facilities ************************************/ + +//! General - careful to make compatible with HRESULT +#define SLANG_FACILITY_GENERAL 0 + +//! Base facility -> so as to not clash with HRESULT values +#define SLANG_FACILITY_BASE 0x100 + +/*! Facilities numbers must be unique across a project to make the resulting result a unique number! +It can be useful to have a consistent short name for a facility, as used in the name prefix */ +#define SLANG_FACILITY_DISK (SLANG_FACILITY_BASE + 1) +#define SLANG_FACILITY_INTERFACE (SLANG_FACILITY_BASE + 2) +#define SLANG_FACILITY_UNKNOWN (SLANG_FACILITY_BASE + 3) +#define SLANG_FACILITY_MEMORY (SLANG_FACILITY_BASE + 4) +#define SLANG_FACILITY_MISC (SLANG_FACILITY_BASE + 5) + +/// Base for external facilities. Facilities should be unique across modules. +#define SLANG_FACILITY_EXTERNAL_BASE 0x210 +#define SLANG_FACILITY_CORE (SLANG_FACILITY_EXTERNAL_BASE + 1) + +/* *************************** Codes **************************************/ + +// Memory +#define SLANG_E_MEM_OUT_OF_MEMORY SLANG_MAKE_ERROR(MEMORY, 1) +#define SLANG_E_MEM_BUFFER_TOO_SMALL SLANG_MAKE_ERROR(MEMORY, 2) + +//! SLANG_OK indicates success, and is equivalent to SLANG_MAKE_RESULT(0, GENERAL, 0) +#define SLANG_OK 0 +//! SLANG_FAIL is the generic failure code - meaning a serious error occurred and the call couldn't complete +#define SLANG_FAIL SLANG_MAKE_ERROR(GENERAL, 1) + +//! Used to identify a Result that has yet to be initialized. +//! It defaults to failure such that if used incorrectly will fail, as similar in concept to using an uninitialized variable. +#define SLANG_E_MISC_UNINITIALIZED SLANG_MAKE_ERROR(MISC, 2) +//! Returned from an async method meaning the output is invalid (thus an error), but a result for the request is pending, and will be returned on a subsequent call with the async handle. +#define SLANG_E_MISC_PENDING SLANG_MAKE_ERROR(MISC, 3) +//! Indicates that a handle passed in as parameter to a method is invalid. +#define SLANG_E_MISC_INVALID_HANDLE SLANG_MAKE_ERROR(MISC, 4) + +/*! Set SLANG_HANDLE_RESULT_FAIL(x) to code to be executed whenever an error occurs, and is detected by one of the macros */ +#ifndef SLANG_HANDLE_RESULT_FAIL +# define SLANG_HANDLE_RESULT_FAIL(x) +#endif + +/* !!!!!!!!!!!!!!!!!!!!!!!!! Checking codes !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */ + +//! Use to test if a result was failure. Never use result != SLANG_OK to test for failure, as there may be successful codes != SLANG_OK. +#define SLANG_FAILED(status) ((status) < 0) +//! Use to test if a result succeeded. Never use result == SLANG_OK to test for success, as will detect other successful codes as a failure. +#define SLANG_SUCCEEDED(status) ((status) >= 0) + +//! Helper macro, that makes it easy to add result checking to calls in functions/methods that themselves return Result. +#define SLANG_RETURN_ON_FAIL(x) { SlangResult _res = (x); if (SLANG_FAILED(_res)) { SLANG_HANDLE_RESULT_FAIL(_res); return _res; } } +//! Helper macro that can be used to test the return value from a call, and will return in a void method/function +#define SLANG_RETURN_VOID_ON_FAIL(x) { SlangResult _res = (x); if (SLANG_FAILED(_res)) { SLANG_HANDLE_RESULT_FAIL(_res); return; } } +//! Helper macro that will return false on failure. +#define SLANG_RETURN_FALSE_ON_FAIL(x) { SlangResult _res = (x); if (SLANG_FAILED(_res)) { SLANG_HANDLE_RESULT_FAIL(_res); return false; } } + +//! Helper macro that will assert if the return code from a call is failure, also returns the failure. +#define SLANG_ASSERT_ON_FAIL(x) { SlangResult _res = (x); if (SLANG_FAILED(_res)) { assert(false); return _res; } } +//! Helper macro that will assert if the result from a call is a failure, also returns. +#define SLANG_ASSERT_VOID_ON_FAIL(x) { SlangResult _res = (x); if (SLANG_FAILED(_res)) { assert(false); return; } } + +#if defined(__cplusplus) +namespace Slang { +typedef SlangResult Result; +} // namespace Slang +#endif // defined(__cplusplus) + +#endif // SLANG_RESULT_H
\ No newline at end of file diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp index 3255f4b9b..89aa9a544 100644 --- a/tools/render-test/main.cpp +++ b/tools/render-test/main.cpp @@ -5,7 +5,9 @@ #include "render-d3d11.h" #include "render-gl.h" #include "render-vk.h" + #include "slang-support.h" + #include "shader-input-layout.h" #include <stdio.h> #include <stdlib.h> @@ -22,9 +24,6 @@ namespace renderer_test { -// - - int gWindowWidth = 1024; int gWindowHeight = 768; @@ -71,7 +70,7 @@ static char const* vertexProfileName = "vs_5_0"; static char const* fragmentProfileName = "ps_5_0"; static char const* computeProfileName = "cs_5_0"; -Error initializeShaders( +SlangResult initializeShaders( ShaderCompiler* shaderCompiler) { // Read in the source code @@ -80,7 +79,7 @@ Error initializeShaders( if( !sourceFile ) { fprintf(stderr, "error: failed to open '%s' for reading\n", sourcePath); - exit(1); + return SLANG_FAIL; } fseek(sourceFile, 0, SEEK_END); size_t sourceSize = ftell(sourceFile); @@ -89,7 +88,7 @@ Error initializeShaders( if( !sourceText ) { fprintf(stderr, "error: out of memory"); - exit(1); + return SLANG_FAIL; } fread(sourceText, sourceSize, 1, sourceFile); fclose(sourceFile); @@ -123,23 +122,20 @@ Error initializeShaders( gShaderProgram = shaderCompiler->compileProgram(compileRequest); if( !gShaderProgram ) { - return Error::Unexpected; + return SLANG_FAIL; } - return Error::None; + return SLANG_OK; } // // 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( +SlangResult initializeInner( Renderer* renderer, ShaderCompiler* shaderCompiler) { - Error err = Error::None; - - err = initializeShaders(shaderCompiler); - if(err != Error::None) return err; + SLANG_RETURN_ON_FAIL(initializeShaders(shaderCompiler)); gBindingState = renderer->createBindingState(gShaderInputLayout); @@ -154,8 +150,8 @@ Error initializeInner( gConstantBuffer = renderer->createBuffer(constantBufferDesc); if(!gConstantBuffer) - return Error::Unexpected; - + return SLANG_FAIL; + // Input Assembler (IA) InputElementDesc inputElements[] = { @@ -166,7 +162,7 @@ Error initializeInner( gInputLayout = renderer->createInputLayout(&inputElements[0], sizeof(inputElements)/sizeof(inputElements[0])); if(!gInputLayout) - return Error::Unexpected; + return SLANG_FAIL; BufferDesc vertexBufferDesc; vertexBufferDesc.size = kVertexCount * sizeof(Vertex); @@ -175,9 +171,9 @@ Error initializeInner( gVertexBuffer = renderer->createBuffer(vertexBufferDesc); if(!gVertexBuffer) - return Error::Unexpected; + return SLANG_FAIL; - return Error::None; + return SLANG_OK; } void renderFrameInner( @@ -248,139 +244,123 @@ static LRESULT CALLBACK windowProc( } -} // renderer_test - - -// - -int main( - int argc, - char** argv) +SlangResult innerMain(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; - } + // Parse command-line options + SLANG_RETURN_ON_FAIL(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 SLANG_FAIL; + } - // Next, we create a window using that window class. - - // We will create a borderless window since our screen-capture logic in GL - // seems to get thrown off by having to deal with a window frame. - DWORD windowStyle = WS_POPUP; - DWORD windowExtendedStyle = 0; - - - RECT windowRect = { 0, 0, gWindowWidth, gWindowHeight }; - AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); - - auto width = windowRect.right - windowRect.left; - auto height = windowRect.bottom - windowRect.top; - - LPWSTR windowName = L"Slang Render Test"; - HWND windowHandle = CreateWindowExW( - windowExtendedStyle, - (LPWSTR)windowClassAtom, - windowName, - windowStyle, - 0, 0, // x, y - width, height, - NULL, // parent - NULL, // menu - instance, - NULL); - if(!windowHandle) - { - fprintf(stderr, "error: failed to create window\n"); - return 1; - } + // Next, we create a window using that window class. + + // We will create a borderless window since our screen-capture logic in GL + // seems to get thrown off by having to deal with a window frame. + DWORD windowStyle = WS_POPUP; + DWORD windowExtendedStyle = 0; + + + RECT windowRect = { 0, 0, gWindowWidth, gWindowHeight }; + AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); + + auto width = windowRect.right - windowRect.left; + auto height = windowRect.bottom - windowRect.top; + + LPWSTR windowName = L"Slang Render Test"; + HWND windowHandle = CreateWindowExW( + windowExtendedStyle, + (LPWSTR)windowClassAtom, + windowName, + windowStyle, + 0, 0, // x, y + width, height, + NULL, // parent + NULL, // menu + instance, + NULL); + if (!windowHandle) + { + fprintf(stderr, "error: failed to create window\n"); + return SLANG_FAIL; + } - Renderer* renderer = nullptr; - SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; - SlangCompileTarget slangTarget = SLANG_TARGET_NONE; - switch( gOptions.rendererID ) - { - case RendererID::D3D11: - renderer = createD3D11Renderer(); - slangTarget = SLANG_HLSL; - nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; - break; - - // TODO: `RendererID::D3D12` - - case RendererID::GL: - renderer = createGLRenderer(); - slangTarget = SLANG_GLSL; - nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; - break; - - case RendererID::VK: - renderer = createVKRenderer(); - slangTarget = SLANG_SPIRV; - nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; - break; - - default: - fprintf(stderr, "error: unexpected\n"); - exit(1); - break; - } + Renderer* renderer = nullptr; + SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; + SlangCompileTarget slangTarget = SLANG_TARGET_NONE; + switch (gOptions.rendererID) + { + case RendererID::D3D11: + renderer = createD3D11Renderer(); + slangTarget = SLANG_HLSL; + nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; + break; + + // TODO: `RendererID::D3D12` + + case RendererID::GL: + renderer = createGLRenderer(); + slangTarget = SLANG_GLSL; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; + break; + + case RendererID::VK: + renderer = createVKRenderer(); + slangTarget = SLANG_SPIRV; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; + break; + + default: + fprintf(stderr, "error: unexpected\n"); + return SLANG_FAIL; + } - renderer->initialize(windowHandle); + SLANG_RETURN_ON_FAIL(renderer->initialize(windowHandle)); - auto shaderCompiler = renderer->getShaderCompiler(); - switch( gOptions.inputLanguageID ) - { - case InputLanguageID::Slang: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, slangTarget); - break; + auto shaderCompiler = renderer->getShaderCompiler(); + switch (gOptions.inputLanguageID) + { + case InputLanguageID::Slang: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, SLANG_SOURCE_LANGUAGE_SLANG, slangTarget); + break; - case InputLanguageID::NativeRewrite: - shaderCompiler = createSlangShaderCompiler(shaderCompiler, nativeLanguage, slangTarget); - break; + case InputLanguageID::NativeRewrite: + shaderCompiler = createSlangShaderCompiler(shaderCompiler, nativeLanguage, slangTarget); + break; - default: - break; - } + default: + break; + } - Error err = initializeInner(renderer, shaderCompiler); - if( err != Error::None ) - { - exit(1); - } + SLANG_RETURN_ON_FAIL(initializeInner(renderer, shaderCompiler)); + // Once initialization is all complete, we show the window... + ShowWindow(windowHandle, showCommand); - // Once initialization is all complete, we show the window... - ShowWindow(windowHandle, showCommand); - // ... and enter the event loop: for (;;) { @@ -391,7 +371,7 @@ int main( { if (message.message == WM_QUIT) { - return (int)message.wParam; + return (int)message.wParam; } TranslateMessage(&message); @@ -399,35 +379,42 @@ int main( } else { - // Whenver we don't have Windows events to process, - // we render a frame. - + // Whenever we don't have Windows events to process, we render a frame. if (gOptions.shaderType == ShaderProgramType::Compute) { runCompute(renderer); } else { - static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; - renderer->setClearColor(kClearColor); - renderer->clearFrame(); + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + renderer->setClearColor(kClearColor); + 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) { - if (gOptions.shaderType == ShaderProgramType::Compute || gOptions.shaderType == ShaderProgramType::GraphicsCompute) - renderer->serializeOutput(gBindingState, gOptions.outputPath); - else - renderer->captureScreenShot(gOptions.outputPath); - return 0; + if (gOptions.shaderType == ShaderProgramType::Compute || gOptions.shaderType == ShaderProgramType::GraphicsCompute) + renderer->serializeOutput(gBindingState, gOptions.outputPath); + else + SLANG_RETURN_ON_FAIL(renderer->captureScreenShot(gOptions.outputPath)); + return SLANG_OK; } renderer->presentFrame(); } } - return 0; + return SLANG_OK; +} + +} // renderer_test + +int main(int argc, char** argv) +{ + SlangResult res = renderer_test::innerMain(argc, argv); + + return SLANG_FAILED(res) ? 1 : 0; } diff --git a/tools/render-test/options.cpp b/tools/render-test/options.cpp index 45a94e137..30aeca6b0 100644 --- a/tools/render-test/options.cpp +++ b/tools/render-test/options.cpp @@ -10,7 +10,7 @@ namespace renderer_test { Options gOptions; -void parseOptions(int* argc, char** argv) +SlangResult parseOptions(int* argc, char** argv) { int argCount = *argc; char const* const* argCursor = argv; @@ -48,7 +48,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "expected argument for '%s' option\n", arg); - exit(1); + return SLANG_FAIL; } gOptions.outputPath = *argCursor++; } @@ -89,12 +89,12 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "expected argument for '%s' option\n", arg); - exit(1); + return SLANG_FAIL; } if( gOptions.slangArgCount == kMaxSlangArgs ) { fprintf(stderr, "maximum number of '%s' options exceeded (%d)\n", arg, kMaxSlangArgs); - exit(1); + return SLANG_FAIL; } gOptions.slangArgs[gOptions.slangArgCount++] = *argCursor++; } @@ -123,7 +123,7 @@ void parseOptions(int* argc, char** argv) else { fprintf(stderr, "unknown option '%s'\n", arg); - exit(1); + return SLANG_FAIL; } } @@ -142,10 +142,11 @@ void parseOptions(int* argc, char** argv) if(argCursor != argEnd) { fprintf(stderr, "unexpected arguments\n"); - exit(1); + return SLANG_FAIL; } *argc = 0; + return SLANG_OK; } } // renderer_test diff --git a/tools/render-test/options.h b/tools/render-test/options.h index b48295878..a33172c6b 100644 --- a/tools/render-test/options.h +++ b/tools/render-test/options.h @@ -3,6 +3,8 @@ #include <stdint.h> +#include "../../source/core/slang-result.h" + namespace renderer_test { typedef intptr_t Int; @@ -61,13 +63,6 @@ extern int gWindowWidth; extern int gWindowHeight; -void parseOptions(int* argc, char** argv); - -enum class Error -{ - None = 0, - InvalidParam, - Unexpected, -}; +SlangResult parseOptions(int* argc, char** argv); } // renderer_test diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp index 690e6299f..ba8d9e46d 100644 --- a/tools/render-test/render-d3d11.cpp +++ b/tools/render-test/render-d3d11.cpp @@ -111,14 +111,14 @@ ID3DBlob* compileHLSLShader( if(!d3dcompiler) { fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); - exit(1); + return nullptr; } D3DCompile_ = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile"); if( !D3DCompile_ ) { fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); - exit(1); + return nullptr; } } @@ -193,7 +193,7 @@ static HRESULT captureTextureToFile( D3D11_TEXTURE2D_DESC dxTextureDesc; dxTexture->GetDesc(&dxTextureDesc); - // Don't bother supporing MSAA for right now + // Don't bother supporting MSAA for right now if( dxTextureDesc.SampleDesc.Count > 1 ) { fprintf(stderr, "ERROR: cannot capture multisample texture\n"); @@ -261,14 +261,14 @@ static HRESULT captureTextureToFile( class D3D11Renderer : public Renderer, public ShaderCompiler { public: - IDXGISwapChain* dxSwapChain = NULL; - ID3D11Device* dxDevice = NULL; - ID3D11DeviceContext* dxImmediateContext = NULL; - ID3D11Texture2D* dxBackBufferTexture = NULL; + IDXGISwapChain* dxSwapChain = nullptr; + ID3D11Device* dxDevice = nullptr; + ID3D11DeviceContext* dxImmediateContext = nullptr; + ID3D11Texture2D* dxBackBufferTexture = nullptr; List<ID3D11RenderTargetView*> dxRenderTargetViews; List<ID3D11Texture2D *> dxRenderTargetTextures; D3DBindingState * currentBindings = nullptr; - virtual void initialize(void* inWindowHandle) override + virtual SlangResult initialize(void* inWindowHandle) override { auto windowHandle = (HWND) inWindowHandle; // Rather than statically link against D3D, we load it dynamically. @@ -277,18 +277,16 @@ public: if(!d3d11) { fprintf(stderr, "error: failed load 'd3d11.dll'\n"); - exit(1); + return SLANG_FAIL; } PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = - (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress( - d3d11, - "D3D11CreateDeviceAndSwapChain"); + (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress(d3d11, "D3D11CreateDeviceAndSwapChain"); if(!D3D11CreateDeviceAndSwapChain_) { fprintf(stderr, "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); - exit(1); + return SLANG_FAIL; } // We create our device in debug mode, just so that we can check that the @@ -296,19 +294,7 @@ public: 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 }; @@ -328,6 +314,19 @@ public: dxSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; dxSwapChainDesc.Flags = 0; + // 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; + const int totalNumFeatureLevels = sizeof(featureLevels) / sizeof(featureLevels[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 @@ -335,17 +334,16 @@ public: // 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_( + const HRESULT hr = D3D11CreateDeviceAndSwapChain_( NULL, // adapter (use default) D3D_DRIVER_TYPE_REFERENCE, //D3D_DRIVER_TYPE_HARDWARE, NULL, // software deviceFlags, &featureLevels[ii], - (sizeof(featureLevels) / sizeof(featureLevels[0])) - 1, + totalNumFeatureLevels - ii, D3D11_SDK_VERSION, &dxSwapChainDesc, &dxSwapChain, @@ -353,16 +351,19 @@ public: &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); + // Failures with `E_INVALIDARG` might be due to feature level 11_1 + // not being supported. + if (hr == E_INVALIDARG) + { + continue; + } + + // Other failures are real, though. + SLANG_RETURN_ON_FAIL(hr); + // We must have a swap chain + break; } - + // 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. @@ -370,35 +371,28 @@ public: 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, 0x95, 0x35, 0xd3, 0x4f, 0x9c }; - dxSwapChain->GetBuffer( - 0, - kIID_ID3D11Texture2D, - (void**)&dxBackBufferTexture); + SLANG_RETURN_ON_FAIL(dxSwapChain->GetBuffer(0, kIID_ID3D11Texture2D, (void**)&dxBackBufferTexture)); for (int i = 0; i < 8; i++) { ID3D11Texture2D* texture; D3D11_TEXTURE2D_DESC textureDesc; dxBackBufferTexture->GetDesc(&textureDesc); - dxDevice->CreateTexture2D(&textureDesc, nullptr, &texture); + SLANG_RETURN_ON_FAIL(dxDevice->CreateTexture2D(&textureDesc, nullptr, &texture)); + ID3D11RenderTargetView * rtv; D3D11_RENDER_TARGET_VIEW_DESC rtvDesc; rtvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; rtvDesc.Texture2D.MipSlice = 0; rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D; - dxDevice->CreateRenderTargetView( - texture, - &rtvDesc, - &rtv); + SLANG_RETURN_ON_FAIL(dxDevice->CreateRenderTargetView(texture, &rtvDesc, &rtv)); + dxRenderTargetViews.Add(rtv); dxRenderTargetTextures.Add(texture); } - dxImmediateContext->OMSetRenderTargets( - (UINT)dxRenderTargetViews.Count(), - dxRenderTargetViews.Buffer(), - NULL); - + dxImmediateContext->OMSetRenderTargets((UINT)dxRenderTargetViews.Count(), dxRenderTargetViews.Buffer(), nullptr); + // Similarly, we are going to set up a viewport once, and then never // switch, since this is a simple test app. D3D11_VIEWPORT dxViewport; @@ -409,6 +403,8 @@ public: dxViewport.MaxDepth = 1; // TODO(tfoley): use reversed depth dxViewport.MinDepth = 0; dxImmediateContext->RSSetViewports(1, &dxViewport); + + return SLANG_OK; } float clearColor[4] = { 0, 0, 0, 0 }; @@ -431,7 +427,7 @@ public: dxSwapChain->Present(0, 0); } - virtual void captureScreenShot(char const* outputPath) override + virtual SlangResult captureScreenShot(char const* outputPath) override { HRESULT hr = captureTextureToFile( dxDevice, @@ -441,8 +437,9 @@ public: if( FAILED(hr) ) { fprintf(stderr, "error: could not capture screenshot to '%s'\n", outputPath); - exit(1); - } + SLANG_RETURN_ON_FAIL(hr); + } + return SLANG_OK; } virtual ShaderCompiler* getShaderCompiler() override @@ -535,7 +532,7 @@ public: hlslCursor+= sprintf(hlslCursor, ",\n"); } - char const* typeName = "Uknown"; + char const* typeName = "Unknown"; switch(inputElements[ii].format) { case Format::RGB_Float32: diff --git a/tools/render-test/render-d3d12.cpp b/tools/render-test/render-d3d12.cpp index 2308c601a..f0b0eb478 100644 --- a/tools/render-test/render-d3d12.cpp +++ b/tools/render-test/render-d3d12.cpp @@ -39,6 +39,7 @@ namespace renderer_test { // The Slang compiler currently generates HLSL source, so we'll need a utility // routine (defined later) to translate that into D3D11 shader bytecode. +// Returns nullptr if compilation fails. ID3DBlob* compileHLSLShader( char const* sourcePath, char const* source, @@ -53,9 +54,9 @@ static char const* fragmentProfileName = "ps_4_0"; class D3D12Renderer : public Renderer, public ShaderCompiler { public: - IDXGISwapChain* dxSwapChain = NULL; + IDXGISwapChain* dxSwapChain = nullptr; - ID3D12Device* dxDevice = NULL; + ID3D12Device* dxDevice = nullptr; virtual PROC loadProc( HMODULE module, @@ -66,17 +67,12 @@ public: { fprintf(stderr, "error: failed load symbol '%s'\n", name); - exit(1); + return nullptr; } return proc; } - void checkResult(HRESULT result) - { - assert(SUCCEEDED(result)); - } - - virtual void initialize(void* inWindowHandle) override + virtual SlangResult initialize(void* inWindowHandle) override { auto windowHandle = (HWND) inWindowHandle; // Rather than statically link against D3D, we load it dynamically. @@ -85,12 +81,12 @@ public: if(!d3d12) { fprintf(stderr, "error: failed load 'd3d12.dll'\n"); - exit(1); + return SLANG_FAIL; } #define LOAD_PROC(TYPE, NAME) \ - TYPE NAME##_ = (TYPE) loadProc(d3d12, #NAME) - + TYPE NAME##_ = (TYPE) loadProc(d3d12, #NAME); \ + if (NAME##_ == nullptr) return SLANG_FAIL; UINT dxgiFactoryFlags = 0; @@ -107,11 +103,10 @@ public: typedef HRESULT (WINAPI *PFN_DXGI_CREATE_FACTORY_2)(UINT Flags, REFIID riid, _COM_Outptr_ void **ppFactory); - LOAD_PROC(PFN_DXGI_CREATE_FACTORY_2, CreateDXGIFactory2); IDXGIFactory4* dxgiFactory; - checkResult(CreateDXGIFactory2_(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory))); + SLANG_RETURN_ON_FAIL(CreateDXGIFactory2_(dxgiFactoryFlags, IID_PPV_ARGS(&dxgiFactory))); D3D_FEATURE_LEVEL featureLevel = D3D_FEATURE_LEVEL_11_0; @@ -135,8 +130,7 @@ public: { // TODO: may want to allow software driver as fallback } - else if( SUCCEEDED(D3D12CreateDevice_( - candidateAdapter, featureLevel, IID_PPV_ARGS(&dxDevice))) ) + else if( SUCCEEDED(D3D12CreateDevice_(candidateAdapter, featureLevel, IID_PPV_ARGS(&dxDevice))) ) { // We found one! adapter = candidateAdapter; @@ -148,20 +142,18 @@ public: if(!adapter) { - return; + // Couldn't find an adapter + return SLANG_FAIL; } // Command Queue - D3D12_COMMAND_QUEUE_DESC queueDesc = {}; queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT; ID3D12CommandQueue* commandQueue; - checkResult(dxDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); + SLANG_RETURN_ON_FAIL(dxDevice->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue))); // Swap Chain - - UINT frameCount = 2; // TODO: configure DXGI_SWAP_CHAIN_DESC1 swapChainDesc = {}; @@ -174,7 +166,7 @@ public: swapChainDesc.SampleDesc.Count = 1; IDXGISwapChain1* swapChain; - checkResult(dxgiFactory->CreateSwapChainForHwnd( + SLANG_RETURN_ON_FAIL(dxgiFactory->CreateSwapChainForHwnd( commandQueue, windowHandle, &swapChainDesc, @@ -183,11 +175,10 @@ public: &swapChain)); // Is this needed? - dxgiFactory->MakeWindowAssociation( - windowHandle, DXGI_MWA_NO_ALT_ENTER); + dxgiFactory->MakeWindowAssociation(windowHandle, DXGI_MWA_NO_ALT_ENTER); IDXGISwapChain3* swapChainEx; - swapChain->QueryInterface(IID_PPV_ARGS(&swapChainEx)); + SLANG_RETURN_ON_FAIL(swapChain->QueryInterface(IID_PPV_ARGS(&swapChainEx))); UINT frameIndex = swapChainEx->GetCurrentBackBufferIndex(); @@ -198,10 +189,9 @@ public: rtvHeapDesc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV; ID3D12DescriptorHeap* rtvHeap; - checkResult(dxDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap))); + SLANG_RETURN_ON_FAIL(dxDevice->CreateDescriptorHeap(&rtvHeapDesc, IID_PPV_ARGS(&rtvHeap))); - UINT rtvDescriptorSize = dxDevice->GetDescriptorHandleIncrementSize( - D3D12_DESCRIPTOR_HEAP_TYPE_RTV); + UINT rtvDescriptorSize = dxDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV); D3D12_CPU_DESCRIPTOR_HANDLE rtvHandle = rtvHeap->GetCPUDescriptorHandleForHeapStart(); @@ -209,18 +199,14 @@ public: ID3D12Resource* backBufferResources[2]; for( UINT ff = 0; ff < frameCount; ++ff ) { - checkResult(swapChainEx->GetBuffer(ff, IID_PPV_ARGS(&backBufferResources[ff]))); - dxDevice->CreateRenderTargetView( - backBufferResources[ff], - nullptr, - rtvHandle); + SLANG_RETURN_ON_FAIL(swapChainEx->GetBuffer(ff, IID_PPV_ARGS(&backBufferResources[ff]))); + dxDevice->CreateRenderTargetView(backBufferResources[ff], nullptr, rtvHandle); rtvHandle.ptr += rtvDescriptorSize; } ID3D12CommandAllocator* commandAllocator; - checkResult(dxDevice->CreateCommandAllocator( - D3D12_COMMAND_LIST_TYPE_DIRECT, - IID_PPV_ARGS(&commandAllocator))); + SLANG_RETURN_ON_FAIL(dxDevice->CreateCommandAllocator( D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&commandAllocator))); + return SLANG_OK; } float clearColor[4] = { 0, 0, 0, 0 }; @@ -237,8 +223,9 @@ public: { } - virtual void captureScreenShot(char const* outputPath) override + virtual SlangResult captureScreenShot(char const* outputPath) override { + return SLANG_FAIL; } virtual ShaderCompiler* getShaderCompiler() override diff --git a/tools/render-test/render-gl.cpp b/tools/render-test/render-gl.cpp index 99b61c03c..c2195f4f0 100644 --- a/tools/render-test/render-gl.cpp +++ b/tools/render-test/render-gl.cpp @@ -86,7 +86,7 @@ public: // Renderer interface - virtual void initialize(void* inWindowHandle) override + virtual SlangResult initialize(void* inWindowHandle) override { auto windowHandle = (HWND) inWindowHandle; @@ -127,6 +127,8 @@ public: glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(staticDebugCallback, this); } + + return SLANG_OK; } void debugCallback( @@ -181,7 +183,7 @@ public: SwapBuffers(deviceContext); } - virtual void captureScreenShot(char const* outputPath) override + virtual SlangResult captureScreenShot(char const* outputPath) override { int width = gWindowWidth; int height = gWindowHeight; @@ -189,7 +191,7 @@ public: int components = 4; int rowStride = width*components; - GLubyte* buffer = (GLubyte*)malloc(components * width * height); + GLubyte* buffer = (GLubyte*)::malloc(components * width * height); glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer); @@ -222,12 +224,16 @@ public: components, buffer, rowStride); - if( !stbResult ) + + ::free(buffer); + + if( !stbResult ) { assert(!"unexpected"); + return SLANG_FAIL; } - delete(buffer); + return SLANG_OK; } virtual ShaderCompiler* getShaderCompiler() override @@ -483,7 +489,7 @@ public: int maxSize = 0; glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &maxSize); - auto infoBuffer = (char*) malloc(maxSize); + auto infoBuffer = (char*)::malloc(maxSize); int infoSize = 0; glGetProgramInfoLog(programID, maxSize, &infoSize, infoBuffer); @@ -493,8 +499,10 @@ public: OutputDebugStringA(infoBuffer); } + ::free(infoBuffer); + glDeleteProgram(programID); - return 0; + return nullptr; } return (ShaderProgram*) (uintptr_t) programID; diff --git a/tools/render-test/render-vk.cpp b/tools/render-test/render-vk.cpp index fd080931d..1f8e11aa6 100644 --- a/tools/render-test/render-vk.cpp +++ b/tools/render-test/render-vk.cpp @@ -73,8 +73,11 @@ M(vkCreateShaderModule) \ /* */ + namespace renderer_test { +#define RETURN_ON_VK_FAIL(x) { VkResult _vkRes = x; if (_vkRes != VK_SUCCESS) { SLANG_RETURN_ON_FAIL(toSlangResult(_vkRes)); }} + class VKRenderer : public Renderer, public ShaderCompiler { public: @@ -101,10 +104,14 @@ public: // Renderer interface - void checkResult(VkResult result) - { - assert(result == VK_SUCCESS); - } + static SlangResult toSlangResult(VkResult res) + { + return (res == VK_SUCCESS) ? SLANG_OK : SLANG_FAIL; + } + void checkResult(VkResult result) + { + assert(result == VK_SUCCESS); + } VkBool32 handleDebugMessage( VkDebugReportFlagsEXT flags, @@ -151,22 +158,21 @@ public: flags, objType, srcObject, location, msgCode, pLayerPrefix, pMsg); } - virtual void initialize(void* inWindowHandle) override + virtual SlangResult initialize(void* inWindowHandle) override { char const* dynamicLibraryName = "vulkan-1.dll"; HMODULE vulkan = LoadLibraryA(dynamicLibraryName); if(!vulkan) { fprintf(stderr, "error: failed load '%s'\n", dynamicLibraryName); - exit(1); + return SLANG_FAIL; } vkGetInstanceProcAddr = (PFN_vkGetInstanceProcAddr) GetProcAddress(vulkan, "vkGetInstanceProcAddr"); if(!vkGetInstanceProcAddr) { - fprintf(stderr, - "error: failed load symbol 'vkGetInstanceProcAddr'\n"); - exit(1); + fprintf(stderr, "error: failed load symbol 'vkGetInstanceProcAddr'\n"); + return SLANG_FAIL; } VkApplicationInfo applicationInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; @@ -209,10 +215,7 @@ public: FOREACH_GLOBAL_PROC(LOAD_INSTANCE_PROC); - checkResult(vkCreateInstance( - &instanceCreateInfo, - nullptr, - &instance)); + RETURN_ON_VK_FAIL(vkCreateInstance(&instanceCreateInfo, nullptr, &instance)); FOREACH_INSTANCE_PROC(LOAD_INSTANCE_PROC); @@ -227,18 +230,16 @@ public: debugCreateInfo.pUserData = this; debugCreateInfo.flags = debugFlags; - checkResult(vkCreateDebugReportCallbackEXT( - instance, &debugCreateInfo, nullptr, &debugReportCallback)); + RETURN_ON_VK_FAIL(vkCreateDebugReportCallbackEXT(instance, &debugCreateInfo, nullptr, &debugReportCallback)); #endif uint32_t physicalDeviceCount = 0; - checkResult(vkEnumeratePhysicalDevices( - instance, &physicalDeviceCount, nullptr)); + RETURN_ON_VK_FAIL(vkEnumeratePhysicalDevices(instance, &physicalDeviceCount, nullptr)); VkPhysicalDevice* physicalDevices = (VkPhysicalDevice*)alloca( physicalDeviceCount * sizeof(VkPhysicalDevice)); - checkResult(vkEnumeratePhysicalDevices( + RETURN_ON_VK_FAIL(vkEnumeratePhysicalDevices( instance, &physicalDeviceCount, physicalDevices)); uint32_t selectedDeviceIndex = 0; @@ -293,7 +294,7 @@ public: deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensions) / sizeof(deviceExtensions[0]); deviceCreateInfo.ppEnabledExtensionNames = &deviceExtensions[0]; - checkResult(vkCreateDevice( + RETURN_ON_VK_FAIL(vkCreateDevice( physicalDevice, &deviceCreateInfo, nullptr, &device)); #define LOAD_DEVICE_PROC(NAME) NAME = (PFN_##NAME) vkGetDeviceProcAddr(device, #NAME); @@ -306,7 +307,7 @@ public: commandPoolCreateInfo.queueFamilyIndex = queueFamilyIndex; commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - checkResult(vkCreateCommandPool( + RETURN_ON_VK_FAIL(vkCreateCommandPool( device, &commandPoolCreateInfo, nullptr, &commandPool)); vkGetDeviceQueue( @@ -317,6 +318,8 @@ public: // set up swap chain + + // create command buffers // depth/stencil? @@ -329,9 +332,9 @@ public: -// create semaphores for sync - + // create semaphores for sync + return SLANG_OK; } float clearColor[4]; @@ -349,8 +352,9 @@ public: { } - virtual void captureScreenShot(char const* outputPath) override + virtual SlangResult captureScreenShot(char const* outputPath) override { + return SLANG_FAIL; } virtual ShaderCompiler* getShaderCompiler() override diff --git a/tools/render-test/render.h b/tools/render-test/render.h index 5eb0c967d..902067d62 100644 --- a/tools/render-test/render.h +++ b/tools/render-test/render.h @@ -5,6 +5,8 @@ #include "window.h" #include "shader-input-layout.h" +#include "../../source/core/slang-result.h" + namespace renderer_test { typedef struct Buffer Buffer; @@ -90,14 +92,14 @@ enum class PrimitiveTopology class Renderer { public: - virtual void initialize(void* inWindowHandle) = 0; + virtual SlangResult initialize(void* inWindowHandle) = 0; virtual void setClearColor(float const* color) = 0; virtual void clearFrame() = 0; virtual void presentFrame() = 0; - virtual void captureScreenShot(char const* outputPath) = 0; + virtual SlangResult captureScreenShot(char const* outputPath) = 0; virtual void serializeOutput(BindingState * state, char const* outputPath) = 0; virtual Buffer* createBuffer(BufferDesc const& desc) = 0; diff --git a/tools/render-test/slang-support.cpp b/tools/render-test/slang-support.cpp index 5599a3396..aa097bb44 100644 --- a/tools/render-test/slang-support.cpp +++ b/tools/render-test/slang-support.cpp @@ -170,7 +170,6 @@ ShaderCompiler* createSlangShaderCompiler( result->target = target; return result; - } diff --git a/tools/slang-test/README.md b/tools/slang-test/README.md new file mode 100644 index 000000000..7da0c3191 --- /dev/null +++ b/tools/slang-test/README.md @@ -0,0 +1,84 @@ +# Slang Test + +Slang Test is a command line tool that is used to coordinate tests via other command line tools. The actual executable is 'slang-test'. It is typically run from the test.bat script in the root directory of the project. + +Slang Test can be thought of as the 'hub' running multiple tests and accumulating the results. In the distribution tests are held in the 'tests' directory. Inside this directory there are tests grouped together via other directories. Inside those directories are the actual tests themselves. The tests exist as .hlsl, .slang and .glsl and other file extensions. The top line of each of these files describe what kind of test will be performed with a specialized comment '//TEST'. + +## Test Categories + +There are the following test categories + +* full +* quick +* smoke +* render +* compute +* vulkan + +A test may be in one or more categories. The categories are specified in the test line, for example: +//TEST(smoke,compute):COMPARE_COMPUTE: + +## Command line options + +### bindir + +Specifies the directory where executables will be found. + +Eg -bindir "windows-x64\Debug\\" + +### category + +The parameter controls what kinds of tests will be run. Categories are listed at the 'test categories' section. + +### exclude + +Used to specify categories to be excluded during a test. + +### appveyor + +A flag that makes output suitable for the appveyor automated test suite. + +### travis + +A flag that makes output suitable for the travis automated test suite. + +### Other Command Line Options + +The following flags/paramteres can be passed but will be ignored by the tool + +* debug (flag) +* release (flag) +* platform (parameter) + +## Test Types + +Test types are controlled via a comment at the top of a test file, that starts with //TEST: +This is then immediately followed by the test type which is one of the following + +* SIMPLE + * Calls the slangc compiler with options after the comment +* REFLECTION + * Runs 'slang-reflection-test' passing in the options as given after the command +* COMPARE_HLSL + * Runs the slangc compiler, forcing dxbc output and compares with file post fixed with '.expected' +* COMPARE_HLSL_RENDER + * Runs 'render-test' rendering two images - one for hlsl (expected), and one for slang saving to .png files. The images must match for the test to pass. +* COMPARE_HLSL_CROSS_COMPILE_RENDER + * Runs 'render-test' rendering two images - one from slag, and the other -glsl-cross. The images must match for the test to pass. +* COMPARE_HLSL_GLSL_RENDER + * Runs 'render-test' rendering two images - one with -hlsl-rewrite and the other -glsl-rewrite. The images must match for test to pass. +* COMPARE_COMPUTE + * Runs 'render-test' producing a compute result written as a text file. Text file contents must be identical. +* COMPARE_COMPUTE_EX + * Same as COMPARE_COMPUTE, but allows specific parameters to be specified. +* HLSL_COMPUTE + * Runs 'render-test' with "-hlsl-rewrite -compute" options. Text files are compared. +* COMPARE_RENDER_COMPUTE + * Runs 'render-test' with "-slang -gcompute" options. Text files are compared. +* COMPARE_GLSL + * Runs the slangc compiler compiling through slang, and without and comparing output in spirv assembly. +* CROSS_COMPILE + * Compiles as glsl pass through and then through slang and comparing output +* EVAL + * Runs 'slang-eval-test' - which runs code on slang VM + diff --git a/tools/slang-test/main.cpp b/tools/slang-test/main.cpp index 4edd8272d..0f469e8d9 100644 --- a/tools/slang-test/main.cpp +++ b/tools/slang-test/main.cpp @@ -3,6 +3,8 @@ #include "../../source/core/slang-io.h" #include "../../source/core/token-reader.h" +#include "../../source/core/slang-result.h" + using namespace Slang; #include "os.h" @@ -26,7 +28,7 @@ enum OutputMode // Default mode is to write test results to the console kOutputMode_Default = 0, - // When running under AppVeyor contiuous integration, we + // When running under AppVeyor continuous integration, we // need to output test results in a way that the AppVeyor // environment can pick up and display. kOutputMode_AppVeyor, @@ -66,12 +68,12 @@ struct Options // Only run tests that match one of the given categories Dictionary<TestCategory*, TestCategory*> includeCategories; - // Exclude test taht match one these categories + // Exclude test that match one these categories Dictionary<TestCategory*, TestCategory*> excludeCategories; }; Options options; -void parseOptions(int* argc, char** argv) +Result parseOptions(int* argc, char** argv) { int argCount = *argc; char const* const* argCursor = argv; @@ -110,7 +112,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "error: expected operand for '%s'\n", arg); - exit(1); + return SLANG_FAIL; } options.binDir = *argCursor++; } @@ -135,7 +137,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "error: expected operand for '%s'\n", arg); - exit(1); + return SLANG_FAIL; } argCursor++; // Assumed to be handle by .bat file that called us @@ -145,7 +147,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "error: expected operand for '%s'\n", arg); - exit(1); + return SLANG_FAIL; } argCursor++; // Assumed to be handle by .bat file that called us @@ -165,7 +167,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "error: expected operand for '%s'\n", arg); - exit(1); + return SLANG_FAIL; } auto category = findTestCategory(*argCursor++); if(category) @@ -178,7 +180,7 @@ void parseOptions(int* argc, char** argv) if( argCursor == argEnd ) { fprintf(stderr, "error: expected operand for '%s'\n", arg); - exit(1); + return SLANG_FAIL; } auto category = findTestCategory(*argCursor++); if(category) @@ -189,7 +191,7 @@ void parseOptions(int* argc, char** argv) else { fprintf(stderr, "unknown option '%s'\n", arg); - exit(1); + return SLANG_FAIL; } } @@ -208,10 +210,11 @@ void parseOptions(int* argc, char** argv) if(argCursor != argEnd) { fprintf(stderr, "unexpected arguments\n"); - exit(1); + return SLANG_FAIL; } *argc = 0; + return SLANG_OK; } // Called for an error in the test-runner (not for an error involving @@ -357,7 +360,7 @@ TestCategory* findTestCategory(String const& name) return category; } -// Optiosn for a particular test +// Options for a particular test struct TestOptions { String command; @@ -976,7 +979,7 @@ TestResult runHLSLComparisonTest(TestInput& input) OSProcessSpawner::ResultCode resultCode = spawner.getResultCode(); - String standardOuptut = spawner.getStandardOutput(); + String standardOutput = spawner.getStandardOutput(); String standardError = spawner.getStandardError(); // We construct a single output string that captures the results @@ -986,7 +989,7 @@ TestResult runHLSLComparisonTest(TestInput& input) actualOutputBuilder.Append("\nstandard error = {\n"); actualOutputBuilder.Append(standardError); actualOutputBuilder.Append("}\nstandard output = {\n"); - actualOutputBuilder.Append(standardOuptut); + actualOutputBuilder.Append(standardOutput); actualOutputBuilder.Append("}\n"); String actualOutput = actualOutputBuilder.ProduceString(); @@ -1008,7 +1011,7 @@ TestResult runHLSLComparisonTest(TestInput& input) { if (resultCode != 0) result = kTestResult_Fail; if (standardError.Length() != 0) result = kTestResult_Fail; - if (standardOuptut.Length() != 0) result = kTestResult_Fail; + if (standardOutput.Length() != 0) result = kTestResult_Fail; } // Otherwise we compare to the expected output else if (actualOutput != expectedOutput) @@ -1230,13 +1233,13 @@ TestResult doRenderComparisonTestRun(TestInput& input, char const* langOption, c { // TODO: delete any existing files at the output path(s) to avoid stale outputs leading to a false pass - auto filePath999 = input.filePath; + auto filePath = input.filePath; auto outputStem = input.outputStem; OSProcessSpawner spawner; spawner.pushExecutablePath(String(options.binDir) + "render-test" + osGetExecutableSuffix()); - spawner.pushArgument(filePath999); + spawner.pushArgument(filePath); for( auto arg : input.testOptions->args ) { @@ -1280,7 +1283,7 @@ 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 + // Allow a relative 1% difference static const float kRelativeDiffCutoff = 0.01f; String expectedPath = filePath + ".expected.png"; @@ -1289,7 +1292,6 @@ TestResult doImageComparison(String const& filePath) 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); @@ -1304,47 +1306,51 @@ TestResult doImageComparison(String const& filePath) 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; - } - - float relativeDiff = 0.0f; - if( expectedVal != 0 ) - { - 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... - - fprintf(stderr, "image compare failure at (%d,%d) channel %d. expected %d got %d (absolute error: %d, relative error: %f)\n", - x, y, n, - expectedVal, - actualVal, - absoluteDiff, - relativeDiff); - - // There was a difference we couldn't excuse! - return kTestResult_Fail; - } + { + 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; + } + + float relativeDiff = 0.0f; + if( expectedVal != 0 ) + { + 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... + + fprintf(stderr, "image compare failure at (%d,%d) channel %d. expected %d got %d (absolute error: %d, relative error: %f)\n", + x, y, n, + expectedVal, + actualVal, + absoluteDiff, + relativeDiff); + + // There was a difference we couldn't excuse! + return kTestResult_Fail; + } + } + } return kTestResult_Pass; } @@ -1354,7 +1360,7 @@ TestResult runHLSLRenderComparisonTestImpl( char const* expectedArg, char const* actualArg) { - auto filePath999 = input.filePath; + auto filePath = input.filePath; auto outputStem = input.outputStem; String expectedOutput; @@ -1395,7 +1401,7 @@ TestResult runHLSLCrossCompileRenderComparisonTest(TestInput& input) return runHLSLRenderComparisonTestImpl(input, "-slang", "-glsl-cross"); } -TestResult runHLSLAndGLSLComparisonTest(TestInput& input) +TestResult runHLSLAndGLSLRenderComparisonTest(TestInput& input) { return runHLSLRenderComparisonTestImpl(input, "-hlsl-rewrite", "-glsl-rewrite"); } @@ -1412,18 +1418,21 @@ TestResult runTest( FileTestList const& testList) { // based on command name, dispatch to an appropriate callback - static const struct TestCommands + struct TestCommands { char const* name; TestCallback callback; - } kTestCommands[] = { + }; + + static const TestCommands kTestCommands[] = + { { "SIMPLE", &runSimpleTest }, { "REFLECTION", &runReflectionTest }, #if SLANG_TEST_SUPPORT_HLSL { "COMPARE_HLSL", &runHLSLComparisonTest }, { "COMPARE_HLSL_RENDER", &runHLSLRenderComparisonTest }, { "COMPARE_HLSL_CROSS_COMPILE_RENDER", &runHLSLCrossCompileRenderComparisonTest}, - { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLComparisonTest }, + { "COMPARE_HLSL_GLSL_RENDER", &runHLSLAndGLSLRenderComparisonTest }, { "COMPARE_COMPUTE", runSlangComputeComparisonTest}, { "COMPARE_COMPUTE_EX", runSlangComputeComparisonTestEx}, { "HLSL_COMPUTE", runHLSLComputeTest}, @@ -1748,8 +1757,11 @@ int main( // - - parseOptions(&argc, argv); + if (SLANG_FAILED(parseOptions(&argc, argv))) + { + // Return exit code with error + return 1; + } if( options.includeCategories.Count() == 0 ) { @@ -1778,7 +1790,6 @@ int main( return 0; } - auto passCount = context.passedTestCount; auto rawTotal = context.totalTestCount; auto ignoredCount = context.ignoredTestCount; @@ -1788,7 +1799,7 @@ int main( printf("\n===\n%d%% of tests passed (%d/%d)", (passCount*100) / runTotal, passCount, runTotal); if(ignoredCount) { - printf(", %d tests ingored", ignoredCount); + printf(", %d tests ignored", ignoredCount); } printf("\n===\n\n"); |
