Skip to content

Instantly share code, notes, and snippets.

@rdeioris
Created May 21, 2019 14:06
Show Gist options
  • Save rdeioris/23278848591e2feb9c17a5dc6ea49a1e to your computer and use it in GitHub Desktop.
Save rdeioris/23278848591e2feb9c17a5dc6ea49a1e to your computer and use it in GitHub Desktop.
#include <iostream>
#include <string>
#include <Windows.h>
#include <dxgi1_6.h>
#include <d3d11_4.h>
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
#include "vs001.h"
#include "ps001.h"
#include <Xinput.h>
namespace aiv
{
void DieBadly(std::string message)
{
MessageBox(nullptr, message.c_str(), nullptr, MB_OK);
std::exit(-1);
}
}
int main(int argc, char **argv)
{
// create a DXGI factory, required for operating system
// integration, like enumerating adapters and creating swap chains
IDXGIFactory2* factory = nullptr;
if (CreateDXGIFactory2(0, __uuidof(IDXGIFactory2), (void **)&factory) != S_OK)
{
aiv::DieBadly("unable to create factory");
}
// find the best adapter (hardware and max dedicated unshared memory)
UINT i = 0;
IDXGIAdapter1* adapter = nullptr;
IDXGIAdapter1* best_adapter = nullptr;
SIZE_T best_memory = 0;
// enum adapters returns DXGI_ERROR_NOT_FOUND if the specified index
// does not exist
while (factory->EnumAdapters1(i++, &adapter) != DXGI_ERROR_NOT_FOUND)
{
DXGI_ADAPTER_DESC1 desc;
if (adapter->GetDesc1(&desc) != S_OK)
{
aiv::DieBadly("unable to get adapter description");
}
std::wcout << desc.Description << " " << desc.DedicatedVideoMemory << std::endl;
if (desc.Flags == DXGI_ADAPTER_FLAG_NONE && desc.DedicatedVideoMemory > best_memory)
{
best_memory = desc.DedicatedVideoMemory;
best_adapter = adapter;
}
}
if (!best_adapter)
{
aiv::DieBadly("unable to find a valid adapter");
}
ID3D11Device5* device = nullptr;
ID3D11DeviceContext4* context = nullptr;
// this is the list of features we want to support
D3D_FEATURE_LEVEL feature_levels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
};
// this will be filled with the selected feature level
D3D_FEATURE_LEVEL feature_level;
// a device is a logical representation of an adapter, allowed to create objects
// (buffers, textures, shaders, ...) in the gpu.
// a context is a channel for issuing commands
if (D3D11CreateDevice(best_adapter, D3D_DRIVER_TYPE_UNKNOWN,
nullptr, 0, feature_levels, _countof(feature_levels), D3D11_SDK_VERSION,
(ID3D11Device**)&device, &feature_level, (ID3D11DeviceContext**)&context) != S_OK)
{
aiv::DieBadly("unable to create device and context");
}
// create a win32 window class (required for creating windows)
WNDCLASS wclass = {};
wclass.lpszClassName = "dx11_window_class";
wclass.hInstance = GetModuleHandle(nullptr);
wclass.lpfnWndProc = DefWindowProc;
RegisterClass(&wclass);
// the window size is cutted by the border/caption size, so
// we need to compute the right window size for having
// a drawable area of 1024x1024
int width = 1024;
int height = 1024;
DWORD style = WS_OVERLAPPEDWINDOW;
RECT rect = { 0, 0, width, height };
// this will compute the right dimension
AdjustWindowRect(&rect, style, false);
HWND window = CreateWindow(wclass.lpszClassName, "D3D11",
style, CW_USEDEFAULT, CW_USEDEFAULT,
rect.right - rect.left, rect.bottom - rect.top, nullptr, nullptr, wclass.hInstance, nullptr);
// prepare the swap chain configuration
DXGI_SWAP_CHAIN_DESC1 sc_desc = {};
sc_desc.BufferCount = 1;
sc_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sc_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sc_desc.Width = width;
sc_desc.Height = height;
sc_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
sc_desc.SampleDesc.Count = 1;
sc_desc.SampleDesc.Quality = 0;
IDXGISwapChain1* swap_chain = nullptr;
if (factory->CreateSwapChainForHwnd(device, window, &sc_desc, nullptr, nullptr, &swap_chain) != S_OK)
{
aiv::DieBadly("unable to create swap chain");
}
// retrieve the texture mapped to the swap chain
ID3D11Texture2D* swap_chain_texture = nullptr;
if (swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void **)&swap_chain_texture))
{
aiv::DieBadly("unable to get texture from swap chain");
}
// make the window visible (win32)
ShowWindow(window, SW_SHOW);
// load a texture
int w, h, c;
BYTE *pixels = stbi_load("D:/Users/rober/Downloads/scorpion.png", &w, &h, &c, 4);
// create a new texture
D3D11_TEXTURE2D_DESC tex_desc = {};
tex_desc.ArraySize = 1;
tex_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
tex_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
tex_desc.Height = h;
tex_desc.Width = w;
tex_desc.MipLevels = 1;
tex_desc.Usage = D3D11_USAGE_DEFAULT;
tex_desc.SampleDesc.Count = 1;
tex_desc.SampleDesc.Quality = 0;
// a subresource contains cpu data that must be uploaded to the gpu
D3D11_SUBRESOURCE_DATA sr_data = {};
sr_data.pSysMem = pixels;
sr_data.SysMemPitch = w * 4; // pitch is required for textures, it is the size (in bytes) of a single line
ID3D11Texture2D* scorpion = nullptr;
if (device->CreateTexture2D(&tex_desc, &sr_data, &scorpion) != S_OK)
{
aiv::DieBadly("unable to create texture");
}
// a render target view is an object able to receive rasterization
// output (like an opengl framebuffer)
ID3D11RenderTargetView1* rtv = nullptr;
if (device->CreateRenderTargetView1(swap_chain_texture, nullptr, &rtv) != S_OK)
{
aiv::DieBadly("unable to create render target view");
}
// we have 2 buffers
ID3D11Buffer* buffer001;
ID3D11Buffer* buffer002;
float vertices[] = {
0, 1, 0,
-1, -1, 0,
1, -1, 0
};
float colors[] = {
1, 0, 0,
0, 1, 0,
1, 1, 0
};
D3D11_BUFFER_DESC buf_desc = {};
buf_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
buf_desc.ByteWidth = sizeof(vertices);
buf_desc.Usage = D3D11_USAGE_DEFAULT;
// like textures, but without pitching
D3D11_SUBRESOURCE_DATA buf_data = {};
buf_data.pSysMem = vertices;
if (device->CreateBuffer(&buf_desc, &buf_data, &buffer001) != S_OK)
{
aiv::DieBadly("unable to create buffer");
}
// NOTE: we are reusing the same buffer description struct
// changing only the required values
buf_desc.ByteWidth = sizeof(colors);
buf_data.pSysMem = colors;
if (device->CreateBuffer(&buf_desc, &buf_data, &buffer002) != S_OK)
{
aiv::DieBadly("unable to create buffer");
}
// input layout maps semantics to input slots (PIPPO here will consume data
// from slot 0, while PLUTO will consume from slot 1)
D3D11_INPUT_ELEMENT_DESC layout_desc[] =
{
{"PIPPO", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"PLUTO", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
// not the input layout is validated with the shader bytecode/opcodes
ID3D11InputLayout* input_layout = nullptr;
if (device->CreateInputLayout(layout_desc, _countof(layout_desc), vs001, sizeof(vs001), &input_layout) != S_OK)
{
aiv::DieBadly("invalid input layout");
}
// setup IA (Input Assembly)
context->IASetInputLayout(input_layout);
context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // draw triangles
UINT stride = { sizeof(float) * 3 }; // how many bytes to read for each vertex
UINT offset = { 0 };
// NOTE: IASetVertexBuffers takes an array of buffers, strides and offsets
// but we can call it multiple times for different slots
context->IASetVertexBuffers(0, 1, &buffer001, &stride, &offset);
context->IASetVertexBuffers(1, 1, &buffer002, &stride, &offset);
// setup VS (Vertex Shader)
ID3D11VertexShader *vertex_shader = nullptr;
if (device->CreateVertexShader(vs001, sizeof(vs001), nullptr, &vertex_shader) != S_OK)
{
aiv::DieBadly("unable to upload shader");
}
context->VSSetShader(vertex_shader, nullptr, 0);
// setup RS (rasterization options)
ID3D11RasterizerState* rs = nullptr;
D3D11_RASTERIZER_DESC rs_desc = {};
rs_desc.CullMode = D3D11_CULL_NONE; // never cull triangles
rs_desc.FillMode = D3D11_FILL_SOLID; // draw solid (can be D3D11_FILL_WIREFRAME)
if (device->CreateRasterizerState(&rs_desc, &rs) != S_OK)
{
aiv::DieBadly("unable to create rasterizer state");
}
context->RSSetState(rs);
// set the rendering area (viewport)
D3D11_VIEWPORT viewport = {};
viewport.Height = height;
viewport.Width = width;
context->RSSetViewports(1, &viewport);
ID3D11PixelShader *pixel_shader = nullptr;
if (device->CreatePixelShader(ps001, sizeof(ps001), nullptr, &pixel_shader) != S_OK)
{
aiv::DieBadly("unable to upload pixel shader");
}
context->PSSetShader(pixel_shader, nullptr, 0);
// set the render target view
context->OMSetRenderTargets(1, (ID3D11RenderTargetView **)&rtv, nullptr);
while (true)
{
// deque and translates win32 messages
MSG msg;
while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// read xbox pad
XINPUT_STATE pad = {};
XInputGetState(0, &pad);
// clear the render target view with a color
float blue[] = { 0, 0, 1, 1 };
context->ClearRenderTargetView(rtv, blue);
//context->CopyResource(swap_chain_texture, scorpion);
// draw 3 vertices using the current pipeline
context->Draw(3, 0);
// bit blit the back buffer to the front buffer, waiting for vsync
swap_chain->Present(1, 0);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment