Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ValveSoftware/vkd3d.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHenri Verbeet <hverbeet@codeweavers.com>2016-11-02 16:56:13 +0300
committerHenri Verbeet <hverbeet@codeweavers.com>2016-11-13 02:55:54 +0300
commit20508aee7c338adea9eabb189d34e9e029d651a9 (patch)
treef12dbded655b3b72c1daac192a6f08b88745b96d
parentcd4f4a2976814a6dcda16cf5e7be25b6917ae002 (diff)
demos/gears: Add a gears demo.
-rw-r--r--Makefile.am18
-rw-r--r--demos/.gitignore1
-rw-r--r--demos/demo.h19
-rw-r--r--demos/demo_win32.h55
-rw-r--r--demos/demo_xcb.h26
-rw-r--r--demos/gears.c888
-rw-r--r--demos/gears.hlsl55
-rw-r--r--demos/gears.vert40
-rw-r--r--demos/gears_flat.frag10
-rw-r--r--demos/gears_smooth.frag10
-rw-r--r--demos/triangle.c2
11 files changed, 1115 insertions, 9 deletions
diff --git a/Makefile.am b/Makefile.am
index 916b34b2..290bdcf2 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -19,20 +19,27 @@ vkd3d_public_headers = \
include/vkd3d_utils.h \
include/vkd3d_windows.h
+spv_gears_shaders = \
+ demos/gears.vert.spv \
+ demos/gears_flat.frag.spv \
+ demos/gears_smooth.frag.spv
spv_triangle_shaders = \
demos/triangle.vert.spv \
demos/triangle.frag.spv
spv_shaders = \
+ $(spv_gears_shaders) \
$(spv_triangle_shaders)
hlsl_shaders = \
+ demos/gears.hlsl \
demos/triangle.hlsl
vkd3d_tests = \
tests/d3d12
vkd3d_demos = \
+ demos/gears \
demos/triangle
vkd3d_demos_headers = \
@@ -84,11 +91,18 @@ AM_DEFAULT_SOURCE_EXT = .c
TESTS = $(vkd3d_tests)
tests_d3d12_LDADD = $(LDADD) @PTHREAD_LIBS@
+DEMOS_LDADD = libvkd3d.la $(LDADD) @XCB_LIBS@
+DEMOS_CFLAGS = @XCB_CFLAGS@
noinst_PROGRAMS = $(vkd3d_demos)
EXTRA_DIST += $(vkd3d_demos_headers)
+
+EXTRA_demos_gears_DEPENDENCIES = $(spv_gears_shaders)
+demos_gears_CFLAGS = $(DEMOS_CFLAGS)
+demos_gears_LDADD = $(DEMOS_LDADD) -lm
+
EXTRA_demos_triangle_DEPENDENCIES = $(spv_triangle_shaders)
-demos_triangle_CFLAGS = @XCB_CFLAGS@
-demos_triangle_LDADD = libvkd3d.la $(LDADD) @XCB_LIBS@
+demos_triangle_CFLAGS = $(DEMOS_CFLAGS)
+demos_triangle_LDADD = $(DEMOS_LDADD)
VKD3D_V_WIDL = $(vkd3d_v_widl_@AM_V@)
vkd3d_v_widl_ = $(vkd3d_v_widl_@AM_DEFAULT_V@)
diff --git a/demos/.gitignore b/demos/.gitignore
index 198d0505..afb83ec5 100644
--- a/demos/.gitignore
+++ b/demos/.gitignore
@@ -1,2 +1,3 @@
+gears
triangle
*.spv
diff --git a/demos/demo.h b/demos/demo.h
index 1f8d7c5f..4e31e3d9 100644
--- a/demos/demo.h
+++ b/demos/demo.h
@@ -47,6 +47,10 @@
#define DEMO_KEY_UNKNOWN 0x0000
#define DEMO_KEY_ESCAPE 0xff1b
+#define DEMO_KEY_LEFT 0xff51
+#define DEMO_KEY_UP 0xff52
+#define DEMO_KEY_RIGHT 0xff53
+#define DEMO_KEY_DOWN 0xff54
struct demo_vec3
{
@@ -68,6 +72,21 @@ struct demo_swapchain_desc
typedef uint32_t demo_key;
+static inline void demo_vec3_set(struct demo_vec3 *v, float x, float y, float z)
+{
+ v->x = x;
+ v->y = y;
+ v->z = z;
+}
+
+static inline void demo_vec4_set(struct demo_vec4 *v, float x, float y, float z, float w)
+{
+ v->x = x;
+ v->y = y;
+ v->z = z;
+ v->w = w;
+}
+
static inline void demo_rasterizer_desc_init_default(D3D12_RASTERIZER_DESC *desc)
{
desc->FillMode = D3D12_FILL_MODE_SOLID;
diff --git a/demos/demo_win32.h b/demos/demo_win32.h
index 92cde9b0..dcd9a890 100644
--- a/demos/demo_win32.h
+++ b/demos/demo_win32.h
@@ -35,6 +35,9 @@ struct demo
ID3DBlob **code, ID3DBlob **errors);
size_t window_count;
bool quit;
+
+ void *user_data;
+ void (*idle_func)(struct demo *demo, void *user_data);
};
struct demo_window
@@ -101,8 +104,32 @@ static inline void demo_window_destroy(struct demo_window *window)
static inline demo_key demo_key_from_vkey(DWORD vkey)
{
- if (vkey == VK_ESCAPE)
- return DEMO_KEY_ESCAPE;
+ static const struct
+ {
+ DWORD vkey;
+ demo_key demo_key;
+ }
+ lookup[] =
+ {
+ {VK_ESCAPE, DEMO_KEY_ESCAPE},
+ {VK_LEFT, DEMO_KEY_LEFT},
+ {VK_RIGHT, DEMO_KEY_RIGHT},
+ {VK_UP, DEMO_KEY_UP},
+ {VK_DOWN, DEMO_KEY_DOWN},
+ };
+ unsigned int i;
+
+ if (vkey >= '0' && vkey <= '9')
+ return vkey;
+ if (vkey >= 'A' && vkey <= 'Z')
+ return vkey;
+
+ for (i = 0; i < ARRAY_SIZE(lookup); ++i)
+ {
+ if (lookup[i].vkey == vkey)
+ return lookup[i].demo_key;
+ }
+
return DEMO_KEY_UNKNOWN;
}
@@ -149,8 +176,19 @@ static inline void demo_process_events(struct demo *demo)
{
MSG msg = {0};
- while (GetMessage(&msg, NULL, 0, 0) != -1)
+ for (;;)
{
+ if (!demo->idle_func)
+ {
+ if (GetMessageW(&msg, NULL, 0, 0) == -1)
+ break;
+ }
+ else if (!PeekMessageW(&msg, 0, 0, 0, PM_REMOVE))
+ {
+ demo->idle_func(demo, demo->user_data);
+ continue;
+ }
+
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
@@ -160,7 +198,7 @@ static inline void demo_process_events(struct demo *demo)
}
}
-static inline bool demo_init(struct demo *demo)
+static inline bool demo_init(struct demo *demo, void *user_data)
{
WNDCLASSEXW wc;
@@ -184,7 +222,10 @@ static inline bool demo_init(struct demo *demo)
if (!RegisterClassExW(&wc))
goto fail;
+ demo->window_count = 0;
demo->quit = false;
+ demo->user_data = user_data;
+ demo->idle_func = NULL;
return true;
@@ -199,6 +240,12 @@ static inline void demo_cleanup(struct demo *demo)
FreeLibrary(demo->d3dcompiler);
}
+static inline void demo_set_idle_func(struct demo *demo,
+ void (*idle_func)(struct demo *demo, void *user_data))
+{
+ demo->idle_func = idle_func;
+}
+
static inline struct demo_swapchain *demo_swapchain_create(ID3D12CommandQueue *command_queue,
struct demo_window *window, const struct demo_swapchain_desc *desc)
{
diff --git a/demos/demo_xcb.h b/demos/demo_xcb.h
index a22822a0..31d308f1 100644
--- a/demos/demo_xcb.h
+++ b/demos/demo_xcb.h
@@ -41,6 +41,9 @@ struct demo
struct demo_window **windows;
size_t windows_size;
size_t window_count;
+
+ void *user_data;
+ void (*idle_func)(struct demo *demo, void *user_data);
};
struct demo_window
@@ -207,8 +210,19 @@ static inline void demo_process_events(struct demo *demo)
xcb_flush(demo->connection);
- while (demo->window_count && (event = xcb_wait_for_event(demo->connection)))
+ while (demo->window_count)
{
+ if (!demo->idle_func)
+ {
+ if (!(event = xcb_wait_for_event(demo->connection)))
+ break;
+ }
+ else if (!(event = xcb_poll_for_event(demo->connection)))
+ {
+ demo->idle_func(demo, demo->user_data);
+ continue;
+ }
+
switch (XCB_EVENT_RESPONSE_TYPE(event))
{
case XCB_EXPOSE:
@@ -238,7 +252,7 @@ static inline void demo_process_events(struct demo *demo)
}
}
-static inline bool demo_init(struct demo *demo)
+static inline bool demo_init(struct demo *demo, void *user_data)
{
if (!(demo->connection = xcb_connect(NULL, NULL)))
return false;
@@ -254,6 +268,8 @@ static inline bool demo_init(struct demo *demo)
demo->windows = NULL;
demo->windows_size = 0;
demo->window_count = 0;
+ demo->user_data = user_data;
+ demo->idle_func = NULL;
return true;
@@ -269,6 +285,12 @@ static inline void demo_cleanup(struct demo *demo)
xcb_disconnect(demo->connection);
}
+static inline void demo_set_idle_func(struct demo *demo,
+ void (*idle_func)(struct demo *demo, void *user_data))
+{
+ demo->idle_func = idle_func;
+}
+
static inline DXGI_FORMAT demo_get_srgb_format(DXGI_FORMAT format)
{
switch (format)
diff --git a/demos/gears.c b/demos/gears.c
new file mode 100644
index 00000000..87fc6804
--- /dev/null
+++ b/demos/gears.c
@@ -0,0 +1,888 @@
+/*
+ * Copyright 2016 Henri Verbeet for CodeWeavers
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+/*
+ * This application contains code derived from glxgears, the license for which
+ * follows:
+ *
+ * Copyright (C) 1999-2001 Brian Paul All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+ * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#define INITGUID
+#define _GNU_SOURCE
+#include <sys/time.h>
+#include <assert.h>
+#include <stdio.h>
+#include <math.h>
+#include "demo.h"
+
+struct cxg_fence
+{
+ ID3D12Fence *fence;
+ UINT64 value;
+ HANDLE event;
+};
+
+struct cxg_cb_data
+{
+ float mvp_matrix[16];
+ float normal_matrix[12];
+};
+
+struct cxg_instance_data
+{
+ struct demo_vec3 diffuse;
+ struct demo_vec4 transform;
+};
+
+struct cxg_vertex
+{
+ struct demo_vec3 position;
+ struct demo_vec3 normal;
+};
+
+struct cxg_face
+{
+ uint16_t v[3];
+};
+
+struct cxg_mesh
+{
+ struct cxg_vertex *vertices;
+ size_t vertex_count;
+
+ struct cxg_face *faces;
+ size_t flat_face_count;
+ size_t smooth_face_count;
+};
+
+struct cxg_draw
+{
+ size_t vertex_idx;
+ size_t flat_index_idx;
+ size_t flat_index_count;
+ size_t smooth_index_idx;
+ size_t smooth_index_count;
+};
+
+struct cx_gears
+{
+ struct demo demo;
+
+ struct demo_window *window;
+
+ unsigned int width;
+ unsigned int height;
+ float aspect_ratio;
+
+ bool animate;
+ float alpha;
+ float theta;
+ float phi;
+
+ D3D12_VIEWPORT vp;
+ D3D12_RECT scissor_rect;
+
+ ID3D12Device *device;
+ ID3D12CommandQueue *command_queue;
+ struct demo_swapchain *swapchain;
+ ID3D12DescriptorHeap *rtv_heap, *dsv_heap;
+ unsigned int rtv_descriptor_size;
+ ID3D12Resource *render_targets[2];
+ ID3D12CommandAllocator *command_allocator[2];
+
+ ID3D12RootSignature *root_signature;
+ ID3D12PipelineState *pipeline_state_smooth, *pipeline_state_flat;
+ ID3D12GraphicsCommandList *command_list[2];
+ ID3D12Resource *ds, *cb, *vb[2], *ib;
+ D3D12_VERTEX_BUFFER_VIEW vbv[2];
+ D3D12_INDEX_BUFFER_VIEW ibv;
+
+ unsigned int rt_idx;
+ struct cxg_fence fence;
+
+ struct cxg_cb_data *cb_data;
+ struct cxg_instance_data *instance_data;
+ struct cxg_draw draws[3];
+};
+
+static void cxg_populate_command_list(struct cx_gears *cxg, unsigned int rt_idx)
+{
+ ID3D12GraphicsCommandList *command_list = cxg->command_list[rt_idx];
+ static const float clear_colour[] = {0.0f, 0.0f, 0.0f, 1.0f};
+
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle, dsv_handle;
+ D3D12_RESOURCE_BARRIER barrier;
+ HRESULT hr;
+ size_t i;
+
+ hr = ID3D12CommandAllocator_Reset(cxg->command_allocator[rt_idx]);
+ assert(SUCCEEDED(hr));
+
+ hr = ID3D12GraphicsCommandList_Reset(command_list, cxg->command_allocator[rt_idx], cxg->pipeline_state_flat);
+ assert(SUCCEEDED(hr));
+
+ ID3D12GraphicsCommandList_SetGraphicsRootSignature(command_list, cxg->root_signature);
+ ID3D12GraphicsCommandList_SetGraphicsRootConstantBufferView(command_list, 0,
+ ID3D12Resource_GetGPUVirtualAddress(cxg->cb));
+
+ ID3D12GraphicsCommandList_RSSetViewports(command_list, 1, &cxg->vp);
+ ID3D12GraphicsCommandList_RSSetScissorRects(command_list, 1, &cxg->scissor_rect);
+
+ barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
+ barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
+ barrier.Transition.pResource = cxg->render_targets[rt_idx];
+ barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barrier);
+
+ rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxg->rtv_heap);
+ rtv_handle.ptr += rt_idx * cxg->rtv_descriptor_size;
+ dsv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxg->dsv_heap);
+ ID3D12GraphicsCommandList_OMSetRenderTargets(command_list, 1, &rtv_handle, FALSE, &dsv_handle);
+
+ ID3D12GraphicsCommandList_ClearRenderTargetView(command_list, rtv_handle, clear_colour, 0, NULL);
+ ID3D12GraphicsCommandList_ClearDepthStencilView(command_list,
+ dsv_handle, D3D12_CLEAR_FLAG_DEPTH, 1.0f, 0, 0, NULL);
+ ID3D12GraphicsCommandList_IASetPrimitiveTopology(command_list, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+ ID3D12GraphicsCommandList_IASetIndexBuffer(command_list, &cxg->ibv);
+ ID3D12GraphicsCommandList_IASetVertexBuffers(command_list, 0, 2, cxg->vbv);
+
+ for (i = 0; i < ARRAY_SIZE(cxg->draws); ++i)
+ {
+ ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, cxg->draws[i].flat_index_count,
+ 1, cxg->draws[i].flat_index_idx, cxg->draws[i].vertex_idx, i);
+
+ ID3D12GraphicsCommandList_SetPipelineState(command_list, cxg->pipeline_state_smooth);
+ ID3D12GraphicsCommandList_DrawIndexedInstanced(command_list, cxg->draws[i].smooth_index_count,
+ 1, cxg->draws[i].smooth_index_idx, cxg->draws[i].vertex_idx, i);
+
+ ID3D12GraphicsCommandList_SetPipelineState(command_list, cxg->pipeline_state_flat);
+ }
+
+ barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_RENDER_TARGET;
+ barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_PRESENT;
+ ID3D12GraphicsCommandList_ResourceBarrier(command_list, 1, &barrier);
+
+ hr = ID3D12GraphicsCommandList_Close(command_list);
+ assert(SUCCEEDED(hr));
+}
+
+static void cxg_wait_for_previous_frame(struct cx_gears *cxg)
+{
+ struct cxg_fence *fence = &cxg->fence;
+ const UINT64 v = fence->value;
+ HRESULT hr;
+
+ hr = ID3D12CommandQueue_Signal(cxg->command_queue, fence->fence, v);
+ assert(SUCCEEDED(hr));
+
+ ++fence->value;
+
+ if (ID3D12Fence_GetCompletedValue(fence->fence) < v)
+ {
+ ID3D12Fence_SetEventOnCompletion(fence->fence, v, fence->event);
+ demo_wait_event(fence->event, INFINITE);
+ }
+
+ cxg->rt_idx = demo_swapchain_get_current_back_buffer_index(cxg->swapchain);
+}
+
+static void cxg_update_mvp(struct cx_gears *cxg)
+{
+ float s1 = sinf(cxg->theta);
+ float c1 = cosf(cxg->theta);
+ float s2 = sinf(cxg->phi);
+ float c2 = cosf(cxg->phi);
+ float z_offset = -40.0f;
+ float z_max = 60.0f;
+ float z_min = 5.0f;
+ float sx = z_min;
+ float sy = z_min * cxg->aspect_ratio;
+ float sz = -((z_max + z_min) / (z_max - z_min));
+ float d = (-2.0f * z_max * z_min) / (z_max - z_min);
+ unsigned int i, j;
+ float world[] =
+ {
+ c1, s2 * s1, c2 * -s1, 0.0f,
+ 0.0f, c2, s2, 0.0f,
+ s1, -s2 * c1, c2 * c1, 0.0f,
+ 0.0f, 0.0f, z_offset, 1.0f,
+ };
+ float projection[] =
+ {
+ sx, 0.0f, 0.0f, 0.0f,
+ 0.0f, sy, 0.0f, 0.0f,
+ 0.0f, 0.0f, sz, -1.0f,
+ 0.0f, 0.0f, d, 0.0f,
+ };
+
+ for (i = 0; i < 4; ++i)
+ {
+ for (j = 0; j < 4; ++j)
+ {
+ cxg->cb_data->mvp_matrix[i * 4 + j] = projection[j] * world[i * 4]
+ + projection[j + 4] * world[i * 4 + 1]
+ + projection[j + 8] * world[i * 4 + 2]
+ + projection[j + 12] * world[i * 4 + 3];
+ }
+ }
+ memcpy(cxg->cb_data->normal_matrix, world, sizeof(cxg->cb_data->normal_matrix));
+}
+
+static void cxg_render_frame(struct cx_gears *cxg)
+{
+ static double t_prev = -1.0;
+ struct timeval tv;
+ double dt, t;
+ float a;
+
+ gettimeofday(&tv, NULL);
+ t = tv.tv_sec + tv.tv_usec / 1000000.0;
+ if (t_prev < 0.0)
+ t_prev = t;
+ dt = t - t_prev;
+ t_prev = t;
+
+ if (cxg->animate)
+ {
+ cxg->alpha += (70.0 * M_PI / 180.0) * dt; /* 70°/s */
+ if (cxg->alpha > 20.0 * M_PI)
+ cxg->alpha -= 20.0 * M_PI;
+ }
+
+ a = cxg->alpha;
+ demo_vec4_set(&cxg->instance_data[0].transform, cosf(a), sinf(a), -3.0f, -2.0f);
+ a = (-2.0f * cxg->alpha) - 9.0f * M_PI / 180.0;
+ demo_vec4_set(&cxg->instance_data[1].transform, cosf(a), sinf(a), 3.1f, -2.0f);
+ a = (-2.0f * cxg->alpha) - 25.0f * M_PI / 180.0;
+ demo_vec4_set(&cxg->instance_data[2].transform, cosf(a), sinf(a), -3.1f, 4.2f);
+
+ ID3D12CommandQueue_ExecuteCommandLists(cxg->command_queue, 1,
+ (ID3D12CommandList **)&cxg->command_list[cxg->rt_idx]);
+ demo_swapchain_present(cxg->swapchain);
+ cxg_wait_for_previous_frame(cxg);
+}
+
+static void cxg_destroy_pipeline(struct cx_gears *cxg)
+{
+ unsigned int i;
+
+ ID3D12CommandAllocator_Release(cxg->command_allocator[1]);
+ ID3D12CommandAllocator_Release(cxg->command_allocator[0]);
+ for (i = 0; i < ARRAY_SIZE(cxg->render_targets); ++i)
+ {
+ ID3D12Resource_Release(cxg->render_targets[i]);
+ }
+ ID3D12DescriptorHeap_Release(cxg->dsv_heap);
+ ID3D12DescriptorHeap_Release(cxg->rtv_heap);
+ demo_swapchain_destroy(cxg->swapchain);
+ ID3D12CommandQueue_Release(cxg->command_queue);
+ ID3D12Device_Release(cxg->device);
+}
+
+static void cxg_load_pipeline(struct cx_gears *cxg)
+{
+ struct demo_swapchain_desc swapchain_desc;
+ D3D12_CPU_DESCRIPTOR_HANDLE rtv_handle;
+ D3D12_DESCRIPTOR_HEAP_DESC heap_desc;
+ D3D12_COMMAND_QUEUE_DESC queue_desc;
+ unsigned int i;
+ HRESULT hr;
+
+ hr = D3D12CreateDevice(NULL, D3D_FEATURE_LEVEL_11_0, &IID_ID3D12Device, (void **)&cxg->device);
+ assert(SUCCEEDED(hr));
+
+ memset(&queue_desc, 0, sizeof(queue_desc));
+ queue_desc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
+ queue_desc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
+ hr = ID3D12Device_CreateCommandQueue(cxg->device, &queue_desc,
+ &IID_ID3D12CommandQueue, (void **)&cxg->command_queue);
+ assert(SUCCEEDED(hr));
+
+ swapchain_desc.buffer_count = ARRAY_SIZE(cxg->render_targets);
+ swapchain_desc.format = DXGI_FORMAT_B8G8R8A8_UNORM;
+ swapchain_desc.width = cxg->width;
+ swapchain_desc.height = cxg->height;
+ cxg->swapchain = demo_swapchain_create(cxg->command_queue, cxg->window, &swapchain_desc);
+ assert(cxg->swapchain);
+ cxg->rt_idx = demo_swapchain_get_current_back_buffer_index(cxg->swapchain);
+
+ memset(&heap_desc, 0, sizeof(heap_desc));
+ heap_desc.NumDescriptors = ARRAY_SIZE(cxg->render_targets);
+ heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_RTV;
+ heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ hr = ID3D12Device_CreateDescriptorHeap(cxg->device, &heap_desc,
+ &IID_ID3D12DescriptorHeap, (void **)&cxg->rtv_heap);
+ assert(SUCCEEDED(hr));
+
+ cxg->rtv_descriptor_size = ID3D12Device_GetDescriptorHandleIncrementSize(cxg->device,
+ D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
+ rtv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxg->rtv_heap);
+ for (i = 0; i < ARRAY_SIZE(cxg->render_targets); ++i)
+ {
+ cxg->render_targets[i] = demo_swapchain_get_back_buffer(cxg->swapchain, i);
+ ID3D12Device_CreateRenderTargetView(cxg->device, cxg->render_targets[i], NULL, rtv_handle);
+ rtv_handle.ptr += cxg->rtv_descriptor_size;
+ }
+
+ heap_desc.NumDescriptors = 1;
+ heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_DSV;
+ heap_desc.Flags = D3D12_DESCRIPTOR_HEAP_FLAG_NONE;
+ hr = ID3D12Device_CreateDescriptorHeap(cxg->device, &heap_desc,
+ &IID_ID3D12DescriptorHeap, (void **)&cxg->dsv_heap);
+ assert(SUCCEEDED(hr));
+
+ hr = ID3D12Device_CreateCommandAllocator(cxg->device, D3D12_COMMAND_LIST_TYPE_DIRECT,
+ &IID_ID3D12CommandAllocator, (void **)&cxg->command_allocator[0]);
+ assert(SUCCEEDED(hr));
+ hr = ID3D12Device_CreateCommandAllocator(cxg->device, D3D12_COMMAND_LIST_TYPE_DIRECT,
+ &IID_ID3D12CommandAllocator, (void **)&cxg->command_allocator[1]);
+ assert(SUCCEEDED(hr));
+}
+
+static void cxg_fence_destroy(struct cxg_fence *cxg_fence)
+{
+ ID3D12Fence_Release(cxg_fence->fence);
+ demo_destroy_event(cxg_fence->event);
+}
+
+static void cxg_destroy_assets(struct cx_gears *cxg)
+{
+ cxg_fence_destroy(&cxg->fence);
+ ID3D12Resource_Release(cxg->ib);
+ ID3D12Resource_Unmap(cxg->vb[1], 0, NULL);
+ ID3D12Resource_Release(cxg->vb[1]);
+ ID3D12Resource_Release(cxg->vb[0]);
+ ID3D12Resource_Unmap(cxg->cb, 0, NULL);
+ ID3D12Resource_Release(cxg->cb);
+ ID3D12Resource_Release(cxg->ds);
+ ID3D12GraphicsCommandList_Release(cxg->command_list[1]);
+ ID3D12GraphicsCommandList_Release(cxg->command_list[0]);
+ ID3D12PipelineState_Release(cxg->pipeline_state_smooth);
+ ID3D12PipelineState_Release(cxg->pipeline_state_flat);
+ ID3D12RootSignature_Release(cxg->root_signature);
+}
+
+static void cxg_fence_create(struct cxg_fence *fence, ID3D12Device *device)
+{
+ HRESULT hr;
+
+ hr = ID3D12Device_CreateFence(device, 0, D3D12_FENCE_FLAG_NONE,
+ &IID_ID3D12Fence, (void **)&fence->fence);
+ assert(SUCCEEDED(hr));
+ fence->value = 1;
+ fence->event = demo_create_event();
+ assert(fence->event);
+}
+
+static void cxg_vertex_set_position(struct cxg_vertex *v, float x, float y, float z)
+{
+ demo_vec3_set(&v->position, x, y, z);
+}
+
+static void cxg_vertex_set_normal(struct cxg_vertex *v, float x, float y, float z)
+{
+ demo_vec3_set(&v->normal, x, y, z);
+}
+
+static void cxg_mesh_create(ID3D12Device *device, float inner_radius, float outer_radius, float width,
+ unsigned int tooth_count, float tooth_depth, struct cxg_mesh *mesh)
+{
+ struct cxg_vertex *vertices;
+ struct cxg_face *faces;
+ unsigned int i, j;
+ float r0, r1, r2;
+ float angle, da;
+
+ if (!(vertices = calloc(tooth_count, 12 * sizeof(*vertices))))
+ return;
+ if (!(faces = calloc(tooth_count, 20 * sizeof(*faces))))
+ {
+ free(vertices);
+ return;
+ }
+
+ r0 = inner_radius;
+ r1 = outer_radius - tooth_depth / 2.0f;
+ r2 = outer_radius + tooth_depth / 2.0f;
+ da = 2.0f * M_PI / tooth_count / 4.0f;
+
+ for (i = 0; i < tooth_count; ++i)
+ {
+ struct cxg_vertex *tooth = &vertices[i * 12];
+ float u, v;
+
+ angle = i * 2.0f * M_PI / tooth_count;
+
+ cxg_vertex_set_position(&tooth[0], r0 * cosf(angle), r0 * sinf(angle), width * 0.5f);
+ cxg_vertex_set_normal(&tooth[0], -cosf(angle), -sinf(angle), 0.0f);
+ cxg_vertex_set_position(&tooth[1], r1 * cosf(angle), r1 * sinf(angle), width * 0.5f);
+ cxg_vertex_set_normal(&tooth[1], 0.0f, 0.0f, 1.0f);
+ cxg_vertex_set_position(&tooth[2], r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), width * 0.5f);
+ cxg_vertex_set_normal(&tooth[2], 0.0f, 0.0f, 1.0f);
+ cxg_vertex_set_position(&tooth[3], r2 * cosf(angle + da), r2 * sinf(angle + da), width * 0.5f);
+ cxg_vertex_set_normal(&tooth[3], cosf(angle), sinf(angle), 0.0f);
+ cxg_vertex_set_position(&tooth[4], r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), width * 0.5f);
+ u = r1 * cosf(angle + 3 * da) - r2 * cosf(angle + 2 * da);
+ v = r1 * sinf(angle + 3 * da) - r2 * sinf(angle + 2 * da);
+ cxg_vertex_set_normal(&tooth[4], v, -u, 0.0f);
+
+ cxg_vertex_set_position(&tooth[5], r0 * cosf(angle), r0 * sinf(angle), -width * 0.5f);
+ cxg_vertex_set_normal(&tooth[5], -cosf(angle), -sinf(angle), 0.0f);
+ cxg_vertex_set_position(&tooth[6], r1 * cosf(angle), r1 * sinf(angle), -width * 0.5f);
+ cxg_vertex_set_normal(&tooth[6], 0.0f, 0.0f, -1.0f);
+ cxg_vertex_set_position(&tooth[7], r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), -width * 0.5f);
+ cxg_vertex_set_normal(&tooth[7], 0.0f, 0.0f, -1.0f);
+ cxg_vertex_set_position(&tooth[8], r2 * cosf(angle + da), r2 * sinf(angle + da), -width * 0.5f);
+ cxg_vertex_set_normal(&tooth[8], 0.0f, 0.0f, -1.0f);
+ cxg_vertex_set_position(&tooth[9], r2 * cosf(angle + 2 * da), r2 * sinf(angle + 2 * da), -width * 0.5f);
+ cxg_vertex_set_normal(&tooth[9], 0.0f, 0.0f, -1.0f);
+
+ cxg_vertex_set_position(&tooth[10], r1 * cosf(angle), r1 * sinf(angle), width * 0.5f);
+ u = r2 * cosf(angle + da) - r1 * cosf(angle);
+ v = r2 * sinf(angle + da) - r1 * sinf(angle);
+ cxg_vertex_set_normal(&tooth[10], v, -u, 0.0f);
+ cxg_vertex_set_position(&tooth[11], r1 * cosf(angle + 3 * da), r1 * sinf(angle + 3 * da), width * 0.5f);
+ cxg_vertex_set_normal(&tooth[11], cosf(angle), sinf(angle), 0.0f);
+ }
+
+ for (i = 0; i < tooth_count; ++i)
+ {
+ struct cxg_face *f = &faces[i * 18];
+ static const struct cxg_face flat_faces[] =
+ {
+ /* Front */
+ {{ 1, 2, 0}},
+ {{ 1, 3, 4}},
+ {{ 1, 4, 2}},
+ {{ 2, 12, 0}},
+ {{ 2, 13, 12}},
+ /* Back */
+ {{ 6, 5, 7}},
+ {{ 6, 9, 8}},
+ {{ 6, 7, 9}},
+ {{ 7, 5, 17}},
+ {{ 7, 17, 18}},
+ /* Outward */
+ {{10, 6, 8}},
+ {{10, 8, 3}},
+ {{ 3, 8, 9}},
+ {{ 3, 9, 4}},
+ {{ 4, 9, 7}},
+ {{ 4, 7, 11}},
+ {{11, 7, 18}},
+ {{11, 18, 22}},
+ };
+
+ for (j = 0; j < ARRAY_SIZE(flat_faces); ++j)
+ {
+ f[j].v[0] = (flat_faces[j].v[0] + i * 12) % (tooth_count * 12);
+ f[j].v[1] = (flat_faces[j].v[1] + i * 12) % (tooth_count * 12);
+ f[j].v[2] = (flat_faces[j].v[2] + i * 12) % (tooth_count * 12);
+ }
+ }
+
+ for (i = 0; i < tooth_count; ++i)
+ {
+ struct cxg_face *f = &faces[i * 2 + tooth_count * 18];
+ static const struct cxg_face smooth_faces[] =
+ {
+ /* Inward */
+ {{ 0, 17, 5}},
+ {{ 0, 12, 17}},
+ };
+
+ for (j = 0; j < ARRAY_SIZE(smooth_faces); ++j)
+ {
+ f[j].v[0] = (smooth_faces[j].v[0] + i * 12) % (tooth_count * 12);
+ f[j].v[1] = (smooth_faces[j].v[1] + i * 12) % (tooth_count * 12);
+ f[j].v[2] = (smooth_faces[j].v[2] + i * 12) % (tooth_count * 12);
+ }
+ }
+
+ mesh->vertices = vertices;
+ mesh->vertex_count = 12 * tooth_count;
+ mesh->faces = faces;
+ mesh->flat_face_count = 18 * tooth_count;
+ mesh->smooth_face_count = 2 * tooth_count;
+}
+
+static void cxg_mesh_destroy(struct cxg_mesh *mesh)
+{
+ free(mesh->faces);
+ free(mesh->vertices);
+}
+
+static void cxg_load_gears(struct cx_gears *cxg)
+{
+ size_t vertex_count, face_count, vertex_idx, face_idx, i;
+ D3D12_RESOURCE_DESC resource_desc;
+ D3D12_HEAP_PROPERTIES heap_desc;
+ D3D12_RANGE read_range = {0, 0};
+ struct cxg_vertex *vertices;
+ struct cxg_mesh gears[3];
+ struct cxg_face *faces;
+ HRESULT hr;
+
+ cxg_mesh_create(cxg->device, 1.0f, 4.0f, 1.0f, 20, 0.7f, &gears[0]);
+ demo_vec3_set(&cxg->instance_data[0].diffuse, 0.8f, 0.1f, 0.0f);
+ cxg_mesh_create(cxg->device, 0.5f, 2.0f, 2.0f, 10, 0.7f, &gears[1]);
+ demo_vec3_set(&cxg->instance_data[1].diffuse, 0.0f, 0.8f, 0.2f);
+ cxg_mesh_create(cxg->device, 1.3f, 2.0f, 0.5f, 10, 0.7f, &gears[2]);
+ demo_vec3_set(&cxg->instance_data[2].diffuse, 0.2f, 0.2f, 1.0f);
+
+ for (i = 0, vertex_count = 0, face_count = 0; i < ARRAY_SIZE(gears); ++i)
+ {
+ vertex_count += gears[i].vertex_count;
+ face_count += gears[i].flat_face_count + gears[i].smooth_face_count;
+ }
+
+ heap_desc.Type = D3D12_HEAP_TYPE_UPLOAD;
+ heap_desc.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heap_desc.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heap_desc.CreationNodeMask = 1;
+ heap_desc.VisibleNodeMask = 1;
+
+ resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ resource_desc.Alignment = 0;
+ resource_desc.Width = vertex_count * sizeof(*vertices);
+ resource_desc.Height = 1;
+ resource_desc.DepthOrArraySize = 1;
+ resource_desc.MipLevels = 1;
+ resource_desc.Format = DXGI_FORMAT_UNKNOWN;
+ resource_desc.SampleDesc.Count = 1;
+ resource_desc.SampleDesc.Quality = 0;
+ resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ hr = ID3D12Device_CreateCommittedResource(cxg->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&cxg->vb[0]);
+ assert(SUCCEEDED(hr));
+
+ resource_desc.Width = face_count * sizeof(*faces);
+ hr = ID3D12Device_CreateCommittedResource(cxg->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&cxg->ib);
+ assert(SUCCEEDED(hr));
+
+ hr = ID3D12Resource_Map(cxg->vb[0], 0, &read_range, (void **)&vertices);
+ assert(SUCCEEDED(hr));
+ hr = ID3D12Resource_Map(cxg->ib, 0, &read_range, (void **)&faces);
+ assert(SUCCEEDED(hr));
+
+ for (i = 0, vertex_idx = 0, face_idx = 0; i < ARRAY_SIZE(gears); ++i)
+ {
+ size_t tmp;
+
+ cxg->draws[i].vertex_idx = vertex_idx;
+ memcpy(&vertices[vertex_idx], gears[i].vertices, gears[i].vertex_count * sizeof(*vertices));
+ vertex_idx += gears[i].vertex_count;
+
+ cxg->draws[i].flat_index_idx = 3 * face_idx;
+ cxg->draws[i].flat_index_count = 3 * gears[i].flat_face_count;
+ cxg->draws[i].smooth_index_idx = cxg->draws[i].flat_index_idx + cxg->draws[i].flat_index_count;
+ cxg->draws[i].smooth_index_count = 3 * gears[i].smooth_face_count;
+ tmp = gears[i].flat_face_count + gears[i].smooth_face_count;
+ memcpy(&faces[face_idx], gears[i].faces, tmp * sizeof(*faces));
+ face_idx += tmp;
+ }
+
+ ID3D12Resource_Unmap(cxg->ib, 0, NULL);
+ ID3D12Resource_Unmap(cxg->vb[0], 0, NULL);
+
+ cxg_mesh_destroy(&gears[2]);
+ cxg_mesh_destroy(&gears[1]);
+ cxg_mesh_destroy(&gears[0]);
+
+ cxg->vbv[0].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cxg->vb[0]);
+ cxg->vbv[0].StrideInBytes = sizeof(*vertices);
+ cxg->vbv[0].SizeInBytes = vertex_count * sizeof(*vertices);
+
+ cxg->ibv.BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cxg->ib);
+ cxg->ibv.SizeInBytes = face_count * sizeof(*faces);
+ cxg->ibv.Format = DXGI_FORMAT_R16_UINT;
+}
+
+static void cxg_load_assets(struct cx_gears *cxg)
+{
+ static const D3D12_INPUT_ELEMENT_DESC il_desc[] =
+ {
+ {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+ {"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0},
+ {"DIFFUSE", 0, DXGI_FORMAT_R32G32B32_FLOAT, 1, 0, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
+ {"TRANSFORM", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 12, D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA, 1},
+ };
+
+ D3D12_ROOT_SIGNATURE_DESC root_signature_desc;
+ D3D12_GRAPHICS_PIPELINE_STATE_DESC pso_desc;
+ D3D12_CPU_DESCRIPTOR_HANDLE dsv_handle;
+ D3D12_ROOT_PARAMETER root_parameter;
+ D3D12_RESOURCE_DESC resource_desc;
+ D3D12_HEAP_PROPERTIES heap_desc;
+ D3D12_RANGE read_range = {0, 0};
+ D3D12_CLEAR_VALUE clear_value;
+ HRESULT hr;
+ bool ret;
+
+ root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_CBV;
+ root_parameter.Descriptor.ShaderRegister = 0;
+ root_parameter.Descriptor.RegisterSpace = 0;
+ root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_VERTEX;
+
+ memset(&root_signature_desc, 0, sizeof(root_signature_desc));
+ root_signature_desc.NumParameters = 1;
+ root_signature_desc.pParameters = &root_parameter;
+ root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
+ | D3D12_ROOT_SIGNATURE_FLAG_DENY_HULL_SHADER_ROOT_ACCESS
+ | D3D12_ROOT_SIGNATURE_FLAG_DENY_DOMAIN_SHADER_ROOT_ACCESS
+ | D3D12_ROOT_SIGNATURE_FLAG_DENY_GEOMETRY_SHADER_ROOT_ACCESS
+ | D3D12_ROOT_SIGNATURE_FLAG_DENY_PIXEL_SHADER_ROOT_ACCESS;
+ hr = demo_create_root_signature(cxg->device, &root_signature_desc, &cxg->root_signature);
+ assert(SUCCEEDED(hr));
+
+ memset(&pso_desc, 0, sizeof(pso_desc));
+ pso_desc.InputLayout.pInputElementDescs = il_desc;
+ pso_desc.InputLayout.NumElements = ARRAY_SIZE(il_desc);
+ pso_desc.pRootSignature = cxg->root_signature;
+ ret = demo_load_shader(&cxg->demo, L"gears.hlsl", "vs_main", "vs_5_0", "gears.vert.spv", &pso_desc.VS);
+ assert(ret);
+ ret = demo_load_shader(&cxg->demo, L"gears.hlsl", "ps_main_flat",
+ "ps_5_0", "gears_flat.frag.spv", &pso_desc.PS);
+ assert(ret);
+
+ demo_rasterizer_desc_init_default(&pso_desc.RasterizerState);
+ pso_desc.RasterizerState.FrontCounterClockwise = TRUE;
+ demo_blend_desc_init_default(&pso_desc.BlendState);
+ pso_desc.DepthStencilState.DepthEnable = TRUE;
+ pso_desc.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL;
+ pso_desc.DepthStencilState.DepthFunc = D3D12_COMPARISON_FUNC_LESS;
+ pso_desc.DepthStencilState.StencilEnable = FALSE;
+ pso_desc.SampleMask = UINT_MAX;
+ pso_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
+ pso_desc.NumRenderTargets = 1;
+ pso_desc.RTVFormats[0] = DXGI_FORMAT_B8G8R8A8_UNORM;
+ pso_desc.DSVFormat = DXGI_FORMAT_D32_FLOAT;
+ pso_desc.SampleDesc.Count = 1;
+ hr = ID3D12Device_CreateGraphicsPipelineState(cxg->device, &pso_desc,
+ &IID_ID3D12PipelineState, (void **)&cxg->pipeline_state_flat);
+ assert(SUCCEEDED(hr));
+
+ free((void *)pso_desc.PS.pShaderBytecode);
+ ret = demo_load_shader(&cxg->demo, L"gears.hlsl", "ps_main_smooth",
+ "ps_5_0", "gears_smooth.frag.spv", &pso_desc.PS);
+ assert(ret);
+ hr = ID3D12Device_CreateGraphicsPipelineState(cxg->device, &pso_desc,
+ &IID_ID3D12PipelineState, (void **)&cxg->pipeline_state_smooth);
+ assert(SUCCEEDED(hr));
+
+ free((void *)pso_desc.PS.pShaderBytecode);
+ free((void *)pso_desc.VS.pShaderBytecode);
+
+ hr = ID3D12Device_CreateCommandList(cxg->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, cxg->command_allocator[0],
+ cxg->pipeline_state_flat, &IID_ID3D12GraphicsCommandList, (void **)&cxg->command_list[0]);
+ assert(SUCCEEDED(hr));
+ hr = ID3D12GraphicsCommandList_Close(cxg->command_list[0]);
+ assert(SUCCEEDED(hr));
+ hr = ID3D12Device_CreateCommandList(cxg->device, 0, D3D12_COMMAND_LIST_TYPE_DIRECT, cxg->command_allocator[1],
+ cxg->pipeline_state_flat, &IID_ID3D12GraphicsCommandList, (void **)&cxg->command_list[1]);
+ assert(SUCCEEDED(hr));
+ hr = ID3D12GraphicsCommandList_Close(cxg->command_list[1]);
+ assert(SUCCEEDED(hr));
+
+ heap_desc.Type = D3D12_HEAP_TYPE_DEFAULT;
+ heap_desc.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN;
+ heap_desc.MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN;
+ heap_desc.CreationNodeMask = 1;
+ heap_desc.VisibleNodeMask = 1;
+
+ resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
+ resource_desc.Alignment = 0;
+ resource_desc.Width = cxg->width;
+ resource_desc.Height = cxg->height;
+ resource_desc.DepthOrArraySize = 1;
+ resource_desc.MipLevels = 1;
+ resource_desc.Format = DXGI_FORMAT_D32_FLOAT;
+ resource_desc.SampleDesc.Count = 1;
+ resource_desc.SampleDesc.Quality = 0;
+ resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
+ resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL;
+
+ clear_value.Format = DXGI_FORMAT_D32_FLOAT;
+ clear_value.DepthStencil.Depth = 1.0f;
+ clear_value.DepthStencil.Stencil = 0;
+
+ hr = ID3D12Device_CreateCommittedResource(cxg->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
+ D3D12_RESOURCE_STATE_DEPTH_WRITE, &clear_value, &IID_ID3D12Resource, (void **)&cxg->ds);
+ assert(SUCCEEDED(hr));
+
+ dsv_handle = ID3D12DescriptorHeap_GetCPUDescriptorHandleForHeapStart(cxg->dsv_heap);
+ ID3D12Device_CreateDepthStencilView(cxg->device, cxg->ds, NULL, dsv_handle);
+
+ heap_desc.Type = D3D12_HEAP_TYPE_UPLOAD;
+
+ resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
+ resource_desc.Width = sizeof(*cxg->cb_data);
+ resource_desc.Height = 1;
+ resource_desc.Format = DXGI_FORMAT_UNKNOWN;
+ resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
+ resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE;
+
+ hr = ID3D12Device_CreateCommittedResource(cxg->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&cxg->cb);
+ assert(SUCCEEDED(hr));
+
+ hr = ID3D12Resource_Map(cxg->cb, 0, &read_range, (void **)&cxg->cb_data);
+ assert(SUCCEEDED(hr));
+ cxg_update_mvp(cxg);
+
+ resource_desc.Width = 3 * sizeof(*cxg->instance_data);
+ hr = ID3D12Device_CreateCommittedResource(cxg->device, &heap_desc, D3D12_HEAP_FLAG_NONE, &resource_desc,
+ D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &IID_ID3D12Resource, (void **)&cxg->vb[1]);
+ assert(SUCCEEDED(hr));
+
+ hr = ID3D12Resource_Map(cxg->vb[1], 0, &read_range, (void **)&cxg->instance_data);
+ assert(SUCCEEDED(hr));
+
+ cxg->vbv[1].BufferLocation = ID3D12Resource_GetGPUVirtualAddress(cxg->vb[1]);
+ cxg->vbv[1].StrideInBytes = sizeof(*cxg->instance_data);
+ cxg->vbv[1].SizeInBytes = 3 * sizeof(*cxg->instance_data);
+
+ cxg_load_gears(cxg);
+
+ cxg_fence_create(&cxg->fence, cxg->device);
+ cxg_wait_for_previous_frame(cxg);
+}
+
+static void cxg_key_press(struct demo_window *window, demo_key key, void *user_data)
+{
+ struct cx_gears *cxg = user_data;
+
+ switch (key)
+ {
+ case 'a':
+ case 'A':
+ cxg->animate = !cxg->animate;
+ break;
+ case DEMO_KEY_ESCAPE:
+ demo_window_destroy(window);
+ break;
+ case DEMO_KEY_LEFT:
+ cxg->theta += M_PI / 36.0f;
+ cxg_update_mvp(cxg);
+ break;
+ case DEMO_KEY_RIGHT:
+ cxg->theta -= M_PI / 36.0f;
+ cxg_update_mvp(cxg);
+ break;
+ case DEMO_KEY_UP:
+ cxg->phi += M_PI / 36.0f;
+ cxg_update_mvp(cxg);
+ break;
+ case DEMO_KEY_DOWN:
+ cxg->phi -= M_PI / 36.0f;
+ cxg_update_mvp(cxg);
+ break;
+ default:
+ break;
+ }
+}
+
+static void cxg_expose(struct demo_window *window, void *user_data)
+{
+ cxg_render_frame(user_data);
+}
+
+static void cxg_idle(struct demo *demo, void *user_data)
+{
+ cxg_render_frame(user_data);
+}
+
+static int cxg_main(void)
+{
+ unsigned int width = 300, height = 300;
+ struct cx_gears cxg;
+
+ memset(&cxg, 0, sizeof(cxg));
+ if (!demo_init(&cxg.demo, &cxg))
+ return EXIT_FAILURE;
+ demo_set_idle_func(&cxg.demo, cxg_idle);
+
+ cxg.window = demo_window_create(&cxg.demo, "Vkd3d Gears", width, height, &cxg);
+ demo_window_set_key_press_func(cxg.window, cxg_key_press);
+ demo_window_set_expose_func(cxg.window, cxg_expose);
+
+ cxg.width = width;
+ cxg.height = height;
+ cxg.aspect_ratio = (float)width / (float)height;
+
+ cxg.animate = true;
+ cxg.theta = M_PI / 6.0f;
+ cxg.phi = M_PI / 9.0f;
+
+ cxg.vp.Width = width;
+ cxg.vp.Height = height;
+ cxg.vp.MaxDepth = 1.0f;
+
+ cxg.scissor_rect.right = width;
+ cxg.scissor_rect.bottom = height;
+
+ cxg_load_pipeline(&cxg);
+ cxg_load_assets(&cxg);
+ cxg_populate_command_list(&cxg, 0);
+ cxg_populate_command_list(&cxg, 1);
+
+ demo_process_events(&cxg.demo);
+
+ cxg_wait_for_previous_frame(&cxg);
+ cxg_destroy_assets(&cxg);
+ cxg_destroy_pipeline(&cxg);
+ demo_cleanup(&cxg.demo);
+
+ return EXIT_SUCCESS;
+}
+
+#ifdef _WIN32
+int wmain(void)
+#else
+int main(void)
+#endif
+{
+ return cxg_main();
+}
diff --git a/demos/gears.hlsl b/demos/gears.hlsl
new file mode 100644
index 00000000..6b5c3579
--- /dev/null
+++ b/demos/gears.hlsl
@@ -0,0 +1,55 @@
+cbuffer gear_block : register(b0)
+{
+ float4x4 mvp_matrix;
+ float3x3 normal_matrix;
+};
+
+struct vs_in
+{
+ float4 position : POSITION;
+ float3 normal : NORMAL;
+ float3 diffuse : DIFFUSE;
+ float4 transform : TRANSFORM;
+};
+
+struct vs_out
+{
+ float4 position : SV_POSITION;
+ float4 colour : COLOR;
+};
+
+struct vs_out vs_main(struct vs_in i)
+{
+ const float3 l_pos = float3(5.0, 5.0, 10.0);
+ float3 dir, normal;
+ float4 position;
+ struct vs_out o;
+ float att;
+
+ position.x = i.transform.x * i.position.x - i.transform.y * i.position.y + i.transform.z;
+ position.y = i.transform.x * i.position.y + i.transform.y * i.position.x + i.transform.w;
+ position.zw = i.position.zw;
+
+ o.position = mul(mvp_matrix, position);
+ dir = normalize(l_pos - o.position.xyz / o.position.w);
+
+ normal.x = i.transform.x * i.normal.x - i.transform.y * i.normal.y;
+ normal.y = i.transform.x * i.normal.y + i.transform.y * i.normal.x;
+ normal.z = i.normal.z;
+ att = 0.2 + dot(dir, normalize(mul(normal_matrix, normal)));
+
+ o.colour.xyz = i.diffuse.xyz * att;
+ o.colour.w = 1.0;
+
+ return o;
+}
+
+float4 ps_main_smooth(float4 position : SV_POSITION, float4 colour : COLOR) : SV_TARGET
+{
+ return colour;
+}
+
+float4 ps_main_flat(float4 position : SV_POSITION, nointerpolation float4 colour : COLOR) : SV_TARGET
+{
+ return colour;
+}
diff --git a/demos/gears.vert b/demos/gears.vert
new file mode 100644
index 00000000..eba84da3
--- /dev/null
+++ b/demos/gears.vert
@@ -0,0 +1,40 @@
+#version 150
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec4 position_in;
+layout(location = 1) in vec3 normal_in;
+layout(location = 2) in vec3 diffuse_in;
+layout(location = 3) in vec4 transform_in;
+
+layout(location = 0) out vec4 colour_out;
+
+uniform gear_block
+{
+ uniform mat4 mvp_matrix;
+ uniform mat3 normal_matrix;
+} gear;
+
+void main()
+{
+ const vec3 l_pos = vec3(5.0, 5.0, 10.0);
+ vec3 dir, normal;
+ vec4 position;
+ float att;
+
+ position.x = transform_in.x * position_in.x - transform_in.y * position_in.y + transform_in.z;
+ position.y = transform_in.x * position_in.y + transform_in.y * position_in.x + transform_in.w;
+ position.zw = position_in.zw;
+
+ gl_Position = gear.mvp_matrix * position;
+ dir = normalize(l_pos - gl_Position.xyz / gl_Position.w);
+
+ normal.x = transform_in.x * normal_in.x - transform_in.y * normal_in.y;
+ normal.y = transform_in.x * normal_in.y + transform_in.y * normal_in.x;
+ normal.z = normal_in.z;
+ att = 0.2 + dot(dir, normalize(gear.normal_matrix * normal));
+
+ colour_out.xyz = diffuse_in.xyz * att;
+ colour_out.w = 1.0;
+
+ gl_Position.y = -gl_Position.y;
+}
diff --git a/demos/gears_flat.frag b/demos/gears_flat.frag
new file mode 100644
index 00000000..0fbabe6f
--- /dev/null
+++ b/demos/gears_flat.frag
@@ -0,0 +1,10 @@
+#version 150
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) flat in vec4 colour_in;
+layout(location = 0) out vec4 colour_out;
+
+void main(void)
+{
+ colour_out = colour_in;
+}
diff --git a/demos/gears_smooth.frag b/demos/gears_smooth.frag
new file mode 100644
index 00000000..aa4bfed5
--- /dev/null
+++ b/demos/gears_smooth.frag
@@ -0,0 +1,10 @@
+#version 150
+#extension GL_ARB_separate_shader_objects : enable
+
+layout(location = 0) in vec4 colour_in;
+layout(location = 0) out vec4 colour_out;
+
+void main(void)
+{
+ colour_out = colour_in;
+}
diff --git a/demos/triangle.c b/demos/triangle.c
index e575dd1b..b8ed8d8c 100644
--- a/demos/triangle.c
+++ b/demos/triangle.c
@@ -371,7 +371,7 @@ static int cxt_main(void)
memset(&cxt, 0, sizeof(cxt));
- if (!demo_init(&cxt.demo))
+ if (!demo_init(&cxt.demo, NULL))
return EXIT_FAILURE;
cxt.window = demo_window_create(&cxt.demo, "Vkd3d Triangle", width, height, &cxt);