diff options
author | 2007-12-17 15:19:59 +0000 | |
---|---|---|
committer | 2007-12-17 15:19:59 +0000 | |
commit | 7b77f1218624ea26dbb2efd85a19f795f5d4e02e (patch) | |
tree | 19209304095572b4fd61c2a2d6a5aa75c4e471ad /src/osd/windows/drawdd.c | |
parent | 3da7f476068b3ffef713218ba2fc1bd5030f2c38 (diff) |
Initial checkin of MAME 0.121.mame0121
Diffstat (limited to 'src/osd/windows/drawdd.c')
-rw-r--r-- | src/osd/windows/drawdd.c | 1435 |
1 files changed, 1435 insertions, 0 deletions
diff --git a/src/osd/windows/drawdd.c b/src/osd/windows/drawdd.c new file mode 100644 index 00000000000..19bbaaee24b --- /dev/null +++ b/src/osd/windows/drawdd.c @@ -0,0 +1,1435 @@ +//============================================================ +// +// drawdd.c - Win32 DirectDraw implementation +// +// Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. +// Visit http://mamedev.org for licensing and usage restrictions. +// +//============================================================ + +// needed for multimonitor +#define _WIN32_WINNT 0x501 + +// standard windows headers +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <mmsystem.h> +#include <ddraw.h> + +// standard C headers +#include <math.h> + +// MAME headers +#include "render.h" +#include "rendutil.h" +#include "options.h" + +// MAMEOS headers +#include "winmain.h" +#include "window.h" +#include "config.h" + + + +//============================================================ +// DEBUGGING +//============================================================ + + + +//============================================================ +// CONSTANTS +//============================================================ + + + +//============================================================ +// MACROS +//============================================================ + + + +//============================================================ +// TYPE DEFINITIONS +//============================================================ + +typedef HRESULT (WINAPI *directdrawcreateex_ptr)(GUID FAR *lpGuid, LPVOID *lplpDD, REFIID iid, IUnknown FAR *pUnkOuter); +typedef HRESULT (WINAPI *directdrawenumerateex_ptr)(LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags); + + +/* dd_info is the information about DirectDraw for the current screen */ +typedef struct _dd_info dd_info; +struct _dd_info +{ + GUID adapter; // current display adapter + GUID * adapter_ptr; // pointer to current display adapter + int width, height; // current width, height + int refresh; // current refresh rate + int clearouter; // clear the outer areas? + + INT32 blitwidth, blitheight; // current blit width/height values + RECT lastdest; // last destination rectangle + + IDirectDraw7 * ddraw; // pointer to the DirectDraw object + IDirectDrawSurface7 * primary; // pointer to the primary surface object + IDirectDrawSurface7 * back; // pointer to the back buffer surface object + IDirectDrawSurface7 * blit; // pointer to the blit surface object + IDirectDrawClipper * clipper; // pointer to the clipper object + IDirectDrawGammaControl *gamma; // pointer to the gamma control object + + DDSURFACEDESC2 primarydesc; // description of the primary surface + DDSURFACEDESC2 blitdesc; // description of the blitting surface + DDSURFACEDESC2 origmode; // original video mode + + DDCAPS ddcaps; // capabilities of the device + DDCAPS helcaps; // capabilities of the hardware + + void * membuffer; // memory buffer for complex rendering + UINT32 membuffersize; // current size of the memory buffer +}; + + +/* monitor_enum_info holds information during a monitor enumeration */ +typedef struct _monitor_enum_info monitor_enum_info; +struct _monitor_enum_info +{ + win_monitor_info * monitor; // pointer to monitor we want + GUID guid; // GUID of the one we found + GUID * guid_ptr; // pointer to our GUID + int foundit; // TRUE if we found what we wanted +}; + + +/* mode_enum_info holds information during a display mode enumeration */ +typedef struct _mode_enum_info mode_enum_info; +struct _mode_enum_info +{ + win_window_info * window; + INT32 minimum_width, minimum_height; + INT32 target_width, target_height; + double target_refresh; + float best_score; +}; + + + +//============================================================ +// GLOBALS +//============================================================ + +static HINSTANCE dllhandle; +static directdrawcreateex_ptr directdrawcreateex; +static directdrawenumerateex_ptr directdrawenumerateex; + + + +//============================================================ +// INLINES +//============================================================ + +INLINE void update_outer_rects(dd_info *dd) +{ + dd->clearouter = (dd->back != NULL) ? 3 : 1; +} + + +INLINE int better_mode(int width0, int height0, int width1, int height1, float desired_aspect) +{ + float aspect0 = (float)width0 / (float)height0; + float aspect1 = (float)width1 / (float)height1; + return (fabs(desired_aspect - aspect0) < fabs(desired_aspect - aspect1)) ? 0 : 1; +} + + + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions +static void drawdd_exit(void); +static int drawdd_window_init(win_window_info *window); +static void drawdd_window_destroy(win_window_info *window); +static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window); +static int drawdd_window_draw(win_window_info *window, HDC dc, int update); + +// surface management +static int ddraw_create(win_window_info *window); +static int ddraw_create_surfaces(win_window_info *window); +static void ddraw_delete(win_window_info *window); +static void ddraw_delete_surfaces(win_window_info *window); +static int ddraw_verify_caps(dd_info *dd); +static int ddraw_test_cooperative(win_window_info *window); +static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type); +static int create_clipper(win_window_info *window); + +// drawing helpers +static void compute_blit_surface_size(win_window_info *window); +static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight); + +// video modes +static int config_adapter_mode(win_window_info *window); +static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor); +static void pick_best_mode(win_window_info *window); + +// rendering +static void drawdd_rgb888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_bgr888_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb565_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb555_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_bgr888_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb565_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); +static void drawdd_rgb555_nr_draw_primitives(const render_primitive *primlist, void *dstdata, UINT32 width, UINT32 height, UINT32 pitch); + + + +//============================================================ +// drawdd_init +//============================================================ + +int drawdd_init(win_draw_callbacks *callbacks) +{ + // dynamically grab the create function from ddraw.dll + dllhandle = LoadLibrary(TEXT("ddraw.dll")); + if (dllhandle == NULL) + { + mame_printf_verbose("DirectDraw: Unable to access ddraw.dll\n"); + return 1; + } + + // import the create function + directdrawcreateex = (directdrawcreateex_ptr)GetProcAddress(dllhandle, "DirectDrawCreateEx"); + if (directdrawcreateex == NULL) + { + mame_printf_verbose("DirectDraw: Unable to find DirectDrawCreateEx\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return 1; + } + + // import the enumerate function + directdrawenumerateex = (directdrawenumerateex_ptr)GetProcAddress(dllhandle, "DirectDrawEnumerateExA"); + if (directdrawenumerateex == NULL) + { + mame_printf_verbose("DirectDraw: Unable to find DirectDrawEnumerateExA\n"); + FreeLibrary(dllhandle); + dllhandle = NULL; + return 1; + } + + // fill in the callbacks + callbacks->exit = drawdd_exit; + callbacks->window_init = drawdd_window_init; + callbacks->window_get_primitives = drawdd_window_get_primitives; + callbacks->window_draw = drawdd_window_draw; + callbacks->window_destroy = drawdd_window_destroy; + + mame_printf_verbose("DirectDraw: Using DirectDraw 7\n"); + return 0; +} + + + +//============================================================ +// drawdd_exit +//============================================================ + +static void drawdd_exit(void) +{ + if (dllhandle != NULL) + FreeLibrary(dllhandle); +} + + + +//============================================================ +// drawdd_window_init +//============================================================ + +static int drawdd_window_init(win_window_info *window) +{ + dd_info *dd; + + // allocate memory for our structures + dd = malloc_or_die(sizeof(*dd)); + memset(dd, 0, sizeof(*dd)); + window->drawdata = dd; + + // configure the adapter for the mode we want + if (config_adapter_mode(window)) + goto error; + + // create the ddraw object + if (ddraw_create(window)) + goto error; + + return 0; + +error: + drawdd_window_destroy(window); + mame_printf_error("Unable to initialize DirectDraw.\n"); + return 1; +} + + + +//============================================================ +// drawdd_window_destroy +//============================================================ + +static void drawdd_window_destroy(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // skip if nothing + if (dd == NULL) + return; + + // delete the ddraw object + ddraw_delete(window); + + // free the memory in the window + free(dd); + window->drawdata = NULL; +} + + + +//============================================================ +// drawdd_window_get_primitives +//============================================================ + +static const render_primitive_list *drawdd_window_get_primitives(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + compute_blit_surface_size(window); + render_target_set_bounds(window->target, dd->blitwidth, dd->blitheight, 0); + render_target_set_max_update_rate(window->target, (dd->refresh == 0) ? dd->origmode.dwRefreshRate : dd->refresh); + + return render_target_get_primitives(window->target); +} + + + +//============================================================ +// drawdd_window_draw +//============================================================ + +static int drawdd_window_draw(win_window_info *window, HDC dc, int update) +{ + dd_info *dd = window->drawdata; + const render_primitive *prim; + int usemembuffer = FALSE; + HRESULT result; + + // if we haven't been created, just punt + if (dd == NULL) + return 1; + + // if we're updating, remember to erase the outer stuff + if (update) + update_outer_rects(dd); + + // if we have a ddraw object, check the cooperative level + if (ddraw_test_cooperative(window)) + return 1; + + // get the size; if we're too small, delete the existing surfaces + if (dd->blitwidth > dd->blitdesc.dwWidth || dd->blitheight > dd->blitdesc.dwHeight) + ddraw_delete_surfaces(window); + + // if we need to create surfaces, do it now + if (dd->blit == NULL && ddraw_create_surfaces(window) != 0) + return 1; + + // select our surface and lock it + result = IDirectDrawSurface7_Lock(dd->blit, NULL, &dd->blitdesc, DDLOCK_WAIT, NULL); + if (result == DDERR_SURFACELOST) + { + mame_printf_verbose("DirectDraw: Lost surfaces; deleting and retrying next frame\n"); + ddraw_delete_surfaces(window); + return 1; + } + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X locking blit surface\n", (int)result); + return 1; + } + + // render to it + osd_lock_acquire(window->primlist->lock); + + // scan the list of primitives for tricky stuff + for (prim = window->primlist->head; prim != NULL; prim = prim->next) + if (PRIMFLAG_GET_BLENDMODE(prim->flags) != BLENDMODE_NONE || + (prim->texture.base != NULL && PRIMFLAG_GET_TEXFORMAT(prim->flags) == TEXFORMAT_ARGB32)) + { + usemembuffer = TRUE; + break; + } + + // if we're using the memory buffer, draw offscreen first and then copy + if (usemembuffer) + { + int x, y; + + // based on the target format, use one of our standard renderers + switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: drawdd_rgb888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0x000000ff: drawdd_bgr888_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0xf800: drawdd_rgb565_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + case 0x7c00: drawdd_rgb555_draw_primitives(window->primlist->head, dd->membuffer, dd->blitwidth, dd->blitheight, dd->blitwidth); break; + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask); + break; + } + + // handle copying to both 16bpp and 32bpp destinations + for (y = 0; y < dd->blitheight; y++) + { + if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 32) + { + UINT32 *src = (UINT32 *)dd->membuffer + y * dd->blitwidth; + UINT32 *dst = (UINT32 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch); + for (x = 0; x < dd->blitwidth; x++) + *dst++ = *src++; + } + else if (dd->blitdesc.ddpfPixelFormat.dwRGBBitCount == 16) + { + UINT16 *src = (UINT16 *)dd->membuffer + y * dd->blitwidth; + UINT16 *dst = (UINT16 *)((UINT8 *)dd->blitdesc.lpSurface + y * dd->blitdesc.lPitch); + for (x = 0; x < dd->blitwidth; x++) + *dst++ = *src++; + } + } + + } + + // otherwise, draw directly + else + { + // based on the target format, use one of our standard renderers + switch (dd->blitdesc.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: drawdd_rgb888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break; + case 0x000000ff: drawdd_bgr888_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 4); break; + case 0xf800: drawdd_rgb565_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break; + case 0x7c00: drawdd_rgb555_nr_draw_primitives(window->primlist->head, dd->blitdesc.lpSurface, dd->blitwidth, dd->blitheight, dd->blitdesc.lPitch / 2); break; + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->blitdesc.ddpfPixelFormat.dwRBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwGBitMask, (int)dd->blitdesc.ddpfPixelFormat.dwBBitMask); + break; + } + } + osd_lock_release(window->primlist->lock); + + // unlock and blit + result = IDirectDrawSurface7_Unlock(dd->blit, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X unlocking blit surface\n", (int)result); + + // sync to VBLANK + if ((video_config.waitvsync || video_config.syncrefresh) && video_get_throttle() && (!window->fullscreen || dd->back == NULL)) + { + result = IDirectDraw7_WaitForVerticalBlank(dd->ddraw, DDWAITVB_BLOCKBEGIN, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result); + } + + // complete the blitting + blit_to_primary(window, dd->blitwidth, dd->blitheight); + return 0; +} + + + +//============================================================ +// ddraw_create +//============================================================ + +static int ddraw_create(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + int verify; + + // if a device exists, free it + if (dd->ddraw != NULL) + ddraw_delete(window); + + // create the DirectDraw object + result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result); + goto error; + } + + // verify the caps + verify = ddraw_verify_caps(dd); + if (verify == 2) + { + mame_printf_error("DirectDraw: Error - Device does not meet minimum requirements for DirectDraw rendering\n"); + goto error; + } + if (verify == 1) + mame_printf_verbose("DirectDraw: Warning - Device may not perform well for DirectDraw rendering\n"); + + // set the cooperative level + // for non-window modes, we will use full screen here + result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, win_window_list->hwnd, DDSCL_SETFOCUSWINDOW); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(FOCUSWINDOW) call\n", (int)result); + goto error; + } + result = IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_SETDEVICEWINDOW | (window->fullscreen ? DDSCL_FULLSCREEN | DDSCL_EXCLUSIVE : DDSCL_NORMAL)); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_SetCooperativeLevel(DEVICEWINDOW) call\n", (int)result); + goto error; + } + + // full screen mode: set the resolution + if (window->fullscreen && video_config.switchres) + { + result = IDirectDraw7_SetDisplayMode(dd->ddraw, dd->width, dd->height, 32, dd->refresh, 0); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X attempting to set video mode %dx%d@%d call\n", (int)result, dd->width, dd->height, dd->refresh); + goto error; + } + } + + return ddraw_create_surfaces(window); + +error: + ddraw_delete(window); + return 1; +} + + + +//============================================================ +// ddraw_create_surfaces +//============================================================ + +static int ddraw_create_surfaces(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // make a description of the primary surface + memset(&dd->primarydesc, 0, sizeof(dd->primarydesc)); + dd->primarydesc.dwSize = sizeof(dd->primarydesc); + dd->primarydesc.dwFlags = DDSD_CAPS; + dd->primarydesc.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE; + + // for triple-buffered full screen mode, allocate flipping surfaces + if (window->fullscreen && video_config.triplebuf) + { + dd->primarydesc.dwFlags |= DDSD_BACKBUFFERCOUNT; + dd->primarydesc.ddsCaps.dwCaps |= DDSCAPS_FLIP | DDSCAPS_COMPLEX; + dd->primarydesc.dwBackBufferCount = 2; + } + + // create the primary surface and report errors + result = create_surface(dd, &dd->primarydesc, &dd->primary, "primary"); + if (result != DD_OK) goto error; + + // full screen mode: get the back surface + dd->back = NULL; + if (window->fullscreen && video_config.triplebuf) + { + DDSCAPS2 caps = { DDSCAPS_BACKBUFFER }; + result = IDirectDrawSurface7_GetAttachedSurface(dd->primary, &caps, &dd->back); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting attached back surface\n", (int)result); + goto error; + } + } + + // now make a description of our blit surface, based on the primary surface + if (dd->blitwidth == 0 || dd->blitheight == 0) + compute_blit_surface_size(window); + dd->blitdesc = dd->primarydesc; + dd->blitdesc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS; + dd->blitdesc.dwWidth = dd->blitwidth; + dd->blitdesc.dwHeight = dd->blitheight; + dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY; + + // then create the blit surface, fall back to system memory if video mem doesn't work + result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit"); + if (result != DD_OK) + { + dd->blitdesc.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY; + result = create_surface(dd, &dd->blitdesc, &dd->blit, "blit"); + } + if (result != DD_OK) goto error; + + // create a memory buffer for offscreen drawing + if (dd->membuffersize < dd->blitwidth * dd->blitheight * 4) + { + dd->membuffersize = dd->blitwidth * dd->blitheight * 4; + dd->membuffer = realloc(dd->membuffer, dd->membuffersize); + } + if (dd->membuffer == NULL) + goto error; + +#ifdef MESS + // create a clipper for all modes, since MESS has dialogs + if (create_clipper(window)) + goto error; +#else + // create a clipper for windowed mode + if (!window->fullscreen && create_clipper(window)) + goto error; +#endif + + // full screen mode: set the gamma + if (window->fullscreen) + { + // only set the gamma if it's not 1.0f + float brightness = options_get_float(mame_options(), WINOPTION_FULLSCREENBRIGHTNESS); + float contrast = options_get_float(mame_options(), WINOPTION_FULLLSCREENCONTRAST); + float gamma = options_get_float(mame_options(), WINOPTION_FULLSCREENGAMMA); + if (brightness != 1.0f || contrast != 1.0f || gamma != 1.0f) + { + // see if we can get a GammaControl object + result = IDirectDrawSurface_QueryInterface(dd->primary, &IID_IDirectDrawGammaControl, (void **)&dd->gamma); + if (result != DD_OK) + { + mame_printf_warning("DirectDraw: Warning - device does not support full screen gamma correction.\n"); + dd->gamma = NULL; + } + + // proceed if we can + if (dd->gamma != NULL) + { + DDGAMMARAMP ramp; + int i; + + // create a standard ramp and set it + for (i = 0; i < 256; i++) + ramp.red[i] = ramp.green[i] = ramp.blue[i] = apply_brightness_contrast_gamma(i, brightness, contrast, gamma) << 8; + + // attempt to set it + result = IDirectDrawGammaControl_SetGammaRamp(dd->gamma, 0, &ramp); + if (result != DD_OK) + mame_printf_verbose("DirectDraw: Error %08X attempting to set gamma correction.\n", (int)result); + } + } + } + + // force some updates + update_outer_rects(dd); + return 0; + +error: + ddraw_delete_surfaces(window); + return 1; +} + + + +//============================================================ +// ddraw_delete +//============================================================ + +static void ddraw_delete(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // free surfaces + ddraw_delete_surfaces(window); + + // restore resolutions + if (dd->ddraw != NULL) + IDirectDraw7_RestoreDisplayMode(dd->ddraw); + + // reset cooperative level + if (dd->ddraw != NULL && window->hwnd != NULL) + IDirectDraw7_SetCooperativeLevel(dd->ddraw, window->hwnd, DDSCL_NORMAL); + + // release the DirectDraw object itself + if (dd->ddraw != NULL) + IDirectDraw7_Release(dd->ddraw); + dd->ddraw = NULL; +} + + + +//============================================================ +// ddraw_delete_surfaces +//============================================================ + +static void ddraw_delete_surfaces(win_window_info *window) +{ + dd_info *dd = window->drawdata; + + // release the gamma control + if (dd->gamma != NULL) + IDirectDrawGammaControl_Release(dd->gamma); + dd->gamma = NULL; + + // release the clipper + if (dd->clipper != NULL) + IDirectDrawClipper_Release(dd->clipper); + dd->clipper = NULL; + + // free the memory buffer + if (dd->membuffer != NULL) + free(dd->membuffer); + dd->membuffer = NULL; + dd->membuffersize = 0; + + // release the blit surface + if (dd->blit != NULL) + IDirectDrawSurface7_Release(dd->blit); + dd->blit = NULL; + + // release the back surface + if (dd->back != NULL) + IDirectDrawSurface7_Release(dd->back); + dd->back = NULL; + + // release the primary surface + if (dd->primary != NULL) + IDirectDrawSurface7_Release(dd->primary); + dd->primary = NULL; +} + + + +//============================================================ +// ddraw_verify_caps +//============================================================ + +static int ddraw_verify_caps(dd_info *dd) +{ + int retval = 0; + HRESULT result; + + // get the capabilities + dd->ddcaps.dwSize = sizeof(dd->ddcaps); + dd->helcaps.dwSize = sizeof(dd->helcaps); + result = IDirectDraw7_GetCaps(dd->ddraw, &dd->ddcaps, &dd->helcaps); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during IDirectDraw7_GetCaps call\n", (int)result); + return 1; + } + + // determine if hardware stretching is available + if ((dd->ddcaps.dwCaps & DDCAPS_BLTSTRETCH) == 0) + { + mame_printf_verbose("DirectDraw: Warning - Device does not support hardware stretching\n"); + retval = 1; + } + + return retval; +} + + + +//============================================================ +// ddraw_test_cooperative +//============================================================ + +static int ddraw_test_cooperative(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // check our current status; if we lost the device, punt to GDI + result = IDirectDraw7_TestCooperativeLevel(dd->ddraw); + switch (result) + { + // punt to GDI if someone else has exclusive mode + case DDERR_NOEXCLUSIVEMODE: + case DDERR_EXCLUSIVEMODEALREADYSET: + ddraw_delete_surfaces(window); + return 1; + + // if we're ok, but we don't have a primary surface, create one + default: + case DD_OK: + if (dd->primary == NULL) + return ddraw_create_surfaces(window); + return 0; + } +} + + + +//============================================================ +// create_surface +//============================================================ + +static HRESULT create_surface(dd_info *dd, DDSURFACEDESC2 *desc, IDirectDrawSurface7 **surface, const char *type) +{ + HRESULT result; + + // create the surface as requested + result = IDirectDraw7_CreateSurface(dd->ddraw, desc, surface, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X creating %s surface\n", (int)result, type); + return result; + } + + // get a description of the primary surface + result = IDirectDrawSurface7_GetSurfaceDesc(*surface, desc); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting %s surface desciption\n", (int)result, type); + IDirectDrawSurface7_Release(*surface); + *surface = NULL; + return result; + } + + // print out the good stuff + mame_printf_verbose("DirectDraw: %s surface created: %dx%dx%d (R=%08X G=%08X B=%08X)\n", + type, + (int)desc->dwWidth, + (int)desc->dwHeight, + (int)desc->ddpfPixelFormat.dwRGBBitCount, + (UINT32)desc->ddpfPixelFormat.dwRBitMask, + (UINT32)desc->ddpfPixelFormat.dwGBitMask, + (UINT32)desc->ddpfPixelFormat.dwBBitMask); + return result; +} + + + +//============================================================ +// create_clipper +//============================================================ + +static int create_clipper(win_window_info *window) +{ + dd_info *dd = window->drawdata; + HRESULT result; + + // create a clipper for the primary surface + result = IDirectDraw7_CreateClipper(dd->ddraw, 0, &dd->clipper, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X creating clipper\n", (int)result); + return 1; + } + + // set the clipper's hwnd + result = IDirectDrawClipper_SetHWnd(dd->clipper, 0, window->hwnd); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X setting clipper hwnd\n", (int)result); + return 1; + } + + // set the clipper on the primary surface + result = IDirectDrawSurface7_SetClipper(dd->primary, dd->clipper); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X setting clipper on primary surface\n", (int)result); + return 1; + } + return 0; +} + + + +//============================================================ +// compute_blit_surface_size +//============================================================ + +static void compute_blit_surface_size(win_window_info *window) +{ + dd_info *dd = window->drawdata; + INT32 newwidth, newheight; + int xscale, yscale; + RECT client; + + // start with the minimum size + render_target_get_minimum_size(window->target, &newwidth, &newheight); + + // get the window's client rectangle + GetClientRect(window->hwnd, &client); + + // hardware stretch case: apply prescale + if (video_config.hwstretch) + { + int prescale = (video_config.prescale < 1) ? 1 : video_config.prescale; + + // clamp the prescale to something smaller than the target bounds + xscale = prescale; + while (xscale > 1 && newwidth * xscale > rect_width(&client)) + xscale--; + yscale = prescale; + while (yscale > 1 && newheight * yscale > rect_height(&client)) + yscale--; + } + + // non stretch case + else + { + INT32 target_width = rect_width(&client); + INT32 target_height = rect_height(&client); + float desired_aspect = 1.0f; + + // compute the appropriate visible area if we're trying to keepaspect + if (video_config.keepaspect) + { + win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL); + render_target_compute_visible_area(window->target, target_width, target_height, winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &target_width, &target_height); + desired_aspect = (float)target_width / (float)target_height; + } + + // compute maximum integral scaling to fit the window + xscale = (target_width + 2) / newwidth; + yscale = (target_height + 2) / newheight; + + // try a little harder to keep the aspect ratio if desired + if (video_config.keepaspect) + { + // if we could stretch more in the X direction, and that makes a better fit, bump the xscale + while (newwidth * (xscale + 1) <= rect_width(&client) && + better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale + 1), newheight * yscale, desired_aspect)) + xscale++; + + // if we could stretch more in the Y direction, and that makes a better fit, bump the yscale + while (newheight * (yscale + 1) <= rect_height(&client) && + better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale + 1), desired_aspect)) + yscale++; + + // now that we've maxed out, see if backing off the maximally stretched one makes a better fit + if (rect_width(&client) - newwidth * xscale < rect_height(&client) - newheight * yscale) + { + while (xscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * (xscale - 1), newheight * yscale, desired_aspect)) + xscale--; + } + else + { + while (yscale > 1 && better_mode(newwidth * xscale, newheight * yscale, newwidth * xscale, newheight * (yscale - 1), desired_aspect)) + yscale--; + } + } + } + + // ensure at least a scale factor of 1 + if (xscale == 0) xscale = 1; + if (yscale == 0) yscale = 1; + + // apply the final scale + newwidth *= xscale; + newheight *= yscale; + if (newwidth != dd->blitwidth || newheight != dd->blitheight) + { + // force some updates + update_outer_rects(dd); + mame_printf_verbose("DirectDraw: New blit size = %dx%d\n", newwidth, newheight); + } + dd->blitwidth = newwidth; + dd->blitheight = newheight; +} + + + +//============================================================ +// calc_fullscreen_margins +//============================================================ + +static void calc_fullscreen_margins(win_window_info *window, DWORD desc_width, DWORD desc_height, RECT *margins) +{ + margins->left = 0; + margins->top = 0; + margins->right = desc_width; + margins->bottom = desc_height; + + if (win_has_menu(window)) + { + static int height_with_menubar = 0; + if (height_with_menubar == 0) + { + RECT with_menu = { 100, 100, 200, 200 }; + RECT without_menu = { 100, 100, 200, 200 }; + AdjustWindowRect(&with_menu, WS_OVERLAPPED, TRUE); + AdjustWindowRect(&without_menu, WS_OVERLAPPED, FALSE); + height_with_menubar = (with_menu.bottom - with_menu.top) - (without_menu.bottom - without_menu.top); + } + margins->top = height_with_menubar; + } +} + + + +//============================================================ +// blit_to_primary +//============================================================ + +static void blit_to_primary(win_window_info *window, int srcwidth, int srcheight) +{ + dd_info *dd = window->drawdata; + IDirectDrawSurface7 *target = (dd->back != NULL) ? dd->back : dd->primary; + win_monitor_info *monitor = winwindow_video_window_monitor(window, NULL); + DDBLTFX blitfx = { sizeof(DDBLTFX) }; + RECT clear, outer, dest, source; + INT32 dstwidth, dstheight; + HRESULT result; + + // compute source rect + source.left = source.top = 0; + source.right = srcwidth; + source.bottom = srcheight; + + // compute outer rect -- windowed version + if (!window->fullscreen) + { + GetClientRect(window->hwnd, &outer); + ClientToScreen(window->hwnd, &((LPPOINT)&outer)[0]); + ClientToScreen(window->hwnd, &((LPPOINT)&outer)[1]); + + // adjust to be relative to the monitor + outer.left -= monitor->info.rcMonitor.left; + outer.right -= monitor->info.rcMonitor.left; + outer.top -= monitor->info.rcMonitor.top; + outer.bottom -= monitor->info.rcMonitor.top; + } + + // compute outer rect -- full screen version + else + { + calc_fullscreen_margins(window, dd->primarydesc.dwWidth, dd->primarydesc.dwHeight, &outer); + } + + // if we're respecting the aspect ratio, we need to adjust to fit + dstwidth = rect_width(&outer); + dstheight = rect_height(&outer); + if (!video_config.hwstretch) + { + // trim the source if necessary + if (rect_width(&outer) < srcwidth) + { + source.left += (srcwidth - rect_width(&outer)) / 2; + source.right = source.left + rect_width(&outer); + } + if (rect_height(&outer) < srcheight) + { + source.top += (srcheight - rect_height(&outer)) / 2; + source.bottom = source.top + rect_height(&outer); + } + + // match the destination and source sizes + dstwidth = srcwidth = source.right - source.left; + dstheight = srcheight = source.bottom - source.top; + } + else if (video_config.keepaspect) + { + // compute the appropriate visible area + render_target_compute_visible_area(window->target, rect_width(&outer), rect_height(&outer), winvideo_monitor_get_aspect(monitor), render_target_get_orientation(window->target), &dstwidth, &dstheight); + } + + // center within + dest.left = outer.left + (rect_width(&outer) - dstwidth) / 2; + dest.right = dest.left + dstwidth; + dest.top = outer.top + (rect_height(&outer) - dstheight) / 2; + dest.bottom = dest.top + dstheight; + + // compare against last destination; if different, force a redraw + if (dest.left != dd->lastdest.left || dest.right != dd->lastdest.right || dest.top != dd->lastdest.top || dest.bottom != dd->lastdest.bottom) + { + dd->lastdest = dest; + update_outer_rects(dd); + } + + // clear outer rects if we need to + if (dd->clearouter != 0) + { + dd->clearouter--; + + // clear the left edge + if (dest.left > outer.left) + { + clear = outer; + clear.right = dest.left; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the right edge + if (dest.right < outer.right) + { + clear = outer; + clear.left = dest.right; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the top edge + if (dest.top > outer.top) + { + clear = outer; + clear.bottom = dest.top; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + + // clear the bottom edge + if (dest.bottom < outer.bottom) + { + clear = outer; + clear.top = dest.bottom; + result = IDirectDrawSurface_Blt(target, &clear, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &blitfx); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X clearing the screen\n", (int)result); + } + } + + // do the blit + result = IDirectDrawSurface7_Blt(target, &dest, dd->blit, &source, DDBLT_WAIT, NULL); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X blitting to the screen\n", (int)result); + + // page flip if triple buffered + if (window->fullscreen && dd->back != NULL) + { + result = IDirectDrawSurface7_Flip(dd->primary, NULL, DDFLIP_WAIT); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X waiting for VBLANK\n", (int)result); + } +} + + + +//============================================================ +// config_adapter_mode +//============================================================ + +static int config_adapter_mode(win_window_info *window) +{ + DDDEVICEIDENTIFIER2 identifier; + dd_info *dd = window->drawdata; + HRESULT result; + + // choose the monitor number + get_adapter_for_monitor(dd, window->monitor); + + // create a temporary DirectDraw object + result = (*directdrawcreateex)(dd->adapter_ptr, (LPVOID *)&dd->ddraw, &IID_IDirectDraw7, NULL); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X during DirectDrawCreateEx call\n", (int)result); + return 1; + } + + // get the identifier + result = IDirectDraw7_GetDeviceIdentifier(dd->ddraw, &identifier, 0); + if (result != DD_OK) + { + mame_printf_error("Error getting identifier for device\n"); + return 1; + } + mame_printf_verbose("DirectDraw: Configuring device %s\n", identifier.szDescription); + + // get the current display mode + memset(&dd->origmode, 0, sizeof(dd->origmode)); + dd->origmode.dwSize = sizeof(dd->origmode); + result = IDirectDraw7_GetDisplayMode(dd->ddraw, &dd->origmode); + if (result != DD_OK) + { + mame_printf_verbose("DirectDraw: Error %08X getting current display mode\n", (int)result); + IDirectDraw7_Release(dd->ddraw); + return 1; + } + + // choose a resolution: full screen mode case + if (window->fullscreen) + { + // default to the current mode exactly + dd->width = dd->origmode.dwWidth; + dd->height = dd->origmode.dwHeight; + dd->refresh = dd->origmode.dwRefreshRate; + + // if we're allowed to switch resolutions, override with something better + if (video_config.switchres) + pick_best_mode(window); + } + + // release the DirectDraw object + IDirectDraw7_Release(dd->ddraw); + dd->ddraw = NULL; + + // if we're not changing resolutions, make sure we have a resolution we can handle + if (!window->fullscreen || !video_config.switchres) + { + switch (dd->origmode.ddpfPixelFormat.dwRBitMask) + { + case 0x00ff0000: + case 0x000000ff: + case 0xf800: + case 0x7c00: + break; + + default: + mame_printf_verbose("DirectDraw: Unknown target mode: R=%08X G=%08X B=%08X\n", (int)dd->origmode.ddpfPixelFormat.dwRBitMask, (int)dd->origmode.ddpfPixelFormat.dwGBitMask, (int)dd->origmode.ddpfPixelFormat.dwBBitMask); + return 1; + } + } + + return 0; +} + + + +//============================================================ +// monitor_enum_callback +//============================================================ + +static BOOL WINAPI monitor_enum_callback(GUID FAR *guid, LPSTR description, LPSTR name, LPVOID context, HMONITOR hmonitor) +{ + monitor_enum_info *einfo = (monitor_enum_info *)context; + + // do we match the desired monitor? + if (hmonitor == einfo->monitor->handle || (hmonitor == NULL && (einfo->monitor->info.dwFlags & MONITORINFOF_PRIMARY) != 0)) + { + einfo->guid_ptr = (guid != NULL) ? &einfo->guid : NULL; + if (guid != NULL) + einfo->guid = *guid; + einfo->foundit = TRUE; + } + return 1; +} + + + +//============================================================ +// get_adapter_for_monitor +//============================================================ + +static void get_adapter_for_monitor(dd_info *dd, win_monitor_info *monitor) +{ + monitor_enum_info einfo; + HRESULT result; + + // try to find our monitor + memset(&einfo, 0, sizeof(einfo)); + einfo.monitor = monitor; + result = (*directdrawenumerateex)(monitor_enum_callback, &einfo, DDENUM_ATTACHEDSECONDARYDEVICES); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during DirectDrawEnumerateEx call\n", (int)result); + + // set up the adapter + if (einfo.foundit && einfo.guid_ptr != NULL) + { + dd->adapter = einfo.guid; + dd->adapter_ptr = &dd->adapter; + } + else + dd->adapter_ptr = NULL; +} + + + +//============================================================ +// pick_best_mode +//============================================================ + +static HRESULT WINAPI enum_modes_callback(LPDDSURFACEDESC2 desc, LPVOID context) +{ + float size_score, refresh_score, final_score; + mode_enum_info *einfo = context; + dd_info *dd = einfo->window->drawdata; + + // skip non-32 bit modes + if (desc->ddpfPixelFormat.dwRGBBitCount != 32) + return DDENUMRET_OK; + + // compute initial score based on difference between target and current + size_score = 1.0f / (1.0f + fabs((INT32)desc->dwWidth - einfo->target_width) + fabs((INT32)desc->dwHeight - einfo->target_height)); + + // if the mode is too small, give a big penalty + if (desc->dwWidth < einfo->minimum_width || desc->dwHeight < einfo->minimum_height) + size_score *= 0.01f; + + // if mode is smaller than we'd like, it only scores up to 0.1 + if (desc->dwWidth < einfo->target_width || desc->dwHeight < einfo->target_height) + size_score *= 0.1f; + + // if we're looking for a particular mode, that's a winner + if (desc->dwWidth == einfo->window->maxwidth && desc->dwHeight == einfo->window->maxheight) + size_score = 2.0f; + + // compute refresh score + refresh_score = 1.0f / (1.0f + fabs((double)desc->dwRefreshRate - einfo->target_refresh)); + + // if refresh is smaller than we'd like, it only scores up to 0.1 + if ((double)desc->dwRefreshRate < einfo->target_refresh) + refresh_score *= 0.1; + + // if we're looking for a particular refresh, make sure it matches + if (desc->dwRefreshRate == einfo->window->refresh) + refresh_score = 2.0f; + + // weight size and refresh equally + final_score = size_score + refresh_score; + + // best so far? + mame_printf_verbose(" %4dx%4d@%3dHz -> %f\n", (int)desc->dwWidth, (int)desc->dwHeight, (int)desc->dwRefreshRate, final_score * 1000.0f); + if (final_score > einfo->best_score) + { + einfo->best_score = final_score; + dd->width = desc->dwWidth; + dd->height = desc->dwHeight; + dd->refresh = desc->dwRefreshRate; + } + return DDENUMRET_OK; +} + + + +//============================================================ +// pick_best_mode +//============================================================ + +static void pick_best_mode(win_window_info *window) +{ + dd_info *dd = window->drawdata; + mode_enum_info einfo; + HRESULT result; + + // determine the minimum width/height for the selected target + // note: technically we should not be calling this from an alternate window + // thread; however, it is only done during init time, and the init code on + // the main thread is waiting for us to finish, so it is safe to do so here + render_target_get_minimum_size(window->target, &einfo.minimum_width, &einfo.minimum_height); + + // use those as the target for now + einfo.target_width = einfo.minimum_width * MAX(1, video_config.prescale); + einfo.target_height = einfo.minimum_height * MAX(1, video_config.prescale); + einfo.target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh); + + // if we're not stretching, allow some slop on the minimum since we can handle it + if (!video_config.hwstretch) + { + einfo.minimum_width -= 4; + einfo.minimum_height -= 4; + } + + // if we are stretching, aim for a mode approximately 2x the game's resolution + else if (video_config.prescale <= 1) + { + einfo.target_width *= 2; + einfo.target_height *= 2; + } + + // fill in the rest of the data + einfo.window = window; + einfo.best_score = 0.0f; + + // enumerate the modes + mame_printf_verbose("DirectDraw: Selecting video mode...\n"); + result = IDirectDraw7_EnumDisplayModes(dd->ddraw, DDEDM_REFRESHRATES, NULL, &einfo, enum_modes_callback); + if (result != DD_OK) mame_printf_verbose("DirectDraw: Error %08X during EnumDisplayModes call\n", (int)result); + mame_printf_verbose("DirectDraw: Mode selected = %4dx%4d@%3dHz\n", dd->width, dd->height, dd->refresh); +} + + + +//============================================================ +// SOFTWARE RENDERING +//============================================================ + +#define FUNC_PREFIX(x) drawdd_rgb888_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 16 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_bgr888_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 0 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 16 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb565_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 2 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 11 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb555_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 3 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 10 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 + +#include "rendersw.c" + + + +//============================================================ +// SOFTWARE RENDERING -- NO READING VARIANTS +//============================================================ + +#define FUNC_PREFIX(x) drawdd_rgb888_nr_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 16 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_bgr888_nr_##x +#define PIXEL_TYPE UINT32 +#define SRCSHIFT_R 0 +#define SRCSHIFT_G 0 +#define SRCSHIFT_B 0 +#define DSTSHIFT_R 0 +#define DSTSHIFT_G 8 +#define DSTSHIFT_B 16 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb565_nr_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 2 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 11 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" + +#define FUNC_PREFIX(x) drawdd_rgb555_nr_##x +#define PIXEL_TYPE UINT16 +#define SRCSHIFT_R 3 +#define SRCSHIFT_G 3 +#define SRCSHIFT_B 3 +#define DSTSHIFT_R 10 +#define DSTSHIFT_G 5 +#define DSTSHIFT_B 0 +#define NO_DEST_READ 1 + +#include "rendersw.c" |