diff options
Diffstat (limited to 'examples/hello/hello.cpp')
| -rw-r--r-- | examples/hello/hello.cpp | 782 |
1 files changed, 294 insertions, 488 deletions
diff --git a/examples/hello/hello.cpp b/examples/hello/hello.cpp index aef6b90d1..a5d90a3f4 100644 --- a/examples/hello/hello.cpp +++ b/examples/hello/hello.cpp @@ -1,43 +1,192 @@ // hello.cpp -// In order to use the Slang API, we need to include its header - +// This file implements an extremely simple example of loading and +// executing a Slang shader program. +// +// The comments in the file will attempt to explain concepts as +// they are introduced. +// +// Of course, in order to use the Slang API, we need to include +// its header. We have set up the build options for this project +// so that it is as simple as: +// #include <slang.h> +// +// Other build setups are possible, and Slang doesn't assume that +// its include directory must be added to your global include +// path. + +// For the purposes of keeping the demo code as simple as possible, +// while still retaining some level of portability, our examples +// make use of a small platform and graphics API abstraction layer, +// which is included in the Slang source distribution under the +// `tools/` directory. +// +// Applications can of course use Slang without ever touching this +// abstraction layer, so we will not focus on it when explaining +// examples, except in places where best practices for interacting +// with Slang may depend on an application/engine making certain +// design choices in their abstraction layer. +// +#include "slang-graphics/render.h" +#include "slang-graphics/render-d3d11.h" +#include "slang-graphics/window.h" +using namespace slang_graphics; + +// We will start with a function that will invoke the Slang compiler +// to generate target-specific code from a shader file, and then +// use that to initialize an API shader program. +// +// Note that `Renderer` and `ShaderProgram` here are types from +// the graphics API abstraction layer, and *not* part of the +// Slang API. This function is representative of code that a user +// might write to integrate Slang into their renderer/engine. +// +ShaderProgram* loadShaderProgram(Renderer* renderer) +{ + // First, we need to create a "session" for interacting with the Slang + // compiler. This scopes all of our application's interactions + // with the Slang library. At the moment, creating a session causes + // Slang to load and validate its standard library, so this is a + // somewhat heavy-weight operation. When possible, an application + // should try to re-use the same session across multiple compiles. + SlangSession* slangSession = spCreateSession(NULL); -// We will be rendering with Direct3D 11, so we need to include -// the Windows and D3D11 headers + // A compile request represents a single invocation of the compiler, + // to process some inputs and produce outputs (or errors). + SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include <Windows.h> -#undef WIN32_LEAN_AND_MEAN -#undef NOMINMAX + // We would like to request a single target (output) format: DirectX shader bytecode (DXBC) + int targetIndex = spAddCodeGenTarget(slangRequest, SLANG_DXBC); -#include <d3d11_2.h> -#include <d3dcompiler.h> + // We will specify the desired "profile" for this one target in terms of the + // DirectX "shader model" that should be supported. + spSetTargetProfile(slangRequest, targetIndex, spFindProfile(slangSession, "sm_4_0")); -// We will use the C standard library just for printing error messages. -#include <stdio.h> + // A compile request can include one or more "translation units," which more or + // less amount to individual source files (think `.c` files, not the `.h` files they + // might include). + // + // For this example, our code will all be in the Slang language. The user may + // also specify HLSL input here, but that currently doesn't affect the compiler's + // behavior much. + int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr); -#ifdef _MSC_VER -#include <stddef.h> -#if (_MSC_VER < 1900) -#define snprintf sprintf_s -#endif -#endif -// + // We will load source code for our translation unit from the file `hello.slang`. + // There are also variations of this API for adding source code from application-provided buffers. + spAddTranslationUnitSourceFile(slangRequest, translationUnitIndex, "hello.slang"); -static int gWindowWidth = 1024; -static int gWindowHeight = 768; + // Next we will specify the entry points we'd like to compile. + // It is often convenient to put more than one entry point in the same file, + // and the Slang API makes it convenient to use a single run of the compiler + // to compile all entry points. + // + // For each entry point, we need to specify the name of a function, the + // translation unit in which that function can be found, and the stage + // that we need to compile for (e.g., vertex, fragment, geometry, ...). + // + char const* vertexEntryPointName = "vertexMain"; + char const* fragmentEntryPointName = "fragmentMain"; + int vertexIndex = spAddEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, SLANG_STAGE_VERTEX); + int fragmentIndex = spAddEntryPoint(slangRequest, translationUnitIndex, fragmentEntryPointName, SLANG_STAGE_FRAGMENT); + + // Once all of the input options for hte compiler have been specified, + // we can invoke `spCompile` to run the compiler and see if any errors + // were detected. + // + int compileErr = spCompile(slangRequest); + + // Even if there were no errors that forced compilation to fail, the + // compiler may have produced "diagnostic" output such as warnings. + // We will go ahead and print that output here. + // + if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) + { + reportError("%s", diagnostics); + } + + // If compilation failed, there is no point in continuing any further. + if(compileErr) + { + spDestroyCompileRequest(slangRequest); + spDestroySession(slangSession); + return nullptr; + } + + // If compilation was successful, then we will extract the code for + // our two entry points as "blobs". + // + // If you are using a D3D API, then your application may want to + // take advantage of the fact taht these blobs are binary compatible + // with the `ID3DBlob`, `ID3D10Blob`, etc. interfaces. + + ISlangBlob* vertexShaderBlob = nullptr; + spGetEntryPointCodeBlob(slangRequest, vertexIndex, 0, &vertexShaderBlob); + + ISlangBlob* fragmentShaderBlob = nullptr; + spGetEntryPointCodeBlob(slangRequest, fragmentIndex, 0, &fragmentShaderBlob); + + // We extract the begin/end pointers to the output code buffers + // using operations on the `ISlangBlob` interface. + char const* vertexCode = (char const*) vertexShaderBlob->getBufferPointer(); + char const* vertexCodeEnd = vertexCode + vertexShaderBlob->getBufferSize(); + char const* fragmentCode = (char const*) fragmentShaderBlob->getBufferPointer(); + char const* fragmentCodeEnd = fragmentCode + fragmentShaderBlob->getBufferSize(); + + // Once we have extract the output blobs, it is safe to destroy + // the compile request and even the session. + // + spDestroyCompileRequest(slangRequest); + spDestroySession(slangSession); + + // Now we use the operations of the example graphics API abstraction + // layer to load shader code into the underlying API. + // + // Reminder: this section does not involve the Slang API at all. + // + + ShaderProgram::KernelDesc kernelDescs[] = + { + { StageType::Vertex, vertexCode, vertexCodeEnd}, + { StageType::Fragment, fragmentCode, fragmentCodeEnd}, + }; + + ShaderProgram::Desc programDesc; + programDesc.pipelineType = PipelineType::Graphics; + programDesc.kernels = &kernelDescs[0]; + programDesc.kernelCount = 2; + + ShaderProgram* shaderProgram = renderer->createProgram(programDesc); + + // Once we've used the output blobs from the Slang compiler to initialize + // the API-specific shader program, we can release their memory. + // + vertexShaderBlob->release(); + fragmentShaderBlob->release(); + + return shaderProgram; +} + +// +// The above function shows the core of what is required to use the +// Slang API as a simple compiler (e.g., a drop-in replacement for +// fxc or dxc). +// +// The rest of this file implements an extremely simple rendering application +// that will execute the vertex/fragment shaders loaded with the function +// we have just defined. // +// We will hard-code the size of our rendering window. // +static int gWindowWidth = 1024; +static 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]; @@ -45,534 +194,191 @@ struct Vertex }; static const int kVertexCount = 3; -static const Vertex kVertexData[kVertexCount] = { +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} }, }; +// We will define global variables for the various platform and +// graphics API objects that our application needs: // - -// Global variabels for the various D3D11 API objects to be used for rendering -ID3D11Buffer* dxConstantBuffer; -ID3D11InputLayout* dxInputLayout; -ID3D11Buffer* dxVertexBuffer; -ID3D11VertexShader* dxVertexShader; -ID3D11PixelShader* dxPixelShader; - -// The Slang compiler currently generates HLSL source, so we'll need a utility -// routine (defined later) to translate that into D3D11 shader bytecode. -ID3DBlob* compileHLSLShader( - char const* source, - char const* entryPointName, - char const* dxProfileName); - -// -// 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. +// As a reminder, *none* of these are Slang API objects. All +// of them come from the utility library we are using to simplify +// building an example program. // -HRESULT initialize( ID3D11Device* dxDevice ) +ApplicationContext* gAppContext; +Window* gWindow; +Renderer* gRenderer; +BufferResource* gConstantBuffer; +InputLayout* gInputLayout; +BufferResource* gVertexBuffer; +ShaderProgram* gShaderProgram; +BindingState* gBindingState; + +enum +{ + OKAY, + FAILURE, +}; + +int initialize() { -#if 1 + // Create a window for our application to render into. + WindowDesc windowDesc; + windowDesc.title = "Hello, World!"; + windowDesc.width = gWindowWidth; + windowDesc.height = gWindowHeight; + gWindow = createWindow(windowDesc); + + // Initialize the rendering layer. // - // First, we will load and compile our Slang source code. + // Note: for now we are hard-coding logic to use the + // Direct3D11 back-end for the graphics API abstraction. + // A future version of this example may support multiple + // platforms/APIs. // + gRenderer = createD3D11Renderer(); + Renderer::Desc rendererDesc; + rendererDesc.width = gWindowWidth; + rendererDesc.height = gWindowHeight; + gRenderer->initialize(rendererDesc, getPlatformWindowHandle(gWindow)); - // The argument here is an optional directory where the Slang compiler - // can cache files to speed up compilation of many kernels. - SlangSession* slangSession = spCreateSession(NULL); - - // A compile request represents a single invocation of the compiler, - // to process some inputs and produce outputs (or errors). - SlangCompileRequest* slangRequest = spCreateCompileRequest(slangSession); - - // Instruct Slang to generate code as HLSL - spSetCodeGenTarget(slangRequest, SLANG_HLSL); - - int translationUnitIndex = spAddTranslationUnit(slangRequest, SLANG_SOURCE_LANGUAGE_SLANG, nullptr); - - spAddTranslationUnitSourceFile(slangRequest, translationUnitIndex, "hello.slang"); - - char const* vertexEntryPointName = "vertexMain"; - char const* fragmentEntryPointName = "fragmentMain"; - - char const* vertexProfileName = "vs_4_0"; - char const* fragmentProfileName = "ps_4_0"; - - int vertexIndex = spAddEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, spFindProfile(slangSession, vertexProfileName)); - int fragmentIndex = spAddEntryPoint(slangRequest, translationUnitIndex, fragmentEntryPointName, spFindProfile(slangSession, fragmentProfileName)); - - int compileErr = spCompile(slangRequest); - if(auto diagnostics = spGetDiagnosticOutput(slangRequest)) - { - OutputDebugStringA(diagnostics); - fprintf(stderr, "%s", diagnostics); - } - if(compileErr) - { - return E_FAIL; - } - - char const* vertexCode = spGetEntryPointSource(slangRequest, vertexIndex); - char const* fragmentCode = spGetEntryPointSource(slangRequest, fragmentIndex); - - // TODO(tfoley): Query the required constant-buffer size + // Create a constant buffer for passing the model-view-projection matrix. + // + // TODO: A future version of this example will show how to + // use the Slang reflection API to query the required size + // for the data in this constant buffer. + // int constantBufferSize = 16 * sizeof(float); - // Compile the generated HLSL code - ID3DBlob* dxVertexShaderBlob = compileHLSLShader(vertexCode, vertexEntryPointName, vertexProfileName); - if(!dxVertexShaderBlob) return E_FAIL; + BufferResource::Desc constantBufferDesc; + constantBufferDesc.init(constantBufferSize); + constantBufferDesc.setDefaults(Resource::Usage::ConstantBuffer); + constantBufferDesc.cpuAccessFlags = Resource::AccessFlag::Write; - ID3DBlob* dxPixelShaderBlob = compileHLSLShader(fragmentCode, fragmentEntryPointName, fragmentProfileName); - if(!dxPixelShaderBlob) return E_FAIL; + gConstantBuffer = gRenderer->createBufferResource( + Resource::Usage::ConstantBuffer, + constantBufferDesc); + if(!gConstantBuffer) return FAILURE; - HRESULT hr = S_OK; + // Input Assembler (IA) + // Input Layout - D3D11_BUFFER_DESC dxConstantBufferDesc = { 0 }; - dxConstantBufferDesc.ByteWidth = constantBufferSize; - dxConstantBufferDesc.Usage = D3D11_USAGE_DYNAMIC; - dxConstantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; - dxConstantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; + InputElementDesc inputElements[] = { + {"POSITION", 0, Format::RGB_Float32, offsetof(Vertex, position) }, + {"COLOR", 0, Format::RGB_Float32, offsetof(Vertex, color) }, + }; + gInputLayout = gRenderer->createInputLayout( + &inputElements[0], + 2); + if(!gInputLayout) return FAILURE; - hr = dxDevice->CreateBuffer( - &dxConstantBufferDesc, - NULL, - &dxConstantBuffer); - if(FAILED(hr)) return hr; + // Vertex Buffer - // We clean up the Slang compilation context and result *after* - // we have done the HLSL-to-bytecode compilation, because Slang - // owns the memory allocation for the generated HLSL, and will - // free it when we destroy the compilation result. - spDestroyCompileRequest(slangRequest); - spDestroySession(slangSession); + BufferResource::Desc vertexBufferDesc; + vertexBufferDesc.init(kVertexCount * sizeof(Vertex)); + vertexBufferDesc.setDefaults(Resource::Usage::VertexBuffer); - // Input Assembler (IA) + gVertexBuffer = gRenderer->createBufferResource( + Resource::Usage::VertexBuffer, + vertexBufferDesc, + &kVertexData[0]); + if(!gVertexBuffer) return FAILURE; - // In Slang-generated HLSL, all vertex shader inputs have a semantic - // like: `A0`, `A1`, `A2`, etc., rather than trying to do by-name - // matching. The user is thus responsibile for ensuring that the - // order of their "input element descs" here matches the order - // in which inputs are declared in the shader code. - D3D11_INPUT_ELEMENT_DESC dxInputElements[] = { - {"A", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, position), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - {"A", 1, DXGI_FORMAT_R32G32B32_FLOAT, 0, offsetof(Vertex, color), D3D11_INPUT_PER_VERTEX_DATA, 0 }, - }; - hr = dxDevice->CreateInputLayout( - &dxInputElements[0], - 2, - dxVertexShaderBlob->GetBufferPointer(), - dxVertexShaderBlob->GetBufferSize(), - &dxInputLayout); - if(FAILED(hr)) return hr; - - D3D11_BUFFER_DESC dxVertexBufferDesc = { 0 }; - dxVertexBufferDesc.ByteWidth = kVertexCount * sizeof(Vertex); - dxVertexBufferDesc.Usage = D3D11_USAGE_IMMUTABLE; - dxVertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; - - D3D11_SUBRESOURCE_DATA dxVertexBufferInitData = { 0 }; - dxVertexBufferInitData.pSysMem = &kVertexData[0]; - - hr = dxDevice->CreateBuffer( - &dxVertexBufferDesc, - &dxVertexBufferInitData, - &dxVertexBuffer); - if(FAILED(hr)) return hr; + // Shaders (VS, PS, ...) - // Vertex Shader (VS) + gShaderProgram = loadShaderProgram(gRenderer); + if(!gShaderProgram) return FAILURE; - hr = dxDevice->CreateVertexShader( - dxVertexShaderBlob->GetBufferPointer(), - dxVertexShaderBlob->GetBufferSize(), - NULL, - &dxVertexShader); - dxVertexShaderBlob->Release(); - if(FAILED(hr)) return hr; + // Resource binding state - // Pixel Shader (PS) + BindingState::Desc bindingStateDesc; + bindingStateDesc.addBufferResource(gConstantBuffer, BindingState::RegisterRange::makeSingle(0)); + gBindingState = gRenderer->createBindingState(bindingStateDesc); + + // Once we've initialized all the graphics API objects, + // it is time to show our application window and start rendering. - hr = dxDevice->CreatePixelShader( - dxPixelShaderBlob->GetBufferPointer(), - dxPixelShaderBlob->GetBufferSize(), - NULL, - &dxPixelShader); - dxPixelShaderBlob->Release(); - if(FAILED(hr)) return hr; -#endif + showWindow(gWindow); - return S_OK; + return OKAY; } -void renderFrame(ID3D11DeviceContext* dxContext) +void renderFrame() { + // Clear our framebuffer (color target only) + // + static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; + gRenderer->setClearColor(kClearColor); + gRenderer->clearFrame(); + + // We update our constant buffer per-frame, just for the purposes // of the example, but we don't actually load different data // per-frame (we always use an identity projection). - D3D11_MAPPED_SUBRESOURCE mapped; - HRESULT hr = dxContext->Map(dxConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mapped); - if(!FAILED(hr)) + // + if(float* data = (float*) gRenderer->map(gConstantBuffer, MapFlavor::WriteDiscard)) { - float* data = (float*) mapped.pData; - static const float kIdentity[] = { 1, 0, 0, 0, - 0, 1, 0, 0, - 0, 0, 1, 0, - 0, 0, 0, 1 }; + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 }; memcpy(data, kIdentity, sizeof(kIdentity)); - dxContext->Unmap(dxConstantBuffer, 0); + gRenderer->unmap(gConstantBuffer); } // Input Assembler (IA) - dxContext->IASetInputLayout(dxInputLayout); - dxContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); + gRenderer->setInputLayout(gInputLayout); + gRenderer->setPrimitiveTopology(PrimitiveTopology::TriangleList); - UINT dxVertexStride = sizeof(Vertex); - UINT dxVertexBufferOffset = 0; - dxContext->IASetVertexBuffers(0, 1, &dxVertexBuffer, &dxVertexStride, &dxVertexBufferOffset); + UInt vertexStride = sizeof(Vertex); + UInt vertexBufferOffset = 0; + gRenderer->setVertexBuffers(0, 1, &gVertexBuffer, &vertexStride, &vertexBufferOffset); // Vertex Shader (VS) - - dxContext->VSSetShader(dxVertexShader, NULL, 0); - dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); - // Pixel Shader (PS) - dxContext->PSSetShader(dxPixelShader, NULL, 0); - dxContext->VSSetConstantBuffers(0, 1, &dxConstantBuffer); + gRenderer->setShaderProgram(gShaderProgram); + gRenderer->setBindingState(gBindingState); // - dxContext->Draw(3, 0); + gRenderer->draw(3); + + gRenderer->presentFrame(); } void finalize() { + // TODO: Proper cleanup. } +// This "inner" main function is used by the platform abstraction +// layer to deal with differences in how an entry point needs +// to be defined for different platforms. // -// Definition of the HLSL-to-bytecode compilation logic. -// -ID3DBlob* compileHLSLShader( - char const* source, - char const* entryPointName, - char const* dxProfileName ) +void innerMain(ApplicationContext* context) { - // Rather than statically link against the `d3dcompile` library, we - // dynamically load it. - // - // Note: A more realistic application would compile from HLSL text to D3D - // shader bytecode as part of an offline process, rather than doing it - // on-the-fly like this - // - static pD3DCompile D3DCompile_ = nullptr; - if( !D3DCompile_ ) - { - // TODO(tfoley): maybe want to search for one of a few versions of the DLL - HMODULE d3dcompiler = LoadLibraryA("d3dcompiler_47.dll"); - if(!d3dcompiler) - { - fprintf(stderr, "error: failed load 'd3dcompiler_47.dll'\n"); - exit(1); - } - - D3DCompile_ = (pD3DCompile)GetProcAddress(d3dcompiler, "D3DCompile"); - if( !D3DCompile_ ) - { - fprintf(stderr, "error: failed load symbol 'D3DCompile'\n"); - exit(1); - } - } - - // For this example, we turn on debug output, and turn off all - // optimization. A real application would only use these flags - // when shader debugging is needed. - UINT flags = 0; - flags |= D3DCOMPILE_DEBUG; - flags |= D3DCOMPILE_OPTIMIZATION_LEVEL0 | D3DCOMPILE_SKIP_OPTIMIZATION; - - // The `D3DCompile` entry point takes a bunch of parameters, but we - // don't really need most of them for Slang-generated code. - ID3DBlob* dxShaderBlob = nullptr; - ID3DBlob* dxErrorBlob = nullptr; - HRESULT hr = D3DCompile_( - source, - strlen(source), - "slangGeneratedCode", // TODO: proper path for error messages - nullptr, - nullptr, - entryPointName, - dxProfileName, - flags, - 0, - &dxShaderBlob, - &dxErrorBlob); - - // If the HLSL-to-bytecode compilation produced any diagnostic messages - // then we will print them out (whether or not the compilation failed). - if( dxErrorBlob ) - { - OutputDebugStringA( - (char const*)dxErrorBlob->GetBufferPointer()); - dxErrorBlob->Release(); - } - - if( FAILED(hr) ) + if(initialize() != OKAY) { - return nullptr; + exitApplication(context, 1); } - return dxShaderBlob; -} - - -// -// 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) + while(dispatchEvents(context)) { - case WM_CLOSE: - PostQuitMessage(0); - return 0; + renderFrame(); } - return DefWindowProcW(windowHandle, message, wParam, lParam); + finalize(); } +// This macro instantiates an appropriate main function to +// invoke the `innerMain` above. // -// Our `WinMain` handles the basic task of getting a window and rendering -// context up and running. There should be nothing suprising or interesting -// here. -// - -int WINAPI WinMain( - HINSTANCE instance, - HINSTANCE /* prevInstance */, - LPSTR /* commandLine */, - int showCommand) -{ - // 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; - } - - - // Rather than statically link against D3D, we load it dynamically. - - HMODULE d3d11 = LoadLibraryA("d3d11.dll"); - if(!d3d11) - { - fprintf(stderr, "error: failed load 'd3d11.dll'\n"); - return 1; - } - - PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN D3D11CreateDeviceAndSwapChain_ = - (PFN_D3D11_CREATE_DEVICE_AND_SWAP_CHAIN)GetProcAddress( - d3d11, - "D3D11CreateDeviceAndSwapChain"); - if(!D3D11CreateDeviceAndSwapChain_) - { - fprintf(stderr, - "error: failed load symbol 'D3D11CreateDeviceAndSwapChain'\n"); - return 1; - } - - // We create our device in debug mode, just so that we can check that the - // example doesn't trigger warnings. - UINT deviceFlags = 0; - deviceFlags |= D3D11_CREATE_DEVICE_DEBUG; - - // We will ask for the highest feature level that can be supported. - - D3D_FEATURE_LEVEL featureLevels[] = { - D3D_FEATURE_LEVEL_11_1, - D3D_FEATURE_LEVEL_11_0, - D3D_FEATURE_LEVEL_10_1, - D3D_FEATURE_LEVEL_10_0, - D3D_FEATURE_LEVEL_9_3, - D3D_FEATURE_LEVEL_9_2, - D3D_FEATURE_LEVEL_9_1, - }; - D3D_FEATURE_LEVEL dxFeatureLevel = D3D_FEATURE_LEVEL_9_1; - - // Our swap chain uses RGBA8 with sRGB, with double buffering. - - DXGI_SWAP_CHAIN_DESC dxSwapChainDesc = { 0 }; - dxSwapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; - dxSwapChainDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; - dxSwapChainDesc.SampleDesc.Count = 1; - dxSwapChainDesc.SampleDesc.Quality = 0; - dxSwapChainDesc.BufferCount = 2; - dxSwapChainDesc.OutputWindow = windowHandle; - dxSwapChainDesc.Windowed = TRUE; - dxSwapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; - dxSwapChainDesc.Flags = 0; - - // On a machine that does not have an up-to-date version of D3D installed, - // the `D3D11CreateDeviceAndSwapChain` call will fail with `E_INVALIDARG` - // if you ask for featuer level 11_1. The workaround is to call - // `D3D11CreateDeviceAndSwapChain` up to twice: the first time with 11_1 - // at the start of the list of requested feature levels, and the second - // time without it. - - IDXGISwapChain* dxSwapChain = NULL; - ID3D11Device* dxDevice = NULL; - ID3D11DeviceContext* dxImmediateContext = NULL; - HRESULT hr = S_OK; - for( int ii = 0; ii < 2; ++ii ) - { - hr = D3D11CreateDeviceAndSwapChain_( - NULL, // adapter (use default) - D3D_DRIVER_TYPE_HARDWARE, - NULL, // software - deviceFlags, - &featureLevels[ii], - (sizeof(featureLevels) / sizeof(featureLevels[0])) - 1, - D3D11_SDK_VERSION, - &dxSwapChainDesc, - &dxSwapChain, - &dxDevice, - &dxFeatureLevel, - &dxImmediateContext); - - // Failures with `E_INVALIDARG` might be due to feature level 11_1 - // not being supported. Other failures are real, though. - if( hr != E_INVALIDARG ) - break; - } - if( FAILED(hr) ) - { - return 1; - } - - // After we've created the swap chain, we can request a pointer to the - // back buffer as a D3D11 texture, and create a render-target view from it. - - ID3D11Texture2D* dxBackBufferTexture = NULL; - static const IID kIID_ID3D11Texture2D = { - 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48, - 0x95, 0x35, 0xd3, 0x4f, 0x9c }; - dxSwapChain->GetBuffer( - 0, - kIID_ID3D11Texture2D, - (void**)&dxBackBufferTexture); - - ID3D11RenderTargetView* dxBackBufferRTV = NULL; - dxDevice->CreateRenderTargetView( - dxBackBufferTexture, - NULL, - &dxBackBufferRTV); - - // We immediately bind the back-buffer render target view, and we aren't - // going to switch. We don't bother with a depth buffer. - dxImmediateContext->OMSetRenderTargets( - 1, - &dxBackBufferRTV, - NULL); - - // Similarly, we are going to set up a viewport once, and then never - // switch, since this is a simple test app. - D3D11_VIEWPORT dxViewport; - dxViewport.TopLeftX = 0; - dxViewport.TopLeftY = 0; - dxViewport.Width = (float) gWindowWidth; - dxViewport.Height = (float) gWindowHeight; - dxViewport.MaxDepth = 1; // TODO(tfoley): use reversed depth - dxViewport.MinDepth = 0; - dxImmediateContext->RSSetViewports(1, &dxViewport); - - // Once we've done the general-purpose initialization, we - // initialize anything specific to the "hello world" application - hr = initialize( dxDevice ); - if( FAILED(hr) ) - { - 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. - - static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 }; - dxImmediateContext->ClearRenderTargetView( - dxBackBufferRTV, - kClearColor); - - renderFrame( dxImmediateContext ); - - dxSwapChain->Present(0, 0); - } - } - - return 0; -} +SG_UI_MAIN(innerMain) |
