summaryrefslogtreecommitdiffstats
path: root/examples/hello/hello.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'examples/hello/hello.cpp')
-rw-r--r--examples/hello/hello.cpp782
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)