From d155eaa92d56a4ec00109d25c8c70fe12fb96c2e Mon Sep 17 00:00:00 2001 From: jsmall-nvidia Date: Mon, 7 Jan 2019 09:14:01 -0500 Subject: Feature/unique tool source names (#766) * Remove AppContext. Use StdChannels to hold writers, and TestToolUtil to hold test tool specific functionality. * StdChannels -> StdWriters * getStdOut -> getOut, getStdError -> getError * Renamed main.cpp files of tools to try and stop visual studio getting confused between files - such that clicking on an error takes editor to the right location. --- tools/render-test/main.cpp | 679 --------------------- tools/render-test/render-test-main.cpp | 679 +++++++++++++++++++++ tools/render-test/render-test-tool.vcxproj | 2 +- tools/render-test/render-test-tool.vcxproj.filters | 6 +- 4 files changed, 683 insertions(+), 683 deletions(-) delete mode 100644 tools/render-test/main.cpp create mode 100644 tools/render-test/render-test-main.cpp (limited to 'tools/render-test') diff --git a/tools/render-test/main.cpp b/tools/render-test/main.cpp deleted file mode 100644 index 69874ac04..000000000 --- a/tools/render-test/main.cpp +++ /dev/null @@ -1,679 +0,0 @@ -// main.cpp - -#include "options.h" -#include "render.h" -#include "render-d3d11.h" -#include "render-d3d12.h" -#include "render-gl.h" -#include "render-vk.h" - -#include "slang-support.h" -#include "surface.h" -#include "png-serialize-util.h" - -#include "shader-renderer-util.h" - -#include "shader-input-layout.h" -#include -#include - -#include "../../source/core/slang-test-tool-util.h" - -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX - -#ifdef _MSC_VER -#pragma warning(disable: 4996) -#endif - -namespace renderer_test { - -using Slang::Result; - -int gWindowWidth = 1024; -int gWindowHeight = 768; - -class Window: public RefObject -{ -public: - SlangResult initialize(int width, int height); - - void show(); - - void* getHandle() const { return m_hwnd; } - - Window() {} - ~Window(); - - static LRESULT CALLBACK windowProc(HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam); - -protected: - - HINSTANCE m_hinst = nullptr; - HWND m_hwnd = nullptr; -}; - -// -// We use a bare-minimum window procedure to get things up and running. -// - -/* static */LRESULT CALLBACK Window::windowProc( - HWND windowHandle, - UINT message, - WPARAM wParam, - LPARAM lParam) -{ - switch (message) - { - case WM_CLOSE: - PostQuitMessage(0); - return 0; - } - - return DefWindowProcW(windowHandle, message, wParam, lParam); -} - -static ATOM _getWindowClassAtom(HINSTANCE hinst) -{ - static ATOM s_windowClassAtom; - - if (s_windowClassAtom) - { - return s_windowClassAtom; - } - WNDCLASSEXW windowClassDesc; - windowClassDesc.cbSize = sizeof(windowClassDesc); - windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; - windowClassDesc.lpfnWndProc = &Window::windowProc; - windowClassDesc.cbClsExtra = 0; - windowClassDesc.cbWndExtra = 0; - windowClassDesc.hInstance = hinst; - windowClassDesc.hIcon = 0; - windowClassDesc.hCursor = 0; - windowClassDesc.hbrBackground = 0; - windowClassDesc.lpszMenuName = 0; - windowClassDesc.lpszClassName = L"SlangRenderTest"; - windowClassDesc.hIconSm = 0; - s_windowClassAtom = RegisterClassExW(&windowClassDesc); - - return s_windowClassAtom; -} - -SlangResult Window::initialize(int widthIn, int heightIn) -{ - // Do initial window-creation stuff here, rather than in the renderer-specific files - - m_hinst = GetModuleHandleA(0); - - // First we register a window class. - ATOM windowClassAtom = _getWindowClassAtom(m_hinst); - 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, widthIn, heightIn }; - AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); - - { - auto width = windowRect.right - windowRect.left; - auto height = windowRect.bottom - windowRect.top; - - LPWSTR windowName = L"Slang Render Test"; - m_hwnd = CreateWindowExW( - windowExtendedStyle, - (LPWSTR)windowClassAtom, - windowName, - windowStyle, - 0, 0, // x, y - width, height, - NULL, // parent - NULL, // menu - m_hinst, - NULL); - } - if (!m_hwnd) - { - fprintf(stderr, "error: failed to create window\n"); - return SLANG_FAIL; - } - - return SLANG_OK; -} - - -void Window::show() -{ - // Once initialization is all complete, we show the window... - int showCommand = SW_SHOW; - ShowWindow(m_hwnd, showCommand); -} - -Window::~Window() -{ - if (m_hwnd) - { - DestroyWindow(m_hwnd); - } -} - -// -// 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]; - float uv[2]; -}; - -static const Vertex kVertexData[] = -{ - { { 0, 0, 0.5 }, {1, 0, 0} , {0, 0} }, - { { 0, 1, 0.5 }, {0, 0, 1} , {1, 0} }, - { { 1, 0, 0.5 }, {0, 1, 0} , {1, 1} }, -}; -static const int kVertexCount = SLANG_COUNT_OF(kVertexData); - -using namespace Slang; - -class RenderTestApp -{ - public: - - // At initialization time, we are going to load and compile our Slang shader - // code, and then create the API objects we need for rendering. - Result initialize(Renderer* renderer, ShaderCompiler* shaderCompiler); - void runCompute(); - void renderFrame(); - void finalize(); - - BindingStateImpl* getBindingState() const { return m_bindingState; } - - Result writeBindingOutput(const char* fileName); - - Result writeScreen(const char* filename); - - protected: - /// Called in initialize - Result initializeShaders(ShaderCompiler* shaderCompiler); - - // variables for state to be used for rendering... - uintptr_t m_constantBufferSize, m_computeResultBufferSize; - - RefPtr m_renderer; - - RefPtr m_constantBuffer; - RefPtr m_inputLayout; - RefPtr m_vertexBuffer; - RefPtr m_shaderProgram; - RefPtr m_pipelineState; - RefPtr m_bindingState; - - ShaderInputLayout m_shaderInputLayout; ///< The binding layout - int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. -}; - -// Entry point name to use for vertex/fragment shader -static const char vertexEntryPointName[] = "vertexMain"; -static const char fragmentEntryPointName[] = "fragmentMain"; -static const char computeEntryPointName[] = "computeMain"; - -SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shaderCompiler) -{ - SLANG_RETURN_ON_FAIL(initializeShaders(shaderCompiler)); - - m_numAddedConstantBuffers = 0; - m_renderer = renderer; - - // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed - { - m_constantBufferSize = 16 * sizeof(float); - - BufferResource::Desc constantBufferDesc; - constantBufferDesc.init(m_constantBufferSize); - constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; - - m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); - if (!m_constantBuffer) - return SLANG_FAIL; - } - - { - //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer - // - // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified - // in the test file - RefPtr addedConstantBuffer; - switch(gOptions.shaderType) - { - default: - break; - - case Options::ShaderProgramType::Graphics: - case Options::ShaderProgramType::GraphicsCompute: - addedConstantBuffer = m_constantBuffer; - m_numAddedConstantBuffers++; - break; - } - - BindingStateImpl* bindingState = nullptr; - SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); - m_bindingState = bindingState; - } - - // Do other initialization that doesn't depend on the source language. - - // Input Assembler (IA) - - const InputElementDesc inputElements[] = { - { "A", 0, Format::RGB_Float32, offsetof(Vertex, position) }, - { "A", 1, Format::RGB_Float32, offsetof(Vertex, color) }, - { "A", 2, Format::RG_Float32, offsetof(Vertex, uv) }, - }; - - m_inputLayout = renderer->createInputLayout(inputElements, SLANG_COUNT_OF(inputElements)); - if(!m_inputLayout) - return SLANG_FAIL; - - BufferResource::Desc vertexBufferDesc; - vertexBufferDesc.init(kVertexCount * sizeof(Vertex)); - - m_vertexBuffer = renderer->createBufferResource(Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData); - if(!m_vertexBuffer) - return SLANG_FAIL; - - { - switch(gOptions.shaderType) - { - default: - assert(!"unexpected test shader type"); - return SLANG_FAIL; - - case Options::ShaderProgramType::Compute: - { - ComputePipelineStateDesc desc; - desc.pipelineLayout = m_bindingState->pipelineLayout; - desc.program = m_shaderProgram; - - m_pipelineState = renderer->createComputePipelineState(desc); - } - break; - - case Options::ShaderProgramType::Graphics: - case Options::ShaderProgramType::GraphicsCompute: - { - GraphicsPipelineStateDesc desc; - desc.pipelineLayout = m_bindingState->pipelineLayout; - desc.program = m_shaderProgram; - desc.inputLayout = m_inputLayout; - desc.renderTargetCount = m_bindingState->m_numRenderTargets; - - m_pipelineState = renderer->createGraphicsPipelineState(desc); - } - break; - } - } - - return SLANG_OK; -} - -Result RenderTestApp::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); - return SLANG_FAIL; - } - fseek(sourceFile, 0, SEEK_END); - size_t sourceSize = ftell(sourceFile); - fseek(sourceFile, 0, SEEK_SET); - - List sourceText; - sourceText.SetSize(sourceSize + 1); - fread(sourceText.Buffer(), sourceSize, 1, sourceFile); - fclose(sourceFile); - sourceText[sourceSize] = 0; - - switch( gOptions.shaderType ) - { - default: - m_shaderInputLayout.numRenderTargets = 1; - break; - - case Options::ShaderProgramType::Compute: - m_shaderInputLayout.numRenderTargets = 0; - break; - } - m_shaderInputLayout.Parse(sourceText.Buffer()); - - ShaderCompileRequest::SourceInfo sourceInfo; - sourceInfo.path = sourcePath; - sourceInfo.dataBegin = sourceText.Buffer(); - sourceInfo.dataEnd = sourceText.Buffer() + sourceSize; - - ShaderCompileRequest compileRequest; - compileRequest.source = sourceInfo; - if (gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) - { - compileRequest.vertexShader.source = sourceInfo; - compileRequest.vertexShader.name = vertexEntryPointName; - compileRequest.fragmentShader.source = sourceInfo; - compileRequest.fragmentShader.name = fragmentEntryPointName; - } - else - { - compileRequest.computeShader.source = sourceInfo; - compileRequest.computeShader.name = computeEntryPointName; - } - compileRequest.entryPointTypeArguments = m_shaderInputLayout.globalTypeArguments; - m_shaderProgram = shaderCompiler->compileProgram(compileRequest); - if (!m_shaderProgram) - { - return SLANG_FAIL; - } - - return SLANG_OK; -} - -void RenderTestApp::renderFrame() -{ - auto mappedData = m_renderer->map(m_constantBuffer, MapFlavor::WriteDiscard); - if(mappedData) - { - const ProjectionStyle projectionStyle = RendererUtil::getProjectionStyle(m_renderer->getRendererType()); - RendererUtil::getIdentityProjection(projectionStyle, (float*)mappedData); - - m_renderer->unmap(m_constantBuffer); - } - - auto pipelineType = PipelineType::Graphics; - - m_renderer->setPipelineState(pipelineType, m_pipelineState); - - m_renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); - m_renderer->setVertexBuffer(0, m_vertexBuffer, sizeof(Vertex)); - - m_bindingState->apply(m_renderer, pipelineType); - - m_renderer->draw(3); -} - -void RenderTestApp::runCompute() -{ - auto pipelineType = PipelineType::Compute; - m_renderer->setPipelineState(pipelineType, m_pipelineState); - m_bindingState->apply(m_renderer, pipelineType); - m_renderer->dispatchCompute(1, 1, 1); -} - -void RenderTestApp::finalize() -{ -} - -Result RenderTestApp::writeBindingOutput(const char* fileName) -{ - // Submit the work - m_renderer->submitGpuWork(); - // Wait until everything is complete - m_renderer->waitForGpu(); - - FILE * f = fopen(fileName, "wb"); - if (!f) - { - return SLANG_FAIL; - } - - for(auto binding : m_bindingState->outputBindings) - { - auto i = binding.entryIndex; - const auto& layoutBinding = m_shaderInputLayout.entries[i]; - - assert(layoutBinding.isOutput); - { - if (binding.resource && binding.resource->isBuffer()) - { - BufferResource* bufferResource = static_cast(binding.resource.Ptr()); - const size_t bufferSize = bufferResource->getDesc().sizeInBytes; - - unsigned int* ptr = (unsigned int*)m_renderer->map(bufferResource, MapFlavor::HostRead); - if (!ptr) - { - fclose(f); - return SLANG_FAIL; - } - - const int size = int(bufferSize / sizeof(unsigned int)); - for (int i = 0; i < size; ++i) - { - fprintf(f, "%X\n", ptr[i]); - } - m_renderer->unmap(bufferResource); - } - else - { - printf("invalid output type at %d.\n", int(i)); - } - } - } - fclose(f); - - return SLANG_OK; -} - - -Result RenderTestApp::writeScreen(const char* filename) -{ - Surface surface; - SLANG_RETURN_ON_FAIL(m_renderer->captureScreenSurface(surface)); - return PngSerializeUtil::write(filename, surface); -} - -} // namespace renderer_test - -SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSession* session, int argcIn, const char*const* argvIn) -{ - using namespace renderer_test; - using namespace Slang; - - StdWriters::setSingleton(stdWriters); - - // Parse command-line options - SLANG_RETURN_ON_FAIL(parseOptions(argcIn, argvIn, StdWriters::getError())); - - RefPtr window(new renderer_test::Window); - SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); - - Slang::RefPtr renderer; - - SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; - SlangCompileTarget slangTarget = SLANG_TARGET_NONE; - SlangPassThrough slangPassThrough = SLANG_PASS_THROUGH_NONE; - char const* profileName = ""; - switch (gOptions.rendererType) - { - case RendererType::DirectX11: - renderer = createD3D11Renderer(); - slangTarget = SLANG_DXBC; - nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; - slangPassThrough = SLANG_PASS_THROUGH_FXC; - profileName = "sm_5_0"; - break; - - case RendererType::DirectX12: - renderer = createD3D12Renderer(); - slangTarget = SLANG_DXBC; - nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; - slangPassThrough = SLANG_PASS_THROUGH_FXC; - profileName = "sm_5_0"; - if( gOptions.useDXIL ) - { - slangTarget = SLANG_DXIL; - slangPassThrough = SLANG_PASS_THROUGH_DXC; - profileName = "sm_6_0"; - } - break; - - case RendererType::OpenGl: - renderer = createGLRenderer(); - slangTarget = SLANG_GLSL; - nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; - slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; - profileName = "glsl_430"; - break; - - case RendererType::Vulkan: - renderer = createVKRenderer(); - slangTarget = SLANG_SPIRV; - nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; - slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; - profileName = "glsl_430"; - break; - - default: - fprintf(stderr, "error: unexpected\n"); - return SLANG_FAIL; - } - - if (!renderer) - { - fprintf(stderr, "Unable to create renderer\n"); - return SLANG_FAIL; - } - - Renderer::Desc desc; - desc.width = gWindowWidth; - desc.height = gWindowHeight; - - { - SlangResult res = renderer->initialize(desc, (HWND)window->getHandle()); - if (SLANG_FAILED(res)) - { - fprintf(stderr, "Unable to initialize renderer\n"); - return res; - } - } - - ShaderCompiler shaderCompiler; - shaderCompiler.renderer = renderer; - shaderCompiler.target = slangTarget; - shaderCompiler.profile = profileName; - shaderCompiler.slangSession = session; - - switch (gOptions.inputLanguageID) - { - case Options::InputLanguageID::Slang: - shaderCompiler.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG; - shaderCompiler.passThrough = SLANG_PASS_THROUGH_NONE; - break; - - case Options::InputLanguageID::Native: - shaderCompiler.sourceLanguage = nativeLanguage; - shaderCompiler.passThrough = slangPassThrough; - break; - - default: - break; - } - - { - RenderTestApp app; - - SLANG_RETURN_ON_FAIL(app.initialize(renderer, &shaderCompiler)); - - window->show(); - - // ... 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 - { - // Whenever we don't have Windows events to process, we render a frame. - if (gOptions.shaderType == Options::ShaderProgramType::Compute) - { - app.runCompute(); - } - else - { - static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; - renderer->setClearColor(kClearColor); - renderer->clearFrame(); - - app.renderFrame(); - } - // If we are in a mode where output is requested, we need to snapshot the back buffer here - if (gOptions.outputPath) - { - // Submit the work - renderer->submitGpuWork(); - // Wait until everything is complete - renderer->waitForGpu(); - - if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) - { - SLANG_RETURN_ON_FAIL(app.writeBindingOutput(gOptions.outputPath)); - } - else - { - SlangResult res = app.writeScreen(gOptions.outputPath); - - if (SLANG_FAILED(res)) - { - fprintf(stderr, "ERROR: failed to write screen capture to file\n"); - return res; - } - } - return SLANG_OK; - } - - renderer->presentFrame(); - } - } - } - - return SLANG_OK; -} - - -int main(int argc, char** argv) -{ - SlangSession* session = spCreateSession(nullptr); - SlangResult res = innerMain(Slang::StdWriters::initDefault(), session, argc, argv); - spDestroySession(session); - - return SLANG_FAILED(res) ? 1 : 0; -} - diff --git a/tools/render-test/render-test-main.cpp b/tools/render-test/render-test-main.cpp new file mode 100644 index 000000000..7cf303631 --- /dev/null +++ b/tools/render-test/render-test-main.cpp @@ -0,0 +1,679 @@ +// render-test-main.cpp + +#include "options.h" +#include "render.h" +#include "render-d3d11.h" +#include "render-d3d12.h" +#include "render-gl.h" +#include "render-vk.h" + +#include "slang-support.h" +#include "surface.h" +#include "png-serialize-util.h" + +#include "shader-renderer-util.h" + +#include "shader-input-layout.h" +#include +#include + +#include "../../source/core/slang-test-tool-util.h" + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#undef WIN32_LEAN_AND_MEAN +#undef NOMINMAX + +#ifdef _MSC_VER +#pragma warning(disable: 4996) +#endif + +namespace renderer_test { + +using Slang::Result; + +int gWindowWidth = 1024; +int gWindowHeight = 768; + +class Window: public RefObject +{ +public: + SlangResult initialize(int width, int height); + + void show(); + + void* getHandle() const { return m_hwnd; } + + Window() {} + ~Window(); + + static LRESULT CALLBACK windowProc(HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam); + +protected: + + HINSTANCE m_hinst = nullptr; + HWND m_hwnd = nullptr; +}; + +// +// We use a bare-minimum window procedure to get things up and running. +// + +/* static */LRESULT CALLBACK Window::windowProc( + HWND windowHandle, + UINT message, + WPARAM wParam, + LPARAM lParam) +{ + switch (message) + { + case WM_CLOSE: + PostQuitMessage(0); + return 0; + } + + return DefWindowProcW(windowHandle, message, wParam, lParam); +} + +static ATOM _getWindowClassAtom(HINSTANCE hinst) +{ + static ATOM s_windowClassAtom; + + if (s_windowClassAtom) + { + return s_windowClassAtom; + } + WNDCLASSEXW windowClassDesc; + windowClassDesc.cbSize = sizeof(windowClassDesc); + windowClassDesc.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW; + windowClassDesc.lpfnWndProc = &Window::windowProc; + windowClassDesc.cbClsExtra = 0; + windowClassDesc.cbWndExtra = 0; + windowClassDesc.hInstance = hinst; + windowClassDesc.hIcon = 0; + windowClassDesc.hCursor = 0; + windowClassDesc.hbrBackground = 0; + windowClassDesc.lpszMenuName = 0; + windowClassDesc.lpszClassName = L"SlangRenderTest"; + windowClassDesc.hIconSm = 0; + s_windowClassAtom = RegisterClassExW(&windowClassDesc); + + return s_windowClassAtom; +} + +SlangResult Window::initialize(int widthIn, int heightIn) +{ + // Do initial window-creation stuff here, rather than in the renderer-specific files + + m_hinst = GetModuleHandleA(0); + + // First we register a window class. + ATOM windowClassAtom = _getWindowClassAtom(m_hinst); + 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, widthIn, heightIn }; + AdjustWindowRectEx(&windowRect, windowStyle, /*hasMenu=*/false, windowExtendedStyle); + + { + auto width = windowRect.right - windowRect.left; + auto height = windowRect.bottom - windowRect.top; + + LPWSTR windowName = L"Slang Render Test"; + m_hwnd = CreateWindowExW( + windowExtendedStyle, + (LPWSTR)windowClassAtom, + windowName, + windowStyle, + 0, 0, // x, y + width, height, + NULL, // parent + NULL, // menu + m_hinst, + NULL); + } + if (!m_hwnd) + { + fprintf(stderr, "error: failed to create window\n"); + return SLANG_FAIL; + } + + return SLANG_OK; +} + + +void Window::show() +{ + // Once initialization is all complete, we show the window... + int showCommand = SW_SHOW; + ShowWindow(m_hwnd, showCommand); +} + +Window::~Window() +{ + if (m_hwnd) + { + DestroyWindow(m_hwnd); + } +} + +// +// 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]; + float uv[2]; +}; + +static const Vertex kVertexData[] = +{ + { { 0, 0, 0.5 }, {1, 0, 0} , {0, 0} }, + { { 0, 1, 0.5 }, {0, 0, 1} , {1, 0} }, + { { 1, 0, 0.5 }, {0, 1, 0} , {1, 1} }, +}; +static const int kVertexCount = SLANG_COUNT_OF(kVertexData); + +using namespace Slang; + +class RenderTestApp +{ + public: + + // At initialization time, we are going to load and compile our Slang shader + // code, and then create the API objects we need for rendering. + Result initialize(Renderer* renderer, ShaderCompiler* shaderCompiler); + void runCompute(); + void renderFrame(); + void finalize(); + + BindingStateImpl* getBindingState() const { return m_bindingState; } + + Result writeBindingOutput(const char* fileName); + + Result writeScreen(const char* filename); + + protected: + /// Called in initialize + Result initializeShaders(ShaderCompiler* shaderCompiler); + + // variables for state to be used for rendering... + uintptr_t m_constantBufferSize, m_computeResultBufferSize; + + RefPtr m_renderer; + + RefPtr m_constantBuffer; + RefPtr m_inputLayout; + RefPtr m_vertexBuffer; + RefPtr m_shaderProgram; + RefPtr m_pipelineState; + RefPtr m_bindingState; + + ShaderInputLayout m_shaderInputLayout; ///< The binding layout + int m_numAddedConstantBuffers; ///< Constant buffers can be added to the binding directly. Will be added at the end. +}; + +// Entry point name to use for vertex/fragment shader +static const char vertexEntryPointName[] = "vertexMain"; +static const char fragmentEntryPointName[] = "fragmentMain"; +static const char computeEntryPointName[] = "computeMain"; + +SlangResult RenderTestApp::initialize(Renderer* renderer, ShaderCompiler* shaderCompiler) +{ + SLANG_RETURN_ON_FAIL(initializeShaders(shaderCompiler)); + + m_numAddedConstantBuffers = 0; + m_renderer = renderer; + + // TODO(tfoley): use each API's reflection interface to query the constant-buffer size needed + { + m_constantBufferSize = 16 * sizeof(float); + + BufferResource::Desc constantBufferDesc; + constantBufferDesc.init(m_constantBufferSize); + constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; + + m_constantBuffer = renderer->createBufferResource(Resource::Usage::ConstantBuffer, constantBufferDesc); + if (!m_constantBuffer) + return SLANG_FAIL; + } + + { + //! Hack -> if doing a graphics test, add an extra binding for our dynamic constant buffer + // + // TODO: Should probably be more sophisticated than this - with 'dynamic' constant buffer/s binding always being specified + // in the test file + RefPtr addedConstantBuffer; + switch(gOptions.shaderType) + { + default: + break; + + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + addedConstantBuffer = m_constantBuffer; + m_numAddedConstantBuffers++; + break; + } + + BindingStateImpl* bindingState = nullptr; + SLANG_RETURN_ON_FAIL(ShaderRendererUtil::createBindingState(m_shaderInputLayout, m_renderer, addedConstantBuffer, &bindingState)); + m_bindingState = bindingState; + } + + // Do other initialization that doesn't depend on the source language. + + // Input Assembler (IA) + + const InputElementDesc inputElements[] = { + { "A", 0, Format::RGB_Float32, offsetof(Vertex, position) }, + { "A", 1, Format::RGB_Float32, offsetof(Vertex, color) }, + { "A", 2, Format::RG_Float32, offsetof(Vertex, uv) }, + }; + + m_inputLayout = renderer->createInputLayout(inputElements, SLANG_COUNT_OF(inputElements)); + if(!m_inputLayout) + return SLANG_FAIL; + + BufferResource::Desc vertexBufferDesc; + vertexBufferDesc.init(kVertexCount * sizeof(Vertex)); + + m_vertexBuffer = renderer->createBufferResource(Resource::Usage::VertexBuffer, vertexBufferDesc, kVertexData); + if(!m_vertexBuffer) + return SLANG_FAIL; + + { + switch(gOptions.shaderType) + { + default: + assert(!"unexpected test shader type"); + return SLANG_FAIL; + + case Options::ShaderProgramType::Compute: + { + ComputePipelineStateDesc desc; + desc.pipelineLayout = m_bindingState->pipelineLayout; + desc.program = m_shaderProgram; + + m_pipelineState = renderer->createComputePipelineState(desc); + } + break; + + case Options::ShaderProgramType::Graphics: + case Options::ShaderProgramType::GraphicsCompute: + { + GraphicsPipelineStateDesc desc; + desc.pipelineLayout = m_bindingState->pipelineLayout; + desc.program = m_shaderProgram; + desc.inputLayout = m_inputLayout; + desc.renderTargetCount = m_bindingState->m_numRenderTargets; + + m_pipelineState = renderer->createGraphicsPipelineState(desc); + } + break; + } + } + + return SLANG_OK; +} + +Result RenderTestApp::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); + return SLANG_FAIL; + } + fseek(sourceFile, 0, SEEK_END); + size_t sourceSize = ftell(sourceFile); + fseek(sourceFile, 0, SEEK_SET); + + List sourceText; + sourceText.SetSize(sourceSize + 1); + fread(sourceText.Buffer(), sourceSize, 1, sourceFile); + fclose(sourceFile); + sourceText[sourceSize] = 0; + + switch( gOptions.shaderType ) + { + default: + m_shaderInputLayout.numRenderTargets = 1; + break; + + case Options::ShaderProgramType::Compute: + m_shaderInputLayout.numRenderTargets = 0; + break; + } + m_shaderInputLayout.Parse(sourceText.Buffer()); + + ShaderCompileRequest::SourceInfo sourceInfo; + sourceInfo.path = sourcePath; + sourceInfo.dataBegin = sourceText.Buffer(); + sourceInfo.dataEnd = sourceText.Buffer() + sourceSize; + + ShaderCompileRequest compileRequest; + compileRequest.source = sourceInfo; + if (gOptions.shaderType == Options::ShaderProgramType::Graphics || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) + { + compileRequest.vertexShader.source = sourceInfo; + compileRequest.vertexShader.name = vertexEntryPointName; + compileRequest.fragmentShader.source = sourceInfo; + compileRequest.fragmentShader.name = fragmentEntryPointName; + } + else + { + compileRequest.computeShader.source = sourceInfo; + compileRequest.computeShader.name = computeEntryPointName; + } + compileRequest.entryPointTypeArguments = m_shaderInputLayout.globalTypeArguments; + m_shaderProgram = shaderCompiler->compileProgram(compileRequest); + if (!m_shaderProgram) + { + return SLANG_FAIL; + } + + return SLANG_OK; +} + +void RenderTestApp::renderFrame() +{ + auto mappedData = m_renderer->map(m_constantBuffer, MapFlavor::WriteDiscard); + if(mappedData) + { + const ProjectionStyle projectionStyle = RendererUtil::getProjectionStyle(m_renderer->getRendererType()); + RendererUtil::getIdentityProjection(projectionStyle, (float*)mappedData); + + m_renderer->unmap(m_constantBuffer); + } + + auto pipelineType = PipelineType::Graphics; + + m_renderer->setPipelineState(pipelineType, m_pipelineState); + + m_renderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); + m_renderer->setVertexBuffer(0, m_vertexBuffer, sizeof(Vertex)); + + m_bindingState->apply(m_renderer, pipelineType); + + m_renderer->draw(3); +} + +void RenderTestApp::runCompute() +{ + auto pipelineType = PipelineType::Compute; + m_renderer->setPipelineState(pipelineType, m_pipelineState); + m_bindingState->apply(m_renderer, pipelineType); + m_renderer->dispatchCompute(1, 1, 1); +} + +void RenderTestApp::finalize() +{ +} + +Result RenderTestApp::writeBindingOutput(const char* fileName) +{ + // Submit the work + m_renderer->submitGpuWork(); + // Wait until everything is complete + m_renderer->waitForGpu(); + + FILE * f = fopen(fileName, "wb"); + if (!f) + { + return SLANG_FAIL; + } + + for(auto binding : m_bindingState->outputBindings) + { + auto i = binding.entryIndex; + const auto& layoutBinding = m_shaderInputLayout.entries[i]; + + assert(layoutBinding.isOutput); + { + if (binding.resource && binding.resource->isBuffer()) + { + BufferResource* bufferResource = static_cast(binding.resource.Ptr()); + const size_t bufferSize = bufferResource->getDesc().sizeInBytes; + + unsigned int* ptr = (unsigned int*)m_renderer->map(bufferResource, MapFlavor::HostRead); + if (!ptr) + { + fclose(f); + return SLANG_FAIL; + } + + const int size = int(bufferSize / sizeof(unsigned int)); + for (int i = 0; i < size; ++i) + { + fprintf(f, "%X\n", ptr[i]); + } + m_renderer->unmap(bufferResource); + } + else + { + printf("invalid output type at %d.\n", int(i)); + } + } + } + fclose(f); + + return SLANG_OK; +} + + +Result RenderTestApp::writeScreen(const char* filename) +{ + Surface surface; + SLANG_RETURN_ON_FAIL(m_renderer->captureScreenSurface(surface)); + return PngSerializeUtil::write(filename, surface); +} + +} // namespace renderer_test + +SLANG_TEST_TOOL_API SlangResult innerMain(Slang::StdWriters* stdWriters, SlangSession* session, int argcIn, const char*const* argvIn) +{ + using namespace renderer_test; + using namespace Slang; + + StdWriters::setSingleton(stdWriters); + + // Parse command-line options + SLANG_RETURN_ON_FAIL(parseOptions(argcIn, argvIn, StdWriters::getError())); + + RefPtr window(new renderer_test::Window); + SLANG_RETURN_ON_FAIL(window->initialize(gWindowWidth, gWindowHeight)); + + Slang::RefPtr renderer; + + SlangSourceLanguage nativeLanguage = SLANG_SOURCE_LANGUAGE_UNKNOWN; + SlangCompileTarget slangTarget = SLANG_TARGET_NONE; + SlangPassThrough slangPassThrough = SLANG_PASS_THROUGH_NONE; + char const* profileName = ""; + switch (gOptions.rendererType) + { + case RendererType::DirectX11: + renderer = createD3D11Renderer(); + slangTarget = SLANG_DXBC; + nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; + slangPassThrough = SLANG_PASS_THROUGH_FXC; + profileName = "sm_5_0"; + break; + + case RendererType::DirectX12: + renderer = createD3D12Renderer(); + slangTarget = SLANG_DXBC; + nativeLanguage = SLANG_SOURCE_LANGUAGE_HLSL; + slangPassThrough = SLANG_PASS_THROUGH_FXC; + profileName = "sm_5_0"; + if( gOptions.useDXIL ) + { + slangTarget = SLANG_DXIL; + slangPassThrough = SLANG_PASS_THROUGH_DXC; + profileName = "sm_6_0"; + } + break; + + case RendererType::OpenGl: + renderer = createGLRenderer(); + slangTarget = SLANG_GLSL; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; + slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; + profileName = "glsl_430"; + break; + + case RendererType::Vulkan: + renderer = createVKRenderer(); + slangTarget = SLANG_SPIRV; + nativeLanguage = SLANG_SOURCE_LANGUAGE_GLSL; + slangPassThrough = SLANG_PASS_THROUGH_GLSLANG; + profileName = "glsl_430"; + break; + + default: + fprintf(stderr, "error: unexpected\n"); + return SLANG_FAIL; + } + + if (!renderer) + { + fprintf(stderr, "Unable to create renderer\n"); + return SLANG_FAIL; + } + + Renderer::Desc desc; + desc.width = gWindowWidth; + desc.height = gWindowHeight; + + { + SlangResult res = renderer->initialize(desc, (HWND)window->getHandle()); + if (SLANG_FAILED(res)) + { + fprintf(stderr, "Unable to initialize renderer\n"); + return res; + } + } + + ShaderCompiler shaderCompiler; + shaderCompiler.renderer = renderer; + shaderCompiler.target = slangTarget; + shaderCompiler.profile = profileName; + shaderCompiler.slangSession = session; + + switch (gOptions.inputLanguageID) + { + case Options::InputLanguageID::Slang: + shaderCompiler.sourceLanguage = SLANG_SOURCE_LANGUAGE_SLANG; + shaderCompiler.passThrough = SLANG_PASS_THROUGH_NONE; + break; + + case Options::InputLanguageID::Native: + shaderCompiler.sourceLanguage = nativeLanguage; + shaderCompiler.passThrough = slangPassThrough; + break; + + default: + break; + } + + { + RenderTestApp app; + + SLANG_RETURN_ON_FAIL(app.initialize(renderer, &shaderCompiler)); + + window->show(); + + // ... 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 + { + // Whenever we don't have Windows events to process, we render a frame. + if (gOptions.shaderType == Options::ShaderProgramType::Compute) + { + app.runCompute(); + } + else + { + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + renderer->setClearColor(kClearColor); + renderer->clearFrame(); + + app.renderFrame(); + } + // If we are in a mode where output is requested, we need to snapshot the back buffer here + if (gOptions.outputPath) + { + // Submit the work + renderer->submitGpuWork(); + // Wait until everything is complete + renderer->waitForGpu(); + + if (gOptions.shaderType == Options::ShaderProgramType::Compute || gOptions.shaderType == Options::ShaderProgramType::GraphicsCompute) + { + SLANG_RETURN_ON_FAIL(app.writeBindingOutput(gOptions.outputPath)); + } + else + { + SlangResult res = app.writeScreen(gOptions.outputPath); + + if (SLANG_FAILED(res)) + { + fprintf(stderr, "ERROR: failed to write screen capture to file\n"); + return res; + } + } + return SLANG_OK; + } + + renderer->presentFrame(); + } + } + } + + return SLANG_OK; +} + + +int main(int argc, char** argv) +{ + SlangSession* session = spCreateSession(nullptr); + SlangResult res = innerMain(Slang::StdWriters::initDefault(), session, argc, argv); + spDestroySession(session); + + return SLANG_FAILED(res) ? 1 : 0; +} + diff --git a/tools/render-test/render-test-tool.vcxproj b/tools/render-test/render-test-tool.vcxproj index 811ffa56d..dea8627e3 100644 --- a/tools/render-test/render-test-tool.vcxproj +++ b/tools/render-test/render-test-tool.vcxproj @@ -186,9 +186,9 @@ - + diff --git a/tools/render-test/render-test-tool.vcxproj.filters b/tools/render-test/render-test-tool.vcxproj.filters index ff3d52a7e..39197c7e9 100644 --- a/tools/render-test/render-test-tool.vcxproj.filters +++ b/tools/render-test/render-test-tool.vcxproj.filters @@ -26,15 +26,15 @@ - - Source Files - Source Files Source Files + + Source Files + Source Files -- cgit v1.2.3