summaryrefslogtreecommitdiffstats
path: root/tools/render-test/render-d3d11.cpp
diff options
context:
space:
mode:
authorTim Foley <tfoley@nvidia.com>2017-06-09 11:34:21 -0700
committerTim Foley <tfoley@nvidia.com>2017-06-09 13:44:59 -0700
commitfcf83dbf9effab3bd98bad2b83b2468b7eb05cfd (patch)
tree41047c94883b86ec085a81597391ce3ef557cd43 /tools/render-test/render-d3d11.cpp
parent52e8d4b9a27ab0060f874c3a63ab531847be35c0 (diff)
Initial import of code.
Diffstat (limited to 'tools/render-test/render-d3d11.cpp')
-rw-r--r--tools/render-test/render-d3d11.cpp900
1 files changed, 900 insertions, 0 deletions
diff --git a/tools/render-test/render-d3d11.cpp b/tools/render-test/render-d3d11.cpp
new file mode 100644
index 000000000..ab981bd45
--- /dev/null
+++ b/tools/render-test/render-d3d11.cpp
@@ -0,0 +1,900 @@
+// render-d3d11.cpp
+#include "render-d3d11.h"
+
+#include "options.h"
+#include "render.h"
+
+// In order to use the Slang API, we need to include its header
+
+#include <Slang.h>
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "external/stb/stb_image_write.h"
+
+// We will be rendering with Direct3D 11, so we need to include
+// the Windows and D3D11 headers
+
+#define WIN32_LEAN_AND_MEAN
+#define NOMINMAX
+#include <Windows.h>
+#undef WIN32_LEAN_AND_MEAN
+#undef NOMINMAX
+
+#include <d3d11_2.h>
+#include <d3dcompiler.h>
+
+// We will use the C standard library just for printing error messages.
+#include <stdio.h>
+
+#ifdef _MSC_VER
+#include <stddef.h>
+#if (_MSC_VER < 1900)
+#define snprintf sprintf_s
+#endif
+#endif
+//
+
+namespace renderer_test {
+
+//
+
+
+//
+
+// 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* sourcePath,
+ char const* source,
+ char const* entryPointName,
+ char const* dxProfileName);
+
+static char const* vertexEntryPointName = "vertexMain";
+static char const* fragmentEntryPointName = "fragmentMain";
+
+static char const* vertexProfileName = "vs_4_0";
+static char const* fragmentProfileName = "ps_4_0";
+
+ID3DBlob* gVertexShaderBlob;
+ID3DBlob* gPixelShaderBlob;
+
+// Initialization when using HLSL for shaders
+HRESULT initializeHLSLInner(ID3D11Device* dxDevice, char const* sourcePath, char const* sourceText)
+{
+ // Compile the generated HLSL code
+ gVertexShaderBlob = compileHLSLShader(sourcePath, sourceText, vertexEntryPointName, vertexProfileName);
+ if(!gVertexShaderBlob) return E_FAIL;
+
+ gPixelShaderBlob = compileHLSLShader(sourcePath, sourceText, fragmentEntryPointName, fragmentProfileName);
+ if(!gPixelShaderBlob) return E_FAIL;
+
+
+ return S_OK;
+}
+
+// Initialization when using HLSL for shaders
+HRESULT initializeHLSL(ID3D11Device* dxDevice, char const* sourceText)
+{
+ HRESULT hr = initializeHLSLInner(dxDevice, gOptions.sourcePath, sourceText);
+ if(FAILED(hr))
+ return hr;
+
+ // TODO: any reflection stuff to do here?
+
+ return S_OK;
+}
+
+// Initialization when using Slang for shaders
+HRESULT initializeSlang(ID3D11Device* dxDevice, char const* sourceText)
+{
+ //
+ // First, we will load and compile our Slang source code.
+ //
+
+ // 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);
+
+ spAddTranslationUnitSourceString(slangRequest, translationUnitIndex, gOptions.sourcePath, sourceText);
+
+ spAddTranslationUnitEntryPoint(slangRequest, translationUnitIndex, vertexEntryPointName, spFindProfile(slangSession, vertexProfileName));
+ spAddTranslationUnitEntryPoint(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* translatedCode = spGetTranslationUnitSource(slangRequest, translationUnitIndex);
+
+ // Compile the generated HLSL code
+ HRESULT hr = initializeHLSLInner(dxDevice, "slangGeneratedCode", translatedCode);
+ if(FAILED(hr))
+ return hr;
+
+ // 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);
+
+ return S_OK;
+}
+
+#if 0
+
+//
+// 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.
+//
+HRESULT initializeInner( ID3D11Device* dxDevice )
+{
+ HRESULT hr = S_OK;
+
+ // 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;
+
+ switch( gOptions.mode )
+ {
+ case Mode::HLSL:
+ hr = initializeHLSL(dxDevice, sourceText);
+ break;
+
+ case Mode::Slang:
+ hr = initializeSlang(dxDevice, sourceText);
+ break;
+
+ default:
+ hr = E_FAIL;
+ break;
+ }
+ if( FAILED(hr) )
+ {
+ return hr;
+ }
+
+ // 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);
+
+
+ D3D11_BUFFER_DESC dxConstantBufferDesc = { 0 };
+ dxConstantBufferDesc.ByteWidth = gConstantBufferSize;
+ dxConstantBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
+ dxConstantBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ dxConstantBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+
+ hr = dxDevice->CreateBuffer(
+ &dxConstantBufferDesc,
+ NULL,
+ &dxConstantBuffer);
+ if(FAILED(hr)) return hr;
+
+
+ // Input Assembler (IA)
+
+ // 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,
+ gVertexShaderBlob->GetBufferPointer(),
+ gVertexShaderBlob->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;
+
+ // Vertex Shader (VS)
+
+ hr = dxDevice->CreateVertexShader(
+ gVertexShaderBlob->GetBufferPointer(),
+ gVertexShaderBlob->GetBufferSize(),
+ NULL,
+ &dxVertexShader);
+ gVertexShaderBlob->Release();
+ if(FAILED(hr)) return hr;
+
+ // Pixel Shader (PS)
+
+ hr = dxDevice->CreatePixelShader(
+ gPixelShaderBlob->GetBufferPointer(),
+ gPixelShaderBlob->GetBufferSize(),
+ NULL,
+ &dxPixelShader);
+ gPixelShaderBlob->Release();
+ if(FAILED(hr)) return hr;
+
+ return S_OK;
+}
+
+void renderFrameInner(ID3D11DeviceContext* dxContext)
+{
+ // 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))
+ {
+ 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 };
+ memcpy(data, kIdentity, sizeof(kIdentity));
+
+ dxContext->Unmap(dxConstantBuffer, 0);
+ }
+
+ // Input Assembler (IA)
+
+ dxContext->IASetInputLayout(dxInputLayout);
+ dxContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+
+ UINT dxVertexStride = sizeof(Vertex);
+ UINT dxVertexBufferOffset = 0;
+ dxContext->IASetVertexBuffers(0, 1, &dxVertexBuffer, &dxVertexStride, &dxVertexBufferOffset);
+
+ // 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);
+
+ //
+
+ dxContext->Draw(3, 0);
+}
+
+void finalize()
+{
+}
+
+#endif
+
+//
+// Definition of the HLSL-to-bytecode compilation logic.
+//
+ID3DBlob* compileHLSLShader(
+ char const* sourcePath,
+ char const* source,
+ char const* entryPointName,
+ char const* dxProfileName )
+{
+ // 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),
+ sourcePath,
+ 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) )
+ {
+ return nullptr;
+ }
+
+ return dxShaderBlob;
+}
+
+
+
+
+// Capture a texture to a file
+
+static HRESULT captureTextureToFile(
+ ID3D11Device* dxDevice,
+ ID3D11DeviceContext* dxContext,
+ ID3D11Texture2D* dxTexture,
+ char const* outputPath)
+{
+ if(!dxContext) return E_INVALIDARG;
+ if(!dxTexture) return E_INVALIDARG;
+
+ D3D11_TEXTURE2D_DESC dxTextureDesc;
+ dxTexture->GetDesc(&dxTextureDesc);
+
+ // Don't bother supporing MSAA for right now
+ if(dxTextureDesc.SampleDesc.Count > 1)
+ return E_INVALIDARG;
+
+ HRESULT hr = S_OK;
+ ID3D11Texture2D* dxStagingTexture = nullptr;
+
+ if( dxTextureDesc.Usage == D3D11_USAGE_STAGING && (dxTextureDesc.CPUAccessFlags & D3D11_CPU_ACCESS_READ) )
+ {
+ dxStagingTexture = dxTexture;
+ dxStagingTexture->AddRef();
+ }
+ else
+ {
+ // Modify the descriptor to give us a staging texture
+ dxTextureDesc.BindFlags = 0;
+ dxTextureDesc.MiscFlags &= D3D11_RESOURCE_MISC_TEXTURECUBE;
+ dxTextureDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
+ dxTextureDesc.Usage = D3D11_USAGE_STAGING;
+
+ hr = dxDevice->CreateTexture2D(&dxTextureDesc, 0, &dxStagingTexture);
+ if(FAILED(hr))
+ return hr;
+
+ dxContext->CopyResource(dxStagingTexture, dxTexture);
+ }
+
+ // Now just read back texels from the staging textures
+
+ D3D11_MAPPED_SUBRESOURCE dxMappedResource;
+ hr = dxContext->Map(dxStagingTexture, 0, D3D11_MAP_READ, 0, &dxMappedResource);
+ if(FAILED(hr))
+ return hr;
+
+ int stbResult = stbi_write_png(
+ outputPath,
+ dxTextureDesc.Width,
+ dxTextureDesc.Height,
+ 4,
+ dxMappedResource.pData,
+ dxMappedResource.RowPitch);
+ if( !stbResult )
+ {
+ return E_UNEXPECTED;
+ }
+
+ dxContext->Unmap(dxStagingTexture, 0);
+
+ dxStagingTexture->Release();
+
+ return S_OK;
+}
+
+//
+
+class D3D11Renderer : public Renderer, public ShaderCompiler
+{
+public:
+ IDXGISwapChain* dxSwapChain = NULL;
+ ID3D11Device* dxDevice = NULL;
+ ID3D11DeviceContext* dxImmediateContext = NULL;
+ ID3D11Texture2D* dxBackBufferTexture = NULL;
+ ID3D11RenderTargetView* dxBackBufferRTV = NULL;
+
+ virtual void initialize(void* inWindowHandle) override
+ {
+ auto windowHandle = (HWND) inWindowHandle;
+
+ // 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");
+ exit(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");
+ exit(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.
+
+ 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) )
+ {
+ exit(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.
+
+ static const IID kIID_ID3D11Texture2D = {
+ 0x6f15aaf2, 0xd208, 0x4e89, 0x9a, 0xb4, 0x48,
+ 0x95, 0x35, 0xd3, 0x4f, 0x9c };
+ dxSwapChain->GetBuffer(
+ 0,
+ kIID_ID3D11Texture2D,
+ (void**)&dxBackBufferTexture);
+
+ 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);
+ }
+
+ virtual void clearFrame() override
+ {
+ static const float kClearColor[] = { 0.25, 0.25, 0.25, 1.0 };
+ dxImmediateContext->ClearRenderTargetView(
+ dxBackBufferRTV,
+ kClearColor);
+ }
+
+ virtual void presentFrame() override
+ {
+ dxSwapChain->Present(0, 0);
+ }
+
+ virtual void captureScreenShot(char const* outputPath) override
+ {
+ HRESULT hr = captureTextureToFile(
+ dxDevice,
+ dxImmediateContext,
+ dxBackBufferTexture,
+ outputPath);
+ if( FAILED(hr) )
+ {
+ fprintf(stderr, "error: could not capture screenshot to '%s'\n", outputPath);
+ exit(1);
+ }
+ }
+
+ virtual ShaderCompiler* getShaderCompiler() override
+ {
+ return this;
+ }
+
+ virtual Buffer* createBuffer(BufferDesc const& desc) override
+ {
+ D3D11_BUFFER_DESC dxBufferDesc = { 0 };
+ dxBufferDesc.ByteWidth = desc.size;
+
+ switch( desc.flavor )
+ {
+ case BufferFlavor::Constant:
+ dxBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
+ dxBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
+ dxBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
+ break;
+
+ case BufferFlavor::Vertex:
+ break;
+
+ default:
+ return nullptr;
+ }
+
+ D3D11_SUBRESOURCE_DATA dxInitData = { 0 };
+ dxInitData.pSysMem = desc.initData;
+
+
+ ID3D11Buffer* dxBuffer = nullptr;
+ HRESULT hr = dxDevice->CreateBuffer(
+ &dxBufferDesc,
+ desc.initData ? &dxInitData : nullptr,
+ &dxBuffer);
+ if(FAILED(hr)) return nullptr;
+
+ return (Buffer*) dxBuffer;
+ }
+
+ static DXGI_FORMAT mapFormat(Format format)
+ {
+ switch( format )
+ {
+ case Format::RGB_Float32:
+ return DXGI_FORMAT_R32G32B32_FLOAT;
+
+ default:
+ return DXGI_FORMAT_UNKNOWN;
+ }
+ }
+
+ virtual InputLayout* createInputLayout(InputElementDesc const* inputElements, UInt inputElementCount) override
+ {
+ D3D11_INPUT_ELEMENT_DESC dxInputElements[16] = {};
+
+ char hlslBuffer[1024];
+ char* hlslCursor = &hlslBuffer[0];
+
+ hlslCursor += sprintf(hlslCursor, "float4 main(\n");
+
+ for( UInt ii = 0; ii < inputElementCount; ++ii )
+ {
+ dxInputElements[ii].SemanticName = inputElements[ii].semanticName;
+ dxInputElements[ii].SemanticIndex = inputElements[ii].semanticIndex;
+ dxInputElements[ii].Format = mapFormat(inputElements[ii].format);
+ dxInputElements[ii].InputSlot = 0;
+ dxInputElements[ii].AlignedByteOffset = inputElements[ii].offset;
+ dxInputElements[ii].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
+ dxInputElements[ii].InstanceDataStepRate = 0;
+
+ if(ii != 0)
+ {
+ hlslCursor+= sprintf(hlslCursor, ",\n");
+ }
+
+ char const* typeName = "Uknown";
+ switch(inputElements[ii].format)
+ {
+ case Format::RGB_Float32:
+ typeName = "float3";
+ break;
+
+ default:
+ return nullptr;
+ }
+
+ hlslCursor+= sprintf(hlslCursor, "%s a%d : %s%d", typeName, ii,
+ inputElements[ii].semanticName,
+ inputElements[ii].semanticIndex);
+ }
+
+ hlslCursor += sprintf(hlslCursor, "\n) : SV_Position { return 0; }");
+
+ auto dxVertexShaderBlob = compileHLSLShader("inputLayout", hlslBuffer, "main", "vs_4_0");
+ if(!dxVertexShaderBlob)
+ return nullptr;
+
+ ID3D11InputLayout* dxInputLayout = nullptr;
+ HRESULT hr = dxDevice->CreateInputLayout(
+ &dxInputElements[0],
+ inputElementCount,
+ dxVertexShaderBlob->GetBufferPointer(),
+ dxVertexShaderBlob->GetBufferSize(),
+ &dxInputLayout);
+
+ dxVertexShaderBlob->Release();
+
+ if(FAILED(hr))
+ return nullptr;
+
+ return (InputLayout*) dxInputLayout;
+ }
+
+ virtual void* map(Buffer* buffer, MapFlavor flavor) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ auto dxBuffer = (ID3D11Buffer*) buffer;
+
+ D3D11_MAP dxMapFlavor;
+ switch( flavor )
+ {
+ case MapFlavor::WriteDiscard:
+ dxMapFlavor = D3D11_MAP_WRITE_DISCARD;
+ break;
+
+ default:
+ return nullptr;
+ }
+
+ // 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 dxMapped;
+ HRESULT hr = dxContext->Map(dxBuffer, 0, dxMapFlavor, 0, &dxMapped);
+ if(FAILED(hr))
+ return nullptr;
+
+ return dxMapped.pData;
+ }
+
+ virtual void unmap(Buffer* buffer) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ auto dxBuffer = (ID3D11Buffer*) buffer;
+
+ dxContext->Unmap(dxBuffer, 0);
+ }
+
+ virtual void setInputLayout(InputLayout* inputLayout) override
+ {
+ auto dxContext = dxImmediateContext;
+ auto dxInputLayout = (ID3D11InputLayout*) inputLayout;
+
+ dxContext->IASetInputLayout(dxInputLayout);
+ }
+
+ virtual void setPrimitiveTopology(PrimitiveTopology topology) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ D3D11_PRIMITIVE_TOPOLOGY dxTopology;
+ switch( topology )
+ {
+ case PrimitiveTopology::TriangleList:
+ dxTopology = D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST;
+ break;
+
+ default:
+ return;
+ }
+
+ dxContext->IASetPrimitiveTopology(dxTopology);
+ }
+
+ virtual void setVertexBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* strides, UInt const* offsets) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ static const int kMaxVertexBuffers = 16;
+
+ UINT dxVertexStrides[kMaxVertexBuffers];
+ UINT dxVertexOffsets[kMaxVertexBuffers];
+
+ for( UInt ii = 0; ii < slotCount; ++ii )
+ {
+ dxVertexStrides[ii] = strides[ii];
+ dxVertexOffsets[ii] = offsets[ii];
+ }
+
+ auto dxVertexBuffers = (ID3D11Buffer* const*) buffers;
+
+ dxContext->IASetVertexBuffers(startSlot, slotCount, &dxVertexBuffers[0], &dxVertexStrides[0], &dxVertexOffsets[0]);
+ }
+
+ virtual void setShaderProgram(ShaderProgram* inProgram) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ auto program = (D3D11ShaderProgram*) inProgram;
+
+ dxContext->VSSetShader(program->dxVertexShader, NULL, 0);
+ dxContext->PSSetShader(program->dxPixelShader, NULL, 0);
+ }
+
+ virtual void setConstantBuffers(UInt startSlot, UInt slotCount, Buffer* const* buffers, UInt const* offsets) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ // TODO: actually use those offsets
+
+ auto dxConstantBuffers = (ID3D11Buffer* const*) buffers;
+
+ dxContext->VSSetConstantBuffers(startSlot, slotCount, &dxConstantBuffers[0]);
+ dxContext->VSSetConstantBuffers(startSlot, slotCount, &dxConstantBuffers[0]);
+ }
+
+
+ virtual void draw(UInt vertexCount, UInt startVertex) override
+ {
+ auto dxContext = dxImmediateContext;
+
+ dxContext->Draw(vertexCount, startVertex);
+ }
+
+
+ // ShaderCompiler interface
+
+ struct D3D11ShaderProgram
+ {
+ ID3D11VertexShader* dxVertexShader;
+ ID3D11PixelShader* dxPixelShader;
+ };
+
+ virtual ShaderProgram* compileProgram(ShaderCompileRequest const& request) override
+ {
+ auto dxVertexShaderBlob = compileHLSLShader(request.source.path, request.source.text, request.vertexShader .name, request.vertexShader .profile);
+ if(!dxVertexShaderBlob) return nullptr;
+
+ auto dxFragmentShaderBlob = compileHLSLShader(request.source.path, request.source.text, request.fragmentShader .name, request.fragmentShader .profile);
+ if(!dxFragmentShaderBlob) return nullptr;
+
+ ID3D11VertexShader* dxVertexShader;
+ ID3D11PixelShader* dxPixelShader;
+
+ HRESULT vsResult = dxDevice->CreateVertexShader( dxVertexShaderBlob ->GetBufferPointer(), dxVertexShaderBlob ->GetBufferSize(), nullptr, &dxVertexShader);
+ HRESULT psResult = dxDevice->CreatePixelShader( dxFragmentShaderBlob->GetBufferPointer(), dxFragmentShaderBlob->GetBufferSize(), nullptr, &dxPixelShader);
+
+ dxVertexShaderBlob ->Release();
+ dxFragmentShaderBlob->Release();
+
+ if(FAILED(vsResult)) return nullptr;
+ if(FAILED(psResult)) return nullptr;
+
+ D3D11ShaderProgram* shaderProgram = new D3D11ShaderProgram();
+ shaderProgram->dxVertexShader = dxVertexShader;
+ shaderProgram->dxPixelShader = dxPixelShader;
+ return (ShaderProgram*) shaderProgram;
+ }
+};
+
+
+
+Renderer* createD3D11Renderer()
+{
+ return new D3D11Renderer();
+}
+
+} // renderer_test