From 7ea15090d9ecc3bd977f98914b8b90af1ad4b998 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= Date: Thu, 19 Jul 2018 04:39:27 +0200 Subject: [PATCH 1/2] Added a Direct2D 1.0 renderer. --- src/Makefile.local | 3 +- src/win/86Box.rc | 3 +- src/win/Makefile.mingw | 6 +- src/win/resource.h | 17 +- src/win/win.c | 23 +- src/win/win.h | 6 +- src/win/win_d2d.cpp | 485 +++++++++++++++++++++++++++++++++++++++++ src/win/win_d2d.h | 35 +++ src/win/win_ui.c | 5 +- 9 files changed, 559 insertions(+), 24 deletions(-) create mode 100644 src/win/win_d2d.cpp create mode 100644 src/win/win_d2d.h diff --git a/src/Makefile.local b/src/Makefile.local index a93152007..6ac2b5f97 100644 --- a/src/Makefile.local +++ b/src/Makefile.local @@ -10,7 +10,7 @@ # settings, so we can avoid changing the main one for all of # our local setups. # -# Version: @(#)Makefile.local 1.0.14 2018/05/26 +# Version: @(#)Makefile.local 1.0.15 2018/07/19 # # Author: Fred N. van Kempen, # @@ -126,6 +126,7 @@ STUFF := # -DENABLE_VOODOO_LOG=N sets logging level at N. # -DENABLE_VRAM_DUMP=N sets logging level at N. # win/ logging: +# -DENABLE_D2D_LOG=N sets logging level at N. # -DENABLE_DDRAW_LOG=N sets logging level at N. # -DENABLE_DYNLD_LOG=N sets logging level at N. # -DENABLE_JOYSTICK_LOG=N sets logging level at N. diff --git a/src/win/86Box.rc b/src/win/86Box.rc index 4312bb3d8..119257941 100644 --- a/src/win/86Box.rc +++ b/src/win/86Box.rc @@ -8,7 +8,7 @@ * * Application resource script for Windows. * - * Version: @(#)86Box.rc 1.0.37 2018/05/25 + * Version: @(#)86Box.rc 1.0.38 2018/07/19 * * Authors: Miran Grca, * Fred N. van Kempen, @@ -65,6 +65,7 @@ BEGIN POPUP "Re&nderer" BEGIN MENUITEM "&DirectDraw", IDM_VID_DDRAW + MENUITEM "Direct&2D 1.0", IDM_VID_D2D MENUITEM "Direct&3D 9", IDM_VID_D3D MENUITEM "&SDL", IDM_VID_SDL #ifdef USE_VNC diff --git a/src/win/Makefile.mingw b/src/win/Makefile.mingw index 14080b8cb..b1e3dc761 100644 --- a/src/win/Makefile.mingw +++ b/src/win/Makefile.mingw @@ -8,7 +8,7 @@ # # Makefile for Win32 (MinGW32) environment. # -# Version: @(#)Makefile.mingw 1.0.119 2018/07/17 +# Version: @(#)Makefile.mingw 1.0.120 2018/07/19 # # Authors: Miran Grca, # Fred N. van Kempen, @@ -291,7 +291,7 @@ ifneq ($(WX), n) UIOBJ := wx_main.o wx_ui.o wx_stbar.o wx_render.o else UIOBJ := win_ui.o win_stbar.o \ - win_ddraw.o win_d3d.o win_sdl.o \ + win_ddraw.o win_d2d.o win_d3d.o win_sdl.o \ win_dialog.o win_about.o \ win_settings.o win_devconf.o win_snd_gain.o \ win_new_floppy.o win_jsconf.o @@ -559,7 +559,7 @@ endif LIBS := -mwindows \ -lopenal.dll \ - -lddraw -ldinput8 -ldxguid -ld3d9 -ld3dx9 \ + -lddraw -ldinput8 -ldxguid -ld3d9 -ld3dx9 -ld2d1\ -lcomctl32 -lwinmm ifeq ($(VNC), y) LIBS += $(VNCLIB) -lws2_32 diff --git a/src/win/resource.h b/src/win/resource.h index 9579a055e..cbe7a5505 100644 --- a/src/win/resource.h +++ b/src/win/resource.h @@ -8,7 +8,7 @@ * * Windows resource defines. * - * Version: @(#)resource.h 1.0.25 2018/05/25 + * Version: @(#)resource.h 1.0.26 2018/07/19 * * Authors: Sarah Walker, * Miran Grca, @@ -242,13 +242,14 @@ #define IDM_VID_RESIZE 40040 #define IDM_VID_REMEMBER 40041 #define IDM_VID_DDRAW 40050 -#define IDM_VID_D3D 40051 -#define IDM_VID_SDL 40052 -#define IDM_VID_VNC 40053 -#define IDM_VID_SCALE_1X 40054 -#define IDM_VID_SCALE_2X 40055 -#define IDM_VID_SCALE_3X 40056 -#define IDM_VID_SCALE_4X 40057 +#define IDM_VID_D2D 40051 +#define IDM_VID_D3D 40052 +#define IDM_VID_SDL 40053 +#define IDM_VID_VNC 40054 +#define IDM_VID_SCALE_1X 40055 +#define IDM_VID_SCALE_2X 40056 +#define IDM_VID_SCALE_3X 40057 +#define IDM_VID_SCALE_4X 40058 #define IDM_VID_FULLSCREEN 40060 #define IDM_VID_FS_FULL 40061 #define IDM_VID_FS_43 40062 diff --git a/src/win/win.c b/src/win/win.c index 90aa29191..68ac81093 100644 --- a/src/win/win.c +++ b/src/win/win.c @@ -8,7 +8,7 @@ * * Platform main support module for Windows. * - * Version: @(#)win.c 1.0.50 2018/07/16 + * Version: @(#)win.c 1.0.51 2018/07/19 * * Authors: Sarah Walker, * Miran Grca, @@ -42,6 +42,7 @@ # include "../vnc.h" #endif # include "win_ddraw.h" +# include "win_d2d.h" # include "win_d3d.h" # include "win_sdl.h" #include "win.h" @@ -84,6 +85,7 @@ static struct { } vid_apis[2][RENDERERS_NUM] = { { { "DDraw", 1, (int(*)(void*))ddraw_init, ddraw_close, NULL, ddraw_pause }, + { "D2D", 1, (int(*)(void*))d2d_init, d2d_close, NULL, d2d_pause }, { "D3D", 1, (int(*)(void*))d3d_init, d3d_close, d3d_resize, d3d_pause }, { "SDL", 1, (int(*)(void*))sdl_init, sdl_close, NULL, sdl_pause } #ifdef USE_VNC @@ -92,6 +94,7 @@ static struct { }, { { "DDraw", 1, (int(*)(void*))ddraw_init_fs, ddraw_close, NULL, ddraw_pause }, + { "D2D", 1, (int(*)(void*))d2d_init_fs, d2d_close, NULL, d2d_pause }, { "D3D", 1, (int(*)(void*))d3d_init_fs, d3d_close, NULL, d3d_pause }, { "SDL", 1, (int(*)(void*))sdl_init_fs, sdl_close, sdl_resize, sdl_pause } #ifdef USE_VNC @@ -606,15 +609,19 @@ plat_vidapi_name(int api) break; case 1: - name = "d3d"; + name = "d2d"; break; case 2: + name = "d3d"; + break; + + case 3: name = "sdl"; break; #ifdef USE_VNC - case 3: + case 4: name = "vnc"; break; @@ -747,16 +754,20 @@ take_screenshot(void) ddraw_take_screenshot(path); break; - case 1: /* d3d9 */ + case 1: /* d2d */ + d2d_take_screenshot(path); + break; + + case 2: /* d3d9 */ d3d_take_screenshot(path); break; - case 2: /* sdl */ + case 3: /* sdl */ sdl_take_screenshot(path); break; #ifdef USE_VNC - case 3: /* vnc */ + case 4: /* vnc */ vnc_take_screenshot(path); break; #endif diff --git a/src/win/win.h b/src/win/win.h index eb62e8aea..098cda60a 100644 --- a/src/win/win.h +++ b/src/win/win.h @@ -8,7 +8,7 @@ * * Platform support defintions for Win32. * - * Version: @(#)win.h 1.0.18 2018/05/26 + * Version: @(#)win.h 1.0.19 2018/07/19 * * Authors: Sarah Walker, * Miran Grca, @@ -52,9 +52,9 @@ #define WM_SENDHWND 0x8891 #ifdef USE_VNC -#define RENDERERS_NUM 4 +#define RENDERERS_NUM 5 #else -#define RENDERERS_NUM 3 +#define RENDERERS_NUM 4 #endif diff --git a/src/win/win_d2d.cpp b/src/win/win_d2d.cpp new file mode 100644 index 000000000..393195abc --- /dev/null +++ b/src/win/win_d2d.cpp @@ -0,0 +1,485 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Rendering module for Microsoft Direct2D. + * + * Version: @(#)win_d2d.cpp 1.0.0 2018/07/19 + * + * Authors: David Hrdlička, + * + * Copyright 2018 David Hrdlička. + */ +#include +#include +#include +#include +#define UNICODE +#define BITMAP WINDOWS_BITMAP +#include +#include +#include +#undef BITMAP + +#define PNG_DEBUG 0 +#include + +#define HAVE_STDARG_H +#include "../86box.h" +#include "../device.h" +#include "../video/video.h" +#include "../plat.h" +#include "../ui.h" +#include "win.h" +#include "win_d2d.h" + + +static HWND d2d_hwnd; +static ID2D1Factory *d2d_factory; +static ID2D1HwndRenderTarget *d2d_hwndRT; +static ID2D1BitmapRenderTarget *d2d_btmpRT; +static ID2D1Bitmap *d2d_bitmap; +static int d2d_width, d2d_height, d2d_screen_width, d2d_screen_height, d2d_fs; + + +#ifdef ENABLE_D2D_LOG +int d2d_do_log = ENABLE_D2D_LOG; +#endif + + +static void +d2d_log(const char *fmt, ...) +{ +#ifdef ENABLE_D2D_LOG + va_list ap; + + if (d2d_do_log) { + va_start(ap, fmt); + pclog_ex(fmt, ap); + va_end(ap); + } +#endif +} + + +static void +d2d_stretch(float *w, float *h, float *x, float *y) +{ + double dw, dh, dx, dy, temp, temp2, ratio_w, ratio_h, gsr, hsr; + + switch (video_fullscreen_scale) + { + case FULLSCR_SCALE_FULL: + *w = d2d_screen_width; + *h = d2d_screen_height; + *x = 0; + *y = 0; + break; + + case FULLSCR_SCALE_43: + dw = (double) d2d_screen_width; + dh = (double) d2d_screen_height; + temp = (dh / 3.0) * 4.0; + dx = (dw - temp) / 2.0; + dw = temp; + *w = (float) dw; + *h = (float) dh; + *x = (float) dx; + *y = 0; + break; + + case FULLSCR_SCALE_SQ: + dw = (double) d2d_screen_width; + dh = (double) d2d_screen_height; + temp = ((double) *w); + temp2 = ((double) *h); + dx = (dw / 2.0) - ((dh * temp) / (temp2 * 2.0)); + dy = 0.0; + if (dx < 0.0) + { + dx = 0.0; + dy = (dw / 2.0) - ((dh * temp2) / (temp * 2.0)); + } + dw -= (dx * 2.0); + dh -= (dy * 2.0); + *w = (float) dw; + *h = (float) dh; + *x = (float) dx; + *y = (float) dy; + break; + + case FULLSCR_SCALE_INT: + dw = (double) d2d_screen_width; + dh = (double) d2d_screen_height; + temp = ((double) *w); + temp2 = ((double) *h); + ratio_w = dw / ((double) *w); + ratio_h = dh / ((double) *h); + if (ratio_h < ratio_w) + { + ratio_w = ratio_h; + } + dx = (dw / 2.0) - ((temp * ratio_w) / 2.0); + dy = (dh / 2.0) - ((temp2 * ratio_h) / 2.0); + dw -= (dx * 2.0); + dh -= (dy * 2.0); + *w = (float) dw; + *h = (float) dh; + *x = (float) dx; + *y = (float) dy; + break; + + case FULLSCR_SCALE_KEEPRATIO: + dw = (double) d2d_screen_width; + dh = (double) d2d_screen_height; + hsr = dw / dh; + gsr = ((double) *w) / ((double) *h); + if (gsr <= hsr) + { + temp = dh * gsr; + dx = (dw - temp) / 2.0; + dw = temp; + *w = (float) dw; + *h = (float) dh; + *x = (float) dx; + *y = 0; + } + else + { + temp = dw / gsr; + dy = (dh - temp) / 2.0; + dh = temp; + *w = (float) dw; + *h = (float) dh; + *x = 0; + *y = (float) dy; + } + break; + } +} + + +static void +d2d_blit(int x, int y, int y1, int y2, int w, int h) +{ + HRESULT hr = S_OK; + + void *srcdata; + int yy; + D2D1_RECT_U rectU; + + ID2D1Bitmap *fs_bitmap; + ID2D1RenderTarget *RT; + + float fs_x, fs_y; + float fs_w = w; + float fs_h = h; + + d2d_log("Direct2D: d2d_blit(x=%d, y=%d, y1=%d, y2=%d, w=%d, h=%d)\n", x, y, y1, y2, w, h); + + // TODO: Detect double scanned mode and resize render target + // appropriately for more clear picture + + if (w != d2d_width || h != d2d_height) + { + if (d2d_fs) + { + if (d2d_btmpRT) + { + d2d_btmpRT->Release(); + d2d_btmpRT = NULL; + } + + hr = d2d_hwndRT->CreateCompatibleRenderTarget( + D2D1::SizeF(w, h), + &d2d_btmpRT); + + if (SUCCEEDED(hr)) + { + d2d_width = w; + d2d_height = h; + } + } + else + { + hr = d2d_hwndRT->Resize(D2D1::SizeU(w, h)); + + if (SUCCEEDED(hr)) + { + d2d_width = w; + d2d_height = h; + } + } + } + + if (y1 == y2) { + video_blit_complete(); + return; + } + + if (buffer32 == NULL) { + video_blit_complete(); + return; + } + + // TODO: Copy data directly from buffer32 to d2d_bitmap + + srcdata = malloc(h * w * 4); + + for (yy = y1; yy < y2; yy++) + { + if ((y + yy) >= 0 && (y + yy) < buffer32->h) + { + if (video_grayscale || invert_display) + video_transform_copy( + (uint32_t *) &(((uint8_t *)srcdata)[yy * w * 4]), + &(((uint32_t *)buffer32->line[y + yy])[x]), + w); + else + memcpy( + (uint32_t *) &(((uint8_t *)srcdata)[yy * w * 4]), + &(((uint32_t *)buffer32->line[y + yy])[x]), + w * 4); + } + } + + video_blit_complete(); + + rectU = D2D1::RectU(0, 0, w, h); + hr = d2d_bitmap->CopyFromMemory(&rectU, srcdata, w * 4); + + // In fullscreen mode we first draw offscreen to an intermediate + // BitmapRenderTarget, which then gets rendered to the actual + // HwndRenderTarget in order to implement different scaling modes + + // In windowed mode we draw directly to the HwndRenderTarget + + if (SUCCEEDED(hr)) + { + RT = d2d_fs ? (ID2D1RenderTarget *) d2d_btmpRT : (ID2D1RenderTarget *) d2d_hwndRT; + + RT->BeginDraw(); + + RT->DrawBitmap( + d2d_bitmap, + D2D1::RectF(0, y1, w, y2), + 1.0f, + D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR, + D2D1::RectF(0, y1, w, y2)); + + hr = RT->EndDraw(); + } + + if (d2d_fs) + { + if (SUCCEEDED(hr)) + { + hr = d2d_btmpRT->GetBitmap(&fs_bitmap); + } + + if (SUCCEEDED(hr)) + { + d2d_stretch(&fs_w, &fs_h, &fs_x, &fs_y); + + d2d_hwndRT->BeginDraw(); + + d2d_hwndRT->Clear( + D2D1::ColorF(D2D1::ColorF::Black)); + + d2d_hwndRT->DrawBitmap( + fs_bitmap, + D2D1::RectF(fs_x, fs_y, fs_x + fs_w, fs_y + fs_h), + 1.0f, + D2D1_BITMAP_INTERPOLATION_MODE_LINEAR, + D2D1::RectF(0, 0, w, h)); + + hr = d2d_hwndRT->EndDraw(); + } + } + + if (FAILED(hr)) + { + d2d_log("Direct2D: d2d_blit: error 0x%08lx\n", hr); + } + + // Tidy up + free(srcdata); + srcdata = NULL; +} + + +void +d2d_close(void) +{ + d2d_log("Direct2D: d2d_close()\n"); + + if (d2d_bitmap) + { + d2d_bitmap->Release(); + d2d_bitmap = NULL; + } + + if (d2d_btmpRT) + { + d2d_btmpRT->Release(); + d2d_btmpRT = NULL; + } + + if (d2d_hwndRT) + { + d2d_hwndRT->Release(); + d2d_hwndRT = NULL; + } + + if (d2d_factory) + { + d2d_factory->Release(); + d2d_factory = NULL; + } + + if (d2d_hwnd) + { + plat_set_input(hwndMain); + DestroyWindow(d2d_hwnd); + d2d_hwnd = NULL; + } +} + + +static int +d2d_init_common(int fs) +{ + HRESULT hr = S_OK; + WCHAR title[200]; + D2D1_HWND_RENDER_TARGET_PROPERTIES props; + + d2d_log("Direct2D: d2d_init_common(fs=%d)\n", fs); + + cgapal_rebuild(); + + if (fs) + { + d2d_screen_width = GetSystemMetrics(SM_CXSCREEN); + d2d_screen_height = GetSystemMetrics(SM_CYSCREEN); + + // Direct2D seems to lack any proper fullscreen mode, + // therefore we just create a full screen window + // and pass its handle to a HwndRenderTarget + + mbstowcs(title, emu_version, sizeof_w(title)); + + d2d_hwnd = CreateWindow( + SUB_CLASS_NAME, + title, + WS_POPUP, + 0, 0, d2d_screen_width, d2d_screen_height, + HWND_DESKTOP, + NULL, + hinstance, + NULL); + + plat_set_input(d2d_hwnd); + + SetFocus(d2d_hwnd); + SetWindowPos(d2d_hwnd, HWND_TOPMOST, 0, 0, d2d_screen_width, d2d_screen_height, SWP_SHOWWINDOW); + } + + if (SUCCEEDED(hr)) + { + hr = D2D1CreateFactory(D2D1_FACTORY_TYPE_MULTI_THREADED, &d2d_factory); + } + + if (fs) + { + props = D2D1::HwndRenderTargetProperties(d2d_hwnd, + D2D1::SizeU(d2d_screen_width, d2d_screen_height)); + } + else + { + // HwndRenderTarget will get resized appropriately by d2d_blit, + // so it's fine to let D2D imply size of 0x0 for now + props = D2D1::HwndRenderTargetProperties(hwndRender); + } + + if (SUCCEEDED(hr)) + { + hr = d2d_factory->CreateHwndRenderTarget( + D2D1::RenderTargetProperties(), + props, + &d2d_hwndRT); + } + + if (SUCCEEDED(hr)) + { + // Create a bitmap for storing intermediate data + hr = d2d_hwndRT->CreateBitmap( + D2D1::SizeU(2048, 2048), + D2D1::BitmapProperties(D2D1::PixelFormat(DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_IGNORE)), + &d2d_bitmap); + } + + if (SUCCEEDED(hr)) + { + d2d_fs = fs; + + d2d_width = 0; + d2d_height = 0; + + // Make sure we get a clean exit. + atexit(d2d_close); + + // Register our renderer! + video_setblit(d2d_blit); + } + + if (FAILED(hr)) + { + d2d_log("Direct2D: d2d_init_common: error 0x%08lx\n", hr); + d2d_close(); + return(0); + } + + return(1); +} + + +int +d2d_init(HWND h) +{ + d2d_log("Direct2D: d2d_init(h=0x%08lx)\n", h); + return d2d_init_common(0); +} + + +int +d2d_init_fs(HWND h) +{ + d2d_log("Direct2D: d2d_init_fs(h=0x%08lx)\n", h); + return d2d_init_common(1); +} + + +int +d2d_pause(void) +{ + // Not implemented in any renderer. The heck is this even for? + + d2d_log("Direct2D: d2d_pause()\n"); + return(0); +} + + +void +d2d_take_screenshot(wchar_t *fn) +{ + // Saving a screenshot of a Direct2D render target is harder than + // one would think. Keeping this stubbed for the moment + // -ryu + + d2d_log("Direct2D: d2d_take_screenshot(%s)\n", fn); + return; +} \ No newline at end of file diff --git a/src/win/win_d2d.h b/src/win/win_d2d.h new file mode 100644 index 000000000..00bfe0887 --- /dev/null +++ b/src/win/win_d2d.h @@ -0,0 +1,35 @@ +/* + * 86Box A hypervisor and IBM PC system emulator that specializes in + * running old operating systems and software designed for IBM + * PC systems and compatibles from 1981 through fairly recent + * system designs based on the PCI bus. + * + * This file is part of the 86Box distribution. + * + * Definitions for the Direct2D rendering module. + * + * Version: @(#)win_d2d.h 1.0.0 2018/07/19 + * + * Authors: David Hrdlička, + * + * Copyright 2018 David Hrdlička. + */ +#ifndef WIN_D2D_H +# define WIN_D2D_H + +#ifdef __cplusplus +extern "C" { +#endif + +extern void d2d_close(void); +extern int d2d_init(HWND h); +extern int d2d_init_fs(HWND h); +extern int d2d_pause(void); +extern void d2d_take_screenshot(wchar_t *fn); + +#ifdef __cplusplus +} +#endif + + +#endif /*WIN_D2D_H*/ \ No newline at end of file diff --git a/src/win/win_ui.c b/src/win/win_ui.c index 786e54986..735baa0a5 100644 --- a/src/win/win_ui.c +++ b/src/win/win_ui.c @@ -8,7 +8,7 @@ * * user Interface module for WinAPI on Windows. * - * Version: @(#)win_ui.c 1.0.28 2018/05/25 + * Version: @(#)win_ui.c 1.0.29 2018/07/19 * * Authors: Sarah Walker, * Miran Grca, @@ -378,6 +378,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) break; case IDM_VID_DDRAW: + case IDM_VID_D2D: case IDM_VID_D3D: case IDM_VID_SDL: #ifdef USE_VNC @@ -596,7 +597,7 @@ MainWindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) pc_onesec(); } break; - + case WM_RESETD3D: startblit(); if (video_fullscreen) From 2125b3a5a6851fd32324fc2832f3f5df459d6700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Hrdli=C4=8Dka?= Date: Thu, 19 Jul 2018 14:52:40 +0200 Subject: [PATCH 2/2] win_d2d: fix mouse --- src/win/win_d2d.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/win/win_d2d.cpp b/src/win/win_d2d.cpp index 393195abc..548a34f64 100644 --- a/src/win/win_d2d.cpp +++ b/src/win/win_d2d.cpp @@ -38,7 +38,7 @@ #include "win_d2d.h" -static HWND d2d_hwnd; +static HWND d2d_hwnd, old_hwndMain; static ID2D1Factory *d2d_factory; static ID2D1HwndRenderTarget *d2d_hwndRT; static ID2D1BitmapRenderTarget *d2d_btmpRT; @@ -343,9 +343,11 @@ d2d_close(void) if (d2d_hwnd) { + hwndMain = old_hwndMain; plat_set_input(hwndMain); DestroyWindow(d2d_hwnd); d2d_hwnd = NULL; + old_hwndMain = NULL; } } @@ -382,6 +384,9 @@ d2d_init_common(int fs) hinstance, NULL); + old_hwndMain = hwndMain; + hwndMain = d2d_hwnd; + plat_set_input(d2d_hwnd); SetFocus(d2d_hwnd);