mirror of
https://github.com/mirkobrombin/raven.git
synced 2026-11-07 22:06:33 +00:00
218 lines
9.4 KiB
C
218 lines
9.4 KiB
C
/* Independent Direct3D 11 workload with a window: renders animated frames into a DXGI swap chain and presents every one of them.
|
|
* Frame number n clears to a colour that depends on n and draws the vertex-coloured triangle of tri.hlsl; the first and the last
|
|
* back buffer are copied to C:\window_first.raw and C:\window_last.raw. The program keeps the last frame on screen for a few seconds
|
|
* so that a compositor capture can be compared with the read-back pixels, then resizes the window and the swap chain to 320 by 200,
|
|
* renders and presents again, writes the new back buffer to C:\window_resized.raw and C:\window_resized.txt, and keeps that frame
|
|
* on screen for another capture. */
|
|
#define COBJMACROS
|
|
#include <windows.h>
|
|
#include <d3d11.h>
|
|
#include <dxgi.h>
|
|
#include "tri_shaders.h"
|
|
|
|
#define SIZE 256
|
|
#define WIDE 320
|
|
#define HIGH 200
|
|
#define FRAMES 60
|
|
|
|
struct vertex { float x, y; float r, g, b, a; };
|
|
|
|
static void write_text(const wchar_t *path, const char *text)
|
|
{
|
|
HANDLE f = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
DWORD n;
|
|
WriteFile(f, text, lstrlenA(text), &n, NULL);
|
|
CloseHandle(f);
|
|
}
|
|
|
|
static void fail(const char *step, HRESULT hr)
|
|
{
|
|
char text[96];
|
|
int length = 0;
|
|
const char *prefix = "FAIL ";
|
|
while (*prefix)
|
|
text[length++] = *prefix++;
|
|
while (*step && length < 60)
|
|
text[length++] = *step++;
|
|
text[length++] = ' ';
|
|
for (int shift = 28; shift >= 0; shift -= 4)
|
|
text[length++] = "0123456789abcdef"[((unsigned)hr >> shift) & 15];
|
|
text[length] = 0;
|
|
write_text(L"C:\\window.txt", text);
|
|
ExitProcess(1);
|
|
}
|
|
|
|
static LRESULT CALLBACK procedure(HWND window, UINT message, WPARAM wparam, LPARAM lparam)
|
|
{
|
|
if (message == WM_DESTROY) {
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
return DefWindowProcW(window, message, wparam, lparam);
|
|
}
|
|
|
|
static void pump(void)
|
|
{
|
|
MSG message;
|
|
while (PeekMessageW(&message, NULL, 0, 0, PM_REMOVE)) {
|
|
TranslateMessage(&message);
|
|
DispatchMessageW(&message);
|
|
}
|
|
}
|
|
|
|
static void save(ID3D11DeviceContext *context, ID3D11Texture2D *staging, ID3D11Texture2D *back, const wchar_t *path, int width, int height)
|
|
{
|
|
D3D11_MAPPED_SUBRESOURCE mapped;
|
|
HRESULT hr;
|
|
ID3D11DeviceContext_CopyResource(context, (ID3D11Resource *)staging, (ID3D11Resource *)back);
|
|
if (FAILED(hr = ID3D11DeviceContext_Map(context, (ID3D11Resource *)staging, 0, D3D11_MAP_READ, 0, &mapped)))
|
|
fail("map", hr);
|
|
HANDLE f = CreateFileW(path, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
|
|
DWORD n;
|
|
for (int row = 0; row < height; ++row)
|
|
WriteFile(f, (const BYTE *)mapped.pData + (size_t)row * mapped.RowPitch, (DWORD)width * 4, &n, NULL);
|
|
CloseHandle(f);
|
|
ID3D11DeviceContext_Unmap(context, (ID3D11Resource *)staging, 0);
|
|
}
|
|
|
|
void __stdcall entry(void)
|
|
{
|
|
HINSTANCE instance = GetModuleHandleW(NULL);
|
|
WNDCLASSEXW class_info = {sizeof class_info, 0, procedure, 0, 0, instance, NULL, LoadCursorW(NULL, (LPCWSTR)IDC_ARROW), (HBRUSH)(COLOR_WINDOW + 1), NULL, L"RavenD3D", NULL};
|
|
RegisterClassExW(&class_info);
|
|
RECT frame = {0, 0, SIZE, SIZE};
|
|
AdjustWindowRect(&frame, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, FALSE);
|
|
HWND window = CreateWindowExW(0, L"RavenD3D", L"Direct3D 11 frame", WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, 100, 100,
|
|
frame.right - frame.left, frame.bottom - frame.top, NULL, NULL, instance, NULL);
|
|
if (!window)
|
|
fail("window", 0);
|
|
ShowWindow(window, SW_SHOW);
|
|
UpdateWindow(window);
|
|
pump();
|
|
|
|
DXGI_SWAP_CHAIN_DESC description = {{SIZE, SIZE, {60, 1}, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, DXGI_MODE_SCALING_UNSPECIFIED},
|
|
{1, 0}, DXGI_USAGE_RENDER_TARGET_OUTPUT, 2, window, TRUE, DXGI_SWAP_EFFECT_DISCARD, 0};
|
|
IDXGISwapChain *swap = NULL;
|
|
ID3D11Device *device = NULL;
|
|
ID3D11DeviceContext *context = NULL;
|
|
D3D_FEATURE_LEVEL level = 0;
|
|
HRESULT hr = D3D11CreateDeviceAndSwapChain(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0, NULL, 0, D3D11_SDK_VERSION, &description, &swap, &device, &level, &context);
|
|
if (FAILED(hr))
|
|
fail("device and swap chain", hr);
|
|
ID3D11Texture2D *back = NULL, *staging = NULL;
|
|
if (FAILED(hr = IDXGISwapChain_GetBuffer(swap, 0, &IID_ID3D11Texture2D, (void **)&back)))
|
|
fail("buffer", hr);
|
|
D3D11_TEXTURE2D_DESC td = {SIZE, SIZE, 1, 1, DXGI_FORMAT_R8G8B8A8_UNORM, {1, 0}, D3D11_USAGE_STAGING, 0, D3D11_CPU_ACCESS_READ, 0};
|
|
if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &td, NULL, &staging)))
|
|
fail("staging", hr);
|
|
ID3D11RenderTargetView *view = NULL;
|
|
if (FAILED(hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)back, NULL, &view)))
|
|
fail("view", hr);
|
|
ID3D11VertexShader *vs = NULL;
|
|
ID3D11PixelShader *ps = NULL;
|
|
if (FAILED(hr = ID3D11Device_CreateVertexShader(device, tri_vs, sizeof(tri_vs), NULL, &vs)))
|
|
fail("vertex shader", hr);
|
|
if (FAILED(hr = ID3D11Device_CreatePixelShader(device, tri_ps, sizeof(tri_ps), NULL, &ps)))
|
|
fail("pixel shader", hr);
|
|
D3D11_INPUT_ELEMENT_DESC elements[] = {
|
|
{"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
|
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 8, D3D11_INPUT_PER_VERTEX_DATA, 0},
|
|
};
|
|
ID3D11InputLayout *layout = NULL;
|
|
if (FAILED(hr = ID3D11Device_CreateInputLayout(device, elements, 2, tri_vs, sizeof(tri_vs), &layout)))
|
|
fail("layout", hr);
|
|
/* Clockwise in clip space: the default rasterizer state culls counter-clockwise (back) faces. */
|
|
struct vertex vertices[3] = {{-0.8f, -0.8f, 1, 0, 0, 1}, {0.0f, 0.8f, 0, 0, 1, 1}, {0.8f, -0.8f, 0, 1, 0, 1}};
|
|
D3D11_BUFFER_DESC bd = {sizeof vertices, D3D11_USAGE_IMMUTABLE, D3D11_BIND_VERTEX_BUFFER, 0, 0, 0};
|
|
D3D11_SUBRESOURCE_DATA sd = {vertices, 0, 0};
|
|
ID3D11Buffer *buffer = NULL;
|
|
if (FAILED(hr = ID3D11Device_CreateBuffer(device, &bd, &sd, &buffer)))
|
|
fail("buffer", hr);
|
|
D3D11_VIEWPORT viewport = {0, 0, SIZE, SIZE, 0, 1};
|
|
UINT stride = sizeof(struct vertex), offset = 0;
|
|
ID3D11DeviceContext_OMSetRenderTargets(context, 1, &view, NULL);
|
|
ID3D11DeviceContext_RSSetViewports(context, 1, &viewport);
|
|
ID3D11DeviceContext_IASetInputLayout(context, layout);
|
|
ID3D11DeviceContext_IASetVertexBuffers(context, 0, 1, &buffer, &stride, &offset);
|
|
ID3D11DeviceContext_IASetPrimitiveTopology(context, D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
|
|
ID3D11DeviceContext_VSSetShader(context, vs, NULL, 0);
|
|
ID3D11DeviceContext_PSSetShader(context, ps, NULL, 0);
|
|
|
|
for (int frame_number = 0; frame_number < FRAMES; ++frame_number) {
|
|
/* The last frame clears to the colour the offscreen workload uses; earlier frames ramp towards it. */
|
|
const float ramp = (float)(FRAMES - 1 - frame_number) / (FRAMES - 1);
|
|
const float clear[4] = {0.1f + 0.5f * ramp, 0.1f, 0.3f, 1.0f};
|
|
ID3D11DeviceContext_ClearRenderTargetView(context, view, clear);
|
|
ID3D11DeviceContext_Draw(context, 3, 0);
|
|
if (frame_number == 0)
|
|
save(context, staging, back, L"C:\\window_first.raw", SIZE, SIZE);
|
|
if (frame_number == FRAMES - 1)
|
|
save(context, staging, back, L"C:\\window_last.raw", SIZE, SIZE);
|
|
if (FAILED(hr = IDXGISwapChain_Present(swap, 0, 0)))
|
|
fail("present", hr);
|
|
pump();
|
|
}
|
|
write_text(L"C:\\window.txt", "OK");
|
|
/* Keep the last frame up for a capture; the back buffer is redrawn so that each present shows the same picture. */
|
|
for (int hold = 0; hold < 40; ++hold) {
|
|
const float clear[4] = {0.1f, 0.1f, 0.3f, 1.0f};
|
|
ID3D11DeviceContext_ClearRenderTargetView(context, view, clear);
|
|
ID3D11DeviceContext_Draw(context, 3, 0);
|
|
IDXGISwapChain_Present(swap, 0, 0);
|
|
pump();
|
|
Sleep(100);
|
|
}
|
|
|
|
/* Resize: the client area and the swap chain buffers become WIDE by HIGH. */
|
|
RECT bigger = {0, 0, WIDE, HIGH};
|
|
AdjustWindowRect(&bigger, WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, FALSE);
|
|
SetWindowPos(window, NULL, 0, 0, bigger.right - bigger.left, bigger.bottom - bigger.top, SWP_NOMOVE | SWP_NOZORDER);
|
|
pump();
|
|
{
|
|
/* The client area the window reports after the resize is what the swap chain has to follow. */
|
|
RECT client;
|
|
char text[32];
|
|
GetClientRect(window, &client);
|
|
wsprintfA(text, "%dx%d", (int)(client.right - client.left), (int)(client.bottom - client.top));
|
|
write_text(L"C:\\window_client.txt", text);
|
|
}
|
|
ID3D11DeviceContext_OMSetRenderTargets(context, 0, NULL, NULL);
|
|
ID3D11RenderTargetView_Release(view);
|
|
ID3D11Texture2D_Release(back);
|
|
ID3D11Texture2D_Release(staging);
|
|
if (FAILED(hr = IDXGISwapChain_ResizeBuffers(swap, 0, WIDE, HIGH, DXGI_FORMAT_UNKNOWN, 0)))
|
|
fail("resize", hr);
|
|
if (FAILED(hr = IDXGISwapChain_GetBuffer(swap, 0, &IID_ID3D11Texture2D, (void **)&back)))
|
|
fail("buffer after resize", hr);
|
|
td.Width = WIDE;
|
|
td.Height = HIGH;
|
|
if (FAILED(hr = ID3D11Device_CreateTexture2D(device, &td, NULL, &staging)))
|
|
fail("staging after resize", hr);
|
|
if (FAILED(hr = ID3D11Device_CreateRenderTargetView(device, (ID3D11Resource *)back, NULL, &view)))
|
|
fail("view after resize", hr);
|
|
viewport.Width = WIDE;
|
|
viewport.Height = HIGH;
|
|
ID3D11DeviceContext_OMSetRenderTargets(context, 1, &view, NULL);
|
|
ID3D11DeviceContext_RSSetViewports(context, 1, &viewport);
|
|
for (int frame_number = 0; frame_number < 10; ++frame_number) {
|
|
const float clear[4] = {0.1f, 0.3f, 0.1f, 1.0f};
|
|
ID3D11DeviceContext_ClearRenderTargetView(context, view, clear);
|
|
ID3D11DeviceContext_Draw(context, 3, 0);
|
|
if (frame_number == 9)
|
|
save(context, staging, back, L"C:\\window_resized.raw", WIDE, HIGH);
|
|
if (FAILED(hr = IDXGISwapChain_Present(swap, 0, 0)))
|
|
fail("present after resize", hr);
|
|
pump();
|
|
}
|
|
write_text(L"C:\\window_resized.txt", "OK");
|
|
for (int hold = 0; hold < 200; ++hold) {
|
|
const float clear[4] = {0.1f, 0.3f, 0.1f, 1.0f};
|
|
ID3D11DeviceContext_ClearRenderTargetView(context, view, clear);
|
|
ID3D11DeviceContext_Draw(context, 3, 0);
|
|
IDXGISwapChain_Present(swap, 0, 0);
|
|
pump();
|
|
Sleep(100);
|
|
}
|
|
ExitProcess(0);
|
|
}
|