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 +#include +#include +#include +#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);