Instantly share code, notes, and snippets.
Save d7samurai/a8dc490c54a714f8385f512d95278e0b to your computer and use it in GitHub Desktop.
Plain 2D rendering in D3D11, without the distracting feature set of acomplete sprite renderer and allowingarbitrarily placed triangle vertices with absolute pixel coordinate positioning (there's reallyno need for the sometimes confusing complication of a full-on projection matrix pipeline for UI and 2D games). Like theoriginal Minimal D3D11, this one uses a canonical 1:1TRIANGLELIST vertex buffer with input layout, so no fancy manual custom buffer fetching and in-shader instanced quad expansion here. As usual: complete, runnable single-function app. ~160 LOC. No modern C++, OOP or (other) obscuring cruft.
Still not plain enough? Here's aneven simpler variant (no texturing or alpha blending).
As with all of these demos, remember to keepgpu.hlsl in theworkingdirectory of the executable.
| #pragma comment(lib, "user32") | |
| #pragma comment(lib, "d3d11") | |
| #pragma comment(lib, "d3dcompiler") | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #include<windows.h> | |
| #include<d3d11.h> | |
| #include<d3dcompiler.h> | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| #defineTEXTURE_WIDTH17 | |
| #defineTEXTURE_HEIGHT25 | |
| unsignedlonglong texturedata[] =// skull texture. 17 x 25 pixels, 8 bits per pixel (grayscale) for compactness | |
| { | |
| 0x0000000000000000,0x0000000000000000,0xdd00000000000000,0x00000000b2e0dede,0xe1dc000000000000,0x00afaeaeaec8c8c8, | |
| 0xc7e3000000000000,0x89b09999c3ddddc3,0xc7dd0000000000ae,0x9b99afb0c6dbddc6,0xc400000000af899b,0xb0aeaec6c6dedcc6, | |
| 0xdd0000008a9c99af,0xadadaec4dedcdcc4,0x0000599b9aaeafaf,0xadadc6c6ddddc6c6,0x005c9a9aafafadae,0xadacc6c3e0ddc600, | |
| 0x5c9d5ab0b0aeadad,0xaddedcdcc6ae0000,0x9f5ac4c4c7c6adad,0x895e89ae5d00005c,0x5b56578dc6aeaeb0,0x18185c3300005a5a, | |
| 0x1818198cae8c1716,0x195a330000305b31,0x171617af5a331718,0x8ec60000315a5b30,0x165b8b8a58321718,0xe000005b8c5a3132, | |
| 0x8a17dd8d5c188db1,0x000089ad8a5a1658,0x3219da888eaf8b5b,0x00323034898b8d5b,0x16aeae1719590000,0x00301717af8d3089, | |
| 0xad5a32dc00000000,0x005a1959ad8c8bb0,0x8d198a0000000000,0x58318b17e217dc5a,0x8a8a000000000000,0x5b3230185a5b2f18, | |
| 0xae00000000000033,0xb02fdc30de18ddaf,0x000000000000335a,0xb0e1aeb1afafae00,0x0000000000005daf,0xadafb1afb0000000, | |
| 0x0000000000005b5c,0x898b8b0000000000,0x0000000000005a5c,0x0000000000000000,0x0000000000000000,0x0000000000000000, | |
| }; | |
| /////////////////////////////////////////////////////////////////////////////////////////////////// | |
| int WINAPIWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine,int nShowCmd) | |
| { | |
| WNDCLASSA wndclass = {0, DefWindowProcA,0,0,0,0,0,0,0,"d7" }; | |
| RegisterClassA(&wndclass); | |
| HWND window =CreateWindowExA(0,"d7",0,0x91000000,0,0,0,0,0,0,0,0); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D_FEATURE_LEVEL featurelevels[] = { D3D_FEATURE_LEVEL_11_0 }; | |
| DXGI_SWAP_CHAIN_DESC swapchaindesc = {}; | |
| swapchaindesc.BufferDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;// non-srgb for simplicity here. see other minimal gists for srgb setup | |
| swapchaindesc.SampleDesc.Count =1; | |
| swapchaindesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; | |
| swapchaindesc.BufferCount =2; | |
| swapchaindesc.OutputWindow = window; | |
| swapchaindesc.Windowed =TRUE; | |
| swapchaindesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD; | |
| IDXGISwapChain* swapchain; | |
| ID3D11Device* device; | |
| ID3D11DeviceContext* devicecontext; | |
| D3D11CreateDeviceAndSwapChain(nullptr, D3D_DRIVER_TYPE_HARDWARE,nullptr, D3D11_CREATE_DEVICE_BGRA_SUPPORT, featurelevels,ARRAYSIZE(featurelevels), D3D11_SDK_VERSION, &swapchaindesc, &swapchain, &device,nullptr, &devicecontext); | |
| swapchain->GetDesc(&swapchaindesc);// get actual dimensions | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3D11Texture2D* framebuffer; | |
| swapchain->GetBuffer(0,__uuidof(ID3D11Texture2D), (void**)&framebuffer);// get the swapchain's buffer | |
| ID3D11RenderTargetView* framebufferRTV; | |
| device->CreateRenderTargetView(framebuffer,nullptr, &framebufferRTV);// and make it a render target [view] | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3DBlob* vertexshaderCSO; | |
| D3DCompileFromFile(L"gpu.hlsl",0,0,"VsMain","vs_5_0",0,0, &vertexshaderCSO,0); | |
| ID3D11VertexShader* vertexshader; | |
| device->CreateVertexShader(vertexshaderCSO->GetBufferPointer(), vertexshaderCSO->GetBufferSize(),0, &vertexshader); | |
| D3D11_INPUT_ELEMENT_DESC inputelementdesc[] =// maps to vertexdesc struct in gpu.hlsl via semantic names ("POS", "TEX", "COL") | |
| { | |
| {"POS",0, DXGI_FORMAT_R32G32_FLOAT,0,0, D3D11_INPUT_PER_VERTEX_DATA,0 },// float2 position (x, y) | |
| {"TEX",0, DXGI_FORMAT_R32G32_FLOAT,0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA,0 },// float2 texcoord (u, v) | |
| {"COL",0, DXGI_FORMAT_R32G32B32A32_FLOAT,0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA,0 },// float4 color (r, g, b, a) | |
| }; | |
| ID3D11InputLayout* inputlayout; | |
| device->CreateInputLayout(inputelementdesc,ARRAYSIZE(inputelementdesc), vertexshaderCSO->GetBufferPointer(), vertexshaderCSO->GetBufferSize(), &inputlayout); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| ID3DBlob* pixelshaderCSO; | |
| D3DCompileFromFile(L"gpu.hlsl",0,0,"PsMain","ps_5_0",0,0, &pixelshaderCSO,0); | |
| ID3D11PixelShader* pixelshader; | |
| device->CreatePixelShader(pixelshaderCSO->GetBufferPointer(), pixelshaderCSO->GetBufferSize(),0, &pixelshader); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_RASTERIZER_DESC rasterizerdesc = { D3D11_FILL_SOLID, D3D11_CULL_NONE }; | |
| ID3D11RasterizerState* rasterizerstate; | |
| device->CreateRasterizerState(&rasterizerdesc, &rasterizerstate); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_SAMPLER_DESC samplerdesc = { D3D11_FILTER_MIN_MAG_MIP_POINT, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP, D3D11_TEXTURE_ADDRESS_WRAP }; | |
| ID3D11SamplerState* samplerstate; | |
| device->CreateSamplerState(&samplerdesc, &samplerstate); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_BLEND_DESC blenddesc = {FALSE,FALSE, {TRUE, D3D11_BLEND_SRC_ALPHA , D3D11_BLEND_INV_SRC_ALPHA, D3D11_BLEND_OP_ADD, D3D11_BLEND_ZERO, D3D11_BLEND_ZERO, D3D11_BLEND_OP_ADD, D3D11_COLOR_WRITE_ENABLE_ALL } }; | |
| ID3D11BlendState* blendstate; | |
| device->CreateBlendState(&blenddesc, &blendstate); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| float constants[2] = {2.0f / swapchaindesc.BufferDesc.Width, -2.0f / swapchaindesc.BufferDesc.Height };// precalc for simple screen coordinate transform in shader (instead of full-on projection matrix) | |
| D3D11_BUFFER_DESC constantbufferdesc = {}; | |
| constantbufferdesc.ByteWidth =sizeof(constants) +0xf &0xfffffff0; | |
| constantbufferdesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| constantbufferdesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; | |
| D3D11_SUBRESOURCE_DATA constantbufferSRD = { constants }; | |
| ID3D11Buffer* constantbuffer; | |
| device->CreateBuffer(&constantbufferdesc, &constantbufferSRD, &constantbuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_TEXTURE2D_DESC texturedesc = {}; | |
| texturedesc.Width = TEXTURE_WIDTH; | |
| texturedesc.Height = TEXTURE_HEIGHT; | |
| texturedesc.MipLevels =1; | |
| texturedesc.ArraySize =1; | |
| texturedesc.Format = DXGI_FORMAT_R8_UNORM;// 8 bits per pixel | |
| texturedesc.SampleDesc.Count =1; | |
| texturedesc.Usage = D3D11_USAGE_IMMUTABLE; | |
| texturedesc.BindFlags = D3D11_BIND_SHADER_RESOURCE; | |
| D3D11_SUBRESOURCE_DATA textureSRD = {}; | |
| textureSRD.pSysMem = texturedata; | |
| textureSRD.SysMemPitch = TEXTURE_WIDTH *1;// 1 byte per pixel (R8) | |
| ID3D11Texture2D* texture; | |
| device->CreateTexture2D(&texturedesc, &textureSRD, &texture); | |
| ID3D11ShaderResourceView* textureSRV; | |
| device->CreateShaderResourceView(texture,nullptr, &textureSRV); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| #defineMAX_VERTICES1024// arbitrary limit | |
| structvertexdesc {float x, y, u, v, r, g, b, a; };// float2 position, float2 texcoord, float4 color | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_BUFFER_DESC vertexbufferdesc = {}; | |
| vertexbufferdesc.ByteWidth =sizeof(vertexdesc) * MAX_VERTICES; | |
| vertexbufferdesc.Usage = D3D11_USAGE_DYNAMIC;// updated every frame | |
| vertexbufferdesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; | |
| vertexbufferdesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; | |
| ID3D11Buffer* vertexbuffer; | |
| device->CreateBuffer(&vertexbufferdesc,nullptr, &vertexbuffer); | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| FLOAT clearcolor[4] = {0.1725f,0.1725f,0.1725f,1.0f };// RGBA | |
| D3D11_VIEWPORT viewport = {0,0, (float)swapchaindesc.BufferDesc.Width, (float)swapchaindesc.BufferDesc.Height,0,1 }; | |
| UINT stride =sizeof(vertexdesc); | |
| UINT offset =0; | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| vertexdesc* vertexbatch = (vertexdesc*)HeapAlloc(GetProcessHeap(),0,sizeof(vertexdesc) * MAX_VERTICES);// per frame vertex batch (cpu-local buffer) | |
| /////////////////////////////////////////////////////////////////////////////////////////////// | |
| while (true) | |
| { | |
| MSG msg; | |
| while (PeekMessageA(&msg,nullptr,0,0, PM_REMOVE)) | |
| { | |
| if (msg.message == WM_KEYDOWN)return0;// PRESS ANY KEY TO EXIT | |
| DispatchMessageA(&msg); | |
| } | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| int vertexcount =0;// start a new vertex batch every frame | |
| // triangle 1: | |
| { | |
| // vertex 1 | |
| vertexdesc vertex; | |
| vertex.x =100;// screen coordinates in pixels (xy) | |
| vertex.y =100; | |
| vertex.u =0.0f;// texture coordinates (uv) | |
| vertex.v =0.0f; | |
| vertex.r =1.0f;// color (rgba) | |
| vertex.g =1.0f; | |
| vertex.b =1.0f; | |
| vertex.a =1.0f; | |
| vertexbatch[vertexcount++] = vertex;// add vertex to batch | |
| // vertex 2 | |
| vertexbatch[vertexcount++] = {252,300,1.0f,1.0f,1.0f,1.0f,1.0f,1.0f };// x, y, u, v, r, g, b, a | |
| // vertex 3 | |
| vertexbatch[vertexcount++] = {100,300,0.0f,1.0f,1.0f,1.0f,1.0f,1.0f }; | |
| } | |
| // triangle 2: | |
| vertexbatch[vertexcount++] = {100,100,0.0f,0.0f,1.0f,1.0f,1.0f,1.0f }; | |
| vertexbatch[vertexcount++] = {252,100,1.0f,0.0f,1.0f,1.0f,1.0f,1.0f }; | |
| vertexbatch[vertexcount++] = {252,300,1.0f,1.0f,1.0f,1.0f,1.0f,1.0f }; | |
| // triangle 3: | |
| vertexbatch[vertexcount++] = {296,104,0.0f,0.0f,0.803f,0.000f,0.290f,1.0f }; | |
| vertexbatch[vertexcount++] = {448,304,1.0f,1.0f,0.952f,0.439f,0.105f,1.0f }; | |
| vertexbatch[vertexcount++] = {296,304,0.0f,1.0f,0.988f,0.721f,0.023f,1.0f }; | |
| // triangle 4: | |
| vertexbatch[vertexcount++] = {304,96,0.0f,0.0f,0.011f,0.698f,0.286f,1.0f }; | |
| vertexbatch[vertexcount++] = {456,96,1.0f,0.0f,0.000f,0.541f,0.819f,1.0f }; | |
| vertexbatch[vertexcount++] = {456,296,1.0f,1.0f,0.392f,0.376f,0.670f,1.0f }; | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| D3D11_MAPPED_SUBRESOURCE vertexbufferMSR; | |
| devicecontext->Map(vertexbuffer,0, D3D11_MAP_WRITE_DISCARD,0, &vertexbufferMSR); | |
| { | |
| memcpy(vertexbufferMSR.pData, vertexbatch,sizeof(vertexdesc) * vertexcount);// send vertex batch to gpu | |
| } | |
| devicecontext->Unmap(vertexbuffer,0); | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| devicecontext->ClearRenderTargetView(framebufferRTV, clearcolor); | |
| devicecontext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);// 3 vertices per triangle | |
| devicecontext->IASetInputLayout(inputlayout); | |
| devicecontext->IASetVertexBuffers(0,1, &vertexbuffer, &stride, &offset); | |
| devicecontext->VSSetShader(vertexshader,nullptr,0); | |
| devicecontext->VSSetConstantBuffers(0,1, &constantbuffer); | |
| devicecontext->RSSetViewports(1, &viewport); | |
| devicecontext->RSSetState(rasterizerstate); | |
| devicecontext->PSSetShader(pixelshader,nullptr,0); | |
| devicecontext->PSSetShaderResources(0,1, &textureSRV); | |
| devicecontext->PSSetSamplers(0,1, &samplerstate); | |
| devicecontext->OMSetRenderTargets(1, &framebufferRTV,nullptr); | |
| devicecontext->OMSetBlendState(blendstate,nullptr,0xffffffff); | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| devicecontext->Draw(vertexcount,0); | |
| /////////////////////////////////////////////////////////////////////////////////////////// | |
| swapchain->Present(1,0); | |
| } | |
| } |
| cbuffer constants :register(b0) | |
| { | |
| float2 rn_screensize;// 2 / width, -2 / height | |
| } | |
| struct vertexdesc | |
| { | |
| float2 position : POS; | |
| float2 texcoord : TEX; | |
| float4 color : COL; | |
| }; | |
| struct pixeldesc | |
| { | |
| float4 position :SV_POSITION; | |
| float2 texcoord : TEX; | |
| float4 color : COL; | |
| }; | |
| Texture2D mytexture :register(t0); | |
| SamplerState mysampler :register(s0); | |
| pixeldescVsMain(vertexdesc vertex) | |
| { | |
| pixeldesc output; | |
| output.position =float4(vertex.position * rn_screensize -float2(1, -1),0,1); | |
| output.texcoord = vertex.texcoord; | |
| output.color = vertex.color; | |
| return output; | |
| } | |
| float4PsMain(pixeldesc pixel) :SV_TARGET | |
| { | |
| returnfloat4(mytexture.Sample(mysampler, pixel.texcoord).rrr * pixel.color.rgb, pixel.color.a);// .rrr because texture is red channel only (DXGI_FORMAT_R8_UNORM) | |
| } |
