diff options
Diffstat (limited to 'tools/render-test/main.cpp')
| -rw-r--r-- | tools/render-test/main.cpp | 369 |
1 files changed, 369 insertions, 0 deletions
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; +} + |
