Skip to content

Instantly share code, notes, and snippets.

@0x61726b
Created June 4, 2017 12:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 0x61726b/524510532d7b6ce63721fb3e3f771c3a to your computer and use it in GitHub Desktop.
Save 0x61726b/524510532d7b6ce63721fb3e3f771c3a to your computer and use it in GitHub Desktop.
Render a full-screen triangle
//----------------------------------------------------------------------------
// Cef3D
// Copyright (C) 2017 arkenthera
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// https://github.com/arkenthera/cef3d
//---------------------------------------------------------------------------
#include <d3d11.h>
#include <dxgi.h>
#include <d3d9.h>
#include <DirectXMath.h>
#include <d3dcompiler.h>
#include <string> // std::string
#include <fstream>
#include <streambuf>
#pragma comment(lib, "dxgi.lib")
#pragma comment(lib, "d3d11.lib")
#pragma comment(lib, "d3dcompiler.lib")
#pragma comment(lib, "windowscodecs.lib")
struct SimpleVertex
{
DirectX::XMFLOAT3 Point;
};
struct VsConstantBuffer
{
DirectX::XMFLOAT4X4 ViewProj;
DirectX::XMFLOAT4X4 ViewProjInv;
DirectX::XMFLOAT3 CamPos;
float padding;
};
enum ShaderType
{
Vertex,
Pixel
};
std::string ShaderPath = "FSTriangle.hlsl"; // Absolute Shader path here
// Some forward declarations
bool CompileShader(ShaderType type, unsigned flags, const char* entryPoint, const std::string& source, ID3D10Blob** outBlob);
bool CompileFsTrianglePixelShader(unsigned flags);
bool CompileFsTriangleVertexShader(unsigned flags);
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
#define D3D_SAFE_RELEASE(p) if (p) { ((IUnknown*)p)->Release(); p = 0; }
IDXGISwapChain* SwapChain;
ID3D11Device* Device;
ID3D11DeviceContext* DeviceContext;
ID3D11RenderTargetView* Backbuffer;
ID3D11InputLayout* FSTriangleIL;
ID3D11VertexShader* FSTriangleVS;
ID3D11PixelShader* FSTrianglePS;
ID3D11Buffer* VertexBuffer; // the pointer to the vertex buffer
ID3D11Buffer* VSConstantBuffer;
ID3D11Texture2D* OffscreenTex;
ID3D11ShaderResourceView* OffscreenTexSRV;
ID3D11SamplerState* OffscreenPSSampler;
D3D11_VIEWPORT Viewport;
bool Vsync;
std::string ReadFile(const std::string & AbsPath)
{
std::ifstream str(AbsPath);
std::string content;
str.seekg(0, std::ios::end);
content.reserve(str.tellg());
str.seekg(0, std::ios::beg);
content.assign((std::istreambuf_iterator<char>(str)),
std::istreambuf_iterator<char>());
return content;
}
bool InitDirect3D(HWND window, int width, int height)
{
if (SwapChain)
D3D_SAFE_RELEASE(SwapChain);
if (Backbuffer)
D3D_SAFE_RELEASE(Backbuffer);
if (OffscreenTex)
D3D_SAFE_RELEASE(OffscreenTex);
if (OffscreenTexSRV)
D3D_SAFE_RELEASE(OffscreenTexSRV);
if (VSConstantBuffer)
D3D_SAFE_RELEASE(VSConstantBuffer);
HRESULT hr = S_OK;
DXGI_SWAP_CHAIN_DESC scd;
ZeroMemory(&scd, sizeof(DXGI_SWAP_CHAIN_DESC));
scd.BufferCount = 1;
scd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
scd.BufferDesc.Width = width;
scd.BufferDesc.Height = height;
scd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
scd.OutputWindow = window;
scd.SampleDesc.Count = 1;
scd.Windowed = TRUE;
scd.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
if (!Device)
{
hr = D3D11CreateDevice(
0,
D3D_DRIVER_TYPE_HARDWARE,
0,
D3D11_CREATE_DEVICE_DEBUG,
0,
0,
D3D11_SDK_VERSION,
&Device,
0,
&DeviceContext
);
if (FAILED(hr))
{
D3D_SAFE_RELEASE(Device);
D3D_SAFE_RELEASE(DeviceContext);
return false;
}
}
IDXGIDevice* dxgiDevice = 0;
Device->QueryInterface(IID_IDXGIDevice, (void**)&dxgiDevice);
IDXGIAdapter* dxgiAdapter = 0;
dxgiDevice->GetParent(IID_IDXGIAdapter, (void**)&dxgiAdapter);
IDXGIFactory* dxgiFactory = 0;
dxgiAdapter->GetParent(IID_IDXGIFactory, (void**)&dxgiFactory);
hr = dxgiFactory->CreateSwapChain(Device, &scd, &SwapChain);
if (FAILED(hr))
{
D3D_SAFE_RELEASE(SwapChain);
return false;
}
dxgiFactory->Release();
dxgiAdapter->Release();
dxgiDevice->Release();
// Debug stuff, not actually necessary
ID3D11InfoQueue *d3dInfoQueue = nullptr;
Device->QueryInterface(__uuidof(ID3D11InfoQueue), (void**)&d3dInfoQueue);
d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, true);
d3dInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, true);
//
ID3D11Texture2D *pBackbuffer;
SwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&pBackbuffer);
Device->CreateRenderTargetView(pBackbuffer, NULL, &Backbuffer);
pBackbuffer->Release();
ZeroMemory(&Viewport, sizeof(D3D11_VIEWPORT));
Viewport.TopLeftX = 0;
Viewport.TopLeftY = 0;
Viewport.Width = width;
Viewport.Height = height;
return true;
}
bool InitResources(int width, int height)
{
SimpleVertex v1 = { DirectX::XMFLOAT3(-1.f,-3.f,1.f) };
SimpleVertex v2 = { DirectX::XMFLOAT3(-1.f,1.f,1.f) };
SimpleVertex v3 = { DirectX::XMFLOAT3(3.f,1.f,1.f) };
SimpleVertex vertices[3] = { v1,v2,v3 };
HRESULT hr = S_OK;
if (!VertexBuffer)
{
// create the vertex buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DYNAMIC;
bd.ByteWidth = sizeof(SimpleVertex) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
Device->CreateBuffer(&bd, NULL, &VertexBuffer);
D3D11_MAPPED_SUBRESOURCE ms;
DeviceContext->Map(VertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);
memcpy(ms.pData, vertices, sizeof(vertices));
DeviceContext->Unmap(VertexBuffer, NULL);
}
if (!FSTriangleVS || !FSTrianglePS)
{
// load and compile the two shaders
unsigned flags = D3DCOMPILE_SKIP_OPTIMIZATION | D3DCOMPILE_DEBUG;
if (!CompileFsTriangleVertexShader(flags))
return false;
if (!CompileFsTrianglePixelShader(flags))
return false;
}
DirectX::XMMATRIX V = DirectX::XMMatrixLookAtLH(DirectX::XMVectorSet(0, 0, -5, 0), DirectX::XMVectorSet(0, 0, 0, 0), DirectX::XMVectorSet(0, 1, 0, 0));
DirectX::XMMATRIX P = DirectX::XMMatrixPerspectiveFovLH(DirectX::XMConvertToRadians(60), width / height, 0.1f, 10.0f);
DirectX::XMMATRIX VP = DirectX::XMMatrixMultiply(V, P);
DirectX::XMVECTOR Det;
DirectX::XMMATRIX VPInv = DirectX::XMMatrixInverse(&Det, VP);
VsConstantBuffer VsConstData;
VsConstData.CamPos = DirectX::XMFLOAT3(0, 0, 0);
DirectX::XMStoreFloat4x4(&VsConstData.ViewProj, VP);
DirectX::XMStoreFloat4x4(&VsConstData.ViewProjInv, VPInv);
D3D11_BUFFER_DESC cbDesc;
cbDesc.ByteWidth = sizeof(VsConstantBuffer);
cbDesc.Usage = D3D11_USAGE_DYNAMIC;
cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
cbDesc.MiscFlags = 0;
cbDesc.StructureByteStride = 0;
D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = &VsConstData;
InitData.SysMemPitch = 0;
InitData.SysMemSlicePitch = 0;
// Create the buffer.
hr = Device->CreateBuffer(&cbDesc, &InitData,
&VSConstantBuffer);
if (FAILED(hr))
return false;
return true;
}
void Render()
{
float clearColor[4] = { 0,0,0,0 };
DeviceContext->ClearRenderTargetView(Backbuffer, clearColor);
UINT stride = sizeof(SimpleVertex);
UINT offset = 0;
// Set render target and viewport
DeviceContext->OMSetRenderTargets(1, &Backbuffer, NULL);
DeviceContext->RSSetViewports(1, &Viewport);
// Set input layout
DeviceContext->IASetInputLayout(FSTriangleIL);
// Set vertex buffers and constant buffers (uniforms)
DeviceContext->IASetVertexBuffers(0, 1, &VertexBuffer, &stride, &offset);
DeviceContext->VSSetConstantBuffers(0, 1, &VSConstantBuffer);
// Set primitive topology
DeviceContext->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
// Set shaders
DeviceContext->VSSetShader(FSTriangleVS, 0, 0);
DeviceContext->PSSetShader(FSTrianglePS, 0, 0);
// Invoke the draw
DeviceContext->Draw(3, 0);
SwapChain->Present(Vsync ? 1 : 0, 0);
}
void Shutdown()
{
SwapChain->SetFullscreenState(FALSE, NULL);
D3D_SAFE_RELEASE(OffscreenPSSampler);
D3D_SAFE_RELEASE(OffscreenTex);
D3D_SAFE_RELEASE(OffscreenTexSRV);
D3D_SAFE_RELEASE(VSConstantBuffer);
D3D_SAFE_RELEASE(FSTriangleIL);
D3D_SAFE_RELEASE(FSTriangleVS);
D3D_SAFE_RELEASE(FSTrianglePS);
D3D_SAFE_RELEASE(VertexBuffer);
D3D_SAFE_RELEASE(SwapChain);
D3D_SAFE_RELEASE(Backbuffer);
D3D_SAFE_RELEASE(DeviceContext);
D3D_SAFE_RELEASE(Device);
}
bool CompileShader(ShaderType type, unsigned flags, const char* entryPoint, const std::string& source, ID3D10Blob** outBlob)
{
ID3DBlob* errorMsgs = 0;
HRESULT hr = D3DCompile(source.c_str(), source.length(), "", 0, 0,
entryPoint, type == Vertex ? "vs_5_0" : "ps_5_0", flags, 0, outBlob, &errorMsgs);
if (FAILED(hr))
return false;
return true;
}
bool CompileFsTriangleVertexShader(unsigned flags)
{
std::string shaderSource = ReadFile(ShaderPath);
ID3DBlob* vsBlob = 0;
if (!CompileShader(Vertex, flags, "fs_triangle_vs", shaderSource, &vsBlob))
return false;
HRESULT hr = Device->CreateVertexShader(vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), NULL, &FSTriangleVS);
if (FAILED(hr))
return false;
D3D11_INPUT_ELEMENT_DESC ied[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }
};
hr = Device->CreateInputLayout(ied, 1, vsBlob->GetBufferPointer(), vsBlob->GetBufferSize(), &FSTriangleIL);
if (FAILED(hr))
return false;
return true;
}
bool CompileFsTrianglePixelShader(unsigned flags)
{
std::string shaderSource = ReadFile(ShaderPath);
ID3DBlob* psBlob = 0;
if (!CompileShader(Pixel, flags, "fs_triangle_ps", shaderSource, &psBlob))
return false;
HRESULT hr = Device->CreatePixelShader(psBlob->GetBufferPointer(), psBlob->GetBufferSize(), NULL, &FSTrianglePS);
if (FAILED(hr))
return false;
return true;
}
HWND CreateNativeWindow(int Width, int Height)
{
WNDCLASSEX wc;
HINSTANCE hInstance = GetModuleHandle(NULL);
LPCSTR className = "Cef3DSampleWindow";
ZeroMemory(&wc, sizeof(WNDCLASSEX));
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.lpszClassName = className;
RegisterClassEx(&wc);
RECT wr = { 0, 0, Width, Height };
AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE);
HWND Handle = CreateWindowEx(NULL,
className,
"Cef3D Rendering Window",
WS_OVERLAPPEDWINDOW,
0,
0,
wr.right - wr.left,
wr.bottom - wr.top,
NULL,
NULL,
hInstance,
NULL);
ShowWindow(Handle, SW_SHOW);
return Handle;
}
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
void PumpMessageLoop()
{
MSG msg;
while (TRUE)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
if (msg.message == WM_QUIT)
break;
Render();
}
}
}
int WINAPI WinMain(_In_ HINSTANCE hInInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ char* lpCmdLine, _In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// Create the Window
int Width = 1024;
int Height = 768;
HWND Window = CreateNativeWindow(Width, Height);
if (!InitDirect3D(Window, Width, Height))
return -1;
if (!InitResources(Width, Height))
return -1;
PumpMessageLoop();
Shutdown();
}
struct PS_INPUT
{
float4 position : SV_POSITION;
float2 tex_coord : TEXCOORD0;
float3 view_ray : TEXCOORD1;
};
cbuffer camera_vs : register(b0)
{
float4x4 vp : packoffset(c0);
float4x4 vp_inv : packoffset(c4);
float3 camera_pos : packoffset(c8);
float pad0 : packoffset(c8.w);
}
PS_INPUT fs_triangle_vs(in float3 pos : POSITION)
{
PS_INPUT outp;
float4 position = float4(pos, 1.f);
outp.position = position;
outp.tex_coord = position.xy * float2(0.5, -0.5) + 0.5;
outp.view_ray = mul(vp_inv, position).xyz;
return outp;
}
float4 fs_triangle_ps(PS_INPUT input) : SV_Target
{
return float4(input.tex_coord,0,0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment