//============================================================
//
//  d3dhlsl.c - Win32 Direct3D HLSL implementation
//
//============================================================
//
//  Copyright Aaron Giles
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or
//  without modification, are permitted provided that the
//  following conditions are met:
//
//    * Redistributions of source code must retain the above
//      copyright notice, this list of conditions and the
//      following disclaimer.
//    * Redistributions in binary form must reproduce the
//      above copyright notice, this list of conditions and
//      the following disclaimer in the documentation and/or
//      other materials provided with the distribution.
//    * Neither the name 'MAME' nor the names of its
//      contributors may be used to endorse or promote
//      products derived from this software without specific
//      prior written permission.
//
//  THIS SOFTWARE IS PROVIDED BY AARON GILES ''AS IS'' AND
//  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
//  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
//  EVENT SHALL AARON GILES BE LIABLE FOR ANY DIRECT,
//  INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
//  DAMAGE (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
//  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
//  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
//  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
//  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
//  IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//============================================================

// 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>
#include <d3dx9.h>
#include <math.h>
#undef interface

// MAME headers
#include "emu.h"
#include "render.h"
#include "ui.h"
#include "rendutil.h"
#include "options.h"
#include "emuopts.h"
#include "aviio.h"
#include "png.h"
#include "screen.h"

// MAMEOS headers
#include "d3dintf.h"
#include "winmain.h"
#include "window.h"
#include "config.h"
#include "strconv.h"
#include "d3dcomm.h"
#include "drawd3d.h"



//============================================================
//  GLOBALS
//============================================================

static hlsl_options g_hlsl_presets[4] =
{
	{	// 25% Shadow mask, 50% Scanlines, 3% Pincushion, 0 defocus, No Tint, 0.9 Exponent, 5% Floor, 25% Phosphor Return, 120% Saturation
		true,
		0.25f, { "aperture.png" }, 320, 240, 0.09375f, 0.109375f,
		0.03f, 0.03f,
		0.5f, 1.0f, 0.5f, 1.0f, 0.0f, 0.0f,
		{ 0.0f, 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 0.0f, 0.0f },
		{ 0.0f, 1.0f, 0.0f },
		{ 0.0f, 0.0f, 1.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 1.0f, 1.0f },
		{ 0.9f, 0.9f, 0.9f },
		{ 0.05f,0.05f,0.05f},
		{ 0.25f,0.25f,0.25f},
		1.2f,
		false, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0
	},
	{	// 25% Shadow mask, 0% Scanlines, 3% Pincushion, 0 defocus, No Tint, 0.9 Exponent, 5% Floor, 25% Phosphor Return, 120% Saturation
		true,
		0.25f, { "aperture.png" }, 320, 240, 0.09375f, 0.109375f,
		0.03f, 0.03f,
		0.0f, 1.0f, 0.5f, 1.0f, 0.0f, 0.0f,
		{ 0.0f, 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 0.0f, 0.0f },
		{ 0.0f, 1.0f, 0.0f },
		{ 0.0f, 0.0f, 1.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 1.0f, 1.0f },
		{ 0.9f, 0.9f, 0.9f },
		{ 0.05f,0.05f,0.05f},
		{ 0.25f,0.25f,0.25f},
		1.2f,
		false, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0
	},
	{	// 25% Shadow mask, 0% Scanlines, 0% Pincushion, 0 defocus, No Tint, 0.9 Exponent, 5% Floor, 25% Phosphor Return, 120% Saturation
		true,
		0.25f, { "aperture.png" }, 320, 240, 0.09375f, 0.109375f,
		0.0f, 0.0f,
		0.0f, 1.0f, 0.5f, 1.0f, 0.0f, 0.0f,
		{ 0.0f, 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 0.0f, 0.0f },
		{ 0.0f, 1.0f, 0.0f },
		{ 0.0f, 0.0f, 1.0f },
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 1.0f, 1.0f },
		{ 0.9f, 0.9f, 0.9f },
		{ 0.05f,0.05f,0.05f},
		{ 0.25f,0.25f,0.25f},
		1.2f,
		false, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0
	},
	{	// 25% Shadow mask, 100% Scanlines, 15% Pincushion, 3 defocus, 24-degree Tint Out, 1.5 Exponent, 5% Floor, 70% Phosphor Return, 80% Saturation, Bad Convergence
		true,
		0.25f, { "aperture.png" }, 320, 240, 0.09375f, 0.109375f,
		0.15f, 0.15f,
		1.0f, 1.0f, 0.5f, 1.0f, 0.0f, 0.5f,
		{ 3.0f, 3.0f, 3.0f, 3.0f },
		{ 0.5f,-0.33f,0.7f },
		{ 0.0f,-1.0f, 0.5f },
		{ 0.0f, 0.2f, 0.3f },
		{ 0.0f, 0.2f, 0.0f },
		{ 0.8f, 0.2f, 0.0f },
		{ 0.0f, 0.8f, 0.2f},
		{ 0.2f, 0.0f, 0.8f},
		{ 0.0f, 0.0f, 0.0f },
		{ 1.0f, 1.0f, 1.0f },
		{ 1.5f, 1.5f, 1.5f },
		{ 0.05f,0.05f,0.05f},
		{ 0.7f, 0.7f, 0.7f},
		0.8f,
		false, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0
	},
};

static slider_state *g_slider_list;



//============================================================
//  PROTOTYPES
//============================================================

static void get_vector(const char *data, int count, float *out, int report_error);
static file_error open_next(d3d_info *d3d, emu_file &file, const char *templ, const char *extension, int idx);



//============================================================
//  hlsl_info constructor
//============================================================

hlsl_info::hlsl_info()
{
	master_enable = false;
	prescale_size_x = 1;
	prescale_size_y = 1;
	prescale_force_x = 0;
	prescale_force_y = 0;
	preset = -1;
	shadow_texture = NULL;
	registered_targets = 0;
	cyclic_target_idx = 0;
	options = NULL;
	paused = true;
	lastidx = -1;
}



//============================================================
//  hlsl_info destructor
//============================================================

hlsl_info::~hlsl_info()
{
	global_free(options);
}



//============================================================
//  hlsl_info::window_save
//============================================================

void hlsl_info::window_save()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	HRESULT result = (*d3dintf->device.create_texture)(d3d->device, snap_width, snap_height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &snap_copy_texture);
	if (result != D3D_OK)
	{
		mame_printf_verbose(_WINDOWS("Direct3D: Unable to init system-memory target for HLSL snapshot (%08x), bailing\n"), (UINT32)result);
		return;
	}
	(*d3dintf->texture.get_surface_level)(snap_copy_texture, 0, &snap_copy_target);

	result = (*d3dintf->device.create_texture)(d3d->device, snap_width, snap_height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &snap_texture);
	if (result != D3D_OK)
	{
		mame_printf_verbose(_WINDOWS("Direct3D: Unable to init video-memory target for HLSL snapshot (%08x), bailing\n"), (UINT32)result);
		return;
	}
	(*d3dintf->texture.get_surface_level)(snap_texture, 0, &snap_target);

	render_snap = true;
	snap_rendered = false;
}



//============================================================
//  hlsl_info::window_record
//============================================================

void hlsl_info::window_record()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	windows_options &options = downcast<windows_options &>(window->machine().options());
	const char *filename = options.d3d_hlsl_write();

	if (avi_output_file != NULL)
		end_avi_recording();
	else if (filename[0] != 0)
		begin_avi_recording(filename);
}


//============================================================
//  hlsl_info::avi_update_snap
//============================================================

void hlsl_info::avi_update_snap(d3d_surface *surface)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	D3DLOCKED_RECT rect;

	// if we don't have a bitmap, or if it's not the right size, allocate a new one
	if (!avi_snap.valid() || (int)snap_width != avi_snap.width() || (int)snap_height != avi_snap.height())
	{
		avi_snap.allocate((int)snap_width, (int)snap_height);
	}

	// copy the texture
	HRESULT result = (*d3dintf->device.get_render_target_data)(d3d->device, surface, avi_copy_surface);
	if (result != D3D_OK)
	{
		printf("Couldn't copy (%08x)\n", (UINT32)result);
		return;
	}

	// lock the texture
	result = (*d3dintf->surface.lock_rect)(avi_copy_surface, &rect, NULL, D3DLOCK_DISCARD);
	if (result != D3D_OK)
	{
		printf("Couldn't lock (%08x)\n", (UINT32)result);
		return;
	}

	// loop over Y
	for (int srcy = 0; srcy < (int)snap_height; srcy++)
	{
		DWORD *src = (DWORD *)((BYTE *)rect.pBits + srcy * rect.Pitch);
		UINT32 *dst = &avi_snap.pix32(srcy);

		for(int x = 0; x < snap_width; x++)
			*dst++ = *src++;
	}

	// unlock
	result = (*d3dintf->surface.unlock_rect)(avi_copy_surface);
	if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during texture unlock_rect call\n"), (int)result);
}



//============================================================
//  hlsl_render_snapshot
//============================================================

void hlsl_info::render_snapshot(d3d_surface *surface)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	D3DLOCKED_RECT rect;

	render_snap = false;

	// if we don't have a bitmap, or if it's not the right size, allocate a new one
	if (!avi_snap.valid() || snap_width != (avi_snap.width() / 2) || snap_height != (avi_snap.height() / 2))
	{
		avi_snap.allocate(snap_width / 2, snap_height / 2);
	}

	// copy the texture
	HRESULT result = (*d3dintf->device.get_render_target_data)(d3d->device, surface, snap_copy_target);
	if (result != D3D_OK)
	{
		printf("Couldn't copy (%08x)\n", (UINT32)result);
		return;
	}

	// lock the texture
	result = (*d3dintf->surface.lock_rect)(snap_copy_target, &rect, NULL, D3DLOCK_DISCARD);
	if (result != D3D_OK)
	{
		printf("Couldn't lock (%08x)\n", (UINT32)result);
		return;
	}

	for(int cy = 0; cy < 2; cy++)
	{
		for(int cx = 0; cx < 2; cx++)
		{
			// loop over Y
			for (int srcy = 0; srcy < snap_height / 2; srcy++)
			{
				int toty = (srcy + cy * (snap_height / 2));
				int totx = cx * (snap_width / 2);
				DWORD *src = (DWORD *)((BYTE *)rect.pBits + toty * rect.Pitch + totx * 4);
				UINT32 *dst = &avi_snap.pix32(srcy);

				for(int x = 0; x < snap_width / 2; x++)
					*dst++ = *src++;
			}

			int idx = cy * 2 + cx;

			emu_file file(window->machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
			file_error filerr = open_next(d3d, file, NULL, "png", idx);
			if (filerr != FILERR_NONE)
				return;

			// add two text entries describing the image
			astring text1(emulator_info::get_appname(), " ", build_version);
			astring text2(window->machine().system().manufacturer, " ", window->machine().system().description);
			png_info pnginfo = { 0 };
			png_add_text(&pnginfo, "Software", text1);
			png_add_text(&pnginfo, "System", text2);

			// now do the actual work
			png_error error = png_write_bitmap(file, &pnginfo, avi_snap, 1 << 24, NULL);
			if (error != PNGERR_NONE)
				mame_printf_error(_WINDOWS("Error generating PNG for HLSL snapshot: png_error = %d\n"), error);

			// free any data allocated
			png_free(&pnginfo);
		}
	}

	// unlock
	result = (*d3dintf->surface.unlock_rect)(snap_copy_target);
	if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during texture unlock_rect call\n"), (int)result);

	if(snap_texture != NULL)
	{
		(*d3dintf->texture.release)(snap_texture);
		snap_texture = NULL;
	}

	if(snap_target != NULL)
	{
		(*d3dintf->surface.release)(snap_target);
		snap_target = NULL;
	}

	if(snap_copy_texture != NULL)
	{
		(*d3dintf->texture.release)(snap_copy_texture);
		snap_copy_texture = NULL;
	}

	if(snap_copy_target != NULL)
	{
		(*d3dintf->surface.release)(snap_copy_target);
		snap_copy_target = NULL;
	}
}


//============================================================
//  hlsl_info::record_texture
//============================================================

void hlsl_info::record_texture()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_surface *surface = avi_final_target;

	// ignore if nothing to do
	if (avi_output_file == NULL || surface == NULL)
		return;

	// get the current time
	attotime curtime = window->machine().time();

	avi_update_snap(surface);

	// loop until we hit the right time
	while (avi_next_frame_time <= curtime)
	{
		// handle an AVI recording
		// write the next frame
		avi_error avierr = avi_append_video_frame(avi_output_file, avi_snap);
		if (avierr != AVIERR_NONE)
		{
			end_avi_recording();
			return;
		}

		// advance time
		avi_next_frame_time += avi_frame_period;
		avi_frame++;
	}
}


//============================================================
//  hlsl_info::frame_complete
//============================================================

void hlsl_info::frame_complete()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	if(render_snap && snap_rendered)
	{
		render_snapshot(snap_target);
	}
}


//============================================================
//  hlsl_info::end_hlsl_avi_recording
//============================================================

void hlsl_info::end_avi_recording()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	if (avi_output_file != NULL)
		avi_close(avi_output_file);

	avi_output_file = NULL;
	avi_frame = 0;
}


//============================================================
//  hlsl_info::begin_avi_recording
//============================================================

void hlsl_info::begin_avi_recording(const char *name)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	// stop any existing recording
	end_avi_recording();

	// reset the state
	avi_frame = 0;
	avi_next_frame_time = window->machine().time();

	// build up information about this new movie
	avi_movie_info info;
	info.video_format = 0;
	info.video_timescale = 1000 * ((window->machine().primary_screen != NULL) ? ATTOSECONDS_TO_HZ(window->machine().primary_screen->frame_period().attoseconds) : screen_device::DEFAULT_FRAME_RATE);
	info.video_sampletime = 1000;
	info.video_numsamples = 0;
	info.video_width = snap_width;
	info.video_height = snap_height;
	info.video_depth = 24;

	info.audio_format = 0;
	info.audio_timescale = window->machine().sample_rate();
	info.audio_sampletime = 1;
	info.audio_numsamples = 0;
	info.audio_channels = 2;
	info.audio_samplebits = 16;
	info.audio_samplerate = window->machine().sample_rate();

	// create a new temporary movie file
	file_error filerr;
	astring fullpath;
	{
		emu_file tempfile(window->machine().options().snapshot_directory(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
		if (name != NULL)
		{
			filerr = tempfile.open(name);
		}
		else
		{
			filerr = open_next(d3d, tempfile, NULL, "avi", 0);
		}

		// compute the frame time
		{
			avi_frame_period = attotime::from_seconds(1000) / info.video_timescale;
		}

		// if we succeeded, make a copy of the name and create the real file over top
		if (filerr == FILERR_NONE)
		{
			fullpath = tempfile.fullpath();
		}
	}

	if (filerr == FILERR_NONE)
	{
		// create the file and free the string
		avi_error avierr = avi_create(fullpath, &info, &avi_output_file);
		if (avierr != AVIERR_NONE)
		{
			mame_printf_error(_WINDOWS("Error creating AVI: %s\n"), avi_error_string(avierr));
		}
	}
}


//============================================================
//  hlsl_info::set_texture
//============================================================

void hlsl_info::set_texture(d3d_texture_info *texture)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	if(texture != NULL)
	{
		if(texture->prev_frame == texture->cur_frame)
		{
			//printf("Paused\n");
			paused = true;
		}
		else
		{
			//printf("Not paused\n");
			paused = false;
		}

		texture->prev_frame = texture->cur_frame;
		//printf("%08x cur_frame is %d\n", (UINT32)(UINT64)texture, texture->cur_frame);
	}

	(*d3dintf->effect.set_texture)(effect, "Diffuse", (texture == NULL) ? d3d->default_texture->d3dfinaltex : texture->d3dfinaltex);
	if (options->yiq_enable)
		(*d3dintf->effect.set_texture)(yiq_encode_effect, "Diffuse", (texture == NULL) ? d3d->default_texture->d3dfinaltex : texture->d3dfinaltex);
	else
		(*d3dintf->effect.set_texture)(color_effect, "Diffuse", (texture == NULL) ? d3d->default_texture->d3dfinaltex : texture->d3dfinaltex);
	(*d3dintf->effect.set_texture)(pincushion_effect, "Diffuse", (texture == NULL) ? d3d->default_texture->d3dfinaltex : texture->d3dfinaltex);
}


//============================================================
//  hlsl_info::init
//============================================================

void hlsl_info::init(d3d *d3dintf, win_window_info *window)
{
	if (!d3dintf->post_fx_available)
		return;

	this->d3dintf = d3dintf;
	this->window = window;

	master_enable = downcast<windows_options &>(window->machine().options()).d3d_hlsl_enable();
	prescale_size_x = 1;
	prescale_size_y = 1;
	preset = downcast<windows_options &>(window->machine().options()).d3d_hlsl_preset();

	snap_width = downcast<windows_options &>(window->machine().options()).d3d_snap_width();
	snap_height = downcast<windows_options &>(window->machine().options()).d3d_snap_height();
}


//============================================================
//  hlsl_info::init_fsfx_quad
//============================================================

void hlsl_info::init_fsfx_quad(void *vertbuf)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	// get a pointer to the vertex buffer
	fsfx_vertices = (d3d_vertex *)vertbuf;
	if (fsfx_vertices == NULL)
		return;

	// fill in the vertexes clockwise
	fsfx_vertices[0].x = 0.0f;
	fsfx_vertices[0].y = 0.0f;
	fsfx_vertices[1].x = d3d->width;
	fsfx_vertices[1].y = 0.0f;
	fsfx_vertices[2].x = 0.0f;
	fsfx_vertices[2].y = d3d->height;
	fsfx_vertices[3].x = d3d->width;
	fsfx_vertices[3].y = 0.0f;
	fsfx_vertices[4].x = 0.0f;
	fsfx_vertices[4].y = d3d->height;
	fsfx_vertices[5].x = d3d->width;
	fsfx_vertices[5].y = d3d->height;

	fsfx_vertices[0].u0 = 0.0f;
	fsfx_vertices[0].v0 = 0.0f;

	fsfx_vertices[1].u0 = 1.0f;
	fsfx_vertices[1].v0 = 0.0f;

	fsfx_vertices[2].u0 = 0.0f;
	fsfx_vertices[2].v0 = 1.0f;

	fsfx_vertices[3].u0 = 1.0f;
	fsfx_vertices[3].v0 = 0.0f;

	fsfx_vertices[4].u0 = 0.0f;
	fsfx_vertices[4].v0 = 1.0f;

	fsfx_vertices[5].u0 = 1.0f;
	fsfx_vertices[5].v0 = 1.0f;

	// set the color, Z parameters to standard values
	for (int i = 0; i < 6; i++)
	{
		fsfx_vertices[i].z = 0.0f;
		fsfx_vertices[i].rhw = 1.0f;
		fsfx_vertices[i].color = D3DCOLOR_ARGB(255, 255, 255, 255);
	}
}


//============================================================
//  hlsl_info::create_resources
//============================================================

int hlsl_info::create_resources()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return 0;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	HRESULT result = (*d3dintf->device.create_texture)(d3d->device, (int)snap_width, (int)snap_height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &avi_copy_texture);
	if (result != D3D_OK)
	{
		mame_printf_verbose(_WINDOWS("Direct3D: Unable to init system-memory target for HLSL AVI dumping (%08x)\n"), (UINT32)result);
		return 1;
	}
	(*d3dintf->texture.get_surface_level)(avi_copy_texture, 0, &avi_copy_surface);

	result = (*d3dintf->device.create_texture)(d3d->device, (int)snap_width, (int)snap_height, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &avi_final_texture);
	if (result != D3D_OK)
	{
		mame_printf_verbose(_WINDOWS("Direct3D: Unable to init video-memory target for HLSL AVI dumping (%08x)\n"), (UINT32)result);
		return 1;
	}
	(*d3dintf->texture.get_surface_level)(avi_final_texture, 0, &avi_final_target);

	windows_options &winoptions = downcast<windows_options &>(window->machine().options());

	options = (hlsl_options*)global_alloc_clear(hlsl_options);

	options->params_dirty = true;
	strcpy(options->shadow_mask_texture, downcast<windows_options &>(window->machine().options()).screen_shadow_mask_texture()); // unsafe

	write_ini = downcast<windows_options &>(window->machine().options()).hlsl_write_ini();
	read_ini = downcast<windows_options &>(window->machine().options()).hlsl_read_ini();

	if(read_ini)
	{
		emu_file ini_file(downcast<windows_options &>(window->machine().options()).screen_post_fx_dir(), OPEN_FLAG_READ | OPEN_FLAG_CREATE_PATHS);
		file_error filerr = open_next((d3d_info*)window->drawdata, ini_file, downcast<windows_options &>(window->machine().options()).hlsl_ini_name(), "ini", 0);

		read_ini = false;
		if (filerr == FILERR_NONE)
		{
			ini_file.seek(0, SEEK_END);
			if (ini_file.tell() >= 1000)
			{
				read_ini = true;
				ini_file.seek(0, SEEK_SET);

				int en = 0;
				char buf[1024];
				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_enable %d\n", &en);
				master_enable = en == 1;

				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_prescale_x %d\n", &prescale_force_x);

				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_prescale_y %d\n", &prescale_force_y);

				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_preset %d\n", &preset);

				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_snap_width %d\n", &snap_width);

				ini_file.gets(buf, 1024);
				sscanf(buf, "hlsl_snap_height %d\n", &snap_height);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_alpha %f\n", &options->shadow_mask_alpha);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_texture %s\n", options->shadow_mask_texture);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_x_count %d\n", &options->shadow_mask_count_x);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_y_count %d\n", &options->shadow_mask_count_y);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_usize %f\n", &options->shadow_mask_u_size);

				ini_file.gets(buf, 1024);
				sscanf(buf, "shadow_mask_vsize %f\n", &options->shadow_mask_v_size);

				ini_file.gets(buf, 1024);
				sscanf(buf, "curvature %f\n", &options->curvature);

				ini_file.gets(buf, 1024);
				sscanf(buf, "pincushion %f\n", &options->pincushion);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_alpha %f\n", &options->scanline_alpha);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_size %f\n", &options->scanline_scale);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_height %f\n", &options->scanline_height);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_bright_scale %f\n", &options->scanline_bright_scale);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_bright_offset %f\n", &options->scanline_bright_offset);

				ini_file.gets(buf, 1024);
				sscanf(buf, "scanline_jitter %f\n", &options->scanline_offset);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "defocus %f %f\n", &options->defocus[0], &options->defocus[1]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "converge_x %f %f %f\n", &options->converge_x[0], &options->converge_x[1], &options->converge_x[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "converge_y %f %f %f\n", &options->converge_y[0], &options->converge_y[1], &options->converge_y[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "radial_converge_x %f %f %f\n", &options->radial_converge_x[0], &options->radial_converge_x[1], &options->radial_converge_x[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "radial_converge_y %f %f %f\n", &options->radial_converge_y[0], &options->radial_converge_y[1], &options->radial_converge_y[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "red_ratio %f %f %f\n", &options->red_ratio[0], &options->red_ratio[1], &options->red_ratio[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "grn_ratio %f %f %f\n", &options->grn_ratio[0], &options->grn_ratio[1], &options->grn_ratio[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "blu_ratio %f %f %f\n", &options->blu_ratio[0], &options->blu_ratio[1], &options->blu_ratio[2]);

				ini_file.gets(buf, 1024);
				sscanf(buf, "saturation %f\n", &options->saturation);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "offset %f %f %f\n", &options->offset[0], &options->offset[1], &options->offset[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "scale %f %f %f\n", &options->scale[0], &options->scale[1], &options->scale[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "power %f %f %f\n", &options->power[0], &options->power[1], &options->power[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "floor %f %f %f\n", &options->floor[0], &options->floor[1], &options->floor[2]);

				ini_file.gets(buf, 1024);
				for(int idx = 0; idx < strlen(buf); idx++) if(buf[idx] == ',') buf[idx] = ' ';
				sscanf(buf, "phosphor_life %f %f %f\n", &options->phosphor[0], &options->phosphor[1], &options->phosphor[2]);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_enable %d\n", &en);
				options->yiq_enable = en == 1;

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_cc %f\n", &options->yiq_cc);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_a %f\n", &options->yiq_a);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_b %f\n", &options->yiq_b);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_o %f\n", &options->yiq_o);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_p %f\n", &options->yiq_p);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_n %f\n", &options->yiq_n);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_y %f\n", &options->yiq_y);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_i %f\n", &options->yiq_i);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_q %f\n", &options->yiq_q);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_scan_time %f\n", &options->yiq_scan_time);

				ini_file.gets(buf, 1024);
				sscanf(buf, "yiq_phase_count %d\n", &options->yiq_phase_count);
			}
		}
	}

	// experimental: load a PNG to use for vector rendering; it is treated
	// as a brightness map
	emu_file file(window->machine().options().art_path(), OPEN_FLAG_READ);

	// experimental: if we have a shadow bitmap, create a texture for it
	render_load_png(shadow_bitmap, file, NULL, options->shadow_mask_texture);
	if (shadow_bitmap.valid())
	{
		render_texinfo texture;

		// fake in the basic data so it looks like it came from render.c
		texture.base = shadow_bitmap.raw_pixptr(0);
		texture.rowpixels = shadow_bitmap.rowpixels();
		texture.width = shadow_bitmap.width();
		texture.height = shadow_bitmap.height();
		texture.palette = NULL;
		texture.seqid = 0;

		// now create it
		shadow_texture = texture_create(d3d, &texture, PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA) | PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32));
	}

	if(!read_ini)
	{
		prescale_force_x = winoptions.d3d_hlsl_prescale_x();
		prescale_force_y = winoptions.d3d_hlsl_prescale_y();
		if(preset == -1)
		{
			options->shadow_mask_alpha = winoptions.screen_shadow_mask_alpha();
			options->shadow_mask_count_x = winoptions.screen_shadow_mask_count_x();
			options->shadow_mask_count_y = winoptions.screen_shadow_mask_count_y();
			options->shadow_mask_u_size = winoptions.screen_shadow_mask_u_size();
			options->shadow_mask_v_size = winoptions.screen_shadow_mask_v_size();
			options->curvature = winoptions.screen_curvature();
			options->pincushion = winoptions.screen_pincushion();
			options->scanline_alpha = winoptions.screen_scanline_amount();
			options->scanline_scale = winoptions.screen_scanline_scale();
			options->scanline_height = winoptions.screen_scanline_height();
			options->scanline_bright_scale = winoptions.screen_scanline_bright_scale();
			options->scanline_bright_offset = winoptions.screen_scanline_bright_offset();
			options->scanline_offset = winoptions.screen_scanline_offset();
			get_vector(winoptions.screen_defocus(), 2, options->defocus, TRUE);
			get_vector(winoptions.screen_converge_x(), 3, options->converge_x, TRUE);
			get_vector(winoptions.screen_converge_y(), 3, options->converge_y, TRUE);
			get_vector(winoptions.screen_radial_converge_x(), 3, options->radial_converge_x, TRUE);
			get_vector(winoptions.screen_radial_converge_y(), 3, options->radial_converge_y, TRUE);
			get_vector(winoptions.screen_red_ratio(), 3, options->red_ratio, TRUE);
			get_vector(winoptions.screen_grn_ratio(), 3, options->grn_ratio, TRUE);
			get_vector(winoptions.screen_blu_ratio(), 3, options->blu_ratio, TRUE);
			get_vector(winoptions.screen_offset(), 3, options->offset, TRUE);
			get_vector(winoptions.screen_scale(), 3, options->scale, TRUE);
			get_vector(winoptions.screen_power(), 3, options->power, TRUE);
			get_vector(winoptions.screen_floor(), 3, options->floor, TRUE);
			get_vector(winoptions.screen_phosphor(), 3, options->phosphor, TRUE);
			options->saturation = winoptions.screen_saturation();
		}
		else
		{
			options = &g_hlsl_presets[preset];
		}

		options->yiq_enable = winoptions.screen_yiq_enable();
		options->yiq_cc = winoptions.screen_yiq_cc();
		options->yiq_a = winoptions.screen_yiq_a();
		options->yiq_b = winoptions.screen_yiq_b();
		options->yiq_o = winoptions.screen_yiq_o();
		options->yiq_p = winoptions.screen_yiq_p();
		options->yiq_n = winoptions.screen_yiq_n();
		options->yiq_y = winoptions.screen_yiq_y();
		options->yiq_i = winoptions.screen_yiq_i();
		options->yiq_q = winoptions.screen_yiq_q();
		options->yiq_scan_time = winoptions.screen_yiq_scan_time();
		options->yiq_phase_count = winoptions.screen_yiq_phase_count();
	}
	g_slider_list = init_slider_list();

	const char *fx_dir = downcast<windows_options &>(window->machine().options()).screen_post_fx_dir();
	char primary_name_cstr[1024];
	char post_name_cstr[1024];
	char prescale_name_cstr[1024];
	char pincushion_name_cstr[1024];
	char phosphor_name_cstr[1024];
	char focus_name_cstr[1024];
	char deconverge_name_cstr[1024];
	char color_name_cstr[1024];
	char yiq_encode_name_cstr[1024];
	char yiq_decode_name_cstr[1024];

	sprintf(primary_name_cstr, "%s\\primary.fx", fx_dir);
	TCHAR *primary_name = tstring_from_utf8(primary_name_cstr);

	sprintf(post_name_cstr, "%s\\post.fx", fx_dir);
	TCHAR *post_name = tstring_from_utf8(post_name_cstr);

	sprintf(prescale_name_cstr, "%s\\prescale.fx", fx_dir);
	TCHAR *prescale_name = tstring_from_utf8(prescale_name_cstr);

	sprintf(pincushion_name_cstr, "%s\\pincushion.fx", fx_dir);
	TCHAR *pincushion_name = tstring_from_utf8(pincushion_name_cstr);

	sprintf(phosphor_name_cstr, "%s\\phosphor.fx", fx_dir);
	TCHAR *phosphor_name = tstring_from_utf8(phosphor_name_cstr);

	sprintf(focus_name_cstr, "%s\\focus.fx", fx_dir);
	TCHAR *focus_name = tstring_from_utf8(focus_name_cstr);

	sprintf(deconverge_name_cstr, "%s\\deconverge.fx", fx_dir);
	TCHAR *deconverge_name = tstring_from_utf8(deconverge_name_cstr);

	sprintf(color_name_cstr, "%s\\color.fx", fx_dir);
	TCHAR *color_name = tstring_from_utf8(color_name_cstr);

	sprintf(yiq_encode_name_cstr, "%s\\yiq_encode.fx", fx_dir);
	TCHAR *yiq_encode_name = tstring_from_utf8(yiq_encode_name_cstr);

	sprintf(yiq_decode_name_cstr, "%s\\yiq_decode.fx", fx_dir);
	TCHAR *yiq_decode_name = tstring_from_utf8(yiq_decode_name_cstr);

	// create the regular shader
	result = (*d3dintf->device.create_effect)(d3d->device, primary_name, &effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load primary.fx\n");
		return 1;
	}

	// create the post-processing shader
	result = (*d3dintf->device.create_effect)(d3d->device, post_name, &post_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load post.fx\n");
		return 1;
	}

	// create the prescaling shader
	result = (*d3dintf->device.create_effect)(d3d->device, prescale_name, &prescale_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load prescale.fx\n");
		return 1;
	}

	// create the pincushion shader
	result = (*d3dintf->device.create_effect)(d3d->device, pincushion_name, &pincushion_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load pincushion.fx\n");
		return 1;
	}

	// create the phosphor shader
	result = (*d3dintf->device.create_effect)(d3d->device, phosphor_name, &phosphor_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load phosphor.fx\n");
		return 1;
	}

	// create the focus shader
	result = (*d3dintf->device.create_effect)(d3d->device, focus_name, &focus_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load focus.fx\n");
		return 1;
	}

	// create the deconvergence shader
	result = (*d3dintf->device.create_effect)(d3d->device, deconverge_name, &deconverge_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load deconverge.fx\n");
		return 1;
	}

	// create the color convolution shader
	result = (*d3dintf->device.create_effect)(d3d->device, color_name, &color_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load color.fx\n");
		return 1;
	}

	// create the YIQ modulation shader
	result = (*d3dintf->device.create_effect)(d3d->device, yiq_encode_name, &yiq_encode_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load yiq_encode.fx\n");
		return 1;
	}

	// create the YIQ demodulation shader
	result = (*d3dintf->device.create_effect)(d3d->device, yiq_decode_name, &yiq_decode_effect);
	if(result != D3D_OK)
	{
		printf("Direct3D: Unable to load yiq_decode.fx\n");
		return 1;
	}

	if (primary_name)
		osd_free(primary_name);
	if (post_name)
		osd_free(post_name);
	if (prescale_name)
		osd_free(prescale_name);
	if (pincushion_name)
		osd_free(pincushion_name);
	if (phosphor_name)
		osd_free(phosphor_name);
	if (focus_name)
		osd_free(focus_name);
	if (deconverge_name)
		osd_free(deconverge_name);
	if (color_name)
		osd_free(color_name);
	if (yiq_encode_name)
		osd_free(yiq_encode_name);
	if (yiq_decode_name)
		osd_free(yiq_decode_name);

	return 0;
}


//============================================================
//  hlsl_info::begin
//============================================================

void hlsl_info::begin()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	curr_effect = effect;

	(*d3dintf->effect.set_technique)(effect, "TestTechnique");
	(*d3dintf->effect.set_technique)(post_effect, "ScanMaskTechnique");
	(*d3dintf->effect.set_technique)(pincushion_effect, "TestTechnique");
	(*d3dintf->effect.set_technique)(phosphor_effect, "TestTechnique");
	(*d3dintf->effect.set_technique)(focus_effect, "TestTechnique");
	(*d3dintf->effect.set_technique)(deconverge_effect, "DeconvergeTechnique");
	(*d3dintf->effect.set_technique)(color_effect, "ColorTechnique");
	(*d3dintf->effect.set_technique)(yiq_encode_effect, "EncodeTechnique");
	(*d3dintf->effect.set_technique)(yiq_decode_effect, "DecodeTechnique");

	HRESULT result = (*d3dintf->device.get_render_target)(d3d->device, 0, &backbuffer);
	if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device get_render_target call\n"), (int)result);

	for (int index = 0; index < 9; index++)
		screen_encountered[index] = false;
}


//============================================================
//  hlsl_info::init_effect_info
//============================================================

void hlsl_info::init_effect_info(d3d_poly_info *poly)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	if(PRIMFLAG_GET_TEXSHADE(d3d->last_texture_flags))
	{
		curr_effect = pincushion_effect;
	}
	else if(PRIMFLAG_GET_SCREENTEX(d3d->last_texture_flags) && poly->texture != NULL)
	{
		// Plug in all of the shader settings we're going to need
		// This is extremely slow, but we're not rendering models here,
		// just post-processing.
		curr_effect = post_effect;

		(*d3dintf->effect.set_float)(curr_effect, "ScanlineOffset", (poly->texture->cur_frame == 0) ? 0.0f : options->scanline_offset);

		if(options->params_dirty)
		{
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_vector)(curr_effect, "Floor", 3, options->floor);
			(*d3dintf->effect.set_float)(curr_effect, "SnapX", snap_width);
			(*d3dintf->effect.set_float)(curr_effect, "SnapY", snap_height);
			(*d3dintf->effect.set_float)(curr_effect, "PincushionAmount", options->pincushion);
			(*d3dintf->effect.set_float)(curr_effect, "CurvatureAmount", options->curvature);
			(*d3dintf->effect.set_float)(curr_effect, "UseShadow", shadow_texture == NULL ? 0.0f : 1.0f);
			(*d3dintf->effect.set_texture)(curr_effect, "Shadow", shadow_texture == NULL ? NULL : shadow_texture->d3dfinaltex);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowBrightness", options->shadow_mask_alpha);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowMaskSizeX", (float)options->shadow_mask_count_x);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowMaskSizeY", (float)options->shadow_mask_count_y);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowU", options->shadow_mask_u_size);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowV", options->shadow_mask_v_size);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowWidth", shadow_texture == NULL ? 1.0f : (float)shadow_texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "ShadowHeight", shadow_texture == NULL ? 1.0f : (float)shadow_texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "ScanlineAmount", options->scanline_alpha);
			(*d3dintf->effect.set_float)(curr_effect, "ScanlineScale", options->scanline_scale);
			(*d3dintf->effect.set_float)(curr_effect, "ScanlineHeight", options->scanline_height);
			(*d3dintf->effect.set_float)(curr_effect, "ScanlineBrightScale", options->scanline_bright_scale);
			(*d3dintf->effect.set_float)(curr_effect, "ScanlineBrightOffset", options->scanline_bright_offset);
			//(*d3dintf->effect.set_float)(curr_effect, "ScanlineOffset", (poly->texture->cur_frame == 0) ? 0.0f : options->scanline_offset);
			(*d3dintf->effect.set_vector)(curr_effect, "Power", 3, options->power);
		}
	}
	else
	{
		curr_effect = effect;

		(*d3dintf->effect.set_float)(curr_effect, "FixedAlpha", 1.0f);
	}
}


//============================================================
//  hlsl_info::render_quad
//============================================================

void hlsl_info::render_quad(d3d_poly_info *poly, int vertnum)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	UINT num_passes = 0;
	d3d_info *d3d = (d3d_info *)window->drawdata;

	if(PRIMFLAG_GET_SCREENTEX(d3d->last_texture_flags) && poly->texture != NULL)
	{
		int rawidx = poly->texture->target_index;
		int targetidx = rawidx % 9;
		int minidx = cyclic_target_idx - (num_screens * 2);
		int wrappedidx = (minidx + ((rawidx - minidx) % num_screens) + num_screens) % 9;
		//printf("rendering %d %d %d %d %d %d %d %d %d %f %f\n", poly->texture->target_index, rawidx, targetidx, minidx, wrappedidx, poly->texture->rawwidth, poly->texture->rawheight, d3d->width, d3d->height, (float)(poly->texture->ustop - poly->texture->ustart), (float)(poly->texture->vstop - poly->texture->vstart));

		screen_encountered[targetidx] = true;
		target_in_use[targetidx] = poly->texture;

		target_use_count[targetidx] = 60;

		if(options->yiq_enable)
		{
			/* Convert our signal into YIQ */
			curr_effect = yiq_encode_effect;

			if(options->params_dirty)
			{
				(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
				(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
				(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
				(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
				(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
				(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
				(*d3dintf->effect.set_float)(curr_effect, "CCValue", options->yiq_cc);
				(*d3dintf->effect.set_float)(curr_effect, "AValue", options->yiq_a);
				(*d3dintf->effect.set_float)(curr_effect, "BValue", (poly->texture->cur_frame == 2) ? 0.0f : ((float)poly->texture->cur_frame * options->yiq_b));
				(*d3dintf->effect.set_float)(curr_effect, "PValue", options->yiq_p);
				(*d3dintf->effect.set_float)(curr_effect, "NotchHalfWidth", options->yiq_n);
				(*d3dintf->effect.set_float)(curr_effect, "YFreqResponse", options->yiq_y);
				(*d3dintf->effect.set_float)(curr_effect, "IFreqResponse", options->yiq_i);
				(*d3dintf->effect.set_float)(curr_effect, "QFreqResponse", options->yiq_q);
				(*d3dintf->effect.set_float)(curr_effect, "ScanTime", options->yiq_scan_time);
			}

			HRESULT result = (*d3dintf->device.set_render_target)(d3d->device, 0, target4[targetidx]);

			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);
			result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);

			/* Convert our signal from YIQ */
			curr_effect = yiq_decode_effect;

			(*d3dintf->effect.set_texture)(curr_effect, "Composite", texture4[targetidx]);
			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", poly->texture->d3dfinaltex);
			if(options->params_dirty)
			{
				(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
				(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
				(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
				(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
				(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
				(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
				(*d3dintf->effect.set_float)(curr_effect, "CCValue", options->yiq_cc);
				(*d3dintf->effect.set_float)(curr_effect, "AValue", options->yiq_a);
				(*d3dintf->effect.set_float)(curr_effect, "BValue", (poly->texture->cur_frame == 2) ? 0.0f : ((float)poly->texture->cur_frame * options->yiq_b));
				(*d3dintf->effect.set_float)(curr_effect, "OValue", options->yiq_o);
				(*d3dintf->effect.set_float)(curr_effect, "PValue", options->yiq_p);
				(*d3dintf->effect.set_float)(curr_effect, "NotchHalfWidth", options->yiq_n);
				(*d3dintf->effect.set_float)(curr_effect, "YFreqResponse", options->yiq_y);
				(*d3dintf->effect.set_float)(curr_effect, "IFreqResponse", options->yiq_i);
				(*d3dintf->effect.set_float)(curr_effect, "QFreqResponse", options->yiq_q);
				(*d3dintf->effect.set_float)(curr_effect, "ScanTime", options->yiq_scan_time);
			}

			result = (*d3dintf->device.set_render_target)(d3d->device, 0, target3[targetidx]);

			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);
			result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);

			curr_effect = color_effect;

			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture3[targetidx]);
		}

		curr_effect = color_effect;

		/* Render the initial color-convolution pass */
		if(options->params_dirty)
		{
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", options->yiq_enable ? 1.0f : (1.0f / (poly->texture->ustop - poly->texture->ustart)));
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", options->yiq_enable ? 1.0f : (1.0f / (poly->texture->vstop - poly->texture->vstart)));
			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "YIQEnable", options->yiq_enable ? 1.0f : 0.0f);
			(*d3dintf->effect.set_vector)(curr_effect, "RedRatios", 3, options->red_ratio);
			(*d3dintf->effect.set_vector)(curr_effect, "GrnRatios", 3, options->grn_ratio);
			(*d3dintf->effect.set_vector)(curr_effect, "BluRatios", 3, options->blu_ratio);
			(*d3dintf->effect.set_vector)(curr_effect, "Offset", 3, options->offset);
			(*d3dintf->effect.set_vector)(curr_effect, "Scale", 3, options->scale);
			(*d3dintf->effect.set_float)(curr_effect, "Saturation", options->saturation);
		}

		HRESULT result = (*d3dintf->device.set_render_target)(d3d->device, 0, smalltarget0[targetidx]);

		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);
		result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		/* Pre-scaling pass */
		curr_effect = prescale_effect;
		(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", smalltexture0[targetidx]);

		if(options->params_dirty)
		{
			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
		}

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		result = (*d3dintf->device.set_render_target)(d3d->device, 0, prescaletarget0[targetidx]);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);
		result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		/* Deconverge pass */
		curr_effect = deconverge_effect;
		(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", prescaletexture0[targetidx]);

		if(options->params_dirty)
		{
			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
			(*d3dintf->effect.set_vector)(curr_effect, "ConvergeX", 3, options->converge_x);
			(*d3dintf->effect.set_vector)(curr_effect, "ConvergeY", 3, options->converge_y);
			(*d3dintf->effect.set_vector)(curr_effect, "RadialConvergeX", 3, options->radial_converge_x);
			(*d3dintf->effect.set_vector)(curr_effect, "RadialConvergeY", 3, options->radial_converge_y);
		}

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		result = (*d3dintf->device.set_render_target)(d3d->device, 0, target2[targetidx]);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call 6\n"), (int)result);
		result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		float defocus_x = options->defocus[0];
		float defocus_y = options->defocus[1];
		bool focus_enable = defocus_x != 0.0f || defocus_y != 0.0f;
		if(focus_enable)
		{
			/* Defocus pass 1 */
			curr_effect = focus_effect;

			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture2[targetidx]);

			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", poly->texture != NULL ? (1.0f / (poly->texture->ustop - poly->texture->ustart)) : 0.0f);
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", poly->texture != NULL ? (1.0f / (poly->texture->vstop - poly->texture->vstart)) : 0.0f);
			(*d3dintf->effect.set_vector)(curr_effect, "Defocus", 2, &options->defocus[0]);
			(*d3dintf->effect.set_float)(curr_effect, "FocusEnable", (defocus_x == 0.0f && defocus_y == 0.0f) ? 0.0f : 1.0f);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			result = (*d3dintf->device.set_render_target)(d3d->device, 0, target0[targetidx]);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call 6\n"), (int)result);
			result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);

			/* Defocus pass 2 */

			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture0[targetidx]);

			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f);
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f);
			(*d3dintf->effect.set_vector)(curr_effect, "Defocus", 2, &options->defocus[0]);
			(*d3dintf->effect.set_float)(curr_effect, "FocusEnable", (defocus_x == 0.0f && defocus_y == 0.0f) ? 0.0f : 1.0f);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			result = (*d3dintf->device.set_render_target)(d3d->device, 0, target1[targetidx]);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call 7\n"), (int)result);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);
		}

		// Simulate phosphorescence. This should happen after the shadow/scanline pass, but since
		// the phosphors are a direct result of the incoming texture, might as well just change the
		// input texture.
		curr_effect = phosphor_effect;
		//printf("num_screens %d\n", num_screens);

		if(options->params_dirty)
		{
			(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
			(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
			(*d3dintf->effect.set_float)(curr_effect, "RawWidth", (float)poly->texture->rawwidth);
			(*d3dintf->effect.set_float)(curr_effect, "RawHeight", (float)poly->texture->rawheight);
			(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", 1.0f / (poly->texture->ustop - poly->texture->ustart));
			(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", 1.0f / (poly->texture->vstop - poly->texture->vstart));
			(*d3dintf->effect.set_vector)(curr_effect, "Phosphor", 3, options->phosphor);
		}
		(*d3dintf->effect.set_float)(curr_effect, "TextureWidth", (float)target_width[targetidx]);
		(*d3dintf->effect.set_float)(curr_effect, "TextureHeight", (float)target_height[targetidx]);
		(*d3dintf->effect.set_float)(curr_effect, "Passthrough", 0.0f);

		(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", focus_enable ? texture1[targetidx] : texture2[targetidx]);
		(*d3dintf->effect.set_texture)(curr_effect, "LastPass", last_texture[wrappedidx]); // Avoid changing targets due to page flipping

		result = (*d3dintf->device.set_render_target)(d3d->device, 0, target0[targetidx]);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call 4\n"), (int)result);
		result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		/* Pass along our phosphor'd screen */
		curr_effect = phosphor_effect;

		(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture0[targetidx]);
		(*d3dintf->effect.set_texture)(curr_effect, "LastPass", texture0[targetidx]);
		(*d3dintf->effect.set_float)(curr_effect, "Passthrough", 1.0f);

		result = (*d3dintf->device.set_render_target)(d3d->device, 0, last_target[wrappedidx]); // Avoid changing targets due to page flipping
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call 5\n"), (int)result);
		result = (*d3dintf->device.clear)(d3d->device, 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_ARGB(0,0,0,0), 0, 0);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device clear call\n"), (int)result);

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, D3DPT_TRIANGLELIST, 0, 2);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		/* Scanlines and shadow mask, at high res for AVI logging*/
		if(avi_output_file != NULL)
		{
			curr_effect = post_effect;

			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture0[targetidx]);

			result = (*d3dintf->device.set_render_target)(d3d->device, 0, avi_final_target);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);
		}

		if(render_snap)
		{
			curr_effect = post_effect;

			(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture0[targetidx]);

			result = (*d3dintf->device.set_render_target)(d3d->device, 0, snap_target);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);

			(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

			for (UINT pass = 0; pass < num_passes; pass++)
			{
				(*d3dintf->effect.begin_pass)(curr_effect, pass);
				// add the primitives
				result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count);
				if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
				(*d3dintf->effect.end_pass)(curr_effect);
			}

			(*d3dintf->effect.end)(curr_effect);

			snap_rendered = true;
		}

		/* Scanlines and shadow mask */
		curr_effect = post_effect;

		(*d3dintf->effect.set_texture)(curr_effect, "Diffuse", texture0[targetidx]);

		result = (*d3dintf->device.set_render_target)(d3d->device, 0, backbuffer);
		if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device set_render_target call\n"), (int)result);

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);

		poly->texture->cur_frame++;
		poly->texture->cur_frame %= options->yiq_phase_count;

		options->params_dirty = false;
	}
	else
	{
		(*d3dintf->effect.set_float)(curr_effect, "RawWidth", poly->texture != NULL ? (float)poly->texture->rawwidth : 8.0f);
		(*d3dintf->effect.set_float)(curr_effect, "RawHeight", poly->texture != NULL ? (float)poly->texture->rawheight : 8.0f);
		(*d3dintf->effect.set_float)(curr_effect, "WidthRatio", poly->texture != NULL ? (1.0f / (poly->texture->ustop - poly->texture->ustart)) : 0.0f);
		(*d3dintf->effect.set_float)(curr_effect, "HeightRatio", poly->texture != NULL ? (1.0f / (poly->texture->vstop - poly->texture->vstart)) : 0.0f);
		(*d3dintf->effect.set_float)(curr_effect, "TargetWidth", (float)d3d->width);
		(*d3dintf->effect.set_float)(curr_effect, "TargetHeight", (float)d3d->height);
		(*d3dintf->effect.set_float)(curr_effect, "PostPass", 0.0f);
		(*d3dintf->effect.set_float)(curr_effect, "PincushionAmount", options->pincushion);

		(*d3dintf->effect.begin)(curr_effect, &num_passes, 0);

		for (UINT pass = 0; pass < num_passes; pass++)
		{
			(*d3dintf->effect.begin_pass)(curr_effect, pass);
			// add the primitives
			HRESULT result = (*d3dintf->device.draw_primitive)(d3d->device, poly->type, vertnum, poly->count);
			if (result != D3D_OK) mame_printf_verbose(_WINDOWS("Direct3D: Error %08X during device draw_primitive call\n"), (int)result);
			(*d3dintf->effect.end_pass)(curr_effect);
		}

		(*d3dintf->effect.end)(curr_effect);
	}
}



//============================================================
//  hlsl_info::end
//============================================================

void hlsl_info::end()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	(*d3dintf->surface.release)(backbuffer);

	//printf("registered_targets %d\n", registered_targets);

	// Don't check de-registration if we're paused.
	if(paused)
	{
		return;
	}

	// Unregister any registered targets we didn't traverse in the past frame. A resolution change must
	// have occurred.
	for(int index = 0; index < 9; index++)
	{
		if(!screen_encountered[index] && smalltarget0[index] != NULL)
		{
			if(target_use_count[index] > 0)
			{
				target_use_count[index]--;
			}
			else
			{
				//printf("deregging %d\n", index);
				// free all textures
				if(target_in_use[index] != NULL)
				{
					d3d_texture_info *tex = target_in_use[index];
					bool found_in_active_list = false;
					d3d_texture_info *test_tex = d3d->texlist;
					while (test_tex != NULL)
					{
						if(test_tex == tex)
						{
							found_in_active_list = true;
							break;
						}
						test_tex = test_tex->next;
					}

					// only clean up a texture if it won't be cleaned up by drawd3d
					if(!found_in_active_list)
					{
						if (tex->d3dfinaltex != NULL)
						{
							(*d3dintf->texture.release)(tex->d3dfinaltex);
							tex->d3dfinaltex = NULL;
						}
						if (tex->d3dtex != NULL && tex->d3dtex != tex->d3dfinaltex)
						{
							(*d3dintf->texture.release)(tex->d3dtex);
							tex->d3dtex = NULL;
						}
						if (tex->d3dsurface != NULL)
						{
							(*d3dintf->surface.release)(tex->d3dsurface);
						}
						global_free(tex);
					}
				}

				if (prescaletexture0[index] != NULL)
				{
					(*d3dintf->texture.release)(prescaletexture0[index]);
					prescaletexture0[index] = NULL;
				}
				if (texture0[index] != NULL)
				{
					(*d3dintf->texture.release)(texture0[index]);
					texture0[index] = NULL;
				}
				if (texture1[index] != NULL)
				{
					(*d3dintf->texture.release)(texture1[index]);
					texture1[index] = NULL;
				}
				if (texture2[index] != NULL)
				{
					(*d3dintf->texture.release)(texture2[index]);
					texture2[index] = NULL;
				}
				if (texture3[index] != NULL)
				{
					(*d3dintf->texture.release)(texture3[index]);
					texture3[index] = NULL;
				}
				if (texture4[index] != NULL)
				{
					(*d3dintf->texture.release)(texture4[index]);
					texture4[index] = NULL;
				}
				if (smalltexture0[index] != NULL)
				{
					(*d3dintf->texture.release)(smalltexture0[index]);
					smalltexture0[index] = NULL;
				}
				if (prescaletarget0[index] != NULL)
				{
					(*d3dintf->surface.release)(prescaletarget0[index]);
					prescaletarget0[index] = NULL;
				}
				if (target0[index] != NULL)
				{
					(*d3dintf->surface.release)(target0[index]);
					target0[index] = NULL;
				}
				if (target1[index] != NULL)
				{
					(*d3dintf->surface.release)(target1[index]);
					target1[index] = NULL;
				}
				if (target2[index] != NULL)
				{
					(*d3dintf->surface.release)(target2[index]);
					target2[index] = NULL;
				}
				if (target3[index] != NULL)
				{
					(*d3dintf->surface.release)(target3[index]);
					target3[index] = NULL;
				}
				if (target4[index] != NULL)
				{
					(*d3dintf->surface.release)(target4[index]);
					target4[index] = NULL;
				}
				if (smalltarget0[index] != NULL)
				{
					(*d3dintf->surface.release)(smalltarget0[index]);
					smalltarget0[index] = NULL;
				}
				if(last_texture[index] != NULL)
				{
					(*d3dintf->texture.release)(last_texture[index]);
					last_texture[index] = NULL;
				}
				if(last_target[index] != NULL)
				{
					(*d3dintf->surface.release)(last_target[index]);
					last_target[index] = NULL;
				}
				target_use_count[index] = 0;
				registered_targets--;
			}
		}
	}
}


//============================================================
//  hlsl_info::register_texture
//============================================================

int hlsl_info::register_prescaled_texture(d3d_texture_info *texture, int scwidth, int scheight)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return 0;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	//printf("registering prescaled texture, seqid %d\n", texture->texinfo.seqid);

	int idx = cyclic_target_idx % 9;

	// Find the nearest prescale factor that is over our screen size
	int hlsl_prescale_x = prescale_force_x ? prescale_force_x : 1;
	if(!prescale_force_x)
	{
		while(scwidth * hlsl_prescale_x < d3d->width) hlsl_prescale_x++;
		prescale_size_x = hlsl_prescale_x;
	}

	int hlsl_prescale_y = prescale_force_y ? prescale_force_y : 1;
	if(!prescale_force_y)
	{
		while(scheight * hlsl_prescale_y < d3d->height) hlsl_prescale_y++;
		prescale_size_y = hlsl_prescale_y;
	}

	HRESULT result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture0[idx], 0, &target0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture1[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture1[idx], 0, &target1[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture2[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture2[idx], 0, &target2[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture3[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture3[idx], 0, &target3[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture4[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture4[idx], 0, &target4[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth, scheight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &smalltexture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(smalltexture0[idx], 0, &smalltarget0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &prescaletexture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(prescaletexture0[idx], 0, &prescaletarget0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, scwidth * hlsl_prescale_x, scheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &last_texture[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(last_texture[idx], 0, &last_target[idx]);

	texture->target_index = cyclic_target_idx;
	target_width[idx] = scwidth * hlsl_prescale_x;
	target_height[idx] = scheight * hlsl_prescale_y;
	target_use_count[idx] = 60;
	target_in_use[idx] = texture;
	raw_target_idx[idx] = cyclic_target_idx;
	registered_targets++;
	cyclic_target_idx++;

	options->params_dirty = true;

	enumerate_screens();

	return 0;
}

//============================================================
//  hlsl_info::enumerate_screens
//============================================================
void hlsl_info::enumerate_screens()
{
	screen_device_iterator iter(window->machine().root_device());
	num_screens = iter.count();
}

//============================================================
//  hlsl_info::register_texture
//============================================================

int hlsl_info::register_texture(d3d_texture_info *texture)
{
	if (!master_enable || !d3dintf->post_fx_available)
		return 0;

	d3d_info *d3d = (d3d_info *)window->drawdata;

	//printf("registering unscaled texture, seqid %d\n", texture->texinfo.seqid);

	int idx = cyclic_target_idx % 9;

	// Find the nearest prescale factor that is over our screen size
	int hlsl_prescale_x = prescale_force_x ? prescale_force_x : 1;
	if(!prescale_force_x)
	{
		while(texture->rawwidth * hlsl_prescale_x < d3d->width) hlsl_prescale_x++;
		prescale_size_x = hlsl_prescale_x;
	}

	int hlsl_prescale_y = prescale_force_y ? prescale_force_y : 1;
	if(!prescale_force_y)
	{
		while(texture->rawheight * hlsl_prescale_y < d3d->height) hlsl_prescale_y++;
		prescale_size_y = hlsl_prescale_y;
	}

	HRESULT result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth * hlsl_prescale_x, texture->rawheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture0[idx], 0, &target0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth * hlsl_prescale_x, texture->rawheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture1[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture1[idx], 0, &target1[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth * hlsl_prescale_x, texture->rawheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture2[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture2[idx], 0, &target2[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture3[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture3[idx], 0, &target3[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &texture4[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(texture4[idx], 0, &target4[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth, texture->rawheight, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &smalltexture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(smalltexture0[idx], 0, &smalltarget0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth * hlsl_prescale_x, texture->rawheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &prescaletexture0[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(prescaletexture0[idx], 0, &prescaletarget0[idx]);

	result = (*d3dintf->device.create_texture)(d3d->device, texture->rawwidth * hlsl_prescale_x, texture->rawheight * hlsl_prescale_y, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &last_texture[idx]);
	if (result != D3D_OK)
		return 1;
	(*d3dintf->texture.get_surface_level)(last_texture[idx], 0, &last_target[idx]);

	texture->target_index = cyclic_target_idx;
	target_width[idx] = texture->rawwidth * hlsl_prescale_x;
	target_height[idx] = texture->rawheight * hlsl_prescale_y;
	target_use_count[idx] = 60;
	target_in_use[idx] = texture;
	raw_target_idx[idx] = cyclic_target_idx;
	registered_targets++;
	cyclic_target_idx++;

	options->params_dirty = true;

	enumerate_screens();

	return 0;
}

//============================================================
//  hlsl_info::delete_resources
//============================================================

void hlsl_info::delete_resources()
{
	if (!master_enable || !d3dintf->post_fx_available)
		return;

	if(write_ini)
	{
		emu_file file(downcast<windows_options &>(window->machine().options()).screen_post_fx_dir(), OPEN_FLAG_WRITE | OPEN_FLAG_CREATE | OPEN_FLAG_CREATE_PATHS);
		file_error filerr = open_next((d3d_info*)window->drawdata, file, downcast<windows_options &>(window->machine().options()).hlsl_ini_name(), "ini", 0);

		if (filerr != FILERR_NONE)
			return;

		file.printf("hlsl_enable            %d\n", master_enable ? 1 : 0);
		file.printf("hlsl_prescale_x        %d\n", prescale_force_x);
		file.printf("hlsl_prescale_y        %d\n", prescale_force_y);
		file.printf("hlsl_preset            %d\n", preset);
		file.printf("hlsl_snap_width        %d\n", snap_width);
		file.printf("hlsl_snap_height       %d\n", snap_height);
		file.printf("shadow_mask_alpha      %f\n", options->shadow_mask_alpha);
		file.printf("shadow_mask_texture    %s\n", options->shadow_mask_texture);
		file.printf("shadow_mask_x_count    %d\n", options->shadow_mask_count_x);
		file.printf("shadow_mask_y_count    %d\n", options->shadow_mask_count_y);
		file.printf("shadow_mask_usize      %f\n", options->shadow_mask_u_size);
		file.printf("shadow_mask_vsize      %f\n", options->shadow_mask_v_size);
		file.printf("curvature              %f\n", options->curvature);
		file.printf("pincushion             %f\n", options->pincushion);
		file.printf("scanline_alpha         %f\n", options->scanline_alpha);
		file.printf("scanline_size          %f\n", options->scanline_scale);
		file.printf("scanline_height        %f\n", options->scanline_height);
		file.printf("scanline_bright_scale  %f\n", options->scanline_bright_scale);
		file.printf("scanline_bright_offset %f\n", options->scanline_bright_offset);
		file.printf("scanline_jitter        %f\n", options->scanline_offset);
		file.printf("defocus                %f,%f\n", options->defocus[0], options->defocus[1]);
		file.printf("converge_x             %f,%f,%f\n", options->converge_x[0], options->converge_x[1], options->converge_x[2]);
		file.printf("converge_y             %f,%f,%f\n", options->converge_y[0], options->converge_y[1], options->converge_y[2]);
		file.printf("radial_converge_x      %f,%f,%f\n", options->radial_converge_x[0], options->radial_converge_x[1], options->radial_converge_x[2]);
		file.printf("radial_converge_y      %f,%f,%f\n", options->radial_converge_y[0], options->radial_converge_y[1], options->radial_converge_y[2]);
		file.printf("red_ratio              %f,%f,%f\n", options->red_ratio[0], options->red_ratio[1], options->red_ratio[2]);
		file.printf("grn_ratio              %f,%f,%f\n", options->grn_ratio[0], options->grn_ratio[1], options->grn_ratio[2]);
		file.printf("blu_ratio              %f,%f,%f\n", options->blu_ratio[0], options->blu_ratio[1], options->blu_ratio[2]);
		file.printf("saturation             %f\n", options->saturation);
		file.printf("offset                 %f,%f,%f\n", options->offset[0], options->offset[1], options->offset[2]);
		file.printf("scale                  %f,%f,%f\n", options->scale[0], options->scale[1], options->scale[2]);
		file.printf("power                  %f,%f,%f\n", options->power[0], options->power[1], options->power[2]);
		file.printf("floor                  %f,%f,%f\n", options->floor[0], options->floor[1], options->floor[2]);
		file.printf("phosphor_life          %f,%f,%f\n", options->phosphor[0], options->phosphor[1], options->phosphor[2]);
		file.printf("yiq_enable             %d\n", options->yiq_enable ? 1 : 0);
		file.printf("yiq_cc                 %f\n", options->yiq_cc);
		file.printf("yiq_a                  %f\n", options->yiq_a);
		file.printf("yiq_b                  %f\n", options->yiq_b);
		file.printf("yiq_o                  %f\n", options->yiq_o);
		file.printf("yiq_p                  %f\n", options->yiq_p);
		file.printf("yiq_n                  %f\n", options->yiq_n);
		file.printf("yiq_y                  %f\n", options->yiq_y);
		file.printf("yiq_i                  %f\n", options->yiq_i);
		file.printf("yiq_q                  %f\n", options->yiq_q);
		file.printf("yiq_scan_time          %f\n", options->yiq_scan_time);
		file.printf("yiq_phase_count        %d\n", options->yiq_phase_count);
	}

	if (effect != NULL)
	{
		(*d3dintf->effect.release)(effect);
		effect = NULL;
	}
	if (post_effect != NULL)
	{
		(*d3dintf->effect.release)(post_effect);
		post_effect = NULL;
	}
	if (prescale_effect != NULL)
	{
		(*d3dintf->effect.release)(prescale_effect);
		prescale_effect = NULL;
	}
	if (pincushion_effect != NULL)
	{
		(*d3dintf->effect.release)(pincushion_effect);
		pincushion_effect = NULL;
	}
	if (phosphor_effect != NULL)
	{
		(*d3dintf->effect.release)(phosphor_effect);
		phosphor_effect = NULL;
	}
	if (focus_effect != NULL)
	{
		(*d3dintf->effect.release)(focus_effect);
		focus_effect = NULL;
	}
	if (deconverge_effect != NULL)
	{
		(*d3dintf->effect.release)(deconverge_effect);
		deconverge_effect = NULL;
	}
	if (color_effect != NULL)
	{
		(*d3dintf->effect.release)(color_effect);
		color_effect = NULL;
	}
	if (yiq_encode_effect != NULL)
	{
		(*d3dintf->effect.release)(yiq_encode_effect);
		yiq_encode_effect = NULL;
	}
	if (yiq_decode_effect != NULL)
	{
		(*d3dintf->effect.release)(yiq_decode_effect);
		yiq_decode_effect = NULL;
	}

	for (int index = 0; index < 9; index++)
	{
		if (prescaletexture0[index] != NULL)
		{
			(*d3dintf->texture.release)(prescaletexture0[index]);
			prescaletexture0[index] = NULL;
		}
		if (texture0[index] != NULL)
		{
			(*d3dintf->texture.release)(texture0[index]);
			texture0[index] = NULL;
		}
		if (texture1[index] != NULL)
		{
			(*d3dintf->texture.release)(texture1[index]);
			texture1[index] = NULL;
		}
		if (texture2[index] != NULL)
		{
			(*d3dintf->texture.release)(texture2[index]);
			texture2[index] = NULL;
		}
		if (texture3[index] != NULL)
		{
			(*d3dintf->texture.release)(texture3[index]);
			texture3[index] = NULL;
		}
		if (texture4[index] != NULL)
		{
			(*d3dintf->texture.release)(texture4[index]);
			texture4[index] = NULL;
		}
		if (smalltexture0[index] != NULL)
		{
			(*d3dintf->texture.release)(smalltexture0[index]);
			smalltexture0[index] = NULL;
		}
		if (prescaletarget0[index] != NULL)
		{
			(*d3dintf->surface.release)(prescaletarget0[index]);
			prescaletarget0[index] = NULL;
		}
		if (target0[index] != NULL)
		{
			(*d3dintf->surface.release)(target0[index]);
			target0[index] = NULL;
		}
		if (target1[index] != NULL)
		{
			(*d3dintf->surface.release)(target1[index]);
			target1[index] = NULL;
		}
		if (target2[index] != NULL)
		{
			(*d3dintf->surface.release)(target2[index]);
			target2[index] = NULL;
		}
		if (target3[index] != NULL)
		{
			(*d3dintf->surface.release)(target3[index]);
			target3[index] = NULL;
		}
		if (target4[index] != NULL)
		{
			(*d3dintf->surface.release)(target4[index]);
			target4[index] = NULL;
		}
		if (smalltarget0[index] != NULL)
		{
			(*d3dintf->surface.release)(smalltarget0[index]);
			smalltarget0[index] = NULL;
		}
		if (last_texture[index] != NULL)
		{
			(*d3dintf->texture.release)(last_texture[index]);
			last_texture[index] = NULL;
		}
		if (last_target[index] != NULL)
		{
			(*d3dintf->surface.release)(last_target[index]);
			last_target[index] = NULL;
		}
	}

	if (avi_copy_texture != NULL)
	{
		(*d3dintf->texture.release)(avi_copy_texture);
		avi_copy_texture = NULL;
	}

	if (avi_copy_surface != NULL)
	{
		(*d3dintf->surface.release)(avi_copy_surface);
		avi_copy_surface = NULL;
	}

	if (avi_final_texture != NULL)
	{
		(*d3dintf->texture.release)(avi_final_texture);
		avi_final_texture = NULL;
	}

	if (avi_final_target != NULL)
	{
		(*d3dintf->surface.release)(avi_final_target);
		avi_final_target = NULL;
	}

	global_free(options);

	registered_targets = 0;

	shadow_bitmap.reset();
}


//============================================================
//  get_vector
//============================================================

static void get_vector(const char *data, int count, float *out, int report_error)
{
	if (count > 3)
	{
		if (sscanf(data, "%f,%f,%f,%f", &out[0], &out[1], &out[2], &out[3]) < 4 && report_error)
			mame_printf_error(_WINDOWS("Illegal quad vector value = %s\n"), data);
	}
	else if(count > 2)
	{
		if (sscanf(data, "%f,%f,%f", &out[0], &out[1], &out[2]) < 3 && report_error)
			mame_printf_error(_WINDOWS("Illegal triple vector value = %s\n"), data);
	}
	else if(count > 1)
	{
		if (sscanf(data, "%f,%f", &out[0], &out[1]) < 2 && report_error)
			mame_printf_error(_WINDOWS("Illegal double vector value = %s\n"), data);
	}
	else if(count > 0)
	{
		if (sscanf(data, "%f", &out[0]) < 1 && report_error)
			mame_printf_error(_WINDOWS("Illegal single vector value = %s\n"), data);
	}
}


/*-------------------------------------------------
    slider_alloc - allocate a new slider entry
    currently duplicated from ui.c, this could
    be done in a more ideal way.
-------------------------------------------------*/

static slider_state *slider_alloc(running_machine &machine, const char *title, INT32 minval, INT32 defval, INT32 maxval, INT32 incval, slider_update update, void *arg)
{
	int size = sizeof(slider_state) + strlen(title);
	slider_state *state = (slider_state *)auto_alloc_array_clear(machine, UINT8, size);

	state->minval = minval;
	state->defval = defval;
	state->maxval = maxval;
	state->incval = incval;
	state->update = update;
	state->arg = arg;
	strcpy(state->description, title);

	return state;
}


//============================================================
//  assorted global slider accessors
//============================================================

static INT32 slider_set(float *option, float scale, const char *fmt, astring *string, INT32 newval)
{
	if (option != NULL && newval != SLIDER_NOCHANGE) *option = (float)newval * scale;
	if (string != NULL) string->printf(fmt, *option);
	return floor(*option / scale + 0.5f);
}

static INT32 slider_shadow_mask_alpha(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	return slider_set(&(((hlsl_options*)arg)->shadow_mask_alpha), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_shadow_mask_x_count(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	hlsl_options *options = (hlsl_options*)arg;
	if (newval != SLIDER_NOCHANGE) options->shadow_mask_count_x = newval;
	if (string != NULL) string->printf("%d", options->shadow_mask_count_x);
	options->params_dirty = true;
	return options->shadow_mask_count_x;
}

static INT32 slider_shadow_mask_y_count(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	hlsl_options *options = (hlsl_options*)arg;
	if (newval != SLIDER_NOCHANGE) options->shadow_mask_count_y = newval;
	if (string != NULL) string->printf("%d", options->shadow_mask_count_y);
	options->params_dirty = true;
	return options->shadow_mask_count_y;
}

static INT32 slider_shadow_mask_usize(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->shadow_mask_u_size), 1.0f / 32.0f, "%2.5f", string, newval);
}

static INT32 slider_shadow_mask_vsize(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->shadow_mask_v_size), 1.0f / 32.0f, "%2.5f", string, newval);
}

static INT32 slider_curvature(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->curvature), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_pincushion(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->pincushion), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_scanline_alpha(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_alpha), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_scanline_scale(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_scale), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_scanline_height(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_height), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_scanline_bright_scale(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_bright_scale), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_scanline_bright_offset(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_bright_offset), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_scanline_offset(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scanline_offset), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_defocus_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->defocus[0]), 0.5f, "%2.1f", string, newval);
}

static INT32 slider_defocus_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->defocus[1]), 0.5f, "%2.1f", string, newval);
}

static INT32 slider_post_defocus_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->defocus[2]), 0.5f, "%2.1f", string, newval);
}

static INT32 slider_post_defocus_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->defocus[3]), 0.5f, "%2.1f", string, newval);
}

static INT32 slider_red_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_x[0]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_red_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_y[0]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_green_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_x[1]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_green_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_y[1]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_blue_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_x[2]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_blue_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->converge_y[2]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_red_radial_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_x[0]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_red_radial_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_y[0]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_green_radial_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_x[1]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_green_radial_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_y[1]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_blue_radial_converge_x(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_x[2]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_blue_radial_converge_y(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->radial_converge_y[2]), 0.1f, "%3.1f", string, newval);
}

static INT32 slider_red_from_r(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->red_ratio[0]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_red_from_g(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->red_ratio[1]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_red_from_b(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->red_ratio[2]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_green_from_r(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->grn_ratio[0]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_green_from_g(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->grn_ratio[1]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_green_from_b(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->grn_ratio[2]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_blue_from_r(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->blu_ratio[0]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_blue_from_g(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->blu_ratio[1]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_blue_from_b(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->blu_ratio[2]), 0.005f, "%2.3f", string, newval);
}

static INT32 slider_red_offset(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->offset[0]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_green_offset(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->offset[1]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_blue_offset(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->offset[2]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_red_scale(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scale[0]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_green_scale(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scale[1]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_blue_scale(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->scale[2]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_red_power(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->power[0]), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_green_power(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->power[1]), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_blue_power(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->power[2]), 0.05f, "%2.2f", string, newval);
}

static INT32 slider_red_floor(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->floor[0]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_green_floor(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->floor[1]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_blue_floor(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->floor[2]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_red_phosphor_life(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->phosphor[0]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_green_phosphor_life(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->phosphor[1]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_blue_phosphor_life(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->phosphor[2]), 0.01f, "%2.2f", string, newval);
}

static INT32 slider_saturation(running_machine &machine, void *arg, astring *string, INT32 newval)
{
	((hlsl_options*)arg)->params_dirty = true;
	return slider_set(&(((hlsl_options*)arg)->saturation), 0.01f, "%2.2f", string, newval);
}

//============================================================
//  init_slider_list
//============================================================

slider_state *hlsl_info::init_slider_list()
{
	if (!master_enable || !d3dintf->post_fx_available)
	{
		g_slider_list = NULL;
		return NULL;
	}

	slider_state *listhead = NULL;
	slider_state **tailptr = &listhead;
	astring string;

	*tailptr = slider_alloc(window->machine(), _WINDOWS("Shadow Mask Darkness"), 0, 0, 100, 1, slider_shadow_mask_alpha, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Shadow Mask X Count"), 1, 640, 1024, 1, slider_shadow_mask_x_count, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Shadow Mask Y Count"), 1, 480, 1024, 1, slider_shadow_mask_y_count, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Shadow Mask Pixel Count X"), 1, 3, 32, 1, slider_shadow_mask_usize, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Shadow Mask Pixel Count Y"), 1, 3, 32, 1, slider_shadow_mask_vsize, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Screen Curvature"), 0, 0, 100, 1, slider_curvature, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Image Pincushion"), 0, 0, 100, 1, slider_pincushion, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Darkness"), 0, 0, 100, 1, slider_scanline_alpha, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Screen Height"), 1, 20, 80, 1, slider_scanline_scale, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Indiv. Height"), 1, 10, 80, 1, slider_scanline_height, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Brightness"), 0, 20, 40, 1, slider_scanline_bright_scale, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Brightness Overdrive"), 0, 12, 20, 1, slider_scanline_bright_offset, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Scanline Jitter"), 0, 0, 40, 1, slider_scanline_offset, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Defocus X"), 0, 0, 64, 1, slider_defocus_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Defocus Y"), 0, 0, 64, 1, slider_defocus_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Phosphor Defocus X"), 0, 0, 64, 1, slider_post_defocus_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Phosphor Defocus Y"), 0, 0, 64, 1, slider_post_defocus_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Position Offset X"), -1500, 0, 1500, 1, slider_red_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Position Offset Y"), -1500, 0, 1500, 1, slider_red_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Position Offset X"), -1500, 0, 1500, 1, slider_green_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Position Offset Y"), -1500, 0, 1500, 1, slider_green_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Position Offset X"), -1500, 0, 1500, 1, slider_blue_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Position Offset Y"), -1500, 0, 1500, 1, slider_blue_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Convergence X"), -1500, 0, 1500, 1, slider_red_radial_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Convergence Y"), -1500, 0, 1500, 1, slider_red_radial_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Convergence X"), -1500, 0, 1500, 1, slider_green_radial_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Convergence Y"), -1500, 0, 1500, 1, slider_green_radial_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Convergence X"), -1500, 0, 1500, 1, slider_blue_radial_converge_x, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Convergence Y"), -1500, 0, 1500, 1, slider_blue_radial_converge_y, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Output from Red Input"), -400, 0, 400, 5, slider_red_from_r, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Output from Green Input"), -400, 0, 400, 5, slider_red_from_g, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Output from Blue Input"), -400, 0, 400, 5, slider_red_from_b, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Output from Red Input"), -400, 0, 400, 5, slider_green_from_r, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Output from Green Input"), -400, 0, 400, 5, slider_green_from_g, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Output from Blue Input"), -400, 0, 400, 5, slider_green_from_b, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Output from Red Input"), -400, 0, 400, 5, slider_blue_from_r, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Output from Green Input"), -400, 0, 400, 5, slider_blue_from_g, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Output from Blue Input"), -400, 0, 400, 5, slider_blue_from_b, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red DC Offset"), -100, 0, 100, 1, slider_red_offset, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green DC Offset"), -100, 0, 100, 1, slider_green_offset, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue DC Offset"), -100, 0, 100, 1, slider_blue_offset, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Scale"), -200, 100, 200, 1, slider_red_scale, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Scale"), -200, 100, 200, 1, slider_green_scale, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Scale"), -200, 100, 200, 1, slider_blue_scale, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Power"), -80, 20, 80, 1, slider_red_power, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Power"), -80, 20, 80, 1, slider_green_power, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Power"), -80, 20, 80, 1, slider_blue_power, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Floor"), 0, 0, 100, 1, slider_red_floor, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Floor"), 0, 0, 100, 1, slider_green_floor, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Floor"), 0, 0, 100, 1, slider_blue_floor, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Red Phosphor Life"), 0, 0, 100, 1, slider_red_phosphor_life, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Green Phosphor Life"), 0, 0, 100, 1, slider_green_phosphor_life, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Blue Phosphor Life"), 0, 0, 100, 1, slider_blue_phosphor_life, (void*)options); tailptr = &(*tailptr)->next;
	*tailptr = slider_alloc(window->machine(), _WINDOWS("Saturation"), 0, 100, 400, 1, slider_saturation, (void*)options); tailptr = &(*tailptr)->next;

	return listhead;
}

//============================================================
//  get_slider_list
//============================================================

void *windows_osd_interface::get_slider_list()
{
	return (void*)g_slider_list;
}



// NOTE: The function below is taken directly from src/emu/video.c and should likely be moved into a global helper function.
//-------------------------------------------------
//  open_next - open the next non-existing file of
//  type filetype according to our numbering
//  scheme
//-------------------------------------------------

static file_error open_next(d3d_info *d3d, emu_file &file, const char *templ, const char *extension, int idx)
{
	UINT32 origflags = file.openflags();

	// handle defaults
	const char *snapname = templ ? templ : d3d->window->machine().options().snap_name();

	if (snapname == NULL || snapname[0] == 0)
		snapname = "%g/%i";
	astring snapstr(snapname);

	// strip any extension in the provided name
	int index = snapstr.rchr(0, '.');
	if (index != -1)
		snapstr.substr(0, index);

	// handle %d in the template (for image devices)
	astring snapdev("%d_");
	int pos = snapstr.find(0, snapdev);

	if (pos != -1)
	{
		// if more %d are found, revert to default and ignore them all
		if (snapstr.find(pos + 3, snapdev) != -1)
			snapstr.cpy("%g/%i");
		// else if there is a single %d, try to create the correct snapname
		else
		{
			int name_found = 0;

			// find length of the device name
			int end1 = snapstr.find(pos + 3, "/");
			int end2 = snapstr.find(pos + 3, "%");
			int end = -1;

			if ((end1 != -1) && (end2 != -1))
				end = MIN(end1, end2);
			else if (end1 != -1)
				end = end1;
			else if (end2 != -1)
				end = end2;
			else
				end = snapstr.len();

			if (end - pos < 3)
				fatalerror("Something very wrong is going on!!!");

			// copy the device name to an astring
			astring snapdevname;
			snapdevname.cpysubstr(snapstr, pos + 3, end - pos - 3);

			// verify that there is such a device for this system
			image_interface_iterator iter(d3d->window->machine().root_device());
			for (device_image_interface *image = iter.first(); image != NULL; iter.next())
			{
				// get the device name
				astring tempdevname(image->brief_instance_name());

				if (snapdevname.cmp(tempdevname) == 0)
				{
					// verify that such a device has an image mounted
					if (image->basename() != NULL)
					{
						astring filename(image->basename());

						// strip extension
						filename.substr(0, filename.rchr(0, '.'));

						// setup snapname and remove the %d_
						snapstr.replace(0, snapdevname, filename);
						snapstr.del(pos, 3);

						name_found = 1;
					}
				}
			}

			// or fallback to default
			if (name_found == 0)
				snapstr.cpy("%g/%i");
		}
	}

	// add our own index
	// add our own extension
	snapstr.cat(".").cat(extension);

	// substitute path and gamename up front
	snapstr.replace(0, "/", PATH_SEPARATOR);
	snapstr.replace(0, "%g", d3d->window->machine().basename());

	// determine if the template has an index; if not, we always use the same name
	astring fname;
	if (snapstr.find(0, "%i") == -1)
		fname.cpy(snapstr);

	// otherwise, we scan for the next available filename
	else
	{
		// try until we succeed
		astring seqtext;
		file.set_openflags(OPEN_FLAG_READ);
		for (int seq = 0; ; seq++)
		{
			// build up the filename
			fname.cpy(snapstr).replace(0, "%i", seqtext.format("%04d_%d", seq, idx).cstr());

			// try to open the file; stop when we fail
			file_error filerr = file.open(fname);
			if (filerr != FILERR_NONE)
				break;
		}
	}

	// create the final file
	file.set_openflags(origflags);
    return file.open(fname);
}
