diff --git a/gfx/d3d9/d3d.cpp b/gfx/d3d9/d3d.cpp index b1b95a00d4..6222866ead 100644 --- a/gfx/d3d9/d3d.cpp +++ b/gfx/d3d9/d3d.cpp @@ -16,61 +16,764 @@ */ #include "d3d9.hpp" +#include "render_chain.hpp" #include "../gfx_common.h" +#ifndef _XBOX +#include "../context/win32_common.h" +#endif + #include "../../compat/posix_string.h" #include "../../performance.h" +static void d3d_render_msg(void *data, const char *msg, void *userdata); + +#ifndef _XBOX +#define HAVE_MONITOR +#define HAVE_WINDOW +#endif + +#ifdef HAVE_MONITOR +#define IDI_ICON 1 +#define MAX_MONITORS 9 + +namespace Monitor +{ + static HMONITOR last_hm; + static HMONITOR all_hms[MAX_MONITORS]; + static unsigned num_mons; + static unsigned cur_mon_id; +} + +static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) +{ + Monitor::all_hms[Monitor::num_mons++] = hMonitor; + return TRUE; +} + +// Multi-monitor support. +RECT D3DVideo::monitor_rect(void) +{ + Monitor::num_mons = 0; + EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, 0); + + if (!Monitor::last_hm) + Monitor::last_hm = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTONEAREST); + HMONITOR hm_to_use = Monitor::last_hm; + + unsigned fs_monitor = g_settings.video.monitor_index; + if (fs_monitor && fs_monitor <= Monitor::num_mons && Monitor::all_hms[fs_monitor - 1]) + { + hm_to_use = Monitor::all_hms[fs_monitor - 1]; + Monitor::cur_mon_id = fs_monitor - 1; + } + else + { + for (unsigned i = 0; i < Monitor::num_mons; i++) + { + if (Monitor::all_hms[i] == hm_to_use) + { + Monitor::cur_mon_id = i; + break; + } + } + } + + MONITORINFOEX current_mon; + memset(¤t_mon, 0, sizeof(current_mon)); + current_mon.cbSize = sizeof(MONITORINFOEX); + GetMonitorInfo(hm_to_use, (MONITORINFO*)¤t_mon); + + return current_mon.rcMonitor; +} +#endif + +static void d3d_deinitialize(void *data) +{ + D3DVideo *d3d = reinterpret_cast(data); + d3d->deinit_font(); + d3d->deinit_chain(); +#ifdef HAVE_CG + d3d->deinit_cg(); +#endif + + d3d->needs_restore = false; +} + +static void d3d_make_d3dpp(void *data, const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp) +{ + D3DVideo *d3d = reinterpret_cast(data); + memset(d3dpp, 0, sizeof(*d3dpp)); + + d3dpp->Windowed = g_settings.video.windowed_fullscreen || !info->fullscreen; + + if (info->vsync) + { + switch (g_settings.video.swap_interval) + { + default: + case 1: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_ONE; break; + case 2: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_TWO; break; + case 3: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_THREE; break; + case 4: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_FOUR; break; + } + } + else + d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; + + d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; + d3dpp->hDeviceWindow = d3d->hWnd; + d3dpp->BackBufferCount = 2; + d3dpp->BackBufferFormat = !d3dpp->Windowed ? D3DFMT_X8R8G8B8 : D3DFMT_UNKNOWN; + + if (!d3dpp->Windowed) + { + d3dpp->BackBufferWidth = d3d->screen_width; + d3dpp->BackBufferHeight = d3d->screen_height; + } +} + +static void d3d_resize(void *data, unsigned new_width, unsigned new_height); + +#ifdef HAVE_WINDOW +namespace Callback +{ + static bool quit = false; + static D3DVideo *curD3D = NULL; + static HRESULT d3d_err; + static void *dinput; + + LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, + WPARAM wParam, LPARAM lParam) + { + switch (message) + { + case WM_CREATE: + LPCREATESTRUCT p_cs; + p_cs = (LPCREATESTRUCT)lParam; + curD3D = (D3DVideo*)p_cs->lpCreateParams; + break; + + case WM_CHAR: + case WM_KEYDOWN: + case WM_KEYUP: + case WM_SYSKEYUP: + case WM_SYSKEYDOWN: + return win32_handle_keyboard_event(hWnd, message, wParam, lParam); + + case WM_DESTROY: + quit = true; + return 0; + + case WM_SIZE: + unsigned new_width, new_height; + new_width = LOWORD(lParam); + new_height = HIWORD(lParam); + + if (new_width && new_height) + d3d_resize(curD3D, new_width, new_height); + return 0; + } + if (dinput_handle_message(dinput, message, wParam, lParam)) + return 0; + return DefWindowProc(hWnd, message, wParam, lParam); + } +} +#endif + +static bool d3d_init_base(void *data, const video_info_t *info) +{ + D3DVideo *d3d = reinterpret_cast(data); + D3DPRESENT_PARAMETERS d3dpp; + d3d_make_d3dpp(d3d, info, &d3dpp); + + d3d->g_pD3D = D3DCREATE_CTX(D3D_SDK_VERSION); + if (!d3d->g_pD3D) + { + RARCH_ERR("Failed to create D3D interface!\n"); + return false; + } + + if (FAILED(Callback::d3d_err = d3d->g_pD3D->CreateDevice( + Monitor::cur_mon_id, + D3DDEVTYPE_HAL, + d3d->hWnd, + D3DCREATE_HARDWARE_VERTEXPROCESSING, + &d3dpp, + &d3d->dev))) + { + RARCH_WARN("[D3D]: Failed to init device with hardware vertex processing (code: 0x%x). Trying to fall back to software vertex processing.\n", + (unsigned)Callback::d3d_err); + + if (FAILED(Callback::d3d_err = d3d->g_pD3D->CreateDevice( + Monitor::cur_mon_id, + D3DDEVTYPE_HAL, + d3d->hWnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, + &d3dpp, + &d3d->dev))) + { + RARCH_ERR("Failed to initialize device.\n"); + return false; + } + } + + return true; +} + +static void d3d_calculate_rect(void *data, unsigned width, unsigned height, + bool keep, float desired_aspect); + +static bool d3d_initialize(void *data, const video_info_t *info) +{ + D3DVideo *d3d = reinterpret_cast(data); + bool ret = true; + if (!d3d->g_pD3D) + ret = d3d_init_base(d3d, info); + else if (d3d->needs_restore) + { + D3DPRESENT_PARAMETERS d3dpp; + d3d_make_d3dpp(d3d, info, &d3dpp); + if (d3d->dev->Reset(&d3dpp) != D3D_OK) + { + HRESULT res = d3d->dev->TestCooperativeLevel(); + const char *err; + switch (res) + { + case D3DERR_DEVICELOST: + err = "DEVICELOST"; + break; + + case D3DERR_DEVICENOTRESET: + err = "DEVICENOTRESET"; + break; + + case D3DERR_DRIVERINTERNALERROR: + err = "DRIVERINTERNALERROR"; + break; + + default: + err = "Unknown"; + } + // Try to recreate the device completely ... + RARCH_WARN("[D3D]: Attempting to recover from dead state (%s).\n", err); + d3d_deinitialize(d3d); + d3d->g_pD3D->Release(); + d3d->g_pD3D = NULL; + ret = d3d_init_base(d3d, info); + if (ret) + RARCH_LOG("[D3D]: Recovered from dead state.\n"); + else + return ret; + } + } + + if (!ret) + return ret; + + d3d_calculate_rect(d3d, d3d->screen_width, d3d->screen_height, info->force_aspect, g_extern.system.aspect_ratio); + +#ifdef HAVE_CG + if (!d3d->init_cg()) + { + RARCH_ERR("Failed to initialize Cg.\n"); + return false; + } +#endif + + if (!d3d->init_chain(info)) + { + RARCH_ERR("Failed to initialize render chain.\n"); + return false; + } + + if (!d3d->init_font()) + { + RARCH_ERR("Failed to initialize font.\n"); + return false; + } + + return true; +} + +static bool d3d_restore(void *data) +{ + D3DVideo *d3d = reinterpret_cast(data); + d3d_deinitialize(d3d); + d3d->needs_restore = !d3d_initialize(d3d, &d3d->video_info); + + if (d3d->needs_restore) + RARCH_ERR("[D3D]: Restore error.\n"); + + return !d3d->needs_restore; +} + +static void d3d_resize(void *data, unsigned new_width, unsigned new_height) +{ + D3DVideo *d3d = reinterpret_cast(data); + if (!d3d->dev) + return; + + RARCH_LOG("[D3D]: Resize %ux%u.\n", new_width, new_height); + + if (new_width != d3d->video_info.width || new_height != d3d->video_info.height) + { + d3d->video_info.width = d3d->screen_width = new_width; + d3d->video_info.height = d3d->screen_height = new_height; + d3d_restore(d3d); + } +} + +#ifdef HAVE_OVERLAY +static void d3d_overlay_render(void *data, overlay_t &overlay) +{ + D3DVideo *d3d = reinterpret_cast(data); + + if (!overlay.tex) + return; + + struct overlay_vertex + { + float x, y, z; + float u, v; + float r, g, b, a; + } vert[4]; + + if (!overlay.vert_buf) + { + d3d->dev->CreateVertexBuffer( + sizeof(vert), + d3d->dev->GetSoftwareVertexProcessing() ? D3DUSAGE_SOFTWAREPROCESSING : 0, + 0, + D3DPOOL_MANAGED, + &overlay.vert_buf, + NULL); + } + + for (unsigned i = 0; i < 4; i++) + { + vert[i].z = 0.5f; + vert[i].r = vert[i].g = vert[i].b = 1.0f; + vert[i].a = overlay.alpha_mod; + } + + float overlay_width = d3d->final_viewport.Width; + float overlay_height = d3d->final_viewport.Height; + + vert[0].x = overlay.vert_coords.x * overlay_width; + vert[1].x = (overlay.vert_coords.x + overlay.vert_coords.w) * overlay_width; + vert[2].x = overlay.vert_coords.x * overlay_width; + vert[3].x = (overlay.vert_coords.x + overlay.vert_coords.w) * overlay_width; + vert[0].y = overlay.vert_coords.y * overlay_height; + vert[1].y = overlay.vert_coords.y * overlay_height; + vert[2].y = (overlay.vert_coords.y + overlay.vert_coords.h) * overlay_height; + vert[3].y = (overlay.vert_coords.y + overlay.vert_coords.h) * overlay_height; + + vert[0].u = overlay.tex_coords.x; + vert[1].u = overlay.tex_coords.x + overlay.tex_coords.w; + vert[2].u = overlay.tex_coords.x; + vert[3].u = overlay.tex_coords.x + overlay.tex_coords.w; + vert[0].v = overlay.tex_coords.y; + vert[1].v = overlay.tex_coords.y; + vert[2].v = overlay.tex_coords.y + overlay.tex_coords.h; + vert[3].v = overlay.tex_coords.y + overlay.tex_coords.h; + + // Align texels and vertices. + for (unsigned i = 0; i < 4; i++) + { + vert[i].x -= 0.5f; + vert[i].y += 0.5f; + } + + void *verts; + overlay.vert_buf->Lock(0, sizeof(vert), &verts, 0); + memcpy(verts, vert, sizeof(vert)); + overlay.vert_buf->Unlock(); + + // enable alpha + d3d->dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); + d3d->dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); + d3d->dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); + + // set vertex decl for overlay + D3DVERTEXELEMENT9 vElems[4] = { + {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, + {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, + {0, 20, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, + D3DDECL_END() + }; + LPDIRECT3DVERTEXDECLARATION vertex_decl; + d3d->dev->CreateVertexDeclaration(vElems, &vertex_decl); + d3d->dev->SetVertexDeclaration(vertex_decl); + vertex_decl->Release(); + + d3d->dev->SetStreamSource(0, overlay.vert_buf, 0, sizeof(overlay_vertex)); + + if (overlay.fullscreen) + { + // set viewport to full window + D3DVIEWPORT vp_full; + vp_full.X = 0; + vp_full.Y = 0; + vp_full.Width = d3d->screen_width; + vp_full.Height = d3d->screen_height; + vp_full.MinZ = 0.0f; + vp_full.MaxZ = 1.0f; + d3d->dev->SetViewport(&vp_full); + } + + // render overlay + d3d->dev->SetTexture(0, overlay.tex); + d3d->dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); + d3d->dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); + d3d->dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + d3d->dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + if (SUCCEEDED(d3d->dev->BeginScene())) + { + d3d->dev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); + d3d->dev->EndScene(); + } + + // restore previous state + d3d->dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); + d3d->dev->SetViewport(&d3d->final_viewport); +} +#endif + +static void d3d_set_viewport(void *data, int x, int y, unsigned width, unsigned height) +{ + D3DVideo *d3d = reinterpret_cast(data); + D3DVIEWPORT viewport; + + // D3D9 doesn't support negative X/Y viewports ... + if (x < 0) + x = 0; + if (y < 0) + y = 0; + + viewport.X = x; + viewport.Y = y; + viewport.Width = width; + viewport.Height = height; + viewport.MinZ = 0.0f; + viewport.MaxZ = 1.0f; + + d3d->final_viewport = viewport; + + d3d->set_font_rect(NULL); +} + +static void d3d_calculate_rect(void *data, unsigned width, unsigned height, + bool keep, float desired_aspect) +{ + D3DVideo *d3d = reinterpret_cast(data); + + if (g_settings.video.scale_integer) + { + struct rarch_viewport vp = {0}; + gfx_scale_integer(&vp, width, height, desired_aspect, keep); + d3d_set_viewport(d3d, vp.x, vp.y, vp.width, vp.height); + } + else if (!keep) + d3d_set_viewport(d3d, 0, 0, width, height); + else + { + if (g_settings.video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) + { + const rarch_viewport_t &custom = g_extern.console.screen.viewports.custom_vp; + d3d_set_viewport(d3d, custom.x, custom.y, custom.width, custom.height); + } + else + { + float device_aspect = static_cast(width) / static_cast(height); + if (fabsf(device_aspect - desired_aspect) < 0.0001f) + d3d_set_viewport(d3d, 0, 0, width, height); + else if (device_aspect > desired_aspect) + { + float delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; + d3d_set_viewport(d3d, int(roundf(width * (0.5f - delta))), 0, unsigned(roundf(2.0f * width * delta)), height); + } + else + { + float delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; + d3d_set_viewport(d3d, 0, int(roundf(height * (0.5f - delta))), width, unsigned(roundf(2.0f * height * delta))); + } + } + } +} + static bool d3d_frame(void *data, const void *frame, unsigned width, unsigned height, unsigned pitch, const char *msg) { - return reinterpret_cast(data)->frame(frame, - width, height, pitch, msg); + D3DVideo *d3d = reinterpret_cast(data); + + if (!frame) + return true; + + RARCH_PERFORMANCE_INIT(d3d_frame); + RARCH_PERFORMANCE_START(d3d_frame); + // We cannot recover in fullscreen. + if (d3d->needs_restore && IsIconic(d3d->hWnd)) + return true; + + if (d3d->needs_restore && !d3d_restore(d3d)) + { + RARCH_ERR("[D3D]: Failed to restore.\n"); + return false; + } + + if (d3d->should_resize) + { + d3d_calculate_rect(d3d, d3d->screen_width, d3d->screen_height, d3d->video_info.force_aspect, g_extern.system.aspect_ratio); + d3d->chain->set_final_viewport(d3d->final_viewport); + d3d->recompute_pass_sizes(); + + d3d->should_resize = false; + } + + // render_chain() only clears out viewport, clear out everything. + D3DVIEWPORT screen_vp; + screen_vp.X = 0; + screen_vp.Y = 0; + screen_vp.MinZ = 0; + screen_vp.MaxZ = 1; + screen_vp.Width = d3d->screen_width; + screen_vp.Height = d3d->screen_height; + d3d->dev->SetViewport(&screen_vp); + d3d->dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); + + // Insert black frame first, so we can screenshot, etc. + if (g_settings.video.black_frame_insertion) + { + if (d3d->dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) + { + RARCH_ERR("[D3D]: Present() failed.\n"); + d3d->needs_restore = true; + return true; + } + d3d->dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); + } + + if (!d3d->chain->render(frame, width, height, pitch, d3d->dev_rotation)) + { + RARCH_ERR("[D3D]: Failed to render scene.\n"); + return false; + } + + d3d_render_msg(d3d, msg, NULL); + +#ifdef HAVE_MENU + if (d3d->rgui.enabled) + d3d_overlay_render(d3d, d3d->rgui); +#endif + +#ifdef HAVE_OVERLAY + if (d3d->overlays_enabled) + { + for (unsigned i = 0; i < d3d->overlays.size(); i++) + d3d_overlay_render(d3d, d3d->overlays[i]); + } +#endif + + RARCH_PERFORMANCE_STOP(d3d_frame); + + if (d3d->dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) + { + d3d->needs_restore = true; + RARCH_ERR("[D3D]: Present() failed.\n"); + return true; + } + + d3d->update_title(); + return true; } static void d3d_set_nonblock_state(void *data, bool state) { - reinterpret_cast(data)->set_nonblock_state(state); + D3DVideo *d3d = reinterpret_cast(data); + d3d->video_info.vsync = !state; + d3d_restore(d3d); } static bool d3d_alive(void *data) { - return reinterpret_cast(data)->alive(); + D3DVideo *d3d = reinterpret_cast(data); + +#ifndef _XBOX + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } +#endif + return !Callback::quit; } static bool d3d_focus(void *data) { - return reinterpret_cast(data)->focus(); +#ifdef _XBOX + return true; +#else + D3DVideo *d3d = reinterpret_cast(data); + return GetFocus() == d3d->hWnd; +#endif } static void d3d_set_rotation(void *data, unsigned rot) { - reinterpret_cast(data)->set_rotation(rot); + D3DVideo *d3d = reinterpret_cast(data); + d3d->dev_rotation = rot; } +#ifdef HAVE_OVERLAY +void d3d_free_overlay(void *data, overlay_t *overlay) +{ + D3DVideo *d3d = reinterpret_cast(data); + if (overlay->tex) + overlay->tex->Release(); + if (overlay->vert_buf) + overlay->vert_buf->Release(); +} + +void d3d_free_overlays(void *data) +{ + D3DVideo *d3d = reinterpret_cast(data); + for (unsigned i = 0; i < d3d->overlays.size(); i++) + d3d_free_overlay(d3d, &d3d->overlays[i]); + d3d->overlays.clear(); +} +#endif + static void d3d_free(void *data) { - delete reinterpret_cast(data); + D3DVideo *d3d = reinterpret_cast(data); + d3d_deinitialize(d3d); +#ifdef HAVE_OVERLAY + d3d_free_overlays(d3d); +#endif +#ifdef HAVE_MENU + d3d_free_overlay(d3d, &d3d->rgui); +#endif + if (d3d->dev) + d3d->dev->Release(); + if (d3d->g_pD3D) + d3d->g_pD3D->Release(); + +#ifdef HAVE_MONITOR + Monitor::last_hm = MonitorFromWindow(d3d->hWnd, MONITOR_DEFAULTTONEAREST); +#endif + DestroyWindow(d3d->hWnd); + + UnregisterClass("RetroArch", GetModuleHandle(NULL)); } static void d3d_viewport_info(void *data, struct rarch_viewport *vp) { - reinterpret_cast(data)->viewport_info(*vp); + D3DVideo *d3d = reinterpret_cast(data); + + vp->x = d3d->final_viewport.X; + vp->y = d3d->final_viewport.Y; + vp->width = d3d->final_viewport.Width; + vp->height = d3d->final_viewport.Height; + + vp->full_width = d3d->screen_width; + vp->full_height = d3d->screen_height; } static bool d3d_read_viewport(void *data, uint8_t *buffer) { - return reinterpret_cast(data)->read_viewport(buffer); + D3DVideo *d3d = reinterpret_cast(data); + + RARCH_PERFORMANCE_INIT(d3d_read_viewport); + RARCH_PERFORMANCE_START(d3d_read_viewport); + bool ret = true; + LPDIRECT3DSURFACE target = NULL; + LPDIRECT3DSURFACE dest = NULL; + + if (FAILED(Callback::d3d_err = d3d->dev->GetRenderTarget(0, &target))) + { + ret = false; + goto end; + } + + if (FAILED(Callback::d3d_err = d3d->dev->CreateOffscreenPlainSurface(d3d->screen_width, + d3d->screen_height, + D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, + &dest, NULL))) + { + ret = false; + goto end; + } + + if (FAILED(Callback::d3d_err = d3d->dev->GetRenderTargetData(target, dest))) + { + ret = false; + goto end; + } + + D3DLOCKED_RECT rect; + if (SUCCEEDED(dest->LockRect(&rect, NULL, D3DLOCK_READONLY))) + { + unsigned pitchpix = rect.Pitch / 4; + const uint32_t *pixels = (const uint32_t*)rect.pBits; + pixels += d3d->final_viewport.X; + pixels += (d3d->final_viewport.Height - 1) * pitchpix; + pixels -= d3d->final_viewport.Y * pitchpix; + + for (unsigned y = 0; y < d3d->final_viewport.Height; y++, pixels -= pitchpix) + { + for (unsigned x = 0; x < d3d->final_viewport.Width; x++) + { + *buffer++ = (pixels[x] >> 0) & 0xff; + *buffer++ = (pixels[x] >> 8) & 0xff; + *buffer++ = (pixels[x] >> 16) & 0xff; + } + } + + dest->UnlockRect(); + } + else + { + ret = false; + goto end; + } + +end: + RARCH_PERFORMANCE_STOP(d3d_read_viewport); + if (target) + target->Release(); + if (dest) + dest->Release(); + return ret; } static bool d3d_set_shader(void *data, enum rarch_shader_type type, const char *path) { + D3DVideo *d3d = reinterpret_cast(data); std::string shader = ""; if (path && type == RARCH_SHADER_CG) shader = path; - return reinterpret_cast(data)->set_shader(shader); + auto old_shader = d3d->cg_shader; + bool restore_old = false; + d3d->cg_shader = path; + + if (!d3d->process_shader() || !d3d_restore(d3d)) + { + RARCH_ERR("[D3D9]: Setting shader failed.\n"); + restore_old = true; + } + + if (restore_old) + { + d3d->cg_shader = old_shader; + d3d->process_shader(); + d3d_restore(d3d); + } + + return !restore_old; } #ifdef HAVE_MENU @@ -78,17 +781,17 @@ static void d3d_get_poke_interface(void *data, const video_poke_interface_t **if #endif #ifdef HAVE_OVERLAY -static bool d3d_overlay_load(void *data, const texture_image *images, unsigned num_images) -{ - return reinterpret_cast(data)->overlay_load(images, num_images); -} - static void d3d_overlay_tex_geom(void *data, unsigned index, float x, float y, float w, float h) { - return reinterpret_cast(data)->overlay_tex_geom(index, x, y, w, h); + D3DVideo *d3d = reinterpret_cast(data); + + d3d->overlays[index].tex_coords.x = x; + d3d->overlays[index].tex_coords.y = y; + d3d->overlays[index].tex_coords.w = w; + d3d->overlays[index].tex_coords.h = h; } static void d3d_overlay_vertex_geom(void *data, @@ -96,22 +799,80 @@ static void d3d_overlay_vertex_geom(void *data, float x, float y, float w, float h) { - return reinterpret_cast(data)->overlay_vertex_geom(index, x, y, w, h); + D3DVideo *d3d = reinterpret_cast(data); + + y = 1.0f - y; + h = -h; + d3d->overlays[index].vert_coords.x = x; + d3d->overlays[index].vert_coords.y = y; + d3d->overlays[index].vert_coords.w = w; + d3d->overlays[index].vert_coords.h = h; } +static bool d3d_overlay_load(void *data, const texture_image *images, unsigned num_images) +{ + D3DVideo *d3d = reinterpret_cast(data); + d3d_free_overlays(data); + d3d->overlays.resize(num_images); + + for (unsigned i = 0; i < num_images; i++) + { + unsigned width = images[i].width; + unsigned height = images[i].height; + overlay_t &overlay = d3d->overlays[i]; + if (FAILED(d3d->dev->CreateTexture(width, height, 1, + 0, + D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + &overlay.tex, NULL))) + { + RARCH_ERR("[D3D]: Failed to create overlay texture\n"); + return false; + } + + D3DLOCKED_RECT d3dlr; + if (SUCCEEDED(overlay.tex->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK))) + { + uint32_t *dst = static_cast(d3dlr.pBits); + const uint32_t *src = images[i].pixels; + unsigned pitch = d3dlr.Pitch >> 2; + for (unsigned y = 0; y < height; y++, dst += pitch, src += width) + memcpy(dst, src, width << 2); + overlay.tex->UnlockRect(0); + } + + overlay.tex_w = width; + overlay.tex_h = height; + d3d_overlay_tex_geom(d3d, i, 0, 0, 1, 1); // Default. Stretch to whole screen. + d3d_overlay_vertex_geom(d3d, i, 0, 0, 1, 1); + } + + return true; +} + +static void d3d_show_mouse(void *data, bool state); + static void d3d_overlay_enable(void *data, bool state) { - return reinterpret_cast(data)->overlay_enable(state); + D3DVideo *d3d = reinterpret_cast(data); + + for (unsigned i = 0; i < d3d->overlays.size(); i++) + d3d->overlays_enabled = state; + d3d_show_mouse(d3d, state); } static void d3d_overlay_full_screen(void *data, bool enable) { - return reinterpret_cast(data)->overlay_full_screen(enable); + D3DVideo *d3d = reinterpret_cast(data); + + for (unsigned i = 0; i < d3d->overlays.size(); i++) + d3d->overlays[i].fullscreen = enable; } static void d3d_overlay_set_alpha(void *data, unsigned index, float mod) { - return reinterpret_cast(data)->overlay_set_alpha(index, mod); + D3DVideo *d3d = reinterpret_cast(data); + d3d->overlays[index].alpha_mod = mod; } static const video_overlay_interface_t d3d_overlay_interface = { @@ -153,7 +914,7 @@ static void d3d_set_aspect_ratio(void *data, unsigned aspect_ratio_idx) } g_extern.system.aspect_ratio = aspectratio_lut[aspect_ratio_idx].value; - d3d->info().force_aspect = true; + d3d->video_info.force_aspect = true; d3d->should_resize = true; return; } @@ -164,15 +925,56 @@ static void d3d_apply_state_changes(void *data) d3d->should_resize = true; } +static void d3d_render_msg(void *data, const char *msg, void *userdata) +{ + font_params_t *params = (font_params_t*)userdata; + D3DVideo *d3d = reinterpret_cast(data); + + if (msg && SUCCEEDED(d3d->dev->BeginScene())) + { + d3d->font->DrawTextA(NULL, + msg, + -1, + &d3d->font_rect_shifted, + DT_LEFT, + ((d3d->font_color >> 2) & 0x3f3f3f) | 0xff000000); + + d3d->font->DrawTextA(NULL, + msg, + -1, + &d3d->font_rect, + DT_LEFT, + d3d->font_color | 0xff000000); + + d3d->dev->EndScene(); + } + + if (params) + d3d->set_font_rect(NULL); +} + static void d3d_set_osd_msg(void *data, const char *msg, void *userdata) { font_params_t *params = (font_params_t*)userdata; - reinterpret_cast(data)->render_msg(msg, params); + D3DVideo *d3d = reinterpret_cast(data); + + if (params) + d3d->set_font_rect(params); + + d3d_render_msg(d3d, msg, params); } static void d3d_show_mouse(void *data, bool state) { - reinterpret_cast(data)->show_cursor(state); + (void)data; + (void)state; + +#ifdef HAVE_WINDOW + if (state) + while (ShowCursor(TRUE) < 0); + else + while (ShowCursor(FALSE) >= 0); +#endif } #ifdef HAVE_MENU @@ -180,12 +982,71 @@ static void d3d_set_rgui_texture_frame(void *data, const void *frame, bool rgb32, unsigned width, unsigned height, float alpha) { - reinterpret_cast(data)->set_rgui_texture_frame(frame, rgb32, width, height, alpha); + D3DVideo *d3d = reinterpret_cast(data); + + if (!d3d->rgui.tex || d3d->rgui.tex_w != width || d3d->rgui.tex_h != height) + { + if (d3d->rgui.tex) + d3d->rgui.tex->Release(); + if (FAILED(d3d->dev->CreateTexture(width, height, 1, + 0, D3DFMT_A8R8G8B8, + D3DPOOL_MANAGED, + &d3d->rgui.tex, NULL))) + { + RARCH_ERR("[D3D]: Failed to create rgui texture\n"); + return; + } + d3d->rgui.tex_w = width; + d3d->rgui.tex_h = height; + } + + d3d->rgui.alpha_mod = alpha; + + + D3DLOCKED_RECT d3dlr; + if (SUCCEEDED(d3d->rgui.tex->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK))) + { + if (rgb32) + { + uint8_t *dst = (uint8_t*)d3dlr.pBits; + const uint32_t *src = (const uint32_t*)frame; + for (unsigned h = 0; h < height; h++, dst += d3dlr.Pitch, src += width) + { + memcpy(dst, src, width * sizeof(uint32_t)); + memset(dst + width * sizeof(uint32_t), 0, d3dlr.Pitch - width * sizeof(uint32_t)); + } + } + else + { + uint32_t *dst = (uint32_t*)d3dlr.pBits; + const uint16_t *src = (const uint16_t*)frame; + for (unsigned h = 0; h < height; h++, dst += d3dlr.Pitch >> 2, src += width) + { + for (unsigned w = 0; w < width; w++) + { + uint16_t c = src[w]; + uint32_t r = (c >> 12) & 0xf; + uint32_t g = (c >> 8) & 0xf; + uint32_t b = (c >> 4) & 0xf; + uint32_t a = (c >> 0) & 0xf; + r = ((r << 4) | r) << 16; + g = ((g << 4) | g) << 8; + b = ((b << 4) | b) << 0; + a = ((a << 4) | a) << 24; + dst[w] = r | g | b | a; + } + } + } + + d3d->rgui.tex->UnlockRect(0); + } } static void d3d_set_rgui_texture_enable(void *data, bool state, bool full_screen) { - reinterpret_cast(data)->set_rgui_texture_enable(state, full_screen); + D3DVideo *d3d = reinterpret_cast(data); + d3d->rgui.enabled = state; + d3d->rgui.fullscreen = full_screen; } #endif @@ -212,14 +1073,153 @@ static void d3d_get_poke_interface(void *data, const video_poke_interface_t **if *iface = &d3d_poke_interface; } +// Delay constructor due to lack of exceptions. +bool d3d_construct(void *data, const video_info_t *info, const input_driver_t **input, + void **input_data) +{ + D3DVideo *d3d = reinterpret_cast(data); + d3d->should_resize = false; + gfx_set_dwm(); + +#ifdef HAVE_MENU + memset(&d3d->rgui, 0, sizeof(d3d->rgui)); + d3d->rgui.tex_coords.x = 0; + d3d->rgui.tex_coords.y = 0; + d3d->rgui.tex_coords.w = 1; + d3d->rgui.tex_coords.h = 1; + d3d->rgui.vert_coords.x = 0; + d3d->rgui.vert_coords.y = 1; + d3d->rgui.vert_coords.w = 1; + d3d->rgui.vert_coords.h = -1; +#endif + +#ifdef HAVE_WINDOW + memset(&d3d->windowClass, 0, sizeof(d3d->windowClass)); + d3d->windowClass.cbSize = sizeof(d3d->windowClass); + d3d->windowClass.style = CS_HREDRAW | CS_VREDRAW; + d3d->windowClass.lpfnWndProc = Callback::WindowProc; + d3d->windowClass.hInstance = NULL; + d3d->windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); + d3d->windowClass.lpszClassName = "RetroArch"; + d3d->windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON)); + d3d->windowClass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0); + if (!info->fullscreen) + d3d->windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW; + + RegisterClassEx(&d3d->windowClass); +#endif + +#ifdef HAVE_MONITOR + RECT mon_rect = d3d->monitor_rect(); +#endif + + bool windowed_full = g_settings.video.windowed_fullscreen; + + unsigned full_x = (windowed_full || info->width == 0) ? (mon_rect.right - mon_rect.left) : info->width; + unsigned full_y = (windowed_full || info->height == 0) ? (mon_rect.bottom - mon_rect.top) : info->height; + RARCH_LOG("[D3D]: Monitor size: %dx%d.\n", (int)(mon_rect.right - mon_rect.left), (int)(mon_rect.bottom - mon_rect.top)); + + d3d->screen_width = info->fullscreen ? full_x : info->width; + d3d->screen_height = info->fullscreen ? full_y : info->height; + + unsigned win_width = d3d->screen_width; + unsigned win_height = d3d->screen_height; + + if (!info->fullscreen) + { + RECT rect = {0}; + rect.right = d3d->screen_width; + rect.bottom = d3d->screen_height; + AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); + win_width = rect.right - rect.left; + win_height = rect.bottom - rect.top; + } + +#ifdef HAVE_WINDOW + char buffer[128]; + gfx_get_fps(buffer, sizeof(buffer), NULL, 0); + std::string title = buffer; + title += " || Direct3D"; + + d3d->hWnd = CreateWindowEx(0, "RetroArch", title.c_str(), + info->fullscreen ? + (WS_EX_TOPMOST | WS_POPUP) : WS_OVERLAPPEDWINDOW, + info->fullscreen ? mon_rect.left : CW_USEDEFAULT, + info->fullscreen ? mon_rect.top : CW_USEDEFAULT, + win_width, win_height, + NULL, NULL, NULL, d3d); +#endif + + driver.display_type = RARCH_DISPLAY_WIN32; + driver.video_display = 0; + driver.video_window = (uintptr_t)d3d->hWnd; + +#ifdef HAVE_WINDOW + d3d_show_mouse(d3d, !info->fullscreen +#ifdef HAVE_OVERLAY + || d3d->overlays_enabled +#endif + ); + Callback::quit = false; + + ShowWindow(d3d->hWnd, SW_RESTORE); + UpdateWindow(d3d->hWnd); + SetForegroundWindow(d3d->hWnd); + SetFocus(d3d->hWnd); +#endif + + // This should only be done once here + // to avoid set_shader() to be overridden + // later. +#ifdef HAVE_CG + enum rarch_shader_type type = gfx_shader_parse_type(g_settings.video.shader_path, RARCH_SHADER_NONE); + if (g_settings.video.shader_enable && type == RARCH_SHADER_CG) + d3d->cg_shader = g_settings.video.shader_path; +#endif + + if (!d3d->process_shader()) + return false; + + d3d->video_info = *info; + if (!d3d_initialize(d3d, &d3d->video_info)) + return false; + + if (input && input_data) + { + Callback::dinput = input_dinput.init(); + *input = Callback::dinput ? &input_dinput : NULL; + *input_data = Callback::dinput; + } + + RARCH_LOG("[D3D]: Init complete.\n"); + return true; +} + static void *d3d_init(const video_info_t *info, const input_driver_t **input, void **input_data) { - D3DVideo *vid = new D3DVideo; - if (!vid->construct(info, input, input_data)) + D3DVideo *vid = (D3DVideo*)calloc(1, sizeof(D3DVideo)); + if (!vid) + return NULL; + + //default values + vid->g_pD3D = NULL; + vid->dev = NULL; + vid->font = NULL; + vid->dev_rotation = 0; + vid->needs_restore = false; +#ifdef HAVE_CG + vid->cgCtx = NULL; +#endif +#ifdef HAVE_OVERLAY + vid->overlays_enabled = false; +#endif + vid->chain = NULL; + + if (!d3d_construct(vid, info, input, input_data)) { RARCH_ERR("[D3D]: Failed to init D3D.\n"); - delete vid; + free(vid); return NULL; } diff --git a/gfx/d3d9/d3d9.hpp b/gfx/d3d9/d3d9.hpp index ee91afc95a..8d1850cbef 100644 --- a/gfx/d3d9/d3d9.hpp +++ b/gfx/d3d9/d3d9.hpp @@ -55,58 +55,13 @@ typedef struct class D3DVideo { - public: - D3DVideo(); - ~D3DVideo(); - - // Delay constructor due to lack of exceptions. - bool construct(const video_info_t *info, - const input_driver_t **input, void **input_data); - - bool frame(const void* frame, - unsigned width, unsigned height, unsigned pitch, - const char *msg); - - bool alive(); - bool focus() const; - void set_nonblock_state(bool state); - void set_rotation(unsigned rot); - void viewport_info(rarch_viewport &vp); - bool read_viewport(uint8_t *buffer); - void resize(unsigned new_width, unsigned new_height); - bool set_shader(const std::string &path); +public: bool process_shader(void); void set_filtering(unsigned index, bool smooth); void set_font_rect(font_params_t *params); - void overlay_render(overlay_t &overlay); - - void show_cursor(bool show); - -#ifdef HAVE_OVERLAY - bool overlay_load(const texture_image *images, unsigned num_images); - void overlay_tex_geom(unsigned index, float x, float y, float w, float h); - void overlay_vertex_geom(unsigned index, float x, float y, float w, float h); - void overlay_enable(bool state); - void overlay_full_screen(bool enable); - void overlay_set_alpha(unsigned index, float mod); -#endif - -#ifdef HAVE_MENU - void set_rgui_texture_frame(const void *frame, - bool rgb32, unsigned width, unsigned height, - float alpha); - void set_rgui_texture_enable(bool state, bool fullscreen); -#endif - - bool restore(); - void render_msg(const char *msg, font_params_t *params = nullptr); - bool should_resize; - inline video_info_t& info() { return video_info; } - - private: WNDCLASSEX windowClass; HWND hWnd; @@ -115,23 +70,15 @@ class D3DVideo LPD3DXFONT font; void recompute_pass_sizes(); - void calculate_rect(unsigned width, unsigned height, bool keep, float aspect); - void set_viewport(int x, int y, unsigned width, unsigned height); unsigned screen_width; unsigned screen_height; - unsigned rotation; + unsigned dev_rotation; D3DVIEWPORT final_viewport; std::string cg_shader; struct gfx_shader shader; - void process(void); - - bool init(const video_info_t *info); - bool init_base(const video_info_t *info); - void make_d3dpp(const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp); - void deinit(void); RECT monitor_rect(void); video_info_t video_info; @@ -162,11 +109,8 @@ class D3DVideo #ifdef HAVE_OVERLAY bool overlays_enabled; std::vector overlays; - void free_overlays(); #endif - void free_overlay(overlay_t &overlay); - #ifdef HAVE_MENU overlay_t rgui; #endif @@ -174,5 +118,9 @@ class D3DVideo RenderChain *chain; }; +#ifndef _XBOX +extern "C" bool dinput_handle_message(void *dinput, UINT message, WPARAM wParam, LPARAM lParam); +#endif + #endif diff --git a/gfx/d3d9/d3d9_pc.cpp b/gfx/d3d9/d3d9_pc.cpp index af40dc1a62..d6a6bece3e 100644 --- a/gfx/d3d9/d3d9_pc.cpp +++ b/gfx/d3d9/d3d9_pc.cpp @@ -34,279 +34,6 @@ #endif #endif -#ifndef _XBOX -#define IDI_ICON 1 -#define MAX_MONITORS 9 - -namespace Monitor -{ - static HMONITOR last_hm; - static HMONITOR all_hms[MAX_MONITORS]; - static unsigned num_mons; - static unsigned cur_mon_id; -} - -static BOOL CALLBACK monitor_enum_proc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData) -{ - Monitor::all_hms[Monitor::num_mons++] = hMonitor; - return TRUE; -} - -extern "C" bool dinput_handle_message(void *dinput, UINT message, WPARAM wParam, LPARAM lParam); - -namespace Callback -{ - static bool quit = false; - static D3DVideo *curD3D = NULL; - static HRESULT d3d_err; - static void *dinput; - - LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, - WPARAM wParam, LPARAM lParam) - { - switch (message) - { - case WM_CREATE: - LPCREATESTRUCT p_cs; - p_cs = (LPCREATESTRUCT)lParam; - curD3D = (D3DVideo*)p_cs->lpCreateParams; - break; - - case WM_CHAR: - case WM_KEYDOWN: - case WM_KEYUP: - case WM_SYSKEYUP: - case WM_SYSKEYDOWN: - return win32_handle_keyboard_event(hWnd, message, wParam, lParam); - - case WM_DESTROY: - quit = true; - return 0; - - case WM_SIZE: - unsigned new_width, new_height; - new_width = LOWORD(lParam); - new_height = HIWORD(lParam); - - if (new_width && new_height) - curD3D->resize(new_width, new_height); - return 0; - } - if (dinput_handle_message(dinput, message, wParam, lParam)) - return 0; - return DefWindowProc(hWnd, message, wParam, lParam); - } -} - -void D3DVideo::show_cursor(bool show) -{ - if (show) - while (ShowCursor(TRUE) < 0); - else - while (ShowCursor(FALSE) >= 0); -} - -// Multi-monitor support. -RECT D3DVideo::monitor_rect(void) -{ - Monitor::num_mons = 0; - EnumDisplayMonitors(NULL, NULL, monitor_enum_proc, 0); - - if (!Monitor::last_hm) - Monitor::last_hm = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTONEAREST); - HMONITOR hm_to_use = Monitor::last_hm; - - unsigned fs_monitor = g_settings.video.monitor_index; - if (fs_monitor && fs_monitor <= Monitor::num_mons && Monitor::all_hms[fs_monitor - 1]) - { - hm_to_use = Monitor::all_hms[fs_monitor - 1]; - Monitor::cur_mon_id = fs_monitor - 1; - } - else - { - for (unsigned i = 0; i < Monitor::num_mons; i++) - { - if (Monitor::all_hms[i] == hm_to_use) - { - Monitor::cur_mon_id = i; - break; - } - } - } - - MONITORINFOEX current_mon; - memset(¤t_mon, 0, sizeof(current_mon)); - current_mon.cbSize = sizeof(MONITORINFOEX); - GetMonitorInfo(hm_to_use, (MONITORINFO*)¤t_mon); - - return current_mon.rcMonitor; -} -#endif - -bool D3DVideo::init_base(const video_info_t *info) -{ - D3DPRESENT_PARAMETERS d3dpp; - make_d3dpp(info, &d3dpp); - - g_pD3D = D3DCREATE_CTX(D3D_SDK_VERSION); - if (!g_pD3D) - { - RARCH_ERR("Failed to create D3D interface!\n"); - return false; - } - - if (FAILED(Callback::d3d_err = g_pD3D->CreateDevice( - Monitor::cur_mon_id, - D3DDEVTYPE_HAL, - hWnd, - D3DCREATE_HARDWARE_VERTEXPROCESSING, - &d3dpp, - &dev))) - { - RARCH_WARN("[D3D]: Failed to init device with hardware vertex processing (code: 0x%x). Trying to fall back to software vertex processing.\n", - (unsigned)Callback::d3d_err); - - if (FAILED(Callback::d3d_err = g_pD3D->CreateDevice( - Monitor::cur_mon_id, - D3DDEVTYPE_HAL, - hWnd, - D3DCREATE_SOFTWARE_VERTEXPROCESSING, - &d3dpp, - &dev))) - { - RARCH_ERR("Failed to initialize device.\n"); - return false; - } - } - - return true; -} - -void D3DVideo::make_d3dpp(const video_info_t *info, D3DPRESENT_PARAMETERS *d3dpp) -{ - memset(d3dpp, 0, sizeof(*d3dpp)); - - d3dpp->Windowed = g_settings.video.windowed_fullscreen || !info->fullscreen; - - if (info->vsync) - { - switch (g_settings.video.swap_interval) - { - default: - case 1: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_ONE; break; - case 2: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_TWO; break; - case 3: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_THREE; break; - case 4: d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_FOUR; break; - } - } - else - d3dpp->PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; - - d3dpp->SwapEffect = D3DSWAPEFFECT_DISCARD; - d3dpp->hDeviceWindow = hWnd; - d3dpp->BackBufferCount = 2; - d3dpp->BackBufferFormat = !d3dpp->Windowed ? D3DFMT_X8R8G8B8 : D3DFMT_UNKNOWN; - - if (!d3dpp->Windowed) - { - d3dpp->BackBufferWidth = screen_width; - d3dpp->BackBufferHeight = screen_height; - } -} - -bool D3DVideo::init(const video_info_t *info) -{ - bool ret = true; - if (!g_pD3D) - ret = init_base(info); - else if (needs_restore) - { - D3DPRESENT_PARAMETERS d3dpp; - make_d3dpp(info, &d3dpp); - if (dev->Reset(&d3dpp) != D3D_OK) - { - HRESULT res = dev->TestCooperativeLevel(); - const char *err; - switch (res) - { - case D3DERR_DEVICELOST: - err = "DEVICELOST"; - break; - - case D3DERR_DEVICENOTRESET: - err = "DEVICENOTRESET"; - break; - - case D3DERR_DRIVERINTERNALERROR: - err = "DRIVERINTERNALERROR"; - break; - - default: - err = "Unknown"; - } - // Try to recreate the device completely ... - RARCH_WARN("[D3D]: Attempting to recover from dead state (%s).\n", err); - deinit(); - g_pD3D->Release(); - g_pD3D = NULL; - ret = init_base(info); - if (ret) - RARCH_LOG("[D3D]: Recovered from dead state.\n"); - else - return ret; - } - } - - if (!ret) - return ret; - - calculate_rect(screen_width, screen_height, info->force_aspect, g_extern.system.aspect_ratio); - -#ifdef HAVE_CG - if (!init_cg()) - { - RARCH_ERR("Failed to initialize Cg.\n"); - return false; - } -#endif - - if (!init_chain(info)) - { - RARCH_ERR("Failed to initialize render chain.\n"); - return false; - } - - if (!init_font()) - { - RARCH_ERR("Failed to initialize font.\n"); - return false; - } - - return true; -} - -void D3DVideo::set_viewport(int x, int y, unsigned width, unsigned height) -{ - D3DVIEWPORT viewport; - - // D3D9 doesn't support negative X/Y viewports ... - if (x < 0) - x = 0; - if (y < 0) - y = 0; - - viewport.X = x; - viewport.Y = y; - viewport.Width = width; - viewport.Height = height; - viewport.MinZ = 0.0f; - viewport.MaxZ = 1.0f; - - final_viewport = viewport; - - set_font_rect(NULL); -} - void D3DVideo::set_font_rect(font_params_t *params) { float pos_x = g_settings.video.msg_pos_x; @@ -332,447 +59,6 @@ void D3DVideo::set_font_rect(font_params_t *params) font_rect_shifted.bottom += 2; } -void D3DVideo::set_rotation(unsigned rot) -{ - rotation = rot; -} - -void D3DVideo::viewport_info(rarch_viewport &vp) -{ - vp.x = final_viewport.X; - vp.y = final_viewport.Y; - vp.width = final_viewport.Width; - vp.height = final_viewport.Height; - - vp.full_width = screen_width; - vp.full_height = screen_height; -} - -bool D3DVideo::read_viewport(uint8_t *buffer) -{ - RARCH_PERFORMANCE_INIT(d3d_read_viewport); - RARCH_PERFORMANCE_START(d3d_read_viewport); - bool ret = true; - LPDIRECT3DSURFACE target = NULL; - LPDIRECT3DSURFACE dest = NULL; - - if (FAILED(Callback::d3d_err = dev->GetRenderTarget(0, &target))) - { - ret = false; - goto end; - } - - if (FAILED(Callback::d3d_err = dev->CreateOffscreenPlainSurface(screen_width, screen_height, - D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, - &dest, NULL))) - { - ret = false; - goto end; - } - - if (FAILED(Callback::d3d_err = dev->GetRenderTargetData(target, dest))) - { - ret = false; - goto end; - } - - D3DLOCKED_RECT rect; - if (SUCCEEDED(dest->LockRect(&rect, NULL, D3DLOCK_READONLY))) - { - unsigned pitchpix = rect.Pitch / 4; - const uint32_t *pixels = (const uint32_t*)rect.pBits; - pixels += final_viewport.X; - pixels += (final_viewport.Height - 1) * pitchpix; - pixels -= final_viewport.Y * pitchpix; - - for (unsigned y = 0; y < final_viewport.Height; y++, pixels -= pitchpix) - { - for (unsigned x = 0; x < final_viewport.Width; x++) - { - *buffer++ = (pixels[x] >> 0) & 0xff; - *buffer++ = (pixels[x] >> 8) & 0xff; - *buffer++ = (pixels[x] >> 16) & 0xff; - } - } - - dest->UnlockRect(); - } - else - { - ret = false; - goto end; - } - -end: - RARCH_PERFORMANCE_STOP(d3d_read_viewport); - if (target) - target->Release(); - if (dest) - dest->Release(); - return ret; -} - -void D3DVideo::calculate_rect(unsigned width, unsigned height, - bool keep, float desired_aspect) -{ - if (g_settings.video.scale_integer) - { - struct rarch_viewport vp = {0}; - gfx_scale_integer(&vp, width, height, desired_aspect, keep); - set_viewport(vp.x, vp.y, vp.width, vp.height); - } - else if (!keep) - set_viewport(0, 0, width, height); - else - { - if (g_settings.video.aspect_ratio_idx == ASPECT_RATIO_CUSTOM) - { - const rarch_viewport_t &custom = g_extern.console.screen.viewports.custom_vp; - set_viewport(custom.x, custom.y, custom.width, custom.height); - } - else - { - float device_aspect = static_cast(width) / static_cast(height); - if (fabsf(device_aspect - desired_aspect) < 0.0001f) - set_viewport(0, 0, width, height); - else if (device_aspect > desired_aspect) - { - float delta = (desired_aspect / device_aspect - 1.0f) / 2.0f + 0.5f; - set_viewport(int(roundf(width * (0.5f - delta))), 0, unsigned(roundf(2.0f * width * delta)), height); - } - else - { - float delta = (device_aspect / desired_aspect - 1.0f) / 2.0f + 0.5f; - set_viewport(0, int(roundf(height * (0.5f - delta))), width, unsigned(roundf(2.0f * height * delta))); - } - } - } -} - -D3DVideo::D3DVideo() : - g_pD3D(NULL), dev(NULL), font(NULL), - rotation(0), needs_restore(false), -#ifdef HAVE_CG - cgCtx(NULL), -#endif -#ifdef HAVE_OVERLAY - overlays_enabled(false), -#endif - chain(NULL) -{} - -bool D3DVideo::construct(const video_info_t *info, const input_driver_t **input, - void **input_data) -{ - should_resize = false; - gfx_set_dwm(); - -#ifdef HAVE_MENU - memset(&rgui, 0, sizeof(rgui)); - rgui.tex_coords.x = 0; - rgui.tex_coords.y = 0; - rgui.tex_coords.w = 1; - rgui.tex_coords.h = 1; - rgui.vert_coords.x = 0; - rgui.vert_coords.y = 1; - rgui.vert_coords.w = 1; - rgui.vert_coords.h = -1; -#endif - - memset(&windowClass, 0, sizeof(windowClass)); - windowClass.cbSize = sizeof(windowClass); - windowClass.style = CS_HREDRAW | CS_VREDRAW; - windowClass.lpfnWndProc = Callback::WindowProc; - windowClass.hInstance = NULL; - windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); - windowClass.lpszClassName = "RetroArch"; - windowClass.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON)); - windowClass.hIconSm = (HICON)LoadImage(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0); - if (!info->fullscreen) - windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW; - - RegisterClassEx(&windowClass); - RECT mon_rect = monitor_rect(); - - bool windowed_full = g_settings.video.windowed_fullscreen; - - unsigned full_x = (windowed_full || info->width == 0) ? (mon_rect.right - mon_rect.left) : info->width; - unsigned full_y = (windowed_full || info->height == 0) ? (mon_rect.bottom - mon_rect.top) : info->height; - RARCH_LOG("[D3D]: Monitor size: %dx%d.\n", (int)(mon_rect.right - mon_rect.left), (int)(mon_rect.bottom - mon_rect.top)); - - screen_width = info->fullscreen ? full_x : info->width; - screen_height = info->fullscreen ? full_y : info->height; - - unsigned win_width = screen_width; - unsigned win_height = screen_height; - - if (!info->fullscreen) - { - RECT rect = {0}; - rect.right = screen_width; - rect.bottom = screen_height; - AdjustWindowRect(&rect, WS_OVERLAPPEDWINDOW, FALSE); - win_width = rect.right - rect.left; - win_height = rect.bottom - rect.top; - } - - char buffer[128]; - gfx_get_fps(buffer, sizeof(buffer), NULL, 0); - std::string title = buffer; - title += " || Direct3D9"; - - hWnd = CreateWindowEx(0, "RetroArch", title.c_str(), - info->fullscreen ? - (WS_EX_TOPMOST | WS_POPUP) : WS_OVERLAPPEDWINDOW, - info->fullscreen ? mon_rect.left : CW_USEDEFAULT, - info->fullscreen ? mon_rect.top : CW_USEDEFAULT, - win_width, win_height, - NULL, NULL, NULL, this); - - driver.display_type = RARCH_DISPLAY_WIN32; - driver.video_display = 0; - driver.video_window = (uintptr_t)hWnd; - - show_cursor(!info->fullscreen -#ifdef HAVE_OVERLAY - || overlays_enabled -#endif - ); - Callback::quit = false; - - ShowWindow(hWnd, SW_RESTORE); - UpdateWindow(hWnd); - SetForegroundWindow(hWnd); - SetFocus(hWnd); - - // This should only be done once here - // to avoid set_shader() to be overridden - // later. -#ifdef HAVE_CG - enum rarch_shader_type type = gfx_shader_parse_type(g_settings.video.shader_path, RARCH_SHADER_NONE); - if (g_settings.video.shader_enable && type == RARCH_SHADER_CG) - cg_shader = g_settings.video.shader_path; -#endif - - if (!process_shader()) - return false; - - video_info = *info; - if (!init(&video_info)) - return false; - - if (input && input_data) - { - Callback::dinput = input_dinput.init(); - *input = Callback::dinput ? &input_dinput : NULL; - *input_data = Callback::dinput; - } - - RARCH_LOG("[D3D]: Init complete.\n"); - return true; -} - -void D3DVideo::deinit(void) -{ - deinit_font(); - deinit_chain(); -#ifdef HAVE_CG - deinit_cg(); -#endif - - needs_restore = false; -} - -#ifdef HAVE_OVERLAY -void D3DVideo::free_overlays(void) -{ - for (unsigned i = 0; i < overlays.size(); i++) - free_overlay(overlays[i]); - overlays.clear(); -} -#endif - -void D3DVideo::free_overlay(overlay_t &overlay) -{ - if (overlay.tex) - overlay.tex->Release(); - if (overlay.vert_buf) - overlay.vert_buf->Release(); -} - -D3DVideo::~D3DVideo(void) -{ - deinit(); -#ifdef HAVE_OVERLAY - free_overlays(); -#endif -#ifdef HAVE_MENU - free_overlay(rgui); -#endif - if (dev) - dev->Release(); - if (g_pD3D) - g_pD3D->Release(); - - Monitor::last_hm = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST); - DestroyWindow(hWnd); - - UnregisterClass("RetroArch", GetModuleHandle(NULL)); -} - -bool D3DVideo::restore(void) -{ - deinit(); - needs_restore = !init(&video_info); - - if (needs_restore) - RARCH_ERR("[D3D]: Restore error.\n"); - - return !needs_restore; -} - -bool D3DVideo::frame(const void *frame, - unsigned width, unsigned height, unsigned pitch, - const char *msg) -{ - if (!frame) - return true; - - RARCH_PERFORMANCE_INIT(d3d_frame); - RARCH_PERFORMANCE_START(d3d_frame); - // We cannot recover in fullscreen. - if (needs_restore && IsIconic(hWnd)) - return true; - - if (needs_restore && !restore()) - { - RARCH_ERR("[D3D]: Failed to restore.\n"); - return false; - } - - if (should_resize) - { - calculate_rect(screen_width, screen_height, video_info.force_aspect, g_extern.system.aspect_ratio); - chain->set_final_viewport(final_viewport); - recompute_pass_sizes(); - - should_resize = false; - } - - // render_chain() only clears out viewport, clear out everything. - D3DVIEWPORT9 screen_vp; - screen_vp.X = 0; - screen_vp.Y = 0; - screen_vp.MinZ = 0; - screen_vp.MaxZ = 1; - screen_vp.Width = screen_width; - screen_vp.Height = screen_height; - dev->SetViewport(&screen_vp); - dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); - - // Insert black frame first, so we can screenshot, etc. - if (g_settings.video.black_frame_insertion) - { - if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) - { - RARCH_ERR("[D3D]: Present() failed.\n"); - needs_restore = true; - return true; - } - dev->Clear(0, 0, D3DCLEAR_TARGET, 0, 1, 0); - } - - if (!chain->render(frame, width, height, pitch, rotation)) - { - RARCH_ERR("[D3D]: Failed to render scene.\n"); - return false; - } - - render_msg(msg); - -#ifdef HAVE_MENU - if (rgui.enabled) - overlay_render(rgui); -#endif - -#ifdef HAVE_OVERLAY - if (overlays_enabled) - { - for (unsigned i = 0; i < overlays.size(); i++) - overlay_render(overlays[i]); - } -#endif - - RARCH_PERFORMANCE_STOP(d3d_frame); - - if (dev->Present(NULL, NULL, NULL, NULL) != D3D_OK) - { - needs_restore = true; - RARCH_ERR("[D3D]: Present() failed.\n"); - return true; - } - - update_title(); - return true; -} - -void D3DVideo::render_msg(const char *msg, font_params_t *params) -{ - if (params) - set_font_rect(params); - - if (msg && SUCCEEDED(dev->BeginScene())) - { - font->DrawTextA(NULL, - msg, - -1, - &font_rect_shifted, - DT_LEFT, - ((font_color >> 2) & 0x3f3f3f) | 0xff000000); - - font->DrawTextA(NULL, - msg, - -1, - &font_rect, - DT_LEFT, - font_color | 0xff000000); - - dev->EndScene(); - } - - if (params) - set_font_rect(NULL); -} - -void D3DVideo::set_nonblock_state(bool state) -{ - video_info.vsync = !state; - restore(); -} - -bool D3DVideo::alive(void) -{ - process(); - return !Callback::quit; -} - -bool D3DVideo::focus(void) const -{ - return GetFocus() == hWnd; -} - -void D3DVideo::process(void) -{ -#ifndef _XBOX - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) - { - TranslateMessage(&msg); - DispatchMessage(&msg); - } -#endif -} - #ifdef HAVE_CG bool D3DVideo::init_cg(void) { @@ -914,28 +200,6 @@ bool D3DVideo::init_multipass(void) return true; } -bool D3DVideo::set_shader(const std::string &path) -{ - auto old_shader = cg_shader; - bool restore_old = false; - cg_shader = path; - - if (!process_shader() || !restore()) - { - RARCH_ERR("[D3D9]: Setting shader failed.\n"); - restore_old = true; - } - - if (restore_old) - { - cg_shader = old_shader; - process_shader(); - restore(); - } - - return !restore_old; -} - bool D3DVideo::process_shader(void) { if (strcmp(path_get_extension(cg_shader.c_str()), "cgp") == 0) @@ -1091,279 +355,4 @@ void D3DVideo::update_title(void) msg_queue_push(g_extern.msg_queue, buffer_fps, 1, 1); g_extern.frame_count++; -} - -void D3DVideo::resize(unsigned new_width, unsigned new_height) -{ - if (!dev) - return; - - RARCH_LOG("[D3D]: Resize %ux%u.\n", new_width, new_height); - - if (new_width != video_info.width || new_height != video_info.height) - { - video_info.width = screen_width = new_width; - video_info.height = screen_height = new_height; - restore(); - } -} - -#ifdef HAVE_OVERLAY -bool D3DVideo::overlay_load(const texture_image *images, unsigned num_images) -{ - free_overlays(); - overlays.resize(num_images); - - for (unsigned i = 0; i < num_images; i++) - { - unsigned width = images[i].width; - unsigned height = images[i].height; - overlay_t &overlay = overlays[i]; - if (FAILED(dev->CreateTexture(width, height, 1, - 0, - D3DFMT_A8R8G8B8, - D3DPOOL_MANAGED, - &overlay.tex, NULL))) - { - RARCH_ERR("[D3D]: Failed to create overlay texture\n"); - return false; - } - - D3DLOCKED_RECT d3dlr; - if (SUCCEEDED(overlay.tex->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK))) - { - uint32_t *dst = static_cast(d3dlr.pBits); - const uint32_t *src = images[i].pixels; - unsigned pitch = d3dlr.Pitch >> 2; - for (unsigned y = 0; y < height; y++, dst += pitch, src += width) - memcpy(dst, src, width << 2); - overlay.tex->UnlockRect(0); - } - - overlay.tex_w = width; - overlay.tex_h = height; - overlay_tex_geom(i, 0, 0, 1, 1); // Default. Stretch to whole screen. - overlay_vertex_geom(i, 0, 0, 1, 1); - } - - return true; -} - -void D3DVideo::overlay_tex_geom(unsigned index, float x, float y, float w, float h) -{ - overlays[index].tex_coords.x = x; - overlays[index].tex_coords.y = y; - overlays[index].tex_coords.w = w; - overlays[index].tex_coords.h = h; -} - -void D3DVideo::overlay_vertex_geom(unsigned index, float x, float y, float w, float h) -{ - y = 1.0f - y; - h = -h; - overlays[index].vert_coords.x = x; - overlays[index].vert_coords.y = y; - overlays[index].vert_coords.w = w; - overlays[index].vert_coords.h = h; -} - -void D3DVideo::overlay_enable(bool state) -{ - for (unsigned i = 0; i < overlays.size(); i++) - overlays_enabled = state; - show_cursor(state); -} - -void D3DVideo::overlay_full_screen(bool enable) -{ - for (unsigned i = 0; i < overlays.size(); i++) - overlays[i].fullscreen = enable; -} - -void D3DVideo::overlay_set_alpha(unsigned index, float mod) -{ - overlays[index].alpha_mod = mod; -} -#endif - -void D3DVideo::overlay_render(overlay_t &overlay) -{ - if (!overlay.tex) - return; - - struct overlay_vertex - { - float x, y, z; - float u, v; - float r, g, b, a; - } vert[4]; - - if (!overlay.vert_buf) - { - dev->CreateVertexBuffer( - sizeof(vert), - dev->GetSoftwareVertexProcessing() ? D3DUSAGE_SOFTWAREPROCESSING : 0, - 0, - D3DPOOL_MANAGED, - &overlay.vert_buf, - NULL); - } - - for (unsigned i = 0; i < 4; i++) - { - vert[i].z = 0.5f; - vert[i].r = vert[i].g = vert[i].b = 1.0f; - vert[i].a = overlay.alpha_mod; - } - - float overlay_width = final_viewport.Width; - float overlay_height = final_viewport.Height; - - vert[0].x = overlay.vert_coords.x * overlay_width; - vert[1].x = (overlay.vert_coords.x + overlay.vert_coords.w) * overlay_width; - vert[2].x = overlay.vert_coords.x * overlay_width; - vert[3].x = (overlay.vert_coords.x + overlay.vert_coords.w) * overlay_width; - vert[0].y = overlay.vert_coords.y * overlay_height; - vert[1].y = overlay.vert_coords.y * overlay_height; - vert[2].y = (overlay.vert_coords.y + overlay.vert_coords.h) * overlay_height; - vert[3].y = (overlay.vert_coords.y + overlay.vert_coords.h) * overlay_height; - - vert[0].u = overlay.tex_coords.x; - vert[1].u = overlay.tex_coords.x + overlay.tex_coords.w; - vert[2].u = overlay.tex_coords.x; - vert[3].u = overlay.tex_coords.x + overlay.tex_coords.w; - vert[0].v = overlay.tex_coords.y; - vert[1].v = overlay.tex_coords.y; - vert[2].v = overlay.tex_coords.y + overlay.tex_coords.h; - vert[3].v = overlay.tex_coords.y + overlay.tex_coords.h; - - // Align texels and vertices. - for (unsigned i = 0; i < 4; i++) - { - vert[i].x -= 0.5f; - vert[i].y += 0.5f; - } - - void *verts; - overlay.vert_buf->Lock(0, sizeof(vert), &verts, 0); - memcpy(verts, vert, sizeof(vert)); - overlay.vert_buf->Unlock(); - - // enable alpha - dev->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE); - dev->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA); - dev->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA); - - // set vertex decl for overlay - D3DVERTEXELEMENT9 vElems[4] = { - {0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, - {0, 12, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0}, - {0, 20, D3DDECLTYPE_FLOAT4, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, - D3DDECL_END() - }; - LPDIRECT3DVERTEXDECLARATION vertex_decl; - dev->CreateVertexDeclaration(vElems, &vertex_decl); - dev->SetVertexDeclaration(vertex_decl); - vertex_decl->Release(); - - dev->SetStreamSource(0, overlay.vert_buf, 0, sizeof(overlay_vertex)); - - if (overlay.fullscreen) - { - // set viewport to full window - D3DVIEWPORT9 vp_full; - vp_full.X = 0; - vp_full.Y = 0; - vp_full.Width = screen_width; - vp_full.Height = screen_height; - vp_full.MinZ = 0.0f; - vp_full.MaxZ = 1.0f; - dev->SetViewport(&vp_full); - } - - // render overlay - dev->SetTexture(0, overlay.tex); - dev->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_BORDER); - dev->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_BORDER); - dev->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - dev->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - if (SUCCEEDED(dev->BeginScene())) - { - dev->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - dev->EndScene(); - } - - // restore previous state - dev->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); - dev->SetViewport(&final_viewport); -} - -#ifdef HAVE_MENU -void D3DVideo::set_rgui_texture_frame(const void *frame, - bool rgb32, unsigned width, unsigned height, - float alpha) -{ - if (!rgui.tex || rgui.tex_w != width || rgui.tex_h != height) - { - if (rgui.tex) - rgui.tex->Release(); - if (FAILED(dev->CreateTexture(width, height, 1, - 0, D3DFMT_A8R8G8B8, - D3DPOOL_MANAGED, - &rgui.tex, NULL))) - { - RARCH_ERR("[D3D]: Failed to create rgui texture\n"); - return; - } - rgui.tex_w = width; - rgui.tex_h = height; - } - - rgui.alpha_mod = alpha; - - - D3DLOCKED_RECT d3dlr; - if (SUCCEEDED(rgui.tex->LockRect(0, &d3dlr, NULL, D3DLOCK_NOSYSLOCK))) - { - if (rgb32) - { - uint8_t *dst = (uint8_t*)d3dlr.pBits; - const uint32_t *src = (const uint32_t*)frame; - for (unsigned h = 0; h < height; h++, dst += d3dlr.Pitch, src += width) - { - memcpy(dst, src, width * sizeof(uint32_t)); - memset(dst + width * sizeof(uint32_t), 0, d3dlr.Pitch - width * sizeof(uint32_t)); - } - } - else - { - uint32_t *dst = (uint32_t*)d3dlr.pBits; - const uint16_t *src = (const uint16_t*)frame; - for (unsigned h = 0; h < height; h++, dst += d3dlr.Pitch >> 2, src += width) - { - for (unsigned w = 0; w < width; w++) - { - uint16_t c = src[w]; - uint32_t r = (c >> 12) & 0xf; - uint32_t g = (c >> 8) & 0xf; - uint32_t b = (c >> 4) & 0xf; - uint32_t a = (c >> 0) & 0xf; - r = ((r << 4) | r) << 16; - g = ((g << 4) | g) << 8; - b = ((b << 4) | b) << 0; - a = ((a << 4) | a) << 24; - dst[w] = r | g | b | a; - } - } - } - - rgui.tex->UnlockRect(0); - } -} - -void D3DVideo::set_rgui_texture_enable(bool state, bool fullscreen) -{ - rgui.enabled = state; - rgui.fullscreen = fullscreen; -} -#endif +} \ No newline at end of file