#ifdef RAPI_D3D11 #if defined(_WIN32) || defined(_WIN64) #include #include #include #include #include #include #include #include #include extern "C" { #include "../configfile.h" #include "../platform.h" } #ifndef _LANGUAGE_C # define _LANGUAGE_C #endif #include #include "gfx_cc.h" #include "gfx_window_manager_api.h" #include "gfx_rendering_api.h" #include "gfx_direct3d_common.h" #include "gfx_screen_config.h" #define WINCLASS_NAME L"SUPERMARIO64" #define WINDOW_CLIENT_MIN_WIDTH 320 #define WINDOW_CLIENT_MIN_HEIGHT 240 #define DEBUG_D3D 0 using namespace Microsoft::WRL; // For ComPtr struct PerFrameCB { uint32_t noise_frame; float noise_scale_x; float noise_scale_y; uint32_t padding; }; struct PerDrawCB { struct Texture { uint32_t width; uint32_t height; uint32_t linear_filtering; uint32_t padding; } textures[2]; }; struct TextureData { ComPtr resource_view; ComPtr sampler_state; uint32_t width; uint32_t height; bool linear_filtering; }; struct ShaderProgram { ComPtr vertex_shader; ComPtr pixel_shader; ComPtr input_layout; ComPtr blend_state; uint32_t shader_id; uint8_t num_inputs; uint8_t num_floats; bool used_textures[2]; }; static struct { ComPtr device; ComPtr context; ComPtr swap_chain; // For Windows versions older than 8.1 ComPtr swap_chain2; // For Windows version 8.1 or newer ComPtr backbuffer_view; ComPtr depth_stencil_view; ComPtr rasterizer_state; ComPtr depth_stencil_state; ComPtr vertex_buffer; ComPtr per_frame_cb; ComPtr per_draw_cb; #if DEBUG_D3D ComPtr debug; #endif HANDLE frame_latency_waitable_object; DXGI_SAMPLE_DESC sample_description; PerFrameCB per_frame_cb_data; PerDrawCB per_draw_cb_data; struct ShaderProgram shader_program_pool[64]; uint8_t shader_program_pool_size; std::vector textures; int current_tile; uint32_t current_texture_ids[2]; // Current state struct ShaderProgram *shader_program; uint32_t current_width, current_height; int8_t depth_test; int8_t depth_mask; int8_t zmode_decal; // Previous states (to prevent setting states needlessly) struct ShaderProgram *last_shader_program = nullptr; uint32_t last_vertex_buffer_stride = 0; ComPtr last_blend_state = nullptr; ComPtr last_resource_views[2] = { nullptr, nullptr }; ComPtr last_sampler_states[2] = { nullptr, nullptr }; int8_t last_depth_test = -1; int8_t last_depth_mask = -1; int8_t last_zmode_decal = -1; D3D_PRIMITIVE_TOPOLOGY last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; // Game loop callback void (*run_one_game_iter)(void); bool (*on_key_down)(int scancode); bool (*on_key_up)(int scancode); void (*on_all_keys_up)(void); } d3d; static HWND h_wnd; static bool lower_latency; static LARGE_INTEGER last_time, accumulated_time, frequency; static uint8_t sync_interval; static RECT last_window_rect; static bool is_full_screen, last_maximized_state; static void toggle_borderless_window_full_screen() { if (is_full_screen) { RECT r = last_window_rect; // Set in window mode with the last saved position and size SetWindowLongPtr(h_wnd, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW); if (last_maximized_state) { SetWindowPos(h_wnd, NULL, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE); ShowWindow(h_wnd, SW_MAXIMIZE); } else { SetWindowPos(h_wnd, NULL, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED); ShowWindow(h_wnd, SW_RESTORE); } is_full_screen = false; } else { // Save if window is maximized or not WINDOWPLACEMENT window_placement; window_placement.length = sizeof(WINDOWPLACEMENT); GetWindowPlacement(h_wnd, &window_placement); last_maximized_state = window_placement.showCmd == SW_SHOWMAXIMIZED; // Save window position and size if the window is not maximized GetWindowRect(h_wnd, &last_window_rect); // Get in which monitor the window is HMONITOR h_monitor = MonitorFromWindow(h_wnd, MONITOR_DEFAULTTONEAREST); // Get info from that monitor MONITORINFOEX monitor_info; monitor_info.cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(h_monitor, &monitor_info); RECT r = monitor_info.rcMonitor; // Set borderless full screen to that monitor SetWindowLongPtr(h_wnd, GWL_STYLE, WS_VISIBLE | WS_POPUP); SetWindowPos(h_wnd, HWND_TOP, r.left, r.top, r.right - r.left, r.bottom - r.top, SWP_FRAMECHANGED); is_full_screen = true; } } static void create_render_target_views(uint32_t width, uint32_t height) { if (width == 0 || height == 0) { return; } if (d3d.current_width == width && d3d.current_height == height) { return; } // Release previous stuff (if any) d3d.backbuffer_view.Reset(); d3d.depth_stencil_view.Reset(); // Resize swap chain if (lower_latency) { UINT swap_chain_flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; ThrowIfFailed(d3d.swap_chain2->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, swap_chain_flags), h_wnd, "Failed to resize IDXGISwapChain2 buffers."); } else { UINT swap_chain_flags = 0; ThrowIfFailed(d3d.swap_chain->ResizeBuffers(0, width, height, DXGI_FORMAT_UNKNOWN, swap_chain_flags), h_wnd, "Failed to resize IDXGISwapChain buffers."); } // Create back buffer ComPtr backbuffer_texture; if (lower_latency) { ThrowIfFailed(d3d.swap_chain2->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *) backbuffer_texture.GetAddressOf()), h_wnd, "Failed to get backbuffer from IDXGISwapChain2."); } else { ThrowIfFailed(d3d.swap_chain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID *) backbuffer_texture.GetAddressOf()), h_wnd, "Failed to get backbuffer from IDXGISwapChain."); } ThrowIfFailed(d3d.device->CreateRenderTargetView(backbuffer_texture.Get(), NULL, d3d.backbuffer_view.GetAddressOf()), h_wnd, "Failed to create render target view."); // Create depth buffer D3D11_TEXTURE2D_DESC depth_stencil_texture_desc; ZeroMemory(&depth_stencil_texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); depth_stencil_texture_desc.Width = width; depth_stencil_texture_desc.Height = height; depth_stencil_texture_desc.MipLevels = 1; depth_stencil_texture_desc.ArraySize = 1; depth_stencil_texture_desc.Format = DXGI_FORMAT_D24_UNORM_S8_UINT; depth_stencil_texture_desc.SampleDesc = d3d.sample_description; depth_stencil_texture_desc.Usage = D3D11_USAGE_DEFAULT; depth_stencil_texture_desc.BindFlags = D3D11_BIND_DEPTH_STENCIL; depth_stencil_texture_desc.CPUAccessFlags = 0; depth_stencil_texture_desc.MiscFlags = 0; ComPtr depth_stencil_texture; ThrowIfFailed(d3d.device->CreateTexture2D(&depth_stencil_texture_desc, NULL, depth_stencil_texture.GetAddressOf())); ThrowIfFailed(d3d.device->CreateDepthStencilView(depth_stencil_texture.Get(), NULL, d3d.depth_stencil_view.GetAddressOf())); // Save resolution d3d.current_width = width; d3d.current_height = height; } static void calculate_sync_interval() { const POINT ptZero = { 0, 0 }; HMONITOR h_monitor = MonitorFromPoint(ptZero, MONITOR_DEFAULTTOPRIMARY); MONITORINFOEX monitor_info; monitor_info.cbSize = sizeof(MONITORINFOEX); GetMonitorInfo(h_monitor, &monitor_info); DEVMODE dev_mode; dev_mode.dmSize = sizeof(DEVMODE); dev_mode.dmDriverExtra = 0; EnumDisplaySettings(monitor_info.szDevice, ENUM_CURRENT_SETTINGS, &dev_mode); if (dev_mode.dmDisplayFrequency >= 29 && dev_mode.dmDisplayFrequency <= 31) { sync_interval = 1; } else if (dev_mode.dmDisplayFrequency >= 59 && dev_mode.dmDisplayFrequency <= 61) { sync_interval = 2; } else if (dev_mode.dmDisplayFrequency >= 89 && dev_mode.dmDisplayFrequency <= 91) { sync_interval = 3; } else if (dev_mode.dmDisplayFrequency >= 119 && dev_mode.dmDisplayFrequency <= 121) { sync_interval = 4; } else { sync_interval = 0; } } LRESULT CALLBACK gfx_d3d11_dxgi_wnd_proc(HWND h_wnd, UINT message, WPARAM w_param, LPARAM l_param) { switch (message) { case WM_SIZE: { RECT rect; GetClientRect(h_wnd, &rect); create_render_target_views(rect.right - rect.left, rect.bottom - rect.top); break; } case WM_EXITSIZEMOVE: { calculate_sync_interval(); break; } case WM_GETMINMAXINFO: { RECT wr = { 0, 0, WINDOW_CLIENT_MIN_WIDTH, WINDOW_CLIENT_MIN_HEIGHT }; AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); LPMINMAXINFO lpMMI = (LPMINMAXINFO) l_param; lpMMI->ptMinTrackSize.x = wr.right - wr.left; lpMMI->ptMinTrackSize.y = wr.bottom - wr.top; break; } case WM_DISPLAYCHANGE: { calculate_sync_interval(); break; } case WM_DESTROY: { #if DEBUG_D3D d3d.debug->ReportLiveDeviceObjects(D3D11_RLDO_DETAIL); #endif exit(0); break; } case WM_ACTIVATEAPP: { if (d3d.on_all_keys_up != nullptr) { d3d.on_all_keys_up(); } break; } case WM_SYSKEYDOWN: { if ((w_param == VK_RETURN) && ((l_param & 1 << 30) == 0)) { toggle_borderless_window_full_screen(); break; } else { return DefWindowProcW(h_wnd, message, w_param, l_param); } } case WM_KEYDOWN: { if (d3d.on_key_down != nullptr) { d3d.on_key_down((l_param >> 16) & 0x1ff); } break; } case WM_KEYUP: { if (d3d.on_key_up != nullptr) { d3d.on_key_up((l_param >> 16) & 0x1ff); } break; } default: { return DefWindowProcW(h_wnd, message, w_param, l_param); } } return 0; } static void gfx_d3d11_dxgi_init(const char *window_title) { // Prepare window title wchar_t w_title[512]; mbstowcs(w_title, window_title, strlen(window_title) + 1); // Create window WNDCLASSEXW wcex; ZeroMemory(&wcex, sizeof(WNDCLASSEX)); wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = gfx_d3d11_dxgi_wnd_proc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = nullptr; wcex.hIcon = nullptr; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszMenuName = nullptr; wcex.lpszClassName = WINCLASS_NAME; wcex.hIconSm = nullptr; RegisterClassExW(&wcex); RECT wr = { 0, 0, DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT }; AdjustWindowRect(&wr, WS_OVERLAPPEDWINDOW, FALSE); h_wnd = CreateWindowW(WINCLASS_NAME, w_title, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, wr.right - wr.left, wr.bottom - wr.top, nullptr, nullptr, nullptr, nullptr); is_full_screen = false; // Center window int screen_width = GetSystemMetrics(SM_CXSCREEN); int screen_height = GetSystemMetrics(SM_CYSCREEN); int xPos = (screen_width - wr.right) * 0.5; int yPos = (screen_height - wr.bottom) * 0.5; SetWindowPos(h_wnd, 0, xPos, yPos, 0, 0, SWP_NOZORDER | SWP_NOSIZE); // Check if a lower latency flip model can be used lower_latency = IsWindows8Point1OrGreater(); // Create D3D11 device #if DEBUG_D3D UINT device_creation_flags = D3D11_CREATE_DEVICE_DEBUG; #else UINT device_creation_flags = 0; #endif D3D_FEATURE_LEVEL FeatureLevels[] = { D3D_FEATURE_LEVEL_11_0, D3D_FEATURE_LEVEL_10_1, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_9_3, D3D_FEATURE_LEVEL_9_2, D3D_FEATURE_LEVEL_9_1 }; ThrowIfFailed(D3D11CreateDevice( nullptr, D3D_DRIVER_TYPE_HARDWARE, nullptr, device_creation_flags, FeatureLevels, ARRAYSIZE(FeatureLevels), D3D11_SDK_VERSION, d3d.device.GetAddressOf(), NULL, d3d.context.GetAddressOf()), h_wnd, "Failed to create D3D11 device."); // Sample description to be used in back buffer and depth buffer d3d.sample_description.Count = 1; d3d.sample_description.Quality = 0; // Create the swap chain if (lower_latency) { // Create swap chain description DXGI_SWAP_CHAIN_DESC1 swap_chain_desc1; ZeroMemory(&swap_chain_desc1, sizeof(DXGI_SWAP_CHAIN_DESC1)); swap_chain_desc1.Width = DESIRED_SCREEN_WIDTH; swap_chain_desc1.Height = DESIRED_SCREEN_HEIGHT; swap_chain_desc1.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc1.Stereo = FALSE; swap_chain_desc1.SampleDesc = d3d.sample_description; swap_chain_desc1.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc1.BufferCount = 2; swap_chain_desc1.Scaling = DXGI_SCALING_STRETCH; swap_chain_desc1.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL; swap_chain_desc1.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED; swap_chain_desc1.Flags = DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT; // Create DXGI Factory ComPtr dxgi_device2; ThrowIfFailed(d3d.device.Get()->QueryInterface(__uuidof(IDXGIDevice2), (void **) dxgi_device2.GetAddressOf()), h_wnd, "Failed to get IDXGIDevice2."); ComPtr dxgi_adapter; ThrowIfFailed(dxgi_device2.Get()->GetAdapter(dxgi_adapter.GetAddressOf()), h_wnd, "Failed to get IDXGIAdapter."); ComPtr dxgi_factory2; ThrowIfFailed(dxgi_adapter.Get()->GetParent(__uuidof(IDXGIFactory2), (void **) dxgi_factory2.GetAddressOf()), h_wnd, "Failed to get IDXGIFactory2."); // Create Swap Chain ComPtr swap_chain1; ThrowIfFailed(dxgi_factory2.Get()->CreateSwapChainForHwnd(d3d.device.Get(), h_wnd, &swap_chain_desc1, NULL, NULL, swap_chain1.GetAddressOf()), h_wnd, "Failed to create IDXGISwapChain1."); ThrowIfFailed(swap_chain1.As(&d3d.swap_chain2), h_wnd, "Failed to get IDXGISwapChain2 from IDXGISwapChain1."); ThrowIfFailed(d3d.swap_chain2.Get()->SetMaximumFrameLatency(1), h_wnd, "Failed to Set Maximum Frame Latency to 1."); d3d.frame_latency_waitable_object = d3d.swap_chain2.Get()->GetFrameLatencyWaitableObject(); // Prevent DXGI from intercepting Alt+Enter ThrowIfFailed(dxgi_factory2.Get()->MakeWindowAssociation(h_wnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER), h_wnd, "Failed to call MakeWindowAssociation."); } else { // Create swap chain description DXGI_SWAP_CHAIN_DESC swap_chain_desc; ZeroMemory(&swap_chain_desc, sizeof(DXGI_SWAP_CHAIN_DESC)); swap_chain_desc.BufferDesc.Width = DESIRED_SCREEN_WIDTH; swap_chain_desc.BufferDesc.Height = DESIRED_SCREEN_HEIGHT; swap_chain_desc.BufferDesc.RefreshRate.Numerator = 0; swap_chain_desc.BufferDesc.RefreshRate.Denominator = 1; swap_chain_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; swap_chain_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED; swap_chain_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED; swap_chain_desc.SampleDesc = d3d.sample_description; swap_chain_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT; swap_chain_desc.BufferCount = 1; swap_chain_desc.OutputWindow = h_wnd; swap_chain_desc.Windowed = TRUE; swap_chain_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD; swap_chain_desc.Flags = 0; // Create DXGI Factory ComPtr dxgi_device; ThrowIfFailed(d3d.device.Get()->QueryInterface(__uuidof(IDXGIDevice), (void **) dxgi_device.GetAddressOf()), h_wnd, "Failed to get IDXGIDevice."); ComPtr dxgi_adapter; ThrowIfFailed(dxgi_device.Get()->GetAdapter(dxgi_adapter.GetAddressOf()), h_wnd, "Failed to get IDXGIAdapter."); ComPtr dxgi_factory; ThrowIfFailed(dxgi_adapter.Get()->GetParent(__uuidof(IDXGIFactory), (void **) dxgi_factory.GetAddressOf()), h_wnd, "Failed to get IDXGIFactory."); // Create Swap Chain ThrowIfFailed(dxgi_factory.Get()->CreateSwapChain(d3d.device.Get(), &swap_chain_desc, d3d.swap_chain.GetAddressOf()), h_wnd, "Failed to create IDXGISwapChain."); // Prevent DXGI from intercepting Alt+Enter ThrowIfFailed(dxgi_factory.Get()->MakeWindowAssociation(h_wnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER), h_wnd, "Failed to call MakeWindowAssociation."); } // Create D3D Debug device if in debug mode #if DEBUG_D3D ThrowIfFailed(d3d.device->QueryInterface(__uuidof(ID3D11Debug), (void **) d3d.debug.GetAddressOf()), h_wnd, "Failed to get ID3D11Debug device."); #endif // Create views create_render_target_views(DESIRED_SCREEN_WIDTH, DESIRED_SCREEN_HEIGHT); // Create main vertex buffer D3D11_BUFFER_DESC vertex_buffer_desc; ZeroMemory(&vertex_buffer_desc, sizeof(D3D11_BUFFER_DESC)); vertex_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; vertex_buffer_desc.ByteWidth = 256 * 26 * 3 * sizeof(float); // Same as buf_vbo size in gfx_pc vertex_buffer_desc.BindFlags = D3D11_BIND_VERTEX_BUFFER; vertex_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; vertex_buffer_desc.MiscFlags = 0; ThrowIfFailed(d3d.device->CreateBuffer(&vertex_buffer_desc, NULL, d3d.vertex_buffer.GetAddressOf()), h_wnd, "Failed to create vertex buffer."); // Create per-frame constant buffer D3D11_BUFFER_DESC constant_buffer_desc; ZeroMemory(&constant_buffer_desc, sizeof(D3D11_BUFFER_DESC)); constant_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; constant_buffer_desc.ByteWidth = sizeof(PerFrameCB); constant_buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; constant_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; constant_buffer_desc.MiscFlags = 0; ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, NULL, d3d.per_frame_cb.GetAddressOf()), h_wnd, "Failed to create per-frame constant buffer."); d3d.context->PSSetConstantBuffers(0, 1, d3d.per_frame_cb.GetAddressOf()); // Create per-draw constant buffer constant_buffer_desc.Usage = D3D11_USAGE_DYNAMIC; constant_buffer_desc.ByteWidth = sizeof(PerDrawCB); constant_buffer_desc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; constant_buffer_desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE; constant_buffer_desc.MiscFlags = 0; ThrowIfFailed(d3d.device->CreateBuffer(&constant_buffer_desc, NULL, d3d.per_draw_cb.GetAddressOf()), h_wnd, "Failed to create per-draw constant buffer."); d3d.context->PSSetConstantBuffers(1, 1, d3d.per_draw_cb.GetAddressOf()); // Initialize some timer values QueryPerformanceFrequency(&frequency); accumulated_time.QuadPart = 0; // Decide vsync interval calculate_sync_interval(); // Show the window ShowWindow(h_wnd, SW_SHOW); } static void gfx_d3d11_dxgi_shutdown(void) { if (d3d.swap_chain) d3d.swap_chain.Get()->SetFullscreenState(false, nullptr); if (d3d.swap_chain2) d3d.swap_chain2.Get()->SetFullscreenState(false, nullptr); for (unsigned int i = 0; i < sizeof(d3d.shader_program_pool) / sizeof(d3d.shader_program_pool[0]); ++i) { d3d.shader_program_pool[i].vertex_shader.Reset(); d3d.shader_program_pool[i].pixel_shader.Reset(); d3d.shader_program_pool[i].input_layout.Reset(); d3d.shader_program_pool[i].blend_state.Reset(); } d3d.rasterizer_state.Reset(); d3d.backbuffer_view.Reset(); d3d.depth_stencil_view.Reset(); d3d.depth_stencil_state.Reset(); d3d.context.Reset(); d3d.device.Reset(); d3d.swap_chain.Reset(); d3d.swap_chain2.Reset(); if (h_wnd) { DestroyWindow(h_wnd); h_wnd = nullptr; } } static void gfx_d3d11_dxgi_set_keyboard_callbacks(bool (*on_key_down)(int scancode), bool (*on_key_up)(int scancode), void (*on_all_keys_up)(void)) { d3d.on_key_down = on_key_down; d3d.on_key_up = on_key_up; d3d.on_all_keys_up = on_all_keys_up; } static void gfx_d3d11_dxgi_main_loop(void (*run_one_game_iter)(void)) { MSG msg = { 0 }; bool quit = false; while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); if (msg.message == WM_QUIT) { quit = true; } } if (quit) { return; } if (IsIconic(h_wnd)) { Sleep(50); return; } d3d.run_one_game_iter = run_one_game_iter; if (sync_interval == 0) { LARGE_INTEGER current_time; QueryPerformanceCounter(¤t_time); LARGE_INTEGER elapsed_time_microseconds; elapsed_time_microseconds.QuadPart = current_time.QuadPart - last_time.QuadPart; elapsed_time_microseconds.QuadPart *= 1000000; elapsed_time_microseconds.QuadPart /= frequency.QuadPart; accumulated_time.QuadPart += elapsed_time_microseconds.QuadPart; last_time = current_time; const uint32_t FRAME_TIME = 1000000 / 30; if (accumulated_time.QuadPart >= FRAME_TIME) { accumulated_time.QuadPart %= FRAME_TIME; if (lower_latency) { WaitForSingleObjectEx(d3d.frame_latency_waitable_object, 1000, true); } if (d3d.run_one_game_iter != nullptr) { d3d.run_one_game_iter(); } if (lower_latency) { d3d.swap_chain2->Present(1, 0); } else { d3d.swap_chain->Present(1, 0); } } else { Sleep(1); } } else { if (lower_latency) { WaitForSingleObjectEx(d3d.frame_latency_waitable_object, 1000, true); } if (d3d.run_one_game_iter != nullptr) { d3d.run_one_game_iter(); } if (lower_latency) { d3d.swap_chain2->Present(sync_interval, 0); } else { d3d.swap_chain->Present(sync_interval, 0); } } } static void gfx_d3d11_dxgi_get_dimensions(uint32_t *width, uint32_t *height) { *width = d3d.current_width; *height = d3d.current_height; } static void gfx_d3d11_dxgi_handle_events(void) { } static bool gfx_d3d11_dxgi_start_frame(void) { return true; } static void gfx_d3d11_dxgi_swap_buffers_begin(void) { } static void gfx_d3d11_dxgi_swap_buffers_end(void) { } double gfx_d3d11_dxgi_get_time(void) { return 0.0; } static bool gfx_d3d11_z_is_from_0_to_1(void) { return true; } static void gfx_d3d11_unload_shader(struct ShaderProgram *old_prg) { } static void gfx_d3d11_load_shader(struct ShaderProgram *new_prg) { d3d.shader_program = new_prg; } static struct ShaderProgram *gfx_d3d11_create_and_load_new_shader(uint32_t shader_id) { CCFeatures cc_features; get_cc_features(shader_id, &cc_features); char buf[4096]; size_t len = 0; size_t num_floats = 4; // Pixel shader input struct append_line(buf, &len, "struct PSInput {"); append_line(buf, &len, " float4 position : SV_POSITION;"); if (cc_features.used_textures[0] || cc_features.used_textures[1]) { append_line(buf, &len, " float2 uv : TEXCOORD;"); num_floats += 2; } if (cc_features.opt_alpha && cc_features.opt_noise) { append_line(buf, &len, " float4 screenPos : TEXCOORD1;"); } if (cc_features.opt_fog) { append_line(buf, &len, " float4 fog : FOG;"); num_floats += 4; } for (uint32_t i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, " float%d input%d : INPUT%d;\r\n", cc_features.opt_alpha ? 4 : 3, i + 1, i); num_floats += cc_features.opt_alpha ? 4 : 3; } append_line(buf, &len, "};"); // Textures and samplers if (cc_features.used_textures[0]) { append_line(buf, &len, "Texture2D g_texture0 : register(t0);"); append_line(buf, &len, "SamplerState g_sampler0 : register(s0);"); } if (cc_features.used_textures[1]) { append_line(buf, &len, "Texture2D g_texture1 : register(t1);"); append_line(buf, &len, "SamplerState g_sampler1 : register(s1);"); } // Constant buffer and random function if (cc_features.opt_alpha && cc_features.opt_noise) { append_line(buf, &len, "cbuffer PerFrameCB : register(b0) {"); append_line(buf, &len, " uint noise_frame;"); append_line(buf, &len, " float2 noise_scale;"); append_line(buf, &len, "}"); append_line(buf, &len, "float random(in float3 value) {"); append_line(buf, &len, " float random = dot(value, float3(12.9898, 78.233, 37.719));"); append_line(buf, &len, " return frac(sin(random) * 143758.5453);"); append_line(buf, &len, "}"); } // 3 point texture filtering // Original author: ArthurCarvalho // Based on GLSL implementation by twinaphex, mupen64plus-libretro project. if (configFiltering == 2) { if (cc_features.used_textures[0] || cc_features.used_textures[1]) { append_line(buf, &len, "cbuffer PerDrawCB : register(b1) {"); append_line(buf, &len, " struct {"); append_line(buf, &len, " uint width;"); append_line(buf, &len, " uint height;"); append_line(buf, &len, " bool linear_filtering;"); append_line(buf, &len, " } textures[2];"); append_line(buf, &len, "}"); append_line(buf, &len, "#define TEX_OFFSET(tex, tSampler, texCoord, off, texSize) tex.Sample(tSampler, texCoord - off / texSize)"); append_line(buf, &len, "float4 tex2D3PointFilter(in Texture2D tex, in SamplerState tSampler, in float2 texCoord, in float2 texSize) {"); append_line(buf, &len, " float2 offset = frac(texCoord * texSize - float2(0.5, 0.5));"); append_line(buf, &len, " offset -= step(1.0, offset.x + offset.y);"); append_line(buf, &len, " float4 c0 = TEX_OFFSET(tex, tSampler, texCoord, offset, texSize);"); append_line(buf, &len, " float4 c1 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x - sign(offset.x), offset.y), texSize);"); append_line(buf, &len, " float4 c2 = TEX_OFFSET(tex, tSampler, texCoord, float2(offset.x, offset.y - sign(offset.y)), texSize);"); append_line(buf, &len, " return c0 + abs(offset.x)*(c1-c0) + abs(offset.y)*(c2-c0);"); append_line(buf, &len, "}"); } } // Vertex shader append_str(buf, &len, "PSInput VSMain(float4 position : POSITION"); if (cc_features.used_textures[0] || cc_features.used_textures[1]) { append_str(buf, &len, ", float2 uv : TEXCOORD"); } if (cc_features.opt_fog) { append_str(buf, &len, ", float4 fog : FOG"); } for (uint32_t i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, ", float%d input%d : INPUT%d", cc_features.opt_alpha ? 4 : 3, i + 1, i); } append_line(buf, &len, ") {"); append_line(buf, &len, " PSInput result;"); append_line(buf, &len, " result.position = position;"); if (cc_features.opt_alpha && cc_features.opt_noise) { append_line(buf, &len, " result.screenPos = position;"); } if (cc_features.used_textures[0] || cc_features.used_textures[1]) { append_line(buf, &len, " result.uv = uv;"); } if (cc_features.opt_fog) { append_line(buf, &len, " result.fog = fog;"); } for (uint32_t i = 0; i < cc_features.num_inputs; i++) { len += sprintf(buf + len, " result.input%d = input%d;\r\n", i + 1, i + 1); } append_line(buf, &len, " return result;"); append_line(buf, &len, "}"); // Pixel shader append_line(buf, &len, "float4 PSMain(PSInput input) : SV_TARGET {"); if (cc_features.used_textures[0]) { if (configFiltering == 2) { append_line(buf, &len, " float4 texVal0;"); append_line(buf, &len, " if (textures[0].linear_filtering)"); append_line(buf, &len, " texVal0 = tex2D3PointFilter(g_texture0, g_sampler0, input.uv, float2(textures[0].width, textures[0].height));"); append_line(buf, &len, " else"); append_line(buf, &len, " texVal0 = g_texture0.Sample(g_sampler0, input.uv);"); } else { append_line(buf, &len, " float4 texVal0 = g_texture0.Sample(g_sampler0, input.uv);"); } } if (cc_features.used_textures[1]) { if (configFiltering == 2) { append_line(buf, &len, " float4 texVal1;"); append_line(buf, &len, " if (textures[1].linear_filtering)"); append_line(buf, &len, " texVal1 = tex2D3PointFilter(g_texture1, g_sampler1, input.uv, float2(textures[1].width, textures[1].height));"); append_line(buf, &len, " else"); append_line(buf, &len, " texVal1 = g_texture1.Sample(g_sampler1, input.uv);"); } else { append_line(buf, &len, " float4 texVal1 = g_texture1.Sample(g_sampler1, input.uv);"); } } append_str(buf, &len, cc_features.opt_alpha ? " float4 texel = " : " float3 texel = "); if (!cc_features.color_alpha_same && cc_features.opt_alpha) { append_str(buf, &len, "float4("); append_formula(buf, &len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], false, false, true); append_str(buf, &len, ", "); append_formula(buf, &len, cc_features.c, cc_features.do_single[1], cc_features.do_multiply[1], cc_features.do_mix[1], true, true, true); append_str(buf, &len, ")"); } else { append_formula(buf, &len, cc_features.c, cc_features.do_single[0], cc_features.do_multiply[0], cc_features.do_mix[0], cc_features.opt_alpha, false, cc_features.opt_alpha); } append_line(buf, &len, ";"); if (cc_features.opt_texture_edge && cc_features.opt_alpha) { append_line(buf, &len, " if (texel.a > 0.3) texel.a = 1.0; else discard;"); } // TODO discard if alpha is 0? if (cc_features.opt_fog) { if (cc_features.opt_alpha) { append_line(buf, &len, " texel = float4(lerp(texel.rgb, input.fog.rgb, input.fog.a), texel.a);"); } else { append_line(buf, &len, " texel = lerp(texel, input.fog.rgb, input.fog.a);"); } } if (cc_features.opt_alpha && cc_features.opt_noise) { append_line(buf, &len, " float2 coords = (input.screenPos.xy / input.screenPos.w) * noise_scale;"); append_line(buf, &len, " texel.a *= round(random(float3(floor(coords), noise_frame)));"); } if (cc_features.opt_alpha) { append_line(buf, &len, " return texel;"); } else { append_line(buf, &len, " return float4(texel, 1.0);"); } append_line(buf, &len, "}"); ComPtr vs, ps; ComPtr error_blob; #if DEBUG_D3D UINT compile_flags = D3DCOMPILE_DEBUG; #else UINT compile_flags = D3DCOMPILE_OPTIMIZATION_LEVEL2; #endif HRESULT hr = D3DCompile(buf, len, nullptr, nullptr, nullptr, "VSMain", "vs_4_0_level_9_1", compile_flags, 0, vs.GetAddressOf(), error_blob.GetAddressOf()); if (FAILED(hr)) sys_fatal("%s", (char *) error_blob->GetBufferPointer()); hr = D3DCompile(buf, len, nullptr, nullptr, nullptr, "PSMain", "ps_4_0_level_9_1", compile_flags, 0, ps.GetAddressOf(), error_blob.GetAddressOf()); if (FAILED(hr)) sys_fatal("%s", (char *) error_blob->GetBufferPointer()); struct ShaderProgram *prg = &d3d.shader_program_pool[d3d.shader_program_pool_size++]; ThrowIfFailed(d3d.device->CreateVertexShader(vs->GetBufferPointer(), vs->GetBufferSize(), NULL, prg->vertex_shader.GetAddressOf())); ThrowIfFailed(d3d.device->CreatePixelShader(ps->GetBufferPointer(), ps->GetBufferSize(), NULL, prg->pixel_shader.GetAddressOf())); // Input Layout D3D11_INPUT_ELEMENT_DESC ied[7]; uint8_t ied_index = 0; ied[ied_index++] = { "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; if (cc_features.used_textures[0] || cc_features.used_textures[1]) { ied[ied_index++] = { "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; } if (cc_features.opt_fog) { ied[ied_index++] = { "FOG", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; } for (unsigned int i = 0; i < cc_features.num_inputs; i++) { DXGI_FORMAT format = cc_features.opt_alpha ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R32G32B32_FLOAT; ied[ied_index++] = { "INPUT", i, format, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 }; } ThrowIfFailed(d3d.device->CreateInputLayout(ied, ied_index, vs->GetBufferPointer(), vs->GetBufferSize(), prg->input_layout.GetAddressOf())); // Blend state D3D11_BLEND_DESC blend_desc; ZeroMemory(&blend_desc, sizeof(D3D11_BLEND_DESC)); if (cc_features.opt_alpha) { blend_desc.RenderTarget[0].BlendEnable = true; blend_desc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA; blend_desc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA; blend_desc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE; blend_desc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO; blend_desc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD; blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; } else { blend_desc.RenderTarget[0].BlendEnable = false; blend_desc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL; } ThrowIfFailed(d3d.device->CreateBlendState(&blend_desc, prg->blend_state.GetAddressOf())); // Save some values prg->shader_id = shader_id; prg->num_inputs = cc_features.num_inputs; prg->num_floats = num_floats; prg->used_textures[0] = cc_features.used_textures[0]; prg->used_textures[1] = cc_features.used_textures[1]; return d3d.shader_program = prg; } static struct ShaderProgram *gfx_d3d11_lookup_shader(uint32_t shader_id) { for (size_t i = 0; i < d3d.shader_program_pool_size; i++) { if (d3d.shader_program_pool[i].shader_id == shader_id) { return &d3d.shader_program_pool[i]; } } return NULL; } static void gfx_d3d11_shader_get_info(struct ShaderProgram *prg, uint8_t *num_inputs, bool used_textures[2]) { *num_inputs = prg->num_inputs; used_textures[0] = prg->used_textures[0]; used_textures[1] = prg->used_textures[1]; } static uint32_t gfx_d3d11_new_texture(void) { d3d.textures.resize(d3d.textures.size() + 1); return (uint32_t)(d3d.textures.size() - 1); } static void gfx_d3d11_select_texture(int tile, uint32_t texture_id) { d3d.current_tile = tile; d3d.current_texture_ids[tile] = texture_id; } static D3D11_TEXTURE_ADDRESS_MODE gfx_cm_to_d3d11(uint32_t val) { if (val & G_TX_CLAMP) { return D3D11_TEXTURE_ADDRESS_CLAMP; } return (val & G_TX_MIRROR) ? D3D11_TEXTURE_ADDRESS_MIRROR : D3D11_TEXTURE_ADDRESS_WRAP; } static void gfx_d3d11_upload_texture(uint8_t *rgba32_buf, int width, int height) { // Create texture D3D11_TEXTURE2D_DESC texture_desc; ZeroMemory(&texture_desc, sizeof(D3D11_TEXTURE2D_DESC)); texture_desc.Width = width; texture_desc.Height = height; texture_desc.Usage = D3D11_USAGE_IMMUTABLE; texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; texture_desc.CPUAccessFlags = 0; texture_desc.MiscFlags = 0; // D3D11_RESOURCE_MISC_GENERATE_MIPS ? texture_desc.ArraySize = 1; texture_desc.MipLevels = 1; texture_desc.SampleDesc.Count = 1; texture_desc.SampleDesc.Quality = 0; D3D11_SUBRESOURCE_DATA resource_data; resource_data.pSysMem = rgba32_buf; resource_data.SysMemPitch = width * 4; resource_data.SysMemSlicePitch = resource_data.SysMemPitch * height; ComPtr texture; ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, &resource_data, texture.GetAddressOf())); // Create shader resource view from texture D3D11_SHADER_RESOURCE_VIEW_DESC resource_view_desc; ZeroMemory(&resource_view_desc, sizeof(D3D11_SHADER_RESOURCE_VIEW_DESC)); resource_view_desc.Format = texture_desc.Format; resource_view_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; resource_view_desc.Texture2D.MostDetailedMip = 0; resource_view_desc.Texture2D.MipLevels = -1; TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[d3d.current_tile]]; texture_data->width = width; texture_data->height = height; ThrowIfFailed(d3d.device->CreateShaderResourceView(texture.Get(), &resource_view_desc, texture_data->resource_view.GetAddressOf())); } static void gfx_d3d11_set_sampler_parameters(int tile, bool linear_filter, uint32_t cms, uint32_t cmt) { D3D11_SAMPLER_DESC sampler_desc; ZeroMemory(&sampler_desc, sizeof(D3D11_SAMPLER_DESC)); if (configFiltering == 2) sampler_desc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT; else sampler_desc.Filter = linear_filter ? D3D11_FILTER_MIN_MAG_MIP_LINEAR : D3D11_FILTER_MIN_MAG_MIP_POINT; sampler_desc.AddressU = gfx_cm_to_d3d11(cms); sampler_desc.AddressV = gfx_cm_to_d3d11(cmt); sampler_desc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP; sampler_desc.MinLOD = 0; sampler_desc.MaxLOD = D3D11_FLOAT32_MAX; TextureData *texture_data = &d3d.textures[d3d.current_texture_ids[tile]]; texture_data->linear_filtering = linear_filter; // This function is called twice per texture, the first one only to set default values. // Maybe that could be skipped? Anyway, make sure to release the first default sampler // state before setting the actual one. texture_data->sampler_state.Reset(); ThrowIfFailed(d3d.device->CreateSamplerState(&sampler_desc, texture_data->sampler_state.GetAddressOf())); } static void gfx_d3d11_set_depth_test(bool depth_test) { d3d.depth_test = depth_test; } static void gfx_d3d11_set_depth_mask(bool depth_mask) { d3d.depth_mask = depth_mask; } static void gfx_d3d11_set_zmode_decal(bool zmode_decal) { d3d.zmode_decal = zmode_decal; } static void gfx_d3d11_set_viewport(int x, int y, int width, int height) { D3D11_VIEWPORT viewport; viewport.TopLeftX = x; viewport.TopLeftY = d3d.current_height - y - height; viewport.Width = width; viewport.Height = height; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; d3d.context->RSSetViewports(1, &viewport); } static void gfx_d3d11_set_scissor(int x, int y, int width, int height) { D3D11_RECT rect; rect.left = x; rect.top = d3d.current_height - y - height; rect.right = x + width; rect.bottom = d3d.current_height - y; d3d.context->RSSetScissorRects(1, &rect); } static void gfx_d3d11_set_use_alpha(bool use_alpha) { // Already part of the pipeline state from shader info } static void gfx_d3d11_draw_triangles(float buf_vbo[], size_t buf_vbo_len, size_t buf_vbo_num_tris) { if (d3d.last_depth_test != d3d.depth_test || d3d.last_depth_mask != d3d.depth_mask) { d3d.last_depth_test = d3d.depth_test; d3d.last_depth_mask = d3d.depth_mask; d3d.depth_stencil_state.Reset(); D3D11_DEPTH_STENCIL_DESC depth_stencil_desc; ZeroMemory(&depth_stencil_desc, sizeof(D3D11_DEPTH_STENCIL_DESC)); depth_stencil_desc.DepthEnable = d3d.depth_test; depth_stencil_desc.DepthWriteMask = d3d.depth_mask ? D3D11_DEPTH_WRITE_MASK_ALL : D3D11_DEPTH_WRITE_MASK_ZERO; depth_stencil_desc.DepthFunc = D3D11_COMPARISON_LESS_EQUAL; depth_stencil_desc.StencilEnable = false; ThrowIfFailed(d3d.device->CreateDepthStencilState(&depth_stencil_desc, d3d.depth_stencil_state.GetAddressOf())); d3d.context->OMSetDepthStencilState(d3d.depth_stencil_state.Get(), 0); } if (d3d.last_zmode_decal != d3d.zmode_decal) { d3d.last_zmode_decal = d3d.zmode_decal; d3d.rasterizer_state.Reset(); D3D11_RASTERIZER_DESC rasterizer_desc; ZeroMemory(&rasterizer_desc, sizeof(D3D11_RASTERIZER_DESC)); rasterizer_desc.FillMode = D3D11_FILL_SOLID; rasterizer_desc.CullMode = D3D11_CULL_NONE; rasterizer_desc.FrontCounterClockwise = true; rasterizer_desc.DepthBias = 0; rasterizer_desc.SlopeScaledDepthBias = d3d.zmode_decal ? -2.0f : 0.0f; rasterizer_desc.DepthBiasClamp = 0.0f; rasterizer_desc.DepthClipEnable = true; rasterizer_desc.ScissorEnable = true; rasterizer_desc.MultisampleEnable = false; rasterizer_desc.AntialiasedLineEnable = false; ThrowIfFailed(d3d.device->CreateRasterizerState(&rasterizer_desc, d3d.rasterizer_state.GetAddressOf())); d3d.context->RSSetState(d3d.rasterizer_state.Get()); } bool textures_changed = false; for (int i = 0; i < 2; i++) { if (d3d.shader_program->used_textures[i]) { if (d3d.last_resource_views[i].Get() != d3d.textures[d3d.current_texture_ids[i]].resource_view.Get()) { d3d.last_resource_views[i] = d3d.textures[d3d.current_texture_ids[i]].resource_view.Get(); d3d.context->PSSetShaderResources(i, 1, d3d.textures[d3d.current_texture_ids[i]].resource_view.GetAddressOf()); if (configFiltering == 2) { d3d.per_draw_cb_data.textures[i].width = d3d.textures[d3d.current_texture_ids[i]].width; d3d.per_draw_cb_data.textures[i].height = d3d.textures[d3d.current_texture_ids[i]].height; d3d.per_draw_cb_data.textures[i].linear_filtering = d3d.textures[d3d.current_texture_ids[i]].linear_filtering; textures_changed = true; } if (d3d.last_sampler_states[i].Get() != d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get()) { d3d.last_sampler_states[i] = d3d.textures[d3d.current_texture_ids[i]].sampler_state.Get(); d3d.context->PSSetSamplers(i, 1, d3d.textures[d3d.current_texture_ids[i]].sampler_state.GetAddressOf()); } } } } // Set per-draw constant buffer if (textures_changed) { D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); d3d.context->Map(d3d.per_draw_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); memcpy(ms.pData, &d3d.per_draw_cb_data, sizeof(PerDrawCB)); d3d.context->Unmap(d3d.per_draw_cb.Get(), 0); } // Set vertex buffer data D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); d3d.context->Map(d3d.vertex_buffer.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); memcpy(ms.pData, buf_vbo, buf_vbo_len * sizeof(float)); d3d.context->Unmap(d3d.vertex_buffer.Get(), 0); uint32_t stride = d3d.shader_program->num_floats * sizeof(float); uint32_t offset = 0; if (d3d.last_vertex_buffer_stride != stride) { d3d.last_vertex_buffer_stride = stride; d3d.context->IASetVertexBuffers(0, 1, d3d.vertex_buffer.GetAddressOf(), &stride, &offset); } if (d3d.last_shader_program != d3d.shader_program) { d3d.last_shader_program = d3d.shader_program; d3d.context->IASetInputLayout(d3d.shader_program->input_layout.Get()); d3d.context->VSSetShader(d3d.shader_program->vertex_shader.Get(), 0, 0); d3d.context->PSSetShader(d3d.shader_program->pixel_shader.Get(), 0, 0); if (d3d.last_blend_state.Get() != d3d.shader_program->blend_state.Get()) { d3d.last_blend_state = d3d.shader_program->blend_state.Get(); d3d.context->OMSetBlendState(d3d.shader_program->blend_state.Get(), 0, 0xFFFFFFFF); } } if (d3d.last_primitive_topology != D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST) { d3d.last_primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; d3d.context->IASetPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST); } d3d.context->Draw(buf_vbo_num_tris * 3, 0); } static void gfx_d3d11_init(void) { } static void gfx_d3d11_shutdown(void) { } static void gfx_d3d11_start_frame(void) { // Set render targets d3d.context->OMSetRenderTargets(1, d3d.backbuffer_view.GetAddressOf(), d3d.depth_stencil_view.Get()); // Clear render targets const float clearColor[] = { 0.0f, 0.0f, 0.0f, 1.0f }; d3d.context->ClearRenderTargetView(d3d.backbuffer_view.Get(), clearColor); d3d.context->ClearDepthStencilView(d3d.depth_stencil_view.Get(), D3D11_CLEAR_DEPTH, 1.0f, 0); // Set per-frame constant buffer d3d.per_frame_cb_data.noise_frame++; if (d3d.per_frame_cb_data.noise_frame > 150) { // No high values, as noise starts to look ugly d3d.per_frame_cb_data.noise_frame = 0; } float aspect_ratio = (float) d3d.current_width / (float) d3d.current_height; d3d.per_frame_cb_data.noise_scale_x = 120 * aspect_ratio; // 120 = N64 height resolution (240) / 2 d3d.per_frame_cb_data.noise_scale_y = 120; D3D11_MAPPED_SUBRESOURCE ms; ZeroMemory(&ms, sizeof(D3D11_MAPPED_SUBRESOURCE)); d3d.context->Map(d3d.per_frame_cb.Get(), 0, D3D11_MAP_WRITE_DISCARD, 0, &ms); memcpy(ms.pData, &d3d.per_frame_cb_data, sizeof(PerFrameCB)); d3d.context->Unmap(d3d.per_frame_cb.Get(), 0); } struct GfxRenderingAPI gfx_d3d11_api = { gfx_d3d11_z_is_from_0_to_1, gfx_d3d11_unload_shader, gfx_d3d11_load_shader, gfx_d3d11_create_and_load_new_shader, gfx_d3d11_lookup_shader, gfx_d3d11_shader_get_info, gfx_d3d11_new_texture, gfx_d3d11_select_texture, gfx_d3d11_upload_texture, gfx_d3d11_set_sampler_parameters, gfx_d3d11_set_depth_test, gfx_d3d11_set_depth_mask, gfx_d3d11_set_zmode_decal, gfx_d3d11_set_viewport, gfx_d3d11_set_scissor, gfx_d3d11_set_use_alpha, gfx_d3d11_draw_triangles, gfx_d3d11_init, gfx_d3d11_start_frame, gfx_d3d11_shutdown, }; struct GfxWindowManagerAPI gfx_dxgi = { gfx_d3d11_dxgi_init, gfx_d3d11_dxgi_set_keyboard_callbacks, gfx_d3d11_dxgi_main_loop, gfx_d3d11_dxgi_get_dimensions, gfx_d3d11_dxgi_handle_events, gfx_d3d11_dxgi_start_frame, gfx_d3d11_dxgi_swap_buffers_begin, gfx_d3d11_dxgi_swap_buffers_end, gfx_d3d11_dxgi_get_time, gfx_d3d11_dxgi_shutdown, }; #else #error "D3D11 is only supported on Windows" #endif // _WIN32 #endif // RAPI_D3D11