//============================================================
//
// 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 <windows.h>
#include <tchar.h>
#include <mmsystem.h>
#include <d3d9.h>
// standard C headers
#include <math.h>
// 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;
}
}