//============================================================ // // drawd3d.c - Win32 Direct3D implementation // // Copyright (c) 1996-2007, Nicola Salmoria and the MAME Team. // Visit http://mamedev.org for licensing and usage restrictions. // //============================================================ // Useful info: // Windows XP/2003 shipped with DirectX 8.1 // Windows 2000 shipped with DirectX 7a // Windows 98SE shipped with DirectX 6.1a // Windows 98 shipped with DirectX 5 // Windows NT shipped with DirectX 3.0a // Windows 95 shipped with DirectX 2 // standard windows headers #define WIN32_LEAN_AND_MEAN #include #include #include #include // standard C headers #include // MAME headers #include "render.h" #include "rendutil.h" #include "options.h" // MAMEOS headers #include "d3dintf.h" #include "winmain.h" #include "window.h" #include "config.h" #include "strconv.h" //============================================================ // DEBUGGING //============================================================ extern void mtlog_add(const char *event); //============================================================ // CONSTANTS //============================================================ #define ENABLE_BORDER_PIX (1) #define VERTEX_FORMAT (D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1) #define VERTEX_BUFFER_SIZE (2048*4) enum { TEXTURE_TYPE_PLAIN, TEXTURE_TYPE_DYNAMIC, TEXTURE_TYPE_SURFACE }; //============================================================ // MACROS //============================================================ #define FSWAP(var1, var2) do { float temp = var1; var1 = var2; var2 = temp; } while (0) //============================================================ // TYPE DEFINITIONS //============================================================ /* texture_info holds information about a texture */ typedef struct _texture_info texture_info; struct _texture_info { texture_info * next; // next texture in the list UINT32 hash; // hash value for the texture UINT32 flags; // rendering flags render_texinfo texinfo; // copy of the texture info float ustart, ustop; // beginning/ending U coordinates float vstart, vstop; // beginning/ending V coordinates int rawwidth, rawheight; // raw width/height of the texture int type; // what type of texture are we? int borderpix; // do we have a 1 pixel border? int xprescale; // what is our X prescale factor? int yprescale; // what is our Y prescale factor? d3d_texture * d3dtex; // Direct3D texture pointer d3d_surface * d3dsurface; // Direct3D offscreen plain surface pointer d3d_texture * d3dfinaltex; // Direct3D final (post-scaled) texture }; /* poly_info holds information about a single polygon/d3d primitive */ typedef struct _poly_info poly_info; struct _poly_info { D3DPRIMITIVETYPE type; // type of primitive UINT32 count; // total number of primitives UINT32 numverts; // total number of vertices UINT32 flags; // rendering flags DWORD modmode; // texture modulation mode texture_info * texture; // pointer to texture info }; /* d3d_vertex describes a single vertex */ typedef struct _d3d_vertex d3d_vertex; struct _d3d_vertex { float x, y, z; // X,Y,Z coordinates float rhw; // 1/W coordinate D3DCOLOR color; // diffuse color float u0, v0; // texture stage 0 coordinates }; /* d3d_info is the information about Direct3D for the current screen */ typedef struct _d3d_info d3d_info; struct _d3d_info { int adapter; // ordinal adapter number int width, height; // current width, height int refresh; // current refresh rate d3d_device * device; // pointer to the Direct3DDevice object int gamma_supported; // is full screen gamma supported? d3d_present_parameters presentation; // set of presentation parameters D3DDISPLAYMODE origmode; // original display mode for the adapter D3DFORMAT pixformat; // pixel format we are using d3d_vertex_buffer * vertexbuf; // pointer to the vertex buffer object d3d_vertex * lockedbuf; // pointer to the locked vertex buffer int numverts; // number of accumulated vertices poly_info poly[VERTEX_BUFFER_SIZE / 3];// array to hold polygons as they are created int numpolys; // number of accumulated polygons texture_info * texlist; // list of active textures int dynamic_supported; // are dynamic textures supported? int stretch_supported; // is StretchRect with point filtering supported? int mod2x_supported; // is D3DTOP_MODULATE2X supported? int mod4x_supported; // is D3DTOP_MODULATE4X supported? D3DFORMAT screen_format; // format to use for screen textures D3DFORMAT yuv_format; // format to use for YUV textures DWORD texture_caps; // textureCaps field DWORD texture_max_aspect; // texture maximum aspect ratio DWORD texture_max_width; // texture maximum width DWORD texture_max_height; // texture maximum height texture_info * last_texture; // previous texture int last_blendenable; // previous blendmode int last_blendop; // previous blendmode int last_blendsrc; // previous blendmode int last_blenddst; // previous blendmode int last_filter; // previous texture filter int last_wrap; // previous wrap state DWORD last_modmode; // previous texture modulation mame_bitmap * vector_bitmap; // experimental: bitmap for vectors texture_info * vector_texture; // experimental: texture for vectors }; /* line_aa_step is used for drawing antialiased lines */ typedef struct _line_aa_step line_aa_step; struct _line_aa_step { float xoffs, yoffs; // X/Y deltas float weight; // weight contribution }; //============================================================ // GLOBALS //============================================================ static d3d * d3dintf; static const line_aa_step line_aa_1step[] = { { 0.00f, 0.00f, 1.00f }, { 0 } }; static const line_aa_step line_aa_4step[] = { { -0.25f, 0.00f, 0.25f }, { 0.25f, 0.00f, 0.25f }, { 0.00f, -0.25f, 0.25f }, { 0.00f, 0.25f, 0.25f }, { 0 } }; //============================================================ // INLINES //============================================================ INLINE UINT32 ycc_to_rgb(UINT8 y, UINT8 cb, UINT8 cr) { /* original equations: C = Y - 16 D = Cb - 128 E = Cr - 128 R = clip(( 298 * C + 409 * E + 128) >> 8) G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8) B = clip(( 298 * C + 516 * D + 128) >> 8) R = clip(( 298 * (Y - 16) + 409 * (Cr - 128) + 128) >> 8) G = clip(( 298 * (Y - 16) - 100 * (Cb - 128) - 208 * (Cr - 128) + 128) >> 8) B = clip(( 298 * (Y - 16) + 516 * (Cb - 128) + 128) >> 8) R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) */ int r, g, b, common; common = 298 * y - 298 * 16; r = (common + 409 * cr - 409 * 128 + 128) >> 8; g = (common - 100 * cb + 100 * 128 - 208 * cr + 208 * 128 + 128) >> 8; b = (common + 516 * cb - 516 * 128 + 128) >> 8; if (r < 0) r = 0; else if (r > 255) r = 255; if (g < 0) g = 0; else if (g > 255) g = 255; if (b < 0) b = 0; else if (b > 255) b = 255; return MAKE_ARGB(0xff, r, g, b); } INLINE UINT32 texture_compute_hash(const render_texinfo *texture, UINT32 flags) { return (FPTR)texture->base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)); } INLINE void set_texture(d3d_info *d3d, texture_info *texture) { HRESULT result; if (texture != d3d->last_texture) { d3d->last_texture = texture; result = (*d3dintf->device.set_texture)(d3d->device, 0, (texture == NULL) ? NULL : texture->d3dfinaltex); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result); } } INLINE void set_filter(d3d_info *d3d, int filter) { HRESULT result; if (filter != d3d->last_filter) { d3d->last_filter = filter; result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MINFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_MAGFILTER, filter ? D3DTEXF_LINEAR : D3DTEXF_POINT); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); } } INLINE void set_wrap(d3d_info *d3d, int wrap) { HRESULT result; if (wrap != d3d->last_wrap) { d3d->last_wrap = wrap; result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSU, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ADDRESSV, wrap ? D3DTADDRESS_WRAP : D3DTADDRESS_CLAMP); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); } } INLINE void set_modmode(d3d_info *d3d, DWORD modmode) { HRESULT result; if (modmode != d3d->last_modmode) { d3d->last_modmode = modmode; result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, modmode); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture_stage_state call\n", (int)result); } } INLINE void set_blendmode(d3d_info *d3d, int blendmode) { HRESULT result; int blendenable; int blendop; int blendsrc; int blenddst; // choose the parameters switch (blendmode) { default: case BLENDMODE_NONE: blendenable = FALSE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break; case BLENDMODE_ALPHA: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_INVSRCALPHA; break; case BLENDMODE_RGB_MULTIPLY: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_DESTCOLOR; blenddst = D3DBLEND_ZERO; break; case BLENDMODE_ADD: blendenable = TRUE; blendop = D3DBLENDOP_ADD; blendsrc = D3DBLEND_SRCALPHA; blenddst = D3DBLEND_ONE; break; } // adjust the bits that changed if (blendenable != d3d->last_blendenable) { d3d->last_blendenable = blendenable; result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHABLENDENABLE, blendenable); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); } if (blendop != d3d->last_blendop) { d3d->last_blendop = blendop; result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_BLENDOP, blendop); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); } if (blendsrc != d3d->last_blendsrc) { d3d->last_blendsrc = blendsrc; result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SRCBLEND, blendsrc); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); } if (blenddst != d3d->last_blenddst) { d3d->last_blenddst = blenddst; result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DESTBLEND, blenddst); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_state call\n", (int)result); } } INLINE void reset_render_states(d3d_info *d3d) { // this ensures subsequent calls to the above setters will force-update the data d3d->last_texture = (texture_info *)~0; d3d->last_filter = -1; d3d->last_blendenable = -1; d3d->last_blendop = -1; d3d->last_blendsrc = -1; d3d->last_blenddst = -1; d3d->last_wrap = -1; } //============================================================ // PROTOTYPES //============================================================ // core functions static void drawd3d_exit(void); static int drawd3d_window_init(win_window_info *window); static void drawd3d_window_destroy(win_window_info *window); static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window); static int drawd3d_window_draw(win_window_info *window, HDC dc, int update); // devices static int device_create(win_window_info *window); static int device_create_resources(d3d_info *d3d); static void device_delete(d3d_info *d3d); static void device_delete_resources(d3d_info *d3d); static int device_verify_caps(d3d_info *d3d); static int device_test_cooperative(d3d_info *d3d); // video modes static int config_adapter_mode(win_window_info *window); static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor); static void pick_best_mode(win_window_info *window); static int update_window_size(win_window_info *window); // drawing static void draw_line(d3d_info *d3d, const render_primitive *prim); static void draw_quad(d3d_info *d3d, const render_primitive *prim); // primitives static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts); static void primitive_flush_pending(d3d_info *d3d); // textures static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags); static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture); static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags); static void texture_prescale(d3d_info *d3d, texture_info *texture); static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim); static void texture_update(d3d_info *d3d, const render_primitive *prim); //============================================================ // drawd3d_init //============================================================ int drawd3d_init(win_draw_callbacks *callbacks) { int version = options_get_int(mame_options(), WINOPTION_D3DVERSION); d3dintf = NULL; // try Direct3D 9 if requested if (version >= 9) d3dintf = drawd3d9_init(); // if that didn't work, try Direct3D 8 if (d3dintf == NULL && version >= 8) d3dintf = drawd3d8_init(); // if we failed, note the error if (d3dintf == NULL) { mame_printf_error("Unable to initialize Direct3D.\n"); return 1; } // fill in the callbacks callbacks->exit = drawd3d_exit; callbacks->window_init = drawd3d_window_init; callbacks->window_get_primitives = drawd3d_window_get_primitives; callbacks->window_draw = drawd3d_window_draw; callbacks->window_destroy = drawd3d_window_destroy; return 0; } //============================================================ // drawd3d_exit //============================================================ static void drawd3d_exit(void) { if (d3dintf != NULL) (*d3dintf->d3d.release)(d3dintf); } //============================================================ // drawd3d_window_init //============================================================ static int drawd3d_window_init(win_window_info *window) { d3d_info *d3d; // allocate memory for our structures d3d = malloc_or_die(sizeof(*d3d)); memset(d3d, 0, sizeof(*d3d)); window->drawdata = d3d; // experimental: load a PNG to use for vector rendering; it is treated // as a brightness map d3d->vector_bitmap = render_load_png(NULL, "vector.png", NULL, NULL); if (d3d->vector_bitmap != NULL) { fillbitmap(d3d->vector_bitmap, MAKE_ARGB(0xff,0xff,0xff,0xff), NULL); d3d->vector_bitmap = render_load_png(NULL, "vector.png", d3d->vector_bitmap, NULL); } // configure the adapter for the mode we want if (config_adapter_mode(window)) goto error; // create the device immediately for the full screen case (defer for window mode) if (window->fullscreen && device_create(window)) goto error; return 0; error: drawd3d_window_destroy(window); mame_printf_error("Unable to initialize Direct3D.\n"); return 1; } //============================================================ // drawd3d_window_destroy //============================================================ static void drawd3d_window_destroy(win_window_info *window) { d3d_info *d3d = window->drawdata; // skip if nothing if (d3d == NULL) return; // delete the device device_delete(d3d); // experimental: free the vector PNG if (d3d->vector_bitmap != NULL) bitmap_free(d3d->vector_bitmap); // free the memory in the window free(d3d); window->drawdata = NULL; } //============================================================ // drawd3d_window_get_primitives //============================================================ static const render_primitive_list *drawd3d_window_get_primitives(win_window_info *window) { d3d_info *d3d = window->drawdata; RECT client; GetClientRect(window->hwnd, &client); if (rect_width(&client) > 0 && rect_height(&client) > 0) { render_target_set_bounds(window->target, rect_width(&client), rect_height(&client), winvideo_monitor_get_aspect(window->monitor)); render_target_set_max_update_rate(window->target, (d3d->refresh == 0) ? d3d->origmode.RefreshRate : d3d->refresh); } return render_target_get_primitives(window->target); } //============================================================ // drawd3d_window_draw //============================================================ static int drawd3d_window_draw(win_window_info *window, HDC dc, int update) { d3d_info *d3d = window->drawdata; const render_primitive *prim; HRESULT result; // if we haven't been created, just punt if (d3d == NULL) return 1; // if we have a device, check the cooperative level if (d3d->device != NULL) { int error = device_test_cooperative(d3d); if (error) return 1; } // in window mode, we need to track the window size if (!window->fullscreen || d3d->device == NULL) { // if the size changes, skip this update since the render target will be out of date if (update_window_size(window)) return 0; // if we have no device, after updating the size, return an error so GDI can try if (d3d->device == NULL) return 1; } mtlog_add("drawd3d_window_draw: begin"); // first update any textures osd_lock_acquire(window->primlist->lock); for (prim = window->primlist->head; prim != NULL; prim = prim->next) if (prim->texture.base != NULL) texture_update(d3d, prim); // begin the scene mtlog_add("drawd3d_window_draw: begin_scene"); result = (*d3dintf->device.begin_scene)(d3d->device); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result); d3d->lockedbuf = NULL; // loop over primitives mtlog_add("drawd3d_window_draw: primitive loop begin"); for (prim = window->primlist->head; prim != NULL; prim = prim->next) switch (prim->type) { case RENDER_PRIMITIVE_LINE: draw_line(d3d, prim); break; case RENDER_PRIMITIVE_QUAD: draw_quad(d3d, prim); break; } mtlog_add("drawd3d_window_draw: primitive loop end"); osd_lock_release(window->primlist->lock); // flush any pending polygons mtlog_add("drawd3d_window_draw: flush_pending begin"); primitive_flush_pending(d3d); mtlog_add("drawd3d_window_draw: flush_pending end"); // finish the scene mtlog_add("drawd3d_window_draw: end_scene begin"); result = (*d3dintf->device.end_scene)(d3d->device); mtlog_add("drawd3d_window_draw: end_scene end"); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result); // present the current buffers mtlog_add("drawd3d_window_draw: present begin"); result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0); mtlog_add("drawd3d_window_draw: present end"); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device present call\n", (int)result); return 0; } //============================================================ // device_create //============================================================ static int device_create(win_window_info *window) { d3d_info *d3d = window->drawdata; HRESULT result; int verify; // if a device exists, free it if (d3d->device != NULL) device_delete(d3d); // verify the caps verify = device_verify_caps(d3d); if (verify == 2) { mame_printf_error("Error: Device does not meet minimum requirements for Direct3D rendering\n"); return 1; } if (verify == 1) mame_printf_warning("Warning: Device may not perform well for Direct3D rendering\n"); // verify texture formats result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_A8R8G8B8); if (result != D3D_OK) { mame_printf_error("Error: A8R8G8B8 format textures not supported\n"); return 1; } // pick a YUV texture format d3d->yuv_format = D3DFMT_UYVY; result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_UYVY); if (result != D3D_OK) { d3d->yuv_format = D3DFMT_YUY2; result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, 0, D3DRTYPE_TEXTURE, D3DFMT_YUY2); if (result != D3D_OK) d3d->yuv_format = D3DFMT_A8R8G8B8; } mame_printf_verbose("Direct3D: YUV format = %s\n", (d3d->yuv_format == D3DFMT_YUY2) ? "YUY2" : (d3d->yuv_format == D3DFMT_UYVY) ? "UYVY" : "RGB"); try_again: // try for XRGB first d3d->screen_format = D3DFMT_X8R8G8B8; result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format); if (result != D3D_OK) { // if not, try for ARGB d3d->screen_format = D3DFMT_A8R8G8B8; result = (*d3dintf->d3d.check_device_format)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0, D3DRTYPE_TEXTURE, d3d->screen_format); if (result != D3D_OK && d3d->dynamic_supported) { d3d->dynamic_supported = FALSE; goto try_again; } if (result != D3D_OK) { mame_printf_error("Error: unable to configure a screen texture format\n"); return 1; } } // initialize the D3D presentation parameters memset(&d3d->presentation, 0, sizeof(d3d->presentation)); d3d->presentation.BackBufferWidth = d3d->width; d3d->presentation.BackBufferHeight = d3d->height; d3d->presentation.BackBufferFormat = d3d->pixformat; d3d->presentation.BackBufferCount = video_config.triplebuf ? 2 : 1; d3d->presentation.MultiSampleType = D3DMULTISAMPLE_NONE; d3d->presentation.SwapEffect = D3DSWAPEFFECT_DISCARD; d3d->presentation.hDeviceWindow = window->hwnd; d3d->presentation.Windowed = !window->fullscreen || HAS_WINDOW_MENU; d3d->presentation.EnableAutoDepthStencil = FALSE; d3d->presentation.AutoDepthStencilFormat = D3DFMT_D16; d3d->presentation.Flags = 0; d3d->presentation.FullScreen_RefreshRateInHz = d3d->refresh; d3d->presentation.PresentationInterval = ((video_config.triplebuf && window->fullscreen) || video_config.waitvsync || video_config.syncrefresh) ? D3DPRESENT_INTERVAL_ONE : D3DPRESENT_INTERVAL_IMMEDIATE; // create the D3D device result = (*d3dintf->d3d.create_device)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, win_window_list->hwnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_FPU_PRESERVE, &d3d->presentation, &d3d->device); if (result != D3D_OK) { mame_printf_error("Unable to create the Direct3D device (%08X)\n", (UINT32)result); return 1; } mame_printf_verbose("Direct3D: Device created at %dx%d\n", d3d->width, d3d->height); // set the max texture size render_target_set_max_texture_size(window->target, d3d->texture_max_width, d3d->texture_max_height); // set the gamma if we need to 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) { // warn if we can't do it if (!d3d->gamma_supported) mame_printf_warning("Direct3D: Warning - device does not support full screen gamma correction.\n"); else { D3DGAMMARAMP 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; (*d3dintf->device.set_gamma_ramp)(d3d->device, 0, &ramp); } } } return device_create_resources(d3d); } //============================================================ // device_create_resources //============================================================ static int device_create_resources(d3d_info *d3d) { HRESULT result; // allocate a vertex buffer to use result = (*d3dintf->device.create_vertex_buffer)(d3d->device, sizeof(d3d_vertex) * VERTEX_BUFFER_SIZE, D3DUSAGE_DYNAMIC | D3DUSAGE_SOFTWAREPROCESSING | D3DUSAGE_WRITEONLY, VERTEX_FORMAT, D3DPOOL_DEFAULT, &d3d->vertexbuf); if (result != D3D_OK) { mame_printf_error("Error creating vertex buffer (%08X)", (UINT32)result); return 1; } // set the vertex format result = (*d3dintf->device.set_vertex_shader)(d3d->device, VERTEX_FORMAT); if (result != D3D_OK) { mame_printf_error("Error setting vertex shader (%08X)", (UINT32)result); return 1; } // set the fixed render state result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZENABLE, D3DZB_FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FILLMODE, D3DFILL_SOLID); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SHADEMODE, D3DSHADE_FLAT); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZWRITEENABLE, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHATESTENABLE, TRUE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LASTPIXEL, TRUE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CULLMODE, D3DCULL_NONE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ZFUNC, D3DCMP_LESS); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAREF, 0); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_ALPHAFUNC, D3DCMP_GREATER); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_DITHERENABLE, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_FOGENABLE, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_SPECULARENABLE, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_STENCILENABLE, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_WRAP0, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_CLIPPING, TRUE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_LIGHTING, FALSE); result = (*d3dintf->device.set_render_state)(d3d->device, D3DRS_COLORVERTEX, TRUE); result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_COLOROP, D3DTOP_MODULATE); result = (*d3dintf->device.set_texture_stage_state)(d3d->device, 0, D3DTSS_ALPHAOP, D3DTOP_MODULATE); // reset the local states to force updates reset_render_states(d3d); // clear the buffer result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0); result = (*d3dintf->device.present)(d3d->device, NULL, NULL, NULL, NULL, 0); // experimental: if we have a vector bitmap, create a texture for it if (d3d->vector_bitmap != NULL) { render_texinfo texture; // fake in the basic data so it looks like it came from render.c texture.base = d3d->vector_bitmap->base; texture.rowpixels = d3d->vector_bitmap->rowpixels; texture.width = d3d->vector_bitmap->width; texture.height = d3d->vector_bitmap->height; texture.palette = NULL; texture.seqid = 0; // now create it d3d->vector_texture = texture_create(d3d, &texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32)); } return 0; } //============================================================ // device_delete //============================================================ static void device_delete(d3d_info *d3d) { // free resources device_delete_resources(d3d); // free the device itself if (d3d->device != NULL) (*d3dintf->device.release)(d3d->device); d3d->device = NULL; } //============================================================ // device_delete_resources //============================================================ static void device_delete_resources(d3d_info *d3d) { // free all textures while (d3d->texlist != NULL) { texture_info *tex = d3d->texlist; d3d->texlist = tex->next; if (tex->d3dfinaltex != NULL) (*d3dintf->texture.release)(tex->d3dfinaltex); if (tex->d3dtex != NULL && tex->d3dtex != tex->d3dfinaltex) (*d3dintf->texture.release)(tex->d3dtex); if (tex->d3dsurface != NULL) (*d3dintf->surface.release)(tex->d3dsurface); free(tex); } // free the vertex buffer if (d3d->vertexbuf != NULL) (*d3dintf->vertexbuf.release)(d3d->vertexbuf); d3d->vertexbuf = NULL; } //============================================================ // device_verify_caps //============================================================ static int device_verify_caps(d3d_info *d3d) { int retval = 0; HRESULT result; DWORD tempcaps; // fetch a few core caps result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_CAPS, &d3d->texture_caps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_ASPECT, &d3d->texture_max_aspect); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_WIDTH, &d3d->texture_max_width); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_MAX_TEXTURE_HEIGHT, &d3d->texture_max_height); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); // verify presentation capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_PRESENTATION_INTERVALS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DPRESENT_INTERVAL_IMMEDIATE)) { mame_printf_verbose("Direct3D: Error - Device does not support immediate presentations\n"); retval = 2; } if (!(tempcaps & D3DPRESENT_INTERVAL_ONE)) { mame_printf_verbose("Direct3D: Error - Device does not support per-refresh presentations\n"); retval = 2; } // verify device capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DEV_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DDEVCAPS_CANRENDERAFTERFLIP)) { mame_printf_verbose("Direct3D: Warning - Device does not support queued rendering after a page flip\n"); retval = 1; } if (!(tempcaps & D3DDEVCAPS_HWRASTERIZATION)) { mame_printf_verbose("Direct3D: Warning - Device does not support hardware rasterization\n"); retval = 1; } // verify source blend capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_SRCBLEND_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DPBLENDCAPS_SRCALPHA)) { mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with source alpha\n"); retval = 2; } if (!(tempcaps & D3DPBLENDCAPS_DESTCOLOR)) { mame_printf_verbose("Direct3D: Error - Device does not support source alpha blending with destination color\n"); retval = 2; } // verify destination blend capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_DSTBLEND_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DPBLENDCAPS_ZERO)) { mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with zero\n"); retval = 2; } if (!(tempcaps & D3DPBLENDCAPS_ONE)) { mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with one\n"); retval = 2; } if (!(tempcaps & D3DPBLENDCAPS_INVSRCALPHA)) { mame_printf_verbose("Direct3D: Error - Device does not support dest alpha blending with inverted source alpha\n"); retval = 2; } // verify texture capabilities if (!(d3d->texture_caps & D3DPTEXTURECAPS_ALPHA)) { mame_printf_verbose("Direct3D: Error - Device does not support texture alpha\n"); retval = 2; } // verify texture filter capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_FILTER_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DPTFILTERCAPS_MAGFPOINT)) { mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for magnification\n"); retval = 1; } if (!(tempcaps & D3DPTFILTERCAPS_MAGFLINEAR)) { mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for magnification\n"); retval = 1; } if (!(tempcaps & D3DPTFILTERCAPS_MINFPOINT)) { mame_printf_verbose("Direct3D: Warning - Device does not support point-sample texture filtering for minification\n"); retval = 1; } if (!(tempcaps & D3DPTFILTERCAPS_MINFLINEAR)) { mame_printf_verbose("Direct3D: Warning - Device does not support bilinear texture filtering for minification\n"); retval = 1; } // verify texture addressing capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_ADDRESS_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DPTADDRESSCAPS_CLAMP)) { mame_printf_verbose("Direct3D: Warning - Device does not support texture clamping\n"); retval = 1; } if (!(tempcaps & D3DPTADDRESSCAPS_WRAP)) { mame_printf_verbose("Direct3D: Warning - Device does not support texture wrapping\n"); retval = 1; } // verify texture operation capabilities result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_TEXTURE_OP_CAPS, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); if (!(tempcaps & D3DTEXOPCAPS_MODULATE)) { mame_printf_verbose("Direct3D: Warning - Device does not support texture modulation\n"); retval = 1; } // set a simpler flag to indicate mod2x and mod4x texture modes d3d->mod2x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE2X) != 0); d3d->mod4x_supported = ((tempcaps & D3DTEXOPCAPS_MODULATE4X) != 0); // set a simpler flag to indicate we can use a gamma ramp result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_CAPS2, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); d3d->dynamic_supported = ((tempcaps & D3DCAPS2_DYNAMICTEXTURES) != 0); d3d->gamma_supported = ((tempcaps & D3DCAPS2_FULLSCREENGAMMA) != 0); if (d3d->dynamic_supported) mame_printf_verbose("Direct3D: Using dynamic textures\n"); // set a simpler flag to indicate we can use StretchRect result = (*d3dintf->d3d.get_caps_dword)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, CAPS_STRETCH_RECT_FILTER, &tempcaps); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during get_caps_dword call\n", (int)result); d3d->stretch_supported = ((tempcaps & D3DPTFILTERCAPS_MAGFPOINT) != 0); if (d3d->stretch_supported && video_config.prescale > 1) mame_printf_verbose("Direct3D: Using StretchRect for prescaling\n"); return retval; } //============================================================ // device_test_cooperative //============================================================ static int device_test_cooperative(d3d_info *d3d) { HRESULT result; // check our current status; if we lost the device, punt to GDI result = (*d3dintf->device.test_cooperative_level)(d3d->device); if (result == D3DERR_DEVICELOST) return 1; // if we're able to reset ourselves, try it if (result == D3DERR_DEVICENOTRESET) { mame_printf_verbose("Direct3D: resetting device\n"); // free all existing resources and call reset on the device device_delete_resources(d3d); result = (*d3dintf->device.reset)(d3d->device, &d3d->presentation); // if it didn't work, punt to GDI if (result != D3D_OK) return 1; // try to create the resources again; if that didn't work, delete the whole thing if (device_create_resources(d3d)) { mame_printf_verbose("Direct3D: failed to recreate resources for device; failing permanently\n"); device_delete(d3d); return 1; } } return 0; } //============================================================ // config_adapter_mode //============================================================ static int config_adapter_mode(win_window_info *window) { d3d_adapter_identifier identifier; d3d_info *d3d = window->drawdata; HRESULT result; // choose the monitor number d3d->adapter = get_adapter_for_monitor(d3d, window->monitor); // get the identifier result = (*d3dintf->d3d.get_adapter_identifier)(d3dintf, d3d->adapter, 0, &identifier); if (result != D3D_OK) { mame_printf_error("Error getting identifier for adapter #%d\n", d3d->adapter); return 1; } mame_printf_verbose("Direct3D: Configuring adapter #%d = %s\n", d3d->adapter, identifier.Description); // get the current display mode result = (*d3dintf->d3d.get_adapter_display_mode)(d3dintf, d3d->adapter, &d3d->origmode); if (result != D3D_OK) { mame_printf_error("Error getting mode for adapter #%d\n", d3d->adapter); return 1; } // choose a resolution: window mode case if (!window->fullscreen || !video_config.switchres || HAS_WINDOW_MENU) { RECT client; // bounds are from the window client rect GetClientRect(window->hwnd, &client); d3d->width = client.right - client.left; d3d->height = client.bottom - client.top; // pix format is from the current mode d3d->pixformat = d3d->origmode.Format; d3d->refresh = 0; // make sure it's a pixel format we can get behind if (d3d->pixformat != D3DFMT_X1R5G5B5 && d3d->pixformat != D3DFMT_R5G6B5 && d3d->pixformat != D3DFMT_X8R8G8B8) { char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice); if (utf8_device != NULL) { mame_printf_error("Device %s currently in an unsupported mode\n", utf8_device); free(utf8_device); } return 1; } } // choose a resolution: full screen mode case else { // default to the current mode exactly d3d->width = d3d->origmode.Width; d3d->height = d3d->origmode.Height; d3d->pixformat = d3d->origmode.Format; d3d->refresh = d3d->origmode.RefreshRate; // if we're allowed to switch resolutions, override with something better if (video_config.switchres) pick_best_mode(window); } // see if we can handle the device type result = (*d3dintf->d3d.check_device_type)(d3dintf, d3d->adapter, D3DDEVTYPE_HAL, d3d->pixformat, d3d->pixformat, !window->fullscreen); if (result != D3D_OK) { char *utf8_device = utf8_from_tstring(window->monitor->info.szDevice); if (utf8_device != NULL) { mame_printf_error("Proposed video mode not supported on device %s\n", utf8_device); free(utf8_device); } return 1; } return 0; } //============================================================ // get_adapter_for_monitor //============================================================ static int get_adapter_for_monitor(d3d_info *d3d, win_monitor_info *monitor) { int maxadapter = (*d3dintf->d3d.get_adapter_count)(d3dintf); int adapternum; // iterate over adapters until we error or find a match for (adapternum = 0; adapternum < maxadapter; adapternum++) { HMONITOR curmonitor; // get the monitor for this adapter curmonitor = (*d3dintf->d3d.get_adapter_monitor)(d3dintf, adapternum); // if we match the proposed monitor, this is it if (curmonitor == monitor->handle) return adapternum; } // default to the default return D3DADAPTER_DEFAULT; } //============================================================ // pick_best_mode //============================================================ static void pick_best_mode(win_window_info *window) { double target_refresh = ATTOSECONDS_TO_HZ(Machine->screen[0].refresh); INT32 target_width, target_height; d3d_info *d3d = window->drawdata; INT32 minwidth, minheight; float best_score = 0.0; int maxmodes; int modenum; // 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, &minwidth, &minheight); // use those as the target for now target_width = minwidth; target_height = minheight; // determine the maximum number of modes maxmodes = (*d3dintf->d3d.get_adapter_mode_count)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8); // enumerate all the video modes and find the best match mame_printf_verbose("Direct3D: Selecting video mode...\n"); for (modenum = 0; modenum < maxmodes; modenum++) { float size_score, refresh_score, final_score; D3DDISPLAYMODE mode; HRESULT result; // check this mode result = (*d3dintf->d3d.enum_adapter_modes)(d3dintf, d3d->adapter, D3DFMT_X8R8G8B8, modenum, &mode); if (result != D3D_OK) break; // skip non-32 bit modes if (mode.Format != D3DFMT_X8R8G8B8) continue; // compute initial score based on difference between target and current size_score = 1.0f / (1.0f + fabs(mode.Width - target_width) + fabs(mode.Height - target_height)); // if the mode is too small, give a big penalty if (mode.Width < minwidth || mode.Height < minheight) size_score *= 0.01f; // if mode is smaller than we'd like, it only scores up to 0.1 if (mode.Width < target_width || mode.Height < target_height) size_score *= 0.1f; // if we're looking for a particular mode, that's a winner if (mode.Width == window->maxwidth && mode.Height == window->maxheight) size_score = 2.0f; // compute refresh score refresh_score = 1.0f / (1.0f + fabs((double)mode.RefreshRate - target_refresh)); // if refresh is smaller than we'd like, it only scores up to 0.1 if ((double)mode.RefreshRate < target_refresh) refresh_score *= 0.1; // if we're looking for a particular refresh, make sure it matches if (mode.RefreshRate == 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", mode.Width, mode.Height, mode.RefreshRate, final_score * 1000.0f); if (final_score > best_score) { best_score = final_score; d3d->width = mode.Width; d3d->height = mode.Height; d3d->pixformat = mode.Format; d3d->refresh = mode.RefreshRate; } } mame_printf_verbose("Direct3D: Mode selected = %4dx%4d@%3dHz\n", d3d->width, d3d->height, d3d->refresh); } //============================================================ // update_window_size //============================================================ static int update_window_size(win_window_info *window) { d3d_info *d3d = window->drawdata; RECT client; // get the current window bounds GetClientRect(window->hwnd, &client); // if we have a device and matching width/height, nothing to do if (d3d->device != NULL && rect_width(&client) == d3d->width && rect_height(&client) == d3d->height) { // clear out any pending resizing if the area didn't change if (window->resize_state == RESIZE_STATE_PENDING) window->resize_state = RESIZE_STATE_NORMAL; return FALSE; } // if we're in the middle of resizing, leave it alone as well if (window->resize_state == RESIZE_STATE_RESIZING) return FALSE; // set the new bounds and create the device again d3d->width = rect_width(&client); d3d->height = rect_height(&client); if (device_create(window)) return FALSE; // reset the resize state to normal, and indicate we made a change window->resize_state = RESIZE_STATE_NORMAL; return TRUE; } //============================================================ // draw_line //============================================================ static void draw_line(d3d_info *d3d, const render_primitive *prim) { const line_aa_step *step = line_aa_4step; render_bounds b0, b1; d3d_vertex *vertex; INT32 r, g, b, a; poly_info *poly; float effwidth; DWORD color; int i; // compute the effective width based on the direction of the line effwidth = prim->width; if (effwidth < 0.5f) effwidth = 0.5f; // determine the bounds of a quad to draw this line render_line_to_quad(&prim->bounds, effwidth, &b0, &b1); // iterate over AA steps for (step = PRIMFLAG_GET_ANTIALIAS(prim->flags) ? line_aa_4step : line_aa_1step; step->weight != 0; step++) { // get a pointer to the vertex buffer vertex = primitive_alloc(d3d, 4); if (vertex == NULL) return; // rotate the unit vector by 135 degrees and add to point 0 vertex[0].x = b0.x0 + step->xoffs; vertex[0].y = b0.y0 + step->yoffs; // rotate the unit vector by -135 degrees and add to point 0 vertex[1].x = b0.x1 + step->xoffs; vertex[1].y = b0.y1 + step->yoffs; // rotate the unit vector by 45 degrees and add to point 1 vertex[2].x = b1.x0 + step->xoffs; vertex[2].y = b1.y0 + step->yoffs; // rotate the unit vector by -45 degrees and add to point 1 vertex[3].x = b1.x1 + step->xoffs; vertex[3].y = b1.y1 + step->yoffs; // determine the color of the line r = (INT32)(prim->color.r * step->weight * 255.0f); g = (INT32)(prim->color.g * step->weight * 255.0f); b = (INT32)(prim->color.b * step->weight * 255.0f); a = (INT32)(prim->color.a * 255.0f); if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; if (a > 255) a = 255; color = D3DCOLOR_ARGB(a, r, g, b); // if we have a texture to use for the vectors, use it here if (d3d->vector_texture != NULL) { vertex[0].u0 = d3d->vector_texture->ustart; vertex[0].v0 = d3d->vector_texture->vstart; vertex[2].u0 = d3d->vector_texture->ustop; vertex[2].v0 = d3d->vector_texture->vstart; vertex[1].u0 = d3d->vector_texture->ustart; vertex[1].v0 = d3d->vector_texture->vstop; vertex[3].u0 = d3d->vector_texture->ustop; vertex[3].v0 = d3d->vector_texture->vstop; } // set the color, Z parameters to standard values for (i = 0; i < 4; i++) { vertex[i].z = 0.0f; vertex[i].rhw = 1.0f; vertex[i].color = color; } // now add a polygon entry poly = &d3d->poly[d3d->numpolys++]; poly->type = D3DPT_TRIANGLESTRIP; poly->count = 2; poly->numverts = 4; poly->flags = prim->flags; poly->modmode = D3DTOP_MODULATE; poly->texture = d3d->vector_texture; } } //============================================================ // draw_quad //============================================================ static void draw_quad(d3d_info *d3d, const render_primitive *prim) { texture_info *texture = texture_find(d3d, prim); DWORD color, modmode; d3d_vertex *vertex; INT32 r, g, b, a; poly_info *poly; int i; // get a pointer to the vertex buffer vertex = primitive_alloc(d3d, 4); if (vertex == NULL) return; // fill in the vertexes clockwise vertex[0].x = prim->bounds.x0 - 0.5f; vertex[0].y = prim->bounds.y0 - 0.5f; vertex[1].x = prim->bounds.x1 - 0.5f; vertex[1].y = prim->bounds.y0 - 0.5f; vertex[2].x = prim->bounds.x0 - 0.5f; vertex[2].y = prim->bounds.y1 - 0.5f; vertex[3].x = prim->bounds.x1 - 0.5f; vertex[3].y = prim->bounds.y1 - 0.5f; // set the texture coordinates if (texture != NULL) { float du = texture->ustop - texture->ustart; float dv = texture->vstop - texture->vstart; vertex[0].u0 = texture->ustart + du * prim->texcoords.tl.u; vertex[0].v0 = texture->vstart + dv * prim->texcoords.tl.v; vertex[1].u0 = texture->ustart + du * prim->texcoords.tr.u; vertex[1].v0 = texture->vstart + dv * prim->texcoords.tr.v; vertex[2].u0 = texture->ustart + du * prim->texcoords.bl.u; vertex[2].v0 = texture->vstart + dv * prim->texcoords.bl.v; vertex[3].u0 = texture->ustart + du * prim->texcoords.br.u; vertex[3].v0 = texture->vstart + dv * prim->texcoords.br.v; } // determine the color, allowing for over modulation r = (INT32)(prim->color.r * 255.0f); g = (INT32)(prim->color.g * 255.0f); b = (INT32)(prim->color.b * 255.0f); a = (INT32)(prim->color.a * 255.0f); modmode = D3DTOP_MODULATE; if (texture != NULL) { if (d3d->mod2x_supported && (r > 255 || g > 255 || b > 255)) { if (d3d->mod4x_supported && (r > 2*255 || g > 2*255 || b > 2*255)) { r >>= 2; g >>= 2; b >>= 2; modmode = D3DTOP_MODULATE4X; } else { r >>= 1; g >>= 1; b >>= 1; modmode = D3DTOP_MODULATE2X; } } } if (r > 255) r = 255; if (g > 255) g = 255; if (b > 255) b = 255; if (a > 255) a = 255; color = D3DCOLOR_ARGB(a, r, g, b); // set the color, Z parameters to standard values for (i = 0; i < 4; i++) { vertex[i].z = 0.0f; vertex[i].rhw = 1.0f; vertex[i].color = color; } // now add a polygon entry poly = &d3d->poly[d3d->numpolys++]; poly->type = D3DPT_TRIANGLESTRIP; poly->count = 2; poly->numverts = 4; poly->flags = prim->flags; poly->modmode = modmode; poly->texture = texture; } //============================================================ // primitive_alloc //============================================================ static d3d_vertex *primitive_alloc(d3d_info *d3d, int numverts) { HRESULT result; // if we're going to overflow, flush if (d3d->lockedbuf != NULL && d3d->numverts + numverts >= VERTEX_BUFFER_SIZE) primitive_flush_pending(d3d); // if we don't have a lock, grab it now if (d3d->lockedbuf == NULL) { result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD); if (result != D3D_OK) return NULL; } // if we already have the lock and enough room, just return a pointer if (d3d->lockedbuf != NULL && d3d->numverts + numverts < VERTEX_BUFFER_SIZE) { int oldverts = d3d->numverts; d3d->numverts += numverts; return &d3d->lockedbuf[oldverts]; } return NULL; } //============================================================ // primitive_flush_pending //============================================================ static void primitive_flush_pending(d3d_info *d3d) { HRESULT result; int polynum; int vertnum; // ignore if we're not locked if (d3d->lockedbuf == NULL) return; // unlock the buffer result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result); d3d->lockedbuf = NULL; // set the stream result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex)); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result); // now do the polys for (polynum = vertnum = 0; polynum < d3d->numpolys; polynum++) { poly_info *poly = &d3d->poly[polynum]; int newfilter; // set the texture if different set_texture(d3d, poly->texture); // set filtering if different if (poly->texture != NULL) { newfilter = FALSE; if (PRIMFLAG_GET_SCREENTEX(poly->flags)) newfilter = video_config.filter; set_filter(d3d, newfilter); set_wrap(d3d, PRIMFLAG_GET_TEXWRAP(poly->flags)); set_modmode(d3d, poly->modmode); } // set the blendmode if different set_blendmode(d3d, PRIMFLAG_GET_BLENDMODE(poly->flags)); // add the primitives assert(vertnum + poly->numverts <= d3d->numverts); result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result); vertnum += poly->numverts; } // reset the vertex count d3d->numverts = 0; d3d->numpolys = 0; } //============================================================ // texture_create //============================================================ static texture_info *texture_create(d3d_info *d3d, const render_texinfo *texsource, UINT32 flags) { texture_info *texture; HRESULT result; // allocate a new texture texture = malloc_or_die(sizeof(*texture)); memset(texture, 0, sizeof(*texture)); // fill in the core data texture->hash = texture_compute_hash(texsource, flags); texture->flags = flags; texture->texinfo = *texsource; texture->xprescale = video_config.prescale; texture->yprescale = video_config.prescale; // compute the size texture_compute_size(d3d, texsource->width, texsource->height, texture); // non-screen textures are easy if (!PRIMFLAG_GET_SCREENTEX(flags)) { assert(PRIMFLAG_TEXFORMAT(flags) != TEXFORMAT_YUY16); result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &texture->d3dtex); if (result != D3D_OK) goto error; texture->d3dfinaltex = texture->d3dtex; texture->type = TEXTURE_TYPE_PLAIN; } // screen textures are allocated differently else { D3DFORMAT format; DWORD usage = d3d->dynamic_supported ? D3DUSAGE_DYNAMIC : 0; DWORD pool = d3d->dynamic_supported ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED; int maxdim = MAX(d3d->presentation.BackBufferWidth, d3d->presentation.BackBufferHeight); // pick the format if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_YUY16) format = d3d->yuv_format; else if (PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_ARGB32 || PRIMFLAG_GET_TEXFORMAT(flags) == TEXFORMAT_PALETTEA16) format = D3DFMT_A8R8G8B8; else format = d3d->screen_format; // don't prescale above screen size while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale >= 2 * maxdim) texture->xprescale--; while (texture->xprescale > 1 && texture->rawwidth * texture->xprescale > d3d->texture_max_width) texture->xprescale--; while (texture->yprescale > 1 && texture->rawheight * texture->yprescale >= 2 * maxdim) texture->yprescale--; while (texture->yprescale > 1 && texture->rawheight * texture->yprescale > d3d->texture_max_height) texture->yprescale--; if (texture->xprescale != video_config.prescale || texture->yprescale != video_config.prescale) mame_printf_verbose("Direct3D: adjusting prescale from %dx%d to %dx%d\n", video_config.prescale, video_config.prescale, texture->xprescale, texture->yprescale); // screen textures with no prescaling are pretty easy if (texture->xprescale == 1 && texture->yprescale == 1) { result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex); if (result != D3D_OK) goto error; texture->d3dfinaltex = texture->d3dtex; texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN; } // screen textures with prescaling require two allocations else { int scwidth, scheight; // use an offscreen plain surface for stretching if supported if (d3d->stretch_supported) { result = (*d3dintf->device.create_offscreen_plain_surface)(d3d->device, texture->rawwidth, texture->rawheight, format, D3DPOOL_DEFAULT, &texture->d3dsurface); if (result != D3D_OK) goto error; texture->type = TEXTURE_TYPE_SURFACE; } // otherwise, we allocate a dynamic texture for the source else { result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, usage, format, pool, &texture->d3dtex); if (result != D3D_OK) goto error; texture->type = d3d->dynamic_supported ? TEXTURE_TYPE_DYNAMIC : TEXTURE_TYPE_PLAIN; } // for the target surface, we allocate a render target texture scwidth = texture->rawwidth * texture->xprescale; scheight = texture->rawheight * texture->yprescale; result = (*d3dintf->device.create_texture)(d3d->device, scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, format, D3DPOOL_DEFAULT, &texture->d3dfinaltex); if (result != D3D_OK) goto error; } } // copy the data to the texture texture_set_data(d3d, texture, texsource, flags); // add us to the texture list texture->next = d3d->texlist; d3d->texlist = texture; return texture; error: if (texture->d3dsurface != NULL) (*d3dintf->surface.release)(texture->d3dsurface); if (texture->d3dtex != NULL) (*d3dintf->texture.release)(texture->d3dtex); free(texture); return NULL; } //============================================================ // texture_compute_size //============================================================ static void texture_compute_size(d3d_info *d3d, int texwidth, int texheight, texture_info *texture) { int finalheight = texheight; int finalwidth = texwidth; // if we're not wrapping, add a 1 pixel border on all sides #if ENABLE_BORDER_PIX texture->borderpix = !(texture->flags & PRIMFLAG_TEXWRAP_MASK); #else texture->borderpix = FALSE; #endif if (texture->borderpix) { finalwidth += 2; finalheight += 2; } // round width/height up to nearest power of 2 if we need to if (!(d3d->texture_caps & D3DPTEXTURECAPS_NONPOW2CONDITIONAL)) { // first the width if (finalwidth & (finalwidth - 1)) { finalwidth |= finalwidth >> 1; finalwidth |= finalwidth >> 2; finalwidth |= finalwidth >> 4; finalwidth |= finalwidth >> 8; finalwidth++; } // then the height if (finalheight & (finalheight - 1)) { finalheight |= finalheight >> 1; finalheight |= finalheight >> 2; finalheight |= finalheight >> 4; finalheight |= finalheight >> 8; finalheight++; } } // round up to square if we need to if (d3d->texture_caps & D3DPTEXTURECAPS_SQUAREONLY) { if (finalwidth < finalheight) finalwidth = finalheight; else finalheight = finalwidth; } // adjust the aspect ratio if we need to while (finalwidth < finalheight && finalheight / finalwidth > d3d->texture_max_aspect) finalwidth *= 2; while (finalheight < finalwidth && finalwidth / finalheight > d3d->texture_max_aspect) finalheight *= 2; // if we added pixels for the border, and that just barely pushed us over, take it back if (texture->borderpix && ((finalwidth > d3d->texture_max_width && finalwidth - 2 <= d3d->texture_max_width) || (finalheight > d3d->texture_max_height && finalheight - 2 <= d3d->texture_max_height))) { texture->borderpix = FALSE; finalwidth -= 2; finalheight -= 2; } // if we're above the max width/height, do what? if (finalwidth > d3d->texture_max_width || finalheight > d3d->texture_max_height) { static int printed = FALSE; if (!printed) mame_printf_warning("Texture too big! (wanted: %dx%d, max is %dx%d)\n", finalwidth, finalheight, (int)d3d->texture_max_width, (int)d3d->texture_max_height); printed = TRUE; } // compute the U/V scale factors if (texture->borderpix) { texture->ustart = 1.0f / (float)finalwidth; texture->ustop = (float)(texwidth + 1) / (float)finalwidth; texture->vstart = 1.0f / (float)finalheight; texture->vstop = (float)(texheight + 1) / (float)finalheight; } else { texture->ustart = 0.0f; texture->ustop = (float)texwidth / (float)finalwidth; texture->vstart = 0.0f; texture->vstop = (float)texheight / (float)finalheight; } // set the final values texture->rawwidth = finalwidth; texture->rawheight = finalheight; } //============================================================ // copyline_palette16 //============================================================ INLINE void copyline_palette16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; if (borderpix) *dst++ = 0xff000000 | palette[*src]; for (x = 0; x < width; x++) *dst++ = 0xff000000 | palette[*src++]; if (borderpix) *dst++ = 0xff000000 | palette[*--src]; } //============================================================ // copyline_palettea16 //============================================================ INLINE void copyline_palettea16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; if (borderpix) *dst++ = palette[*src]; for (x = 0; x < width; x++) *dst++ = palette[*src++]; if (borderpix) *dst++ = palette[*--src]; } //============================================================ // copyline_rgb15 //============================================================ INLINE void copyline_rgb15(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT16 pix = *src; *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; } for (x = 0; x < width; x++) { UINT16 pix = *src++; *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; } if (borderpix) { UINT16 pix = *--src; *dst++ = 0xff000000 | palette[0x40 + ((pix >> 10) & 0x1f)] | palette[0x20 + ((pix >> 5) & 0x1f)] | palette[0x00 + ((pix >> 0) & 0x1f)]; } } // direct case else { if (borderpix) { UINT16 pix = *src; UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); } for (x = 0; x < width; x++) { UINT16 pix = *src++; UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); } if (borderpix) { UINT16 pix = *--src; UINT32 color = ((pix & 0x7c00) << 9) | ((pix & 0x03e0) << 6) | ((pix & 0x001f) << 3); *dst++ = 0xff000000 | color | ((color >> 5) & 0x070707); } } } //============================================================ // copyline_rgb32 //============================================================ INLINE void copyline_rgb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT32 srcpix = *src; *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } for (x = 0; x < width; x++) { UINT32 srcpix = *src++; *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } if (borderpix) { UINT32 srcpix = *--src; *dst++ = 0xff000000 | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } } // direct case else { if (borderpix) *dst++ = 0xff000000 | *src; for (x = 0; x < width; x++) *dst++ = 0xff000000 | *src++; if (borderpix) *dst++ = 0xff000000 | *--src; } } //============================================================ // copyline_argb32 //============================================================ INLINE void copyline_argb32(UINT32 *dst, const UINT32 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT32 srcpix = *src; *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } for (x = 0; x < width; x++) { UINT32 srcpix = *src++; *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } if (borderpix) { UINT32 srcpix = *--src; *dst++ = (srcpix & 0xff000000) | palette[0x200 + RGB_RED(srcpix)] | palette[0x100 + RGB_GREEN(srcpix)] | palette[RGB_BLUE(srcpix)]; } } // direct case else { if (borderpix) *dst++ = *src; for (x = 0; x < width; x++) *dst++ = *src++; if (borderpix) *dst++ = *--src; } } //============================================================ // copyline_yuy16_to_yuy2 //============================================================ INLINE void copyline_yuy16_to_yuy2(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT16 srcpix = *src; *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); } for (x = 0; x < width; x++) { UINT16 srcpix = *src++; *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); } if (borderpix) { UINT16 srcpix = *--src; *dst++ = palette[0x000 + (srcpix >> 8)] | (srcpix << 8); } } // direct case else { if (borderpix) { UINT16 srcpix = *src; *dst++ = (srcpix >> 8) | (srcpix << 8); } for (x = 0; x < width; x++) { UINT16 srcpix = *src++; *dst++ = (srcpix >> 8) | (srcpix << 8); } if (borderpix) { UINT16 srcpix = *--src; *dst++ = (srcpix >> 8) | (srcpix << 8); } } } //============================================================ // copyline_yuy16_to_uyvy //============================================================ INLINE void copyline_yuy16_to_uyvy(UINT16 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT16 srcpix = *src; *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); } for (x = 0; x < width; x++) { UINT16 srcpix = *src++; *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); } if (borderpix) { UINT16 srcpix = *--src; *dst++ = palette[0x100 + (srcpix >> 8)] | (srcpix & 0xff); } } // direct case else { if (borderpix) { UINT16 srcpix = *src; *dst++ = (srcpix >> 8) | (srcpix << 8); } for (x = 0; x < width; x++) { UINT16 srcpix = *src++; *dst++ = (srcpix >> 8) | (srcpix << 8); } if (borderpix) { UINT16 srcpix = *--src; *dst++ = (srcpix >> 8) | (srcpix << 8); } } } //============================================================ // copyline_yuy16_to_argb //============================================================ INLINE void copyline_yuy16_to_argb(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int borderpix) { int x; // palette (really RGB map) case if (palette != NULL) { if (borderpix) { UINT16 srcpix0 = src[0]; UINT16 srcpix1 = src[1]; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); } for (x = 0; x < width / 2; x++) { UINT16 srcpix0 = *src++; UINT16 srcpix1 = *src++; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); } if (borderpix) { UINT16 srcpix1 = *--src; UINT16 srcpix0 = *--src; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); } } // direct case else { if (borderpix) { UINT16 srcpix0 = src[0]; UINT16 srcpix1 = src[1]; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); } for (x = 0; x < width / 2; x++) { UINT16 srcpix0 = *src++; UINT16 srcpix1 = *src++; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); } if (borderpix) { UINT16 srcpix1 = *--src; UINT16 srcpix0 = *--src; UINT8 cb = srcpix0 & 0xff; UINT8 cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); } } } //============================================================ // texture_set_data //============================================================ static void texture_set_data(d3d_info *d3d, texture_info *texture, const render_texinfo *texsource, UINT32 flags) { D3DLOCKED_RECT rect; HRESULT result; int miny, maxy; int dsty; // lock the texture switch (texture->type) { default: case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, 0); break; case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.lock_rect)(texture->d3dtex, 0, &rect, NULL, D3DLOCK_DISCARD); break; case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.lock_rect)(texture->d3dsurface, &rect, NULL, D3DLOCK_DISCARD); break; } if (result != D3D_OK) return; // loop over Y miny = 0 - texture->borderpix; maxy = texsource->height + texture->borderpix; for (dsty = miny; dsty < maxy; dsty++) { int srcy = (dsty < 0) ? 0 : (dsty >= texsource->height) ? texsource->height - 1 : dsty; void *dst = (BYTE *)rect.pBits + (dsty + texture->borderpix) * rect.Pitch; // switch off of the format and switch (PRIMFLAG_GET_TEXFORMAT(flags)) { case TEXFORMAT_PALETTE16: copyline_palette16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; case TEXFORMAT_PALETTEA16: copyline_palettea16(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; case TEXFORMAT_RGB15: copyline_rgb15(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; case TEXFORMAT_RGB32: copyline_rgb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; case TEXFORMAT_ARGB32: copyline_argb32(dst, (UINT32 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; case TEXFORMAT_YUY16: if (d3d->yuv_format == D3DFMT_YUY2) copyline_yuy16_to_yuy2(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); else if (d3d->yuv_format == D3DFMT_UYVY) copyline_yuy16_to_uyvy(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); else copyline_yuy16_to_argb(dst, (UINT16 *)texsource->base + srcy * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix); break; default: mame_printf_error("Unknown texture blendmode=%d format=%d\n", PRIMFLAG_GET_BLENDMODE(flags), PRIMFLAG_GET_TEXFORMAT(flags)); break; } } // unlock switch (texture->type) { default: case TEXTURE_TYPE_PLAIN: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break; case TEXTURE_TYPE_DYNAMIC: result = (*d3dintf->texture.unlock_rect)(texture->d3dtex, 0); break; case TEXTURE_TYPE_SURFACE: result = (*d3dintf->surface.unlock_rect)(texture->d3dsurface); break; } if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture unlock_rect call\n", (int)result); // prescale texture_prescale(d3d, texture); } //============================================================ // texture_prescale //============================================================ static void texture_prescale(d3d_info *d3d, texture_info *texture) { d3d_surface *surface; HRESULT result; int i; // if we don't need to, just skip it if (texture->d3dtex == texture->d3dfinaltex) return; // for all cases, we need to get the surface of the render target result = (*d3dintf->texture.get_surface_level)(texture->d3dfinaltex, 0, &surface); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during texture get_surface_level call\n", (int)result); // if we have an offscreen plain surface, we can just StretchRect to it if (texture->type == TEXTURE_TYPE_SURFACE) { RECT source, dest; assert(texture->d3dsurface != NULL); // set the source bounds source.left = source.top = 0; source.right = texture->texinfo.width + 2 * texture->borderpix; source.bottom = texture->texinfo.height + 2 * texture->borderpix; // set the target bounds dest = source; dest.right *= texture->xprescale; dest.bottom *= texture->yprescale; // do the stretchrect result = (*d3dintf->device.stretch_rect)(d3d->device, texture->d3dsurface, &source, surface, &dest, D3DTEXF_POINT); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device stretct_rect call\n", (int)result); } // if we are using a texture render target, we need to do more preparations else { d3d_surface *backbuffer; assert(texture->d3dtex != NULL); // first remember the original render target and set the new one result = (*d3dintf->device.get_render_target)(d3d->device, 0, &backbuffer); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device get_render_target call\n", (int)result); result = (*d3dintf->device.set_render_target)(d3d->device, 0, surface); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result); reset_render_states(d3d); // start the scene result = (*d3dintf->device.begin_scene)(d3d->device); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device begin_scene call\n", (int)result); // configure the rendering pipeline set_filter(d3d, FALSE); set_blendmode(d3d, BLENDMODE_NONE); result = (*d3dintf->device.set_texture)(d3d->device, 0, texture->d3dtex); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_texture call\n", (int)result); // lock the vertex buffer result = (*d3dintf->vertexbuf.lock)(d3d->vertexbuf, 0, 0, (VOID **)&d3d->lockedbuf, D3DLOCK_DISCARD); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer lock call\n", (int)result); // configure the X/Y coordinates on the target surface d3d->lockedbuf[0].x = -0.5f; d3d->lockedbuf[0].y = -0.5f; d3d->lockedbuf[1].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f; d3d->lockedbuf[1].y = -0.5f; d3d->lockedbuf[2].x = -0.5f; d3d->lockedbuf[2].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f; d3d->lockedbuf[3].x = (float)((texture->texinfo.width + 2 * texture->borderpix) * texture->xprescale) - 0.5f; d3d->lockedbuf[3].y = (float)((texture->texinfo.height + 2 * texture->borderpix) * texture->yprescale) - 0.5f; // configure the U/V coordintes on the source texture d3d->lockedbuf[0].u0 = 0.0f; d3d->lockedbuf[0].v0 = 0.0f; d3d->lockedbuf[1].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth; d3d->lockedbuf[1].v0 = 0.0f; d3d->lockedbuf[2].u0 = 0.0f; d3d->lockedbuf[2].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight; d3d->lockedbuf[3].u0 = (float)(texture->texinfo.width + 2 * texture->borderpix) / (float)texture->rawwidth; d3d->lockedbuf[3].v0 = (float)(texture->texinfo.height + 2 * texture->borderpix) / (float)texture->rawheight; // reset the remaining vertex parameters for (i = 0; i < 4; i++) { d3d->lockedbuf[i].z = 0.0f; d3d->lockedbuf[i].rhw = 1.0f; d3d->lockedbuf[i].color = D3DCOLOR_ARGB(0xff,0xff,0xff,0xff); } // unlock the vertex buffer result = (*d3dintf->vertexbuf.unlock)(d3d->vertexbuf); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during vertex buffer unlock call\n", (int)result); d3d->lockedbuf = NULL; // set the stream and draw the triangle strip result = (*d3dintf->device.set_stream_source)(d3d->device, 0, d3d->vertexbuf, sizeof(d3d_vertex)); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_stream_source call\n", (int)result); result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLESTRIP, 0, 2); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device draw_primitive call\n", (int)result); // end the scene result = (*d3dintf->device.end_scene)(d3d->device); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device end_scene call\n", (int)result); // reset the render target and release our reference to the backbuffer result = (*d3dintf->device.set_render_target)(d3d->device, 0, backbuffer); if (result != D3D_OK) mame_printf_verbose("Direct3D: Error %08X during device set_render_target call\n", (int)result); (*d3dintf->surface.release)(backbuffer); reset_render_states(d3d); } // release our reference to the target surface (*d3dintf->surface.release)(surface); } //============================================================ // texture_find //============================================================ static texture_info *texture_find(d3d_info *d3d, const render_primitive *prim) { UINT32 texhash = texture_compute_hash(&prim->texture, prim->flags); texture_info *texture; // find a match for (texture = d3d->texlist; texture != NULL; texture = texture->next) if (texture->hash == texhash && texture->texinfo.base == prim->texture.base && texture->texinfo.width == prim->texture.width && texture->texinfo.height == prim->texture.height && ((texture->flags ^ prim->flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0) return texture; // nothing found return NULL; } //============================================================ // texture_update //============================================================ static void texture_update(d3d_info *d3d, const render_primitive *prim) { texture_info *texture = texture_find(d3d, prim); // if we didn't find one, create a new texture if (texture == NULL) texture = texture_create(d3d, &prim->texture, prim->flags); // if we found it, but with a different seqid, copy the data if (texture->texinfo.seqid != prim->texture.seqid) { texture_set_data(d3d, texture, &prim->texture, prim->flags); texture->texinfo.seqid = prim->texture.seqid; } }