
/* Direct3D 11 graphics renderer */

#include <windows.h>
#include "resource.h"

#include <DXGI1_5.h>
#include <dxgi1_6.h>
#include <d3d11.h>
#include <D3Dcompiler.h>

#include <wrl/client.h>
using Microsoft::WRL::ComPtr;

#include "sysconfig.h"
#include "sysdeps.h"

#include "options.h"
#include "xwin.h"
#include "render.h"
#include "win32.h"
#include "win32gfx.h"
#include "direct3d.h"
#include "uae.h"
#include "custom.h"
#include "gfxfilter.h"
#include "zfile.h"
#include "statusline.h"
#include "hq2x_d3d.h"
#include "gui.h"
#include "gfxboard.h"

#include "d3dx.h"

#include "shaders/PixelShaderPlain.h"
#include "shaders/PixelShaderPlain_HDR.h"
#include "shaders/PixelShaderAlpha.h"
#include "shaders/PixelShaderAlpha_HDR.h"
#include "shaders/PixelShaderMask.h"
#include "shaders/PixelShaderMask_HDR.h"
#include "shaders/VertexShader.h"

#include "FX11/d3dx11effect.h"

#include <process.h>
#include <Dwmapi.h>

void (*D3D_free)(int, bool immediate);
const TCHAR *(*D3D_init)(HWND ahwnd, int, int w_w, int h_h, int depth, int *freq, int mmulth, int mmultv, int *err);
bool (*D3D_alloctexture)(int, int, int);
void(*D3D_refresh)(int);
bool(*D3D_renderframe)(int, int,bool);
void(*D3D_showframe)(int);
void(*D3D_showframe_special)(int, int);
uae_u8 *(*D3D_locktexture)(int, int*, int*, int*, int);
void (*D3D_unlocktexture)(int, int, int);
void (*D3D_flushtexture)(int, int miny, int maxy);
void (*D3D_guimode)(int, int);
HDC (*D3D_getDC)(int, HDC hdc);
int (*D3D_isenabled)(int);
void (*D3D_clear)(int);
int (*D3D_canshaders)(void);
int (*D3D_goodenough)(void);
bool (*D3D_setcursor)(int, int x, int y, int width, int height, float mx, float my, bool visible, bool noscale);
uae_u8 *(*D3D_setcursorsurface)(int, bool, int *pitch);
float (*D3D_getrefreshrate)(int);
void(*D3D_restore)(int, bool);
void(*D3D_resize)(int, int);
void (*D3D_change)(int, int);
bool(*D3D_getscalerect)(int, float *mx, float *my, float *sx, float *sy, int width, int height);
bool(*D3D_run)(int);
int(*D3D_debug)(int, int);
void(*D3D_led)(int, int, int);
bool(*D3D_getscanline)(int*, bool*);
bool(*D3D_extoverlay)(struct extoverlay*,int);
void(*D3D_paint)(void);

static HMODULE hd3d11, hdxgi, hd3dcompiler, dwmapi;

static int d3d11_feature_level;

static int leds[LED_MAX];
static int debugcolors;
static bool cannoclear;
static bool noclear;
static int clearcnt;
static int slicecnt;

static const TCHAR *overlayleds[] = {
	_T("power"),
	_T("df0"),
	_T("df1"),
	_T("df2"),
	_T("df3"),
	_T("hd"),
	_T("cd"),
	_T("md"),
	_T("net"),
	NULL
};
static const int ledtypes[] = {
	LED_POWER,
	LED_DF0,
	LED_DF1,
	LED_DF2,
	LED_DF3,
	LED_HD,
	LED_CD,
	LED_MD,
	LED_NET
};

#define SHADERTYPE_BEFORE 1
#define SHADERTYPE_AFTER 2
#define SHADERTYPE_MIDDLE 3
#define SHADERTYPE_MASK_BEFORE 4
#define SHADERTYPE_MASK_AFTER 5
#define SHADERTYPE_MASK_MIDDLE 6
#define SHADERTYPE_POST 10

struct shadertex
{
	ID3D11Texture2D *tex;
	ID3D11ShaderResourceView *rv;
	ID3D11RenderTargetView *rt;
	D3D11_VIEWPORT *vp;
};

#define MAX_TECHNIQUE_LAYOUTS 8
struct shaderdata11
{
	int type;
	bool psPreProcess;
	int worktex_width;
	int worktex_height;
	int targettex_width;
	int targettex_height;
	ID3D11ShaderResourceView *masktexturerv;
	ID3D11Texture2D *masktexture;
	struct shadertex lpWorkTexture1;
	struct shadertex lpWorkTexture2;
	struct shadertex lpTempTexture;
	ID3D11Texture3D *lpHq2xLookupTexture;
	ID3D11ShaderResourceView *lpHq2xLookupTexturerv;
	int masktexture_w, masktexture_h;
	ID3DX11Effect *effect;
	ID3DX11EffectScalarVariable *framecounterHandle;
	ID3D11InputLayout *layouts[MAX_TECHNIQUE_LAYOUTS];
	ID3D11Buffer *indexBuffer;
	ID3D11Buffer *vertexBuffer;
	D3D11_VIEWPORT viewport;

	// Technique stuff
	ID3DX11EffectTechnique *m_PreprocessTechnique1EffectHandle;
	int PreprocessTechnique1EffectIndex;
	ID3DX11EffectTechnique *m_PreprocessTechnique2EffectHandle;
	int PreprocessTechnique2EffectIndex;
	ID3DX11EffectTechnique *m_CombineTechniqueEffectHandle;
	int CombineTechniqueEffectIndex;
	// Matrix Handles
	ID3DX11EffectMatrixVariable *m_MatWorldEffectHandle;
	ID3DX11EffectMatrixVariable *m_MatViewEffectHandle;
	ID3DX11EffectMatrixVariable *m_MatProjEffectHandle;
	ID3DX11EffectMatrixVariable *m_MatWorldViewEffectHandle;
	ID3DX11EffectMatrixVariable *m_MatViewProjEffectHandle;
	ID3DX11EffectMatrixVariable *m_MatWorldViewProjEffectHandle;
	// Vector Handles
	ID3DX11EffectVectorVariable *m_SourceDimsEffectHandle;
	ID3DX11EffectVectorVariable *m_InputDimsEffectHandle;
	ID3DX11EffectVectorVariable *m_TargetDimsEffectHandle;
	ID3DX11EffectVectorVariable *m_TexelSizeEffectHandle;
	ID3DX11EffectVectorVariable *m_ScaleEffectHandle;
	// Texture Handles
	ID3DX11EffectShaderResourceVariable *m_SourceTextureEffectHandle;
	ID3DX11EffectShaderResourceVariable *m_WorkingTexture1EffectHandle;
	ID3DX11EffectShaderResourceVariable *m_WorkingTexture2EffectHandle;
	ID3DX11EffectShaderResourceVariable *m_Hq2xLookupTextureHandle;

	ID3DX11EffectStringVariable *m_strName;
	ID3DX11EffectScalarVariable *m_scale;
	TCHAR loadedshader[256];
};
#define MAX_SHADERS (2 * MAX_FILTERSHADERS + 2)
#define SHADER_POST 0

#define VERTEXCOUNT 4
#define INDEXCOUNT 6

struct d3d11sprite
{
	ID3D11Texture2D *texture;
	ID3D11ShaderResourceView *texturerv;
	ID3D11VertexShader *vertexshader;
	ID3D11PixelShader *pixelshader;
	ID3D11InputLayout *layout;
	ID3D11Buffer *vertexbuffer, *indexbuffer;
	ID3D11Buffer *matrixbuffer;
	int width, height;
	float x, y;
	float outwidth, outheight;
	bool enabled;
	bool alpha;
	bool bilinear;
	bool rotation;
	bool screenlimit;
	uae_u8 *texbuf;
	bool updated;
	bool empty;
};

struct d3doverlay
{
	struct d3doverlay *next;
	int id;
	struct d3d11sprite s;
};

struct d3d11struct
{
	int num;
	IDXGISwapChain1 *m_swapChain;
	ID3D11Device *m_device;
	ID3D11DeviceContext *m_deviceContext;
	ID3D11RenderTargetView *m_renderTargetView;
	ID3D11RasterizerState *m_rasterState;
	D3DXMATRIX m_orthoMatrix;
	D3DXMATRIX m_worldMatrix;
	D3DXMATRIX m_viewMatrix;

	D3DXMATRIX m_matPreProj;
	D3DXMATRIX m_matPreView;
	D3DXMATRIX m_matPreWorld;
	D3DXMATRIX m_matProj;
	D3DXMATRIX m_matView;
	D3DXMATRIX m_matWorld;
	D3DXMATRIX m_matProj2;
	D3DXMATRIX m_matView2;
	D3DXMATRIX m_matWorld2;
	D3DXMATRIX m_matProj_out;
	D3DXMATRIX m_matView_out;
	D3DXMATRIX m_matWorld_out;

	D3D11_VIEWPORT viewport;
	ID3D11Buffer *m_vertexBuffer, *m_indexBuffer;
	ID3D11Buffer *m_matrixBuffer;
	ID3D11Buffer *m_psBuffer;
	int m_screenWidth, m_screenHeight;
	int m_bitmapWidth, m_bitmapHeight;
	int m_bitmapWidth2, m_bitmapHeight2;
	int m_bitmapWidthX, m_bitmapHeightX;
	int index_buffer_bytes;
	float m_positionX, m_positionY, m_positionZ;
	float m_rotationX, m_rotationY, m_rotationZ;
	ID3D11ShaderResourceView *texture2drv;
	ID3D11ShaderResourceView *sltexturerv;
	ID3D11Texture2D *texture2d, *texture2dstaging;
	ID3D11Texture2D *sltexture;
	ID3D11Texture2D *screenshottexturert, *screenshottexturetx;
	ID3D11VertexShader *m_vertexShader;
	ID3D11PixelShader *m_pixelShader, *m_pixelShaderSL, *m_pixelShaderMask;
	ID3D11SamplerState *m_sampleState_point_clamp, *m_sampleState_linear_clamp;
	ID3D11SamplerState *m_sampleState_point_wrap, *m_sampleState_linear_wrap;
	ID3D11InputLayout *m_layout;
	ID3D11BlendState *m_alphaEnableBlendingState;
	ID3D11BlendState *m_alphaDisableBlendingState;
	struct shadertex lpPostTempTexture;
	int texturelocked;
	DXGI_FORMAT scrformat;
	DXGI_FORMAT texformat;
	bool m_tearingSupport;
	int dmult, dmultxh, dmultxv, dmode;
	float xoffset, yoffset;
	float xmult, ymult;
	DXGI_SWAP_CHAIN_DESC1 swapChainDesc;
	DXGI_SWAP_CHAIN_FULLSCREEN_DESC  fsSwapChainDesc;
	IDXGIOutput *outputAdapter;
	HWND ahwnd;
	int fsmode;
	bool fsresizedo;
	bool fsmodechange;
	bool invalidmode;
	int vblankintervals;
	bool blackscreen;
	int framecount;
	UINT syncinterval;
	float vblank;
	DWM_FRAME_COUNT lastframe;
	int frames_since_init;
	bool resizeretry;
	bool d3dinit_done;
	bool hdr;

	struct d3d11sprite osd;
	struct d3d11sprite hwsprite;
	struct d3d11sprite mask2texture;
	struct d3d11sprite mask2textureleds[9], mask2textureled_power_dim;
	int mask2textureledoffsets[9 * 2];
	struct d3d11sprite blanksprite;

	struct d3doverlay *extoverlays;

	float mask2texture_w, mask2texture_h, mask2texture_ww, mask2texture_wh;
	float mask2texture_wwx, mask2texture_hhx, mask2texture_minusx, mask2texture_minusy;
	float mask2texture_multx, mask2texture_multy, mask2texture_offsetw;
	RECT mask2rect;

	IDXGISurface1 *hdc_surface;
	HANDLE filenotificationhandle;

	RECT sr2, dr2, zr2;
	int guimode;
	int delayedfs;
	int device_errors;
	bool delayedrestore;
	bool reloadshaders;
	int ledwidth, ledheight;
	int statusbar_hx, statusbar_vx;

	int cursor_offset_x, cursor_offset_y, cursor_offset2_x, cursor_offset2_y;
	float cursor_x, cursor_y;
	bool cursor_v, cursor_scale;

	struct gfx_filterdata *filterd3d;
	int filterd3didx;
	int scanline_osl1, scanline_osl2, scanline_osl3, scanline_osl4;

	struct shaderdata11 shaders[MAX_SHADERS];
	ID3DX11EffectTechnique *technique;
	ID3DX11EffectPass *effectpass;

#ifndef NDEBUG
	ID3D11InfoQueue *m_debugInfoQueue;
	ID3D11Debug *m_debug;
#endif
};

#define NUMVERTICES 8

struct TLVERTEX {
	D3DXVECTOR3 position;       // vertex position
	D3DCOLOR    diffuse;
	D3DXVECTOR2 texcoord;       // texture coords
};


struct VertexType
{
	D3DXVECTOR3 position;
	D3DXVECTOR2 texture;
	D3DXVECTOR2 sltexture;
};

struct MatrixBufferType
{
	D3DXMATRIX world;
	D3DXMATRIX view;
	D3DXMATRIX projection;
};

struct PSBufferType
{
	float brightness;
	float contrast;
	float d2;
	float d3;
};

static struct d3d11struct d3d11data[MAX_AMIGAMONITORS];

typedef HRESULT (WINAPI* CREATEDXGIFACTORY1)(REFIID riid, void **ppFactory);
typedef HRESULT (WINAPI* D3DCOMPILEFROMFILE)(LPCWSTR pFileName,
	CONST D3D_SHADER_MACRO* pDefines,
	ID3DInclude* pInclude,
	LPCSTR pEntrypoint,
	LPCSTR pTarget,
	UINT Flags1,
	UINT Flags2,
	ID3DBlob** ppCode,
	ID3DBlob** ppErrorMsgs);
typedef HRESULT(WINAPI* D3DCOMPILE)(LPCVOID pSrcData,
	SIZE_T SrcDataSize,
	LPCSTR pSourceName,
	CONST D3D_SHADER_MACRO* pDefines,
	ID3DInclude* pInclude,
	LPCSTR pEntrypoint,
	LPCSTR pTarget,
	UINT Flags1,
	UINT Flags2,
	ID3DBlob** ppCode,
	ID3DBlob** ppErrorMsgs);
typedef HRESULT (WINAPI* D3DCOMPILE2)(LPCVOID pSrcData,
	SIZE_T SrcDataSize,
	LPCSTR pSourceName,
	CONST D3D_SHADER_MACRO* pDefines,
	ID3DInclude* pInclude,
	LPCSTR pEntrypoint,
	LPCSTR pTarget,
	UINT Flags1,
	UINT Flags2,
	UINT SecondaryDataFlags,
	LPCVOID pSecondaryData,
	SIZE_T SecondaryDataSize,
	ID3DBlob** ppCode,
	ID3DBlob** ppErrorMsgs);
typedef HRESULT(WINAPI *D3DREFLECT)(LPCVOID pSrcData, SIZE_T SrcDataSize, REFIID pInterface, void **ppReflector);
typedef HRESULT(WINAPI *D3DGETBLOBPART)(LPCVOID pSrcData, SIZE_T SrcDataSize, D3D_BLOB_PART Part, UINT Flags, ID3DBlob** ppPart);
typedef HRESULT(WINAPI *DWMGETCOMPOSITIONTIMINGINFO)(HWND hwnd, DWM_TIMING_INFO *pTimingInfo);

static PFN_D3D11_CREATE_DEVICE pD3D11CreateDevice;
static CREATEDXGIFACTORY1 pCreateDXGIFactory1;
static DWMGETCOMPOSITIONTIMINGINFO pDwmGetCompositionTimingInfo;
D3DCOMPILE ppD3DCompile;
D3DCOMPILE2 ppD3DCompile2;
D3DCOMPILEFROMFILE pD3DCompileFromFile;
D3DREFLECT pD3DReflect;
D3DGETBLOBPART pD3DGetBlobPart;

static int isfs(struct d3d11struct *d3d)
{
	int fs = isfullscreen();
	if (fs > 0 && d3d->guimode)
		return -1;
	return fs;
}

static void TurnOnAlphaBlending(struct d3d11struct *d3d)
{
	float blendFactor[4];

	// Setup the blend factor.
	blendFactor[0] = 0.0f;
	blendFactor[1] = 0.0f;
	blendFactor[2] = 0.0f;
	blendFactor[3] = 0.0f;

	// Turn on the alpha blending.
	d3d->m_deviceContext->OMSetBlendState(d3d->m_alphaEnableBlendingState, blendFactor, 0xffffffff);
}

static void TurnOffAlphaBlending(struct d3d11struct *d3d)
{
	float blendFactor[4];

	// Setup the blend factor.
	blendFactor[0] = 0.0f;
	blendFactor[1] = 0.0f;
	blendFactor[2] = 0.0f;
	blendFactor[3] = 0.0f;

	// Turn off the alpha blending.
	d3d->m_deviceContext->OMSetBlendState(d3d->m_alphaDisableBlendingState, blendFactor, 0xffffffff);
}

static float get_rotation(struct d3d11struct *d3d, int monid)
{
	struct amigadisplay *ad = &adisplays[monid];
	int rota = currprefs.gf[ad->picasso_on ? GF_RTG : ad->interlace_on ? GF_INTERLACE : GF_NORMAL].gfx_filter_rotation;
	const float PI = 3.14159265358979f;
	float rot = PI / 180.0f * rota;
	return rot;
}

#define D3DX_DEFAULT ((UINT) -1)

static bool psEffect_ParseParameters(struct d3d11struct *d3d, ID3DX11Effect *effect, struct shaderdata11 *s, char *fxname, char *data, UINT flags1)
{
	HRESULT hr = S_OK;
	// Look at parameters for semantics and annotations that we know how to interpret

	if (effect == NULL || !effect->IsValid())
		return false;

	D3DX11_EFFECT_DESC effectDesc;
	hr = effect->GetDesc(&effectDesc);
	if (FAILED(hr))
		return false;

	if (!effectDesc.Techniques) {
		write_log(_T("D3D11 No techniques found!\n"));
		return false;
	}

	ID3DX11EffectVariable *v = effect->GetVariableByName("framecounter");
	if (v && v->IsValid()) {
		s->framecounterHandle = v->AsScalar();
	}

	for (UINT iParam = 0; iParam < effectDesc.GlobalVariables; iParam++) {
		ID3DX11EffectVariable *hParam;
		D3DX11_EFFECT_VARIABLE_DESC ParamDesc;

		hParam = effect->GetVariableByIndex(iParam);
		if (!hParam->IsValid())
			continue;
		hr = hParam->GetDesc(&ParamDesc);

		TCHAR *name = au(ParamDesc.Name);
		TCHAR *semantic = au(ParamDesc.Semantic);
		write_log(_T("FX Flags=%08x Ans=%d Name='%s' Semantic='%s'\n"), ParamDesc.Flags, ParamDesc.Annotations, name, semantic);
		xfree(semantic);
		xfree(name);

		if (ParamDesc.Semantic) {
			if (strcmpi(ParamDesc.Semantic, "world") == 0)
				s->m_MatWorldEffectHandle = hParam->AsMatrix();
			else if (strcmpi(ParamDesc.Semantic, "view") == 0)
				s->m_MatViewEffectHandle = hParam->AsMatrix();
			else if (strcmpi(ParamDesc.Semantic, "projection") == 0)
				s->m_MatProjEffectHandle = hParam->AsMatrix();
			else if (strcmpi(ParamDesc.Semantic, "worldview") == 0)
				s->m_MatWorldViewEffectHandle = hParam->AsMatrix();
			else if (strcmpi(ParamDesc.Semantic, "viewprojection") == 0)
				s->m_MatViewProjEffectHandle = hParam->AsMatrix();
			else if (strcmpi(ParamDesc.Semantic, "worldviewprojection") == 0)
				s->m_MatWorldViewProjEffectHandle = hParam->AsMatrix();

			if (strcmpi(ParamDesc.Semantic, "sourcedims") == 0)
				s->m_SourceDimsEffectHandle = hParam->AsVector();
			if (strcmpi(ParamDesc.Semantic, "inputdims") == 0)
				s->m_InputDimsEffectHandle = hParam->AsVector();
			if (strcmpi(ParamDesc.Semantic, "targetdims") == 0)
				s->m_TargetDimsEffectHandle = hParam->AsVector();
			else if (strcmpi(ParamDesc.Semantic, "texelsize") == 0)
				s->m_TexelSizeEffectHandle = hParam->AsVector();
			else if (strcmpi(ParamDesc.Semantic, "sourcescale") == 0)
				s->m_ScaleEffectHandle = hParam->AsVector();

			if (strcmpi(ParamDesc.Semantic, "SCALING") == 0)
				s->m_scale = hParam->AsScalar();

			if (strcmpi(ParamDesc.Semantic, "SOURCETEXTURE") == 0)
				s->m_SourceTextureEffectHandle = hParam->AsShaderResource();
			if (strcmpi(ParamDesc.Semantic, "WORKINGTEXTURE") == 0)
				s->m_WorkingTexture1EffectHandle = hParam->AsShaderResource();
			if (strcmpi(ParamDesc.Semantic, "WORKINGTEXTURE1") == 0)
				s->m_WorkingTexture2EffectHandle = hParam->AsShaderResource();
			if (strcmpi(ParamDesc.Semantic, "HQ2XLOOKUPTEXTURE") == 0)
				s->m_Hq2xLookupTextureHandle = hParam->AsShaderResource();

			ID3DX11EffectStringVariable *pstrTechnique = NULL;
			LPCSTR name;
			if (strcmpi(ParamDesc.Semantic, "COMBINETECHNIQUE") == 0) {
				pstrTechnique = hParam->AsString();
				pstrTechnique->GetString(&name);
				s->m_CombineTechniqueEffectHandle = effect->GetTechniqueByName(name);
			} else if (strcmpi(ParamDesc.Semantic, "PREPROCESSTECHNIQUE") == 0) {
				pstrTechnique = hParam->AsString();
				pstrTechnique->GetString(&name);
				s->m_PreprocessTechnique1EffectHandle = effect->GetTechniqueByName(name);
			} else if (strcmpi(ParamDesc.Semantic, "PREPROCESSTECHNIQUE1") == 0) {
				pstrTechnique = hParam->AsString();
				pstrTechnique->GetString(&name);
				s->m_PreprocessTechnique2EffectHandle = effect->GetTechniqueByName(name);
			} else if (strcmpi(ParamDesc.Semantic, "NAME") == 0) {
				s->m_strName = hParam->AsString();
			}
			if (pstrTechnique)
				pstrTechnique->Release();
		}

		LPCSTR pstrFunctionHandle = NULL;
		LPCSTR pstrTarget = NULL;
		LPCSTR pstrTextureType = NULL;
		INT Width = D3DX_DEFAULT;
		INT Height = D3DX_DEFAULT;
		INT Depth = D3DX_DEFAULT;

		for (UINT iAnnot = 0; iAnnot < ParamDesc.Annotations; iAnnot++) {
			ID3DX11EffectVariable *hAnnot = hParam->GetAnnotationByIndex(iAnnot);
			D3DX11_EFFECT_VARIABLE_DESC AnnotDesc;
			ID3DX11EffectStringVariable *pstrName = NULL;

			hr = hAnnot->GetDesc(&AnnotDesc);
			if (FAILED(hr)) {
				write_log(_T("Direct3D11: GetParameterDescAnnot(%d) failed: %s\n"), iAnnot, hr);
				continue;
			}

			TCHAR *name = au(AnnotDesc.Name);
			TCHAR *semantic = au(AnnotDesc.Semantic);
			write_log(_T("Effect Annotation Name='%s' Semantic='%s'\n"), name, semantic);
			xfree(semantic);
			xfree(name);

			if (strcmpi(AnnotDesc.Name, "name") == 0) {
				pstrName = hAnnot->AsString();
			} else if (strcmpi(AnnotDesc.Name, "function") == 0) {
				hAnnot->AsString()->GetString(&pstrFunctionHandle);
			} else if (strcmpi(AnnotDesc.Name, "target") == 0) {
				hAnnot->AsString()->GetString(&pstrTarget);
			} else if (strcmpi(AnnotDesc.Name, "width") == 0) {
				hAnnot->AsScalar()->GetInt(&Width);
			} else if (strcmpi(AnnotDesc.Name, "height") == 0) {
				hAnnot->AsScalar()->GetInt(&Height);
			} else if (strcmpi(AnnotDesc.Name, "depth") == 0) {
				hAnnot->AsScalar()->GetInt(&Depth);
			} else if (strcmpi(AnnotDesc.Name, "type") == 0) {
				hAnnot->AsString()->GetString(&pstrTextureType);
			}
			if (pstrName)
				pstrName->Release();
			hAnnot->Release();
		}

		if (pstrFunctionHandle != NULL) {
#if 0
			if (pstrTarget == NULL || strcmp(pstrTarget, "tx_1_1"))
				pstrTarget = "tx_1_0";

			ID3DBlob *errors = NULL;
			ID3DBlob *code = NULL;
			TCHAR *n = au(pstrFunctionHandle);
			hr = ppD3DCompile2(data, strlen(data), fxname, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, pstrFunctionHandle, pstrTarget, flags1, 0, 0, NULL, 0, &code, &errors);
			if (SUCCEEDED(hr)) {

				if (Width == D3DX_DEFAULT)
					Width = 64;
				if (Height == D3DX_DEFAULT)
					Height = 64;
				if (Depth == D3DX_DEFAULT)
					Depth = 64;

				ID3D11Resource *res = NULL;
				D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
				memset(&srvDesc, 0, sizeof srvDesc);
				if (pstrTextureType && strcmpi(pstrTextureType, "volume") == 0) {

				} else if (pstrTextureType && strcmpi(pstrTextureType, "cube") == 0) {

				} else {
					ID3D11Texture2D *texture;
					D3D11_TEXTURE2D_DESC desc;
					memset(&desc, 0, sizeof desc);
					desc.Width = Width;
					desc.Height = Height;
					desc.MipLevels = 1;
					desc.ArraySize = 1;
					desc.Format = d3d->scrformat;
					desc.SampleDesc.Count = 1;
					desc.SampleDesc.Quality = 0;
					desc.Usage = D3D11_USAGE_DEFAULT;
					desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
					desc.CPUAccessFlags = 0;
					hr = d3d->m_device->CreateTexture2D(&desc, NULL, &texture);
					if (SUCCEEDED(hr)) {
						res = texture;
						srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
					} else {
						write_log(_T("CreateTexture2D ('%s' %d%*%d) failed: %08x\n"), n, Width, Height, hr);
					}
				}
				if (res) {
					ID3D11ShaderResourceView *rv = NULL;
					srvDesc.Texture2D.MostDetailedMip = 0;
					srvDesc.Texture2D.MipLevels = 1;
					srvDesc.Format = d3d->scrformat;
					hr = d3d->m_device->CreateShaderResourceView(res, &srvDesc, &rv);
					if (SUCCEEDED(hr)) {
						hParam->AsShaderResource()->SetResource(rv);
						rv->Release();
					} else {
						write_log(_T("CreateShaderResourceView ('%s' %dx%d) failed: %08x\n"), n, Width, Height, hr);
					}
					res->Release();
				}
				
			} else {
				void *p = errors->GetBufferPointer();
				TCHAR *s = au((char*)p);
				write_log(_T("Effect compiler '%s' errors:\n%s\n"), n, s);
				xfree(s);
			}
			xfree(n);
			if (errors)
				errors->Release();
#endif
		}

		hParam->Release();
	}

	if (!s->m_CombineTechniqueEffectHandle && effectDesc.Techniques > 0) {
		s->m_CombineTechniqueEffectHandle = effect->GetTechniqueByIndex(0);
	}

	return true;
}

static bool allocfxdata(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	struct TLVERTEX *vertices[NUMVERTICES] = { 0 };
	D3D11_BUFFER_DESC vertexBufferDesc;
	D3D11_SUBRESOURCE_DATA vertexData;
	D3D11_BUFFER_DESC indexBufferDesc;
	D3D11_SUBRESOURCE_DATA indexData;
	uae_u32 indices[INDEXCOUNT * 2];

	vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	vertexBufferDesc.ByteWidth = sizeof(struct TLVERTEX) * NUMVERTICES;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the vertex data.
	vertexData.pSysMem = vertices;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;

	// Now create the vertex buffer.
	HRESULT hr = d3d->m_device->CreateBuffer(&vertexBufferDesc, &vertexData, &s->vertexBuffer);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateBuffer(fxvertex) %08x\n"), hr);
		return false;
	}
#ifndef NDEBUG
	static const char vname[] = "fxvertexbuffer";
	s->vertexBuffer->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(vname) - 1, vname);
#endif

	static const uae_u16 indexes[INDEXCOUNT * 2] = {
		2, 1, 0, 2, 3, 1,
		6, 5, 4, 6, 7, 5
	};
	// Load the index array with data.
	for (int i = 0; i < INDEXCOUNT * 2; i++)
	{
		if (d3d->index_buffer_bytes == 4)
			((uae_u32*)indices)[i] = indexes[i];
		else
			((uae_u16*)indices)[i] = indexes[i];
	}

	// Set up the description of the static index buffer.
	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = d3d->index_buffer_bytes * INDEXCOUNT * 2;
	indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags = 0;
	indexBufferDesc.MiscFlags = 0;
	indexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the index data.
	indexData.pSysMem = indices;
	indexData.SysMemPitch = 0;
	indexData.SysMemSlicePitch = 0;

	// Create the index buffer.
	hr = d3d->m_device->CreateBuffer(&indexBufferDesc, &indexData, &s->indexBuffer);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateBuffer(index) %08x\n"), hr);
		return false;
	}
#ifndef NDEBUG
	static const char iname[] = "fxindexbuffer";
	s->indexBuffer->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(iname) - 1, iname);
#endif

	return true;
}

static bool createfxvertices(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	HRESULT hr;
	struct TLVERTEX *vertices;
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	float sizex, sizey;

	sizex = 1.0f;
	sizey = 1.0f;
	hr = d3d->m_deviceContext->Map(s->vertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11DeviceContext map(fxvertex) %08x\n"), hr);
		return false;
	}
	vertices = (struct TLVERTEX*)mappedResource.pData;

	//Setup vertices

	vertices[0].position.x = -0.5f; vertices[0].position.y = -0.5f;
	vertices[0].diffuse = 0xFFFFFFFF;
	vertices[0].texcoord.x = 0.0f; vertices[0].texcoord.y = sizey;

	vertices[1].position.x = -0.5f; vertices[1].position.y = 0.5f;
	vertices[1].diffuse = 0xFFFFFFFF;
	vertices[1].texcoord.x = 0.0f; vertices[1].texcoord.y = 0.0f;

	vertices[2].position.x = 0.5f; vertices[2].position.y = -0.5f;
	vertices[2].diffuse = 0xFFFFFFFF;
	vertices[2].texcoord.x = sizex; vertices[2].texcoord.y = sizey;

	vertices[3].position.x = 0.5f; vertices[3].position.y = 0.5f;
	vertices[3].diffuse = 0xFFFFFFFF;
	vertices[3].texcoord.x = sizex; vertices[3].texcoord.y = 0.0f;

	// Additional vertices required for some PS effects
	vertices[4].position.x = 0.0f; vertices[4].position.y = 0.0f;
	vertices[4].diffuse = 0xFFFFFF00;
	vertices[4].texcoord.x = 0.0f; vertices[4].texcoord.y = 1.0f;
	vertices[5].position.x = 0.0f; vertices[5].position.y = 1.0f;
	vertices[5].diffuse = 0xFFFFFF00;
	vertices[5].texcoord.x = 0.0f; vertices[5].texcoord.y = 0.0f;
	vertices[6].position.x = 1.0f; vertices[6].position.y = 0.0f;
	vertices[6].diffuse = 0xFFFFFF00;
	vertices[6].texcoord.x = 1.0f; vertices[6].texcoord.y = 1.0f;
	vertices[7].position.x = 1.0f; vertices[7].position.y = 1.0f;
	vertices[7].diffuse = 0xFFFFFF00;
	vertices[7].texcoord.x = 1.0f; vertices[7].texcoord.y = 0.0f;

	d3d->m_deviceContext->Unmap(s->vertexBuffer, 0);

	return true;
}

static int createfxlayout(struct d3d11struct *d3d, struct shaderdata11 *s, ID3DX11EffectTechnique *technique, int idx)
{
	HRESULT hr;

	if (!technique || !technique->IsValid())
		return -1;

	D3DX11_TECHNIQUE_DESC techDesc;
	hr = technique->GetDesc(&techDesc);
	for (int pass = 0; pass < techDesc.Passes && idx < MAX_TECHNIQUE_LAYOUTS; pass++) {
		ID3DX11EffectPass *effectpass = technique->GetPassByIndex(pass);
		D3DX11_PASS_SHADER_DESC effectVsDesc;
		D3DX11_EFFECT_SHADER_DESC effectVsDesc2;

		hr = effectpass->GetVertexShaderDesc(&effectVsDesc);
		hr = effectVsDesc.pShaderVariable->GetShaderDesc(effectVsDesc.ShaderIndex, &effectVsDesc2);
		const void *vsCodePtr = effectVsDesc2.pBytecode;
		unsigned vsCodeLen = effectVsDesc2.BytecodeLength;

		D3D11_INPUT_ELEMENT_DESC polygonLayout[3];
		int pidx = 0;

		// Create the vertex input layout description.
		// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
		polygonLayout[pidx].SemanticName = "POSITION";
		polygonLayout[pidx].SemanticIndex = 0;
		polygonLayout[pidx].Format = DXGI_FORMAT_R32G32B32_FLOAT;
		polygonLayout[pidx].InputSlot = 0;
		polygonLayout[pidx].AlignedByteOffset = 0;
		polygonLayout[pidx].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[pidx].InstanceDataStepRate = 0;
		pidx++;

		polygonLayout[pidx].SemanticName = "DIFFUSE";
		polygonLayout[pidx].SemanticIndex = 0;
		polygonLayout[pidx].Format = DXGI_FORMAT_R32_UINT;
		polygonLayout[pidx].InputSlot = 0;
		polygonLayout[pidx].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[pidx].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[pidx].InstanceDataStepRate = 0;
		pidx++;

		polygonLayout[pidx].SemanticName = "TEXCOORD";
		polygonLayout[pidx].SemanticIndex = 0;
		polygonLayout[pidx].Format = DXGI_FORMAT_R32G32_FLOAT;
		polygonLayout[pidx].InputSlot = 0;
		polygonLayout[pidx].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
		polygonLayout[pidx].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
		polygonLayout[pidx].InstanceDataStepRate = 0;
		pidx++;

		hr = d3d->m_device->CreateInputLayout(polygonLayout, pidx, vsCodePtr, vsCodeLen, &s->layouts[idx]);
		if (FAILED(hr)) {
			write_log(_T("technique CreateInputLayout %08x %d\n"), hr, idx);
		}
		idx++;
	}
	return idx;
}

static bool psEffect_hasPreProcess(struct shaderdata11 *s) { return s->m_PreprocessTechnique1EffectHandle != 0; }
static bool psEffect_hasPreProcess2(struct shaderdata11 *s) { return s->m_PreprocessTechnique2EffectHandle != 0; }

static bool isws(char c)
{
	return c == '\n' || c == '\r' || c == '\t' || c == ' ';
}
static bool islf(char c)
{
	return c == '\n' || c == '\r' || c == ';';
}

static bool fxneedconvert(char *s)
{
	char *t = s;
	int len = uaestrlen(s);
	while (len > 0) {
		if (t != s && isws(t[-1]) && (!strnicmp(t, "technique10", 11) || !strnicmp(t, "technique11", 11)) && isws(t[11])) {
			return false;
		}
		len--;
		t++;
	}
	return true;
}

static void fxspecials(char *s, char *dst)
{
	char *t = s;
	char *d = dst;
	*d = 0;
	int len = uaestrlen(s);
	while (len > 0) {
		bool found = false;
		if (t != s && !strnicmp(t, "minfilter", 9) && (isws(t[9]) || t[9] == '=') && isws(t[-1])) {
			found = true;
			t += 10;
			len -= 10;
			while (!islf(*t) && len > 0) {
				if (!strnicmp(t, "point", 5)) {
					strcpy(d, "Filter=MIN_MAG_MIP_POINT");
					d += uaestrlen(d);
					write_log("FX: 'minfilter' -> 'Filter=MIN_MAG_MIP_POINT'\n");
				}
				if (!strnicmp(t, "linear", 6)) {
					strcpy(d, "Filter=MIN_MAG_MIP_LINEAR");
					d += uaestrlen(d);
					write_log("FX: 'minfiler' -> 'Filter=MIN_MAG_MIP_LINEAR'\n");
				}
				t++;
				len--;
			}
		}
		if (!found) {
			*d++ = *t++;
			len--;
		}
	}
	*d = 0;
}

static void fxconvert(char *s, char *dst, const char **convert1, const char **convert2)
{
	char *t = s;
	char *d = dst;
	int len = uaestrlen(s);
	while (len > 0) {
		bool found = false;
		for (int i = 0; convert1[i]; i++) {
			int slen = uaestrlen(convert1[i]);
			int dlen = uaestrlen(convert2[i]);
			if (len > slen && !strnicmp(t, convert1[i], slen)) {
				if ((t == s || isws(t[-1])) && isws(t[slen])) {
					memcpy(d, convert2[i], dlen);
					t += slen;
					d += dlen;
					len -= slen;
					found = true;
					write_log("FX: '%s' -> '%s'\n", convert1[i], convert2[i]);
				}
			}
		}
		if (!found) {
			*d++ = *t++;
			len--;
		}
	}
	*d = 0;
}

static void fxremoveline(char *s, char *dst, const char **lines)
{
	char *t = s;
	char *d = dst;
	int len = uaestrlen(s);
	while (len > 0) {
		bool found = false;
		for (int i = 0; lines[i]; i++) {
			int slen = uaestrlen(lines[i]);
			if (len > slen && !strnicmp(t, lines[i], slen)) {
				d--;
				while (d != dst) {
					if (islf(*d)) {
						d++;
						break;
					}
					d--;
				}
				while (*t && len > 0) {
					if (islf(*t)) {
						t++;
						len--;
						break;
					}
					t++;
					len--;
				}
				found = true;
				write_log("FX: '%s' line removed\n", lines[i]);
			}
		}
		if (!found) {
			*d++ = *t++;
			len--;
		}
	}
	*d = 0;
}

#if 1

static bool psEffect_LoadEffect(struct d3d11struct *d3d, const TCHAR *shaderfile, struct shaderdata11 *s, int num)
{
	int ret = 0;
	HRESULT hr;
	TCHAR tmp[MAX_DPATH];
	TCHAR tmp2[MAX_DPATH], tmp3[MAX_DPATH];
	static int first;
	int canusefile = 0, existsfile = 0;
	bool plugin_path;
	struct zfile *z = NULL;
	char *fx1 = NULL;
	char *fx2 = NULL;
	char *name = NULL;
	int layout = 0;
	char *fx;
	int size;

	if (!pD3DCompileFromFile || !ppD3DCompile) {
		write_log(_T("D3D11 No shader compiler available (D3DCompiler_46.dll or D3DCompiler_47.dll).\n"));
		return false;
	}

	DWORD dwShaderFlags = D3DCOMPILE_ENABLE_BACKWARDS_COMPATIBILITY;
#ifndef NDEBUG
	//dwShaderFlags |= D3DCOMPILE_DEBUG;
	//Disable optimizations to further improve shader debugging
	//dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif

	GetCurrentDirectory(MAX_DPATH, tmp3);
	name = ua(shaderfile);

	plugin_path = get_plugin_path(tmp2, sizeof tmp2 / sizeof(TCHAR), _T("filtershaders\\direct3d"));
	_tcscpy(tmp, tmp2);

	d3d->frames_since_init = 0;
	if (!d3d->filenotificationhandle)
		d3d->filenotificationhandle = FindFirstChangeNotification(tmp, FALSE, FILE_NOTIFY_CHANGE_LAST_WRITE);

	_tcscat(tmp, shaderfile);
	write_log(_T("Direct3D11: Attempting to load '%s'\n"), tmp);
	_tcscpy(s->loadedshader, shaderfile);

	ID3DX11Effect *g_pEffect = NULL;
	ID3DBlob *errors = NULL;

	z = zfile_fopen(tmp, _T("rb"));
	if (!z) {
		write_log(_T("Failed to open '%s'\n"), tmp);
		goto end;
	}
	size = zfile_size32(z);
	fx1 = xcalloc(char, size * 4);
	fx2 = xcalloc(char, size * 4);
	if (zfile_fread(fx1, 1, size, z) != size) {
		write_log(_T("Failed to read '%s'\n"), tmp);
		goto end;

	}
	zfile_fclose(z);
	z = NULL;

	fx = fx1;
	if (fxneedconvert(fx1)) {
		static const char *converts1[] = { "technique", "vs_3_0", "vs_2_0", "vs_1_1", "ps_3_0", "ps_2_0", NULL };
		static const char *converts2[] = { "technique10", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "vs_4_0_level_9_3", "ps_4_0_level_9_3", "ps_4_0_level_9_3", NULL };
		fxconvert(fx1, fx2, converts1, converts2);

		static const char *lines[] = { "alphablendenable", "colorwriteenable", "srgbwriteenable", "magfilter", NULL };
		fxremoveline(fx2, fx1, lines);

		fxspecials(fx1, fx2);
		
		fx = fx2;
	}

	SetCurrentDirectory(tmp2);
	hr = D3DX11CompileEffectFromMemory(fx, strlen(fx), name, NULL, D3D_COMPILE_STANDARD_FILE_INCLUDE, dwShaderFlags, 0, d3d->m_device, &g_pEffect, &errors);

#if 0
	hr = D3DX11CompileEffectFromFile(tmp, nullptr, D3D_COMPILE_STANDARD_FILE_INCLUDE, dwShaderFlags, 0, d3d->m_device, &g_pEffect, &errors);
#endif

	if (FAILED(hr)) {
		write_log(_T("Direct3D11: D3DX11CompileEffectFromMemory('%s') failed: %08x\n"), tmp, hr);
		void *p = errors->GetBufferPointer();
		TCHAR *s = au((char*)p);
		write_log(_T("Effect compiler errors:\n%s\n"), s);
		xfree(s);
		goto end;
	}

	if (errors) {
		void *p = errors->GetBufferPointer();
		TCHAR *s = au((char*)p);
		write_log(_T("Effect compiler warnings:\n%s\n"), s);
		errors->Release();
	}

	if (!psEffect_ParseParameters(d3d, g_pEffect, s, name, fx, dwShaderFlags))
		goto end;

	SetCurrentDirectory(tmp3);

	s->effect = g_pEffect;

	s->CombineTechniqueEffectIndex = layout;
	layout = createfxlayout(d3d, s, s->m_CombineTechniqueEffectHandle, layout);
	s->PreprocessTechnique1EffectIndex = layout;
	layout = createfxlayout(d3d, s, s->m_PreprocessTechnique1EffectHandle, layout);
	s->PreprocessTechnique2EffectIndex = layout;
	layout = createfxlayout(d3d, s, s->m_PreprocessTechnique2EffectHandle, layout);

	allocfxdata(d3d, s);
	createfxvertices(d3d, s);

	if (s->type == SHADERTYPE_BEFORE) {
		s->viewport.Width = (float)d3d->m_bitmapWidth * d3d->dmultxh;
		s->viewport.Height = (float)d3d->m_bitmapHeight * d3d->dmultxv;
	} else {
		s->viewport.Width = (float)d3d->m_screenWidth * d3d->dmultxh;
		s->viewport.Height = (float)d3d->m_screenHeight * d3d->dmultxv;
	}
	s->viewport.MinDepth = 0.0f;
	s->viewport.MaxDepth = 1.0f;
	s->viewport.TopLeftX = 0.0f;
	s->viewport.TopLeftY = 0.0f;

	s->psPreProcess = false;
	if (psEffect_hasPreProcess(s))
		s->psPreProcess = true;

	xfree(fx1);
	xfree(fx2);
	xfree(name);

	return true;

end:
	SetCurrentDirectory(tmp3);
	if (g_pEffect)
		g_pEffect->Release();
	if (errors)
		errors->Release();
	zfile_fclose(z);
	s->loadedshader[0] = 0;
	xfree(fx1);
	xfree(fx2);
	xfree(name);
	return false;
}

#else

static bool psEffect_LoadEffect(struct d3d11struct* d3d, const TCHAR* shaderfile, struct shaderdata11* s, int num)
{
	write_log(_T("FX11 disabled\n"));
	return false;
}

#endif

static bool psEffect_SetMatrices(D3DXMATRIX *matProj, D3DXMATRIX *matView, D3DXMATRIX *matWorld, struct shaderdata11 *s)
{
	if (s->m_MatWorldEffectHandle) {
		s->m_MatWorldEffectHandle->SetMatrix((float*)matWorld);
	}
	if (s->m_MatViewEffectHandle) {
		s->m_MatViewEffectHandle->SetMatrix((float*)matView);
	}
	if (s->m_MatProjEffectHandle) {
		s->m_MatProjEffectHandle->SetMatrix((float*)matProj);
	}
	if (s->m_MatWorldViewEffectHandle) {
		D3DXMATRIX matWorldView;
		xD3DXMatrixMultiply(&matWorldView, matWorld, matView);
		s->m_MatWorldViewEffectHandle->SetMatrix((float*)&matWorldView);
	}
	if (s->m_MatViewProjEffectHandle) {
		D3DXMATRIX matViewProj;
		xD3DXMatrixMultiply(&matViewProj, matView, matProj);
		s->m_MatViewProjEffectHandle->SetMatrix((float*)&matViewProj);
	}
	if (s->m_MatWorldViewProjEffectHandle) {
		D3DXMATRIX tmp, matWorldViewProj;
		xD3DXMatrixMultiply(&tmp, matWorld, matView);
		xD3DXMatrixMultiply(&matWorldViewProj, &tmp, matProj);
		s->m_MatWorldViewProjEffectHandle->SetMatrix((float*)&matWorldViewProj);
	}
	return true;
}

static void settransform_pre(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	// Projection is (0,0,0) -> (1,1,1)
	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matProj, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
	// Align texels with pixels
	xD3DXMatrixTranslation(&d3d->m_matView, -0.5f / d3d->m_bitmapWidthX, 0.5f / d3d->m_bitmapHeightX, 0.0f);
	// Identity for world
	xD3DXMatrixIdentity(&d3d->m_matWorld);
}

static void settransform(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	// Projection is (0,0,0) -> (1,1,1)
	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matPreProj, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
	// Align texels with pixels
	xD3DXMatrixTranslation(&d3d->m_matPreView, -0.5f / d3d->m_bitmapWidthX, 0.5f / d3d->m_bitmapHeightX, 0.0f);
	// Identity for world
	xD3DXMatrixIdentity(&d3d->m_matPreWorld);

	if (s)
		psEffect_SetMatrices(&d3d->m_matProj, &d3d->m_matView, &d3d->m_matWorld, s);

	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matProj2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);

	xD3DXMatrixTranslation(&d3d->m_matView2, 0.5f - 0.5f / d3d->m_bitmapWidthX, 0.5f + 0.5f / d3d->m_bitmapHeightX, 0.0f);

	xD3DXMatrixIdentity(&d3d->m_matWorld2);
}

static void settransform2(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	// Projection is (0,0,0) -> (1,1,1)
	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matPreProj, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
	// Align texels with pixels
	xD3DXMatrixTranslation(&d3d->m_matPreView, -0.5f / d3d->m_screenWidth, 0.5f / d3d->m_screenHeight, 0.0f);
	// Identity for world
	xD3DXMatrixIdentity(&d3d->m_matPreWorld);

	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matProj2, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);

	xD3DXMatrixTranslation(&d3d->m_matView2, 0.5f - 0.5f / d3d->m_screenWidth, 0.5f + 0.5f / d3d->m_screenHeight, 0.0f);
	xD3DXMatrixIdentity(&d3d->m_matWorld2);
}

static int psEffect_SetTextures(ID3D11Texture2D *lpSourceTex, ID3D11ShaderResourceView *lpSourcerv, struct shaderdata11 *s)
{
	D3DXVECTOR4 fDims, fTexelSize;

	if (!s->m_SourceTextureEffectHandle) {
		write_log(_T("D3D11 Texture with SOURCETEXTURE semantic not found\n"));
		return 0;
	}
	s->m_SourceTextureEffectHandle->SetResource(lpSourcerv);
	if (s->m_WorkingTexture1EffectHandle) {
		s->m_WorkingTexture1EffectHandle->SetResource(s->lpWorkTexture1.rv);
	}
	if (s->m_WorkingTexture2EffectHandle) {
		s->m_WorkingTexture2EffectHandle->SetResource(s->lpWorkTexture2.rv);
	}
	if (s->m_Hq2xLookupTextureHandle) {
		s->m_Hq2xLookupTextureHandle->SetResource(s->lpHq2xLookupTexturerv);
	}
	fDims.x = 256; fDims.y = 256; fDims.z = 1; fDims.w = 1;
	fTexelSize.x = 1; fTexelSize.y = 1; fTexelSize.z = 1; fTexelSize.w = 1;
	if (lpSourceTex) {
		D3D11_TEXTURE2D_DESC desc;
		lpSourceTex->GetDesc(&desc);
		fDims.x = (FLOAT)desc.Width;
		fDims.y = (FLOAT)desc.Height;
	}

	fTexelSize.x = 1.0f / fDims.x;
	fTexelSize.y = 1.0f / fDims.y;

	if (s->m_SourceDimsEffectHandle) {
		s->m_SourceDimsEffectHandle->SetFloatVector((float*)&fDims);
	}
	if (s->m_InputDimsEffectHandle) {
		s->m_InputDimsEffectHandle->SetFloatVector((float*)&fDims);
	}
	if (s->m_TargetDimsEffectHandle) {
		D3DXVECTOR4 fDimsTarget;
		fDimsTarget.x = (float)s->targettex_width;
		fDimsTarget.y = (float)s->targettex_height;
		fDimsTarget.z = 1;
		fDimsTarget.w = 1;
		s->m_TargetDimsEffectHandle->SetFloatVector((float*)&fDimsTarget);
	}
	if (s->m_TexelSizeEffectHandle) {
		s->m_TexelSizeEffectHandle->SetFloatVector((float*)&fTexelSize);
	}
	if (s->m_ScaleEffectHandle) {
		D3DXVECTOR4 fScale;
		fScale.x = (float)(1 << currprefs.gfx_resolution);
		fScale.y = (float)(1 << currprefs.gfx_vresolution);
		fScale.w = fScale.z = 1;
		s->m_ScaleEffectHandle->SetFloatVector((float*)&fScale);
	}
	if (s->framecounterHandle) {
		s->framecounterHandle->SetFloat((float)vsync_counter);
	}

	return 1;
}

enum psEffect_Pass { psEffect_None, psEffect_PreProcess1, psEffect_PreProcess2, psEffect_Combine };

static bool psEffect_Begin(struct d3d11struct *d3d, enum psEffect_Pass pass, int *pPasses, int *pIndex, struct shaderdata11 *s)
{
	ID3DX11Effect *effect = s->effect;
	D3DX11_TECHNIQUE_DESC desc;
	int idx = 0;

	d3d->technique = NULL;
	switch (pass)
	{
	case psEffect_PreProcess1:
		d3d->technique = s->m_PreprocessTechnique1EffectHandle;
		*pIndex = s->PreprocessTechnique1EffectIndex;
		break;
	case psEffect_PreProcess2:
		d3d->technique = s->m_PreprocessTechnique2EffectHandle;
		*pIndex = s->PreprocessTechnique2EffectIndex;
		break;
	case psEffect_Combine:
		d3d->technique = s->m_CombineTechniqueEffectHandle;
		*pIndex = s->CombineTechniqueEffectIndex;
		break;
	}
	if (!d3d->technique)
		return false;
	HRESULT hr = d3d->technique->GetDesc(&desc);
	if (FAILED(hr)) {
		write_log(_T("technique->GetDesc %08x\n"), hr);
		return false;
	}
	*pPasses = desc.Passes;
	return true;
}

static bool psEffect_BeginPass(struct d3d11struct *d3d, struct shaderdata11 *s, int Pass, int Index)
{
	ID3DX11EffectPass *effectpass = d3d->technique->GetPassByIndex(Pass);
	if (!effectpass->IsValid()) {
		write_log(_T("psEffect_BeginPass pass %d not valid\n"), Pass);
		return false;
	}
	d3d->effectpass = effectpass;
	HRESULT hr = effectpass->Apply(0, d3d->m_deviceContext);
	if (FAILED(hr)) {
		write_log(_T("effectpass->Apply %08x\n"), hr);
	}
	UINT stride = sizeof(TLVERTEX);
	UINT offset = 0;
	d3d->m_deviceContext->IASetVertexBuffers(0, 1, &s->vertexBuffer, &stride, &offset);
	d3d->m_deviceContext->IASetIndexBuffer(s->indexBuffer, d3d->index_buffer_bytes == 4 ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT, 0);
	d3d->m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	d3d->m_deviceContext->IASetInputLayout(s->layouts[Index + Pass]);
	return true;
}
static bool psEffect_EndPass(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	d3d->technique = NULL;
	d3d->effectpass = NULL;
	return true;
}
static bool psEffect_End(struct d3d11struct *d3d, struct shaderdata11 *s)
{
	return true;
}

static bool processshader(struct d3d11struct *d3d, struct shadertex *st, struct shaderdata11 *s, bool rendertarget)
{
	int uPasses, uPass, uIndex;
	ID3D11RenderTargetView *lpRenderTarget = NULL;
	ID3D11RenderTargetView *lpNewRenderTarget;
	struct shadertex *lpWorkTexture;

	d3d->m_deviceContext->RSSetViewports(1, st->vp);

	TurnOffAlphaBlending(d3d);
	
	if (!psEffect_SetTextures(st->tex, st->rv, s))
		return NULL;

	if (s->psPreProcess) {

		if (!psEffect_SetMatrices(&d3d->m_matPreProj, &d3d->m_matPreView, &d3d->m_matPreWorld, s))
			return NULL;

		d3d->m_deviceContext->OMGetRenderTargets(1, &lpRenderTarget, NULL);

		lpWorkTexture = &s->lpWorkTexture1;
		lpNewRenderTarget = lpWorkTexture->rt;

		d3d->m_deviceContext->OMSetRenderTargets(1, &lpNewRenderTarget, NULL);
pass2:
		uPasses = 0;
		if (psEffect_Begin(d3d, (lpWorkTexture == &s->lpWorkTexture1) ? psEffect_PreProcess1 : psEffect_PreProcess2, &uPasses, &uIndex, s)) {
			for (uPass = 0; uPass < uPasses; uPass++) {
				if (psEffect_BeginPass(d3d, s, uPass, uIndex)) {
					d3d->m_deviceContext->DrawIndexed(6, 6, 0);
					psEffect_EndPass(d3d, s);
				}
			}
			psEffect_End(d3d, s);
		}

		if (lpRenderTarget) {
			d3d->m_deviceContext->OMSetRenderTargets(1, &lpRenderTarget, NULL);
		}
		lpNewRenderTarget = NULL;

		if (psEffect_hasPreProcess2(s) && lpWorkTexture == &s->lpWorkTexture1) {
			lpWorkTexture = &s->lpWorkTexture2;
			goto pass2;
		}
		
		if (lpRenderTarget) {
			lpRenderTarget->Release();
			lpRenderTarget = NULL;
		}
	}

	psEffect_SetMatrices(&d3d->m_matProj2, &d3d->m_matView2, &d3d->m_matWorld2, s);

	if (rendertarget) {
		d3d->m_deviceContext->OMGetRenderTargets(1, &lpRenderTarget, NULL);
		d3d->m_deviceContext->OMSetRenderTargets(1, &s->lpTempTexture.rt, NULL);
	}

	TurnOffAlphaBlending(d3d);

	uPasses = 0;
	if (psEffect_Begin(d3d, psEffect_Combine, &uPasses, &uIndex, s)) {
		for (uPass = 0; uPass < uPasses; uPass++) {
			if (!psEffect_BeginPass(d3d, s, uPass, uIndex)) {
				if (lpRenderTarget) {
					lpRenderTarget->Release();
				}
				return false;
			}
			d3d->m_deviceContext->DrawIndexed(6, 0, 0);
			psEffect_EndPass(d3d, s);
		}
		psEffect_End(d3d, s);
	}

	if (rendertarget) {
		d3d->m_deviceContext->OMSetRenderTargets(1, &lpRenderTarget, NULL);
	}

	if (lpRenderTarget) {
		lpRenderTarget->Release();
	}

	memcpy(st, &s->lpTempTexture, sizeof(struct shadertex));

	return true;
}

static bool UpdateVertexArray(struct d3d11struct *d3d, ID3D11Buffer *vertexbuffer,
	float left, float top, float right, float bottom,
	float slleft, float sltop, float slright, float slbottom)
{
	VertexType* verticesPtr;
	D3D11_MAPPED_SUBRESOURCE mappedResource;
	HRESULT result;
	VertexType vertices[4];

	if (!vertexbuffer)
		return false;

	// Load the vertex array with data.
	vertices[0].position = D3DXVECTOR3(left, top, 0.0f);  // Top left.
	vertices[0].texture = D3DXVECTOR2(0.0f, 0.0f);
	vertices[0].sltexture = D3DXVECTOR2(slleft, sltop);

	vertices[1].position = D3DXVECTOR3(right, top, 0.0f);  // Top right.
	vertices[1].texture = D3DXVECTOR2(1.0f, 0.0f);
	vertices[1].sltexture = D3DXVECTOR2(slright, sltop);

	vertices[2].position = D3DXVECTOR3(right, bottom, 0.0f);  // Bottom right.
	vertices[2].texture = D3DXVECTOR2(1.0f, 1.0f);
	vertices[2].sltexture = D3DXVECTOR2(slright, slbottom);

	vertices[3].position = D3DXVECTOR3(left, bottom, 0.0f);  // Bottom left.
	vertices[3].texture = D3DXVECTOR2(0.0f, 1.0f);
	vertices[3].sltexture = D3DXVECTOR2(slleft, slbottom);

	// Lock the vertex buffer so it can be written to.
	result = d3d->m_deviceContext->Map(vertexbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (FAILED(result))
	{
		write_log(_T("ID3D11DeviceContext map(vertex) %08x\n"), result);
		return false;
	}

	// Get a pointer to the data in the vertex buffer.
	verticesPtr = (VertexType*)mappedResource.pData;

	// Copy the data into the vertex buffer.
	memcpy(verticesPtr, (void*)vertices, (sizeof(VertexType) * VERTEXCOUNT));

	// Unlock the vertex buffer.
	d3d->m_deviceContext->Unmap(vertexbuffer, 0);

	return true;
}

static bool UpdateBuffers(struct d3d11struct *d3d, int monid)
{
	int bw = d3d->m_bitmapWidth;
	int bh = d3d->m_bitmapHeight;
	int sw = d3d->m_screenWidth;
	int sh = d3d->m_screenHeight;

	float positionX = d3d->xoffset;
	float positionY = -d3d->yoffset;

	float slleft = 0;
	float sltop = 0;
	float slright = (float)bw * d3d->xmult / sw;
	float slbottom = (float)bh * d3d->ymult / sh;

	slright = slleft + slright;
	slbottom = sltop + slbottom;

	float rot = get_rotation(d3d, monid);

	D3DXMATRIX scaling, rotation, translation, worldMatrix;
	xD3DXMatrixScaling(&scaling, bw * d3d->xmult, bh * d3d->ymult, 1.0f);
	xD3DXMatrixRotationZ(&rotation, rot);

	xD3DXMatrixTranslation(&translation, positionX, positionY, 0.0f);

	xD3DXMatrixMultiply(&worldMatrix, &scaling, &rotation);
	xD3DXMatrixMultiply(&worldMatrix, &worldMatrix, &translation);

	d3d->m_worldMatrix = worldMatrix;

	UpdateVertexArray(d3d, d3d->m_vertexBuffer, -0.5f, 0.5f, 0.5f, -0.5f, slleft, sltop, slright, slbottom);
	return true;
}

static void setupscenecoords(struct d3d11struct *d3d, bool normalrender, int monid)
{
	RECT sr, dr, zr;
	static RECT sr2[MAX_AMIGAMONITORS], dr2[MAX_AMIGAMONITORS], zr2[MAX_AMIGAMONITORS];

	if (!normalrender)
		return;

	getfilterrect2(d3d->num, &dr, &sr, &zr, d3d->m_screenWidth, d3d->m_screenHeight, d3d->m_bitmapWidth / d3d->dmult, d3d->m_bitmapHeight / d3d->dmult, d3d->dmult, &d3d->dmode, d3d->m_bitmapWidth, d3d->m_bitmapHeight);

	if (memcmp(&sr, &sr2[monid], sizeof RECT) || memcmp(&dr, &dr2[monid], sizeof RECT) || memcmp(&zr, &zr2[monid], sizeof RECT)) {
		write_log(_T("POS (%d %d %d %d) - (%d %d %d %d)[%d,%d] (%d %d) S=%d*%d B=%d*%d\n"),
			dr.left, dr.top, dr.right, dr.bottom,
			sr.left, sr.top, sr.right, sr.bottom,
			sr.right - sr.left, sr.bottom - sr.top,
			zr.left, zr.top,
			d3d->m_screenWidth, d3d->m_screenHeight,
			d3d->m_bitmapWidth, d3d->m_bitmapHeight);
		sr2[monid] = sr;
		dr2[monid] = dr;
		zr2[monid] = zr;
	}

	d3d->sr2 = sr;
	d3d->dr2 = dr;
	d3d->zr2 = zr;

	float dw = (float)dr.right - dr.left;
	float dh = (float)dr.bottom - dr.top;
	float w = (float)sr.right - sr.left;
	float h = (float)sr.bottom - sr.top;

	int tx = ((dr.right - dr.left) * d3d->m_bitmapWidth) / (d3d->m_screenWidth * 2);
	int ty = ((dr.bottom - dr.top) * d3d->m_bitmapHeight) / (d3d->m_screenHeight * 2);

	float sw = dw / d3d->m_screenWidth;
	float sh = dh / d3d->m_screenHeight;

	int xshift = -zr.left - sr.left;
	int yshift = -zr.top - sr.top;

	xshift -= ((sr.right - sr.left) - d3d->m_screenWidth) / 2;
	yshift -= ((sr.bottom - sr.top) - d3d->m_screenHeight) / 2;

	d3d->xoffset = (float)tx + xshift - d3d->m_screenWidth / 2.0f;
	d3d->yoffset = (float)ty + yshift - d3d->m_screenHeight / 2.0f;

	d3d->xmult = filterrectmult(d3d->m_screenWidth, w, d3d->dmode);
	d3d->ymult = filterrectmult(d3d->m_screenHeight, h, d3d->dmode);

	d3d->xoffset *= d3d->xmult;
	d3d->yoffset *= d3d->ymult;

	d3d->cursor_offset_x = -zr.left;
	d3d->cursor_offset_y = -zr.top;

	UpdateBuffers(d3d, monid);

	xD3DXMatrixOrthoOffCenterLH(&d3d->m_matProj_out, 0, w + 0.05f, 0, h + 0.05f, 0.0f, 1.0f);
	xD3DXMatrixTranslation(&d3d->m_matView_out, (float)tx, (float)ty, 1.0f);
	sw *= d3d->m_bitmapWidth;
	sh *= d3d->m_bitmapHeight;
	xD3DXMatrixScaling(&d3d->m_matWorld_out, sw + 0.5f / sw, sh + 0.5f / sh, 1.0f);
}

static void updateleds(struct d3d11struct *d3d)
{
	HRESULT hr;
	D3D11_MAPPED_SUBRESOURCE map;
	static uae_u32 rc[256], gc[256], bc[256], a[256];
	static int done;
	int osdx, osdy;

	if (!done) {
		for (int i = 0; i < 256; i++) {
			rc[i] = i << 16;
			gc[i] = i << 8;
			bc[i] = i << 0;
			a[i] = i << 24;
		}
		done = 1;
	}

	if (!d3d->osd.texture || d3d != d3d11data)
		return;

	statusline_getpos(d3d->num, &osdx, &osdy, d3d->m_screenWidth, d3d->m_screenHeight);
	d3d->osd.x = (float)osdx;
	d3d->osd.y = (float)osdy;

	hr = d3d->m_deviceContext->Map(d3d->osd.texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (FAILED(hr)) {
		write_log(_T("Led Map failed %08x\n"), hr);
		return;
	}
	for (int y = 0; y < d3d->osd.height; y++) {
		uae_u8 *buf = (uae_u8*)map.pData + y * map.RowPitch;
		statusline_single_erase(d3d->num, buf, 32 / 8, y, d3d->ledwidth);
	}
	statusline_render(d3d->num, (uae_u8*)map.pData, 32 / 8, map.RowPitch, d3d->ledwidth, d3d->ledheight, rc, gc, bc, a);

	for (int y = 0; y < d3d->osd.height; y++) {
		uae_u8 *buf = (uae_u8*)map.pData + y * map.RowPitch;
		draw_status_line_single(d3d->num, buf, 32 / 8, y, d3d->ledwidth, rc, gc, bc, a);
	}

	d3d->m_deviceContext->Unmap(d3d->osd.texture, 0);
}

static bool createvertexshader(struct d3d11struct *d3d, ID3D11VertexShader **vertexshader, ID3D11Buffer **matrixbuffer, ID3D11InputLayout **layout)
{
	D3D11_INPUT_ELEMENT_DESC polygonLayout[3];
	HRESULT hr;
	unsigned int numElements;
	D3D11_BUFFER_DESC matrixBufferDesc;

	// Create the vertex shader from the buffer.
	hr = d3d->m_device->CreateVertexShader(VertexShader, sizeof(VertexShader), NULL, vertexshader);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateVertexShader %08x\n"), hr);
		return false;
	}
#ifndef NDEBUG
	static const char vname[] = "vertexbuffer";
	(*vertexshader)->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(vname) - 1, vname);
#endif
	// Create the vertex input layout description.
	// This setup needs to match the VertexType stucture in the ModelClass and in the shader.
	polygonLayout[0].SemanticName = "POSITION";
	polygonLayout[0].SemanticIndex = 0;
	polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[0].InputSlot = 0;
	polygonLayout[0].AlignedByteOffset = 0;
	polygonLayout[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[0].InstanceDataStepRate = 0;

	polygonLayout[1].SemanticName = "TEXCOORD";
	polygonLayout[1].SemanticIndex = 0;
	polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[1].InputSlot = 0;
	polygonLayout[1].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[1].InstanceDataStepRate = 0;

	polygonLayout[2].SemanticName = "TEXCOORD";
	polygonLayout[2].SemanticIndex = 1;
	polygonLayout[2].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[2].InputSlot = 0;
	polygonLayout[2].AlignedByteOffset = D3D11_APPEND_ALIGNED_ELEMENT;
	polygonLayout[2].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
	polygonLayout[2].InstanceDataStepRate = 0;

	// Get a count of the elements in the layout.
	numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

	// Create the vertex input layout.
	hr = d3d->m_device->CreateInputLayout(polygonLayout, numElements, VertexShader, sizeof(VertexShader), layout);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateInputLayout %08x\n"), hr);
		return false;
	}

	// Setup the description of the dynamic matrix constant buffer that is in the vertex shader.
	matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	matrixBufferDesc.ByteWidth = sizeof(MatrixBufferType);
	matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	matrixBufferDesc.MiscFlags = 0;
	matrixBufferDesc.StructureByteStride = 0;

	// Create the constant buffer pointer so we can access the vertex shader constant buffer from within this class.
	hr = d3d->m_device->CreateBuffer(&matrixBufferDesc, NULL, matrixbuffer);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateBuffer(matrix) %08x\n"), hr);
		return false;
	}

#ifndef NDEBUG
	static const char mname[] = "matrixbuffer";
	(*matrixbuffer)->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(mname) - 1, mname);
#endif
	return true;
}

static void FreeTexture2D(ID3D11Texture2D **t, ID3D11ShaderResourceView **v)
{
	if (t && *t) {
		(*t)->Release();
		(*t) = NULL;
	}
	if (v && *v) {
		(*v)->Release();
		(*v) = NULL;
	}
}

static void freesprite(struct d3d11sprite *s)
{
	if (!s)
		return;
	FreeTexture2D(&s->texture, &s->texturerv);

	if (s->pixelshader)
		s->pixelshader->Release();
	if (s->vertexshader)
		s->vertexshader->Release();
	if (s->layout)
		s->layout->Release();
	if (s->vertexbuffer)
		s->vertexbuffer->Release();
	if (s->indexbuffer)
		s->indexbuffer->Release();
	if (s->matrixbuffer)
		s->matrixbuffer->Release();
	if (s->texbuf)
		xfree(s->texbuf);

	memset(s, 0, sizeof(struct d3d11sprite));
}

static void FreeShaderTex(struct shadertex *t)
{
	FreeTexture2D(&t->tex, &t->rv);
	if (t->rt)
		t->rt->Release();
	t->rt = NULL;
}

static void freeshaderdata(struct shaderdata11 *s)
{
	if (s->effect) {
		s->effect->Release();
		s->effect = NULL;
	}
	FreeTexture2D(&s->masktexture, &s->masktexturerv);
	FreeShaderTex(&s->lpWorkTexture1);
	FreeShaderTex(&s->lpWorkTexture2);
	FreeShaderTex(&s->lpTempTexture);
	if (s->lpHq2xLookupTexture) {
		s->lpHq2xLookupTexture->Release();
		s->lpHq2xLookupTexture = NULL;
	}
	if (s->lpHq2xLookupTexturerv) {
		s->lpHq2xLookupTexturerv->Release();
		s->lpHq2xLookupTexturerv = NULL;
	}
	for (int j = 0; j < MAX_TECHNIQUE_LAYOUTS; j++) {
		if (s->layouts[j])
			s->layouts[j]->Release();
		s->layouts[j] = NULL;
	}
	if (s->vertexBuffer) {
		s->vertexBuffer->Release();
		s->vertexBuffer = NULL;
	}
	if (s->indexBuffer) {
		s->indexBuffer->Release();
		s->indexBuffer = NULL;
	}
	memset(s, 0, sizeof(struct shaderdata11));
}

static void FreeTextures(struct d3d11struct *d3d)
{
	FreeTexture2D(&d3d->texture2d, &d3d->texture2drv);
	FreeTexture2D(&d3d->texture2dstaging, NULL);
	FreeTexture2D(&d3d->screenshottexturetx, NULL);
	FreeTexture2D(&d3d->screenshottexturert, NULL);

	freesprite(&d3d->osd);
	freesprite(&d3d->hwsprite);
	freesprite(&d3d->mask2texture);
	for (int i = 0; overlayleds[i]; i++) {
		freesprite(&d3d->mask2textureleds[i]);
	}
	freesprite(&d3d->mask2textureled_power_dim);
	freesprite(&d3d->blanksprite);
	struct d3doverlay *ov = d3d->extoverlays;
	while (ov) {
		struct d3doverlay *next = ov->next;
		freesprite(&ov->s);
		xfree(ov);
		ov = next;
	}
	d3d->extoverlays = NULL;
	for (int i = 0; i < MAX_SHADERS; i++) {
		freeshaderdata(&d3d->shaders[i]);
	}
}

static bool InitializeBuffers(struct d3d11struct *d3d, ID3D11Buffer **vertexBuffer, ID3D11Buffer **indexBuffer)
{
	D3D11_BUFFER_DESC vertexBufferDesc, indexBufferDesc;
	D3D11_SUBRESOURCE_DATA vertexData, indexData;
	HRESULT result;
	VertexType vertices[VERTEXCOUNT];
	uae_u32 indices[INDEXCOUNT];

	d3d->index_buffer_bytes = d3d11_feature_level >= D3D10_FEATURE_LEVEL_9_2 ? sizeof(uae_u32) : sizeof(uae_u16);

	// Initialize vertex array to zeros at first.
	memset(vertices, 0, (sizeof(VertexType) * VERTEXCOUNT));

	static const uae_u16 indexes[6] = { 0, 2, 1, 2, 0, 3 };
	// Load the index array with data.
	for (int i = 0; i < INDEXCOUNT; i++)
	{
		if (d3d->index_buffer_bytes == 4)
			((uae_u32*)indices)[i] = indexes[i];
		else
			((uae_u16*)indices)[i] = indexes[i];
	}

	// Set up the description of the static vertex buffer.
	vertexBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	vertexBufferDesc.ByteWidth = sizeof(VertexType) * VERTEXCOUNT;
	vertexBufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	vertexBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	vertexBufferDesc.MiscFlags = 0;
	vertexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the vertex data.
	vertexData.pSysMem = vertices;
	vertexData.SysMemPitch = 0;
	vertexData.SysMemSlicePitch = 0;

	// Now create the vertex buffer.
	result = d3d->m_device->CreateBuffer(&vertexBufferDesc, &vertexData, vertexBuffer);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateBuffer(vertex) %08x\n"), result);
		return false;
	}
#ifndef NDEBUG
	static const char vname[] = "mvertexbuffer";
	(*vertexBuffer)->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(vname) - 1, vname);
#endif

	// Set up the description of the static index buffer.
	indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
	indexBufferDesc.ByteWidth = d3d->index_buffer_bytes * INDEXCOUNT;
	indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
	indexBufferDesc.CPUAccessFlags = 0;
	indexBufferDesc.MiscFlags = 0;
	indexBufferDesc.StructureByteStride = 0;

	// Give the subresource structure a pointer to the index data.
	indexData.pSysMem = indices;
	indexData.SysMemPitch = 0;
	indexData.SysMemSlicePitch = 0;

	// Create the index buffer.
	result = d3d->m_device->CreateBuffer(&indexBufferDesc, &indexData, indexBuffer);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateBuffer(index) %08x\n"), result);
		return false;
	}
#ifndef NDEBUG
	static const char iname[] = "mindexbuffer";
	(*indexBuffer)->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(iname) - 1, iname);
#endif

	return true;
}

static void setsprite(struct d3d11struct *d3d, struct d3d11sprite *s, float x, float y)
{
	s->x = x;
	s->y = y;
}

static bool allocsprite(struct d3d11struct *d3d, struct d3d11sprite *s, int width, int height, bool alpha, bool rotation, bool screenlimit)
{
	D3D11_TEXTURE2D_DESC desc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	HRESULT hr;

	freesprite(s);

	s->width = width;
	s->height = height;
	s->alpha = alpha;
	s->rotation = rotation;
	s->screenlimit = screenlimit;
	s->empty = true;

	if (screenlimit) {
		s->texbuf = xcalloc(uae_u8, width * height * 4);
		if (!s->texbuf) {
			return false;
		}
	}

	if (!InitializeBuffers(d3d, &s->vertexbuffer, &s->indexbuffer))
		goto err;

	if (gfx_hdr) {
		hr = d3d->m_device->CreatePixelShader(PS_PostPlain_HDR, sizeof(PS_PostPlain_HDR), NULL, &s->pixelshader);
	} else {
		hr = d3d->m_device->CreatePixelShader(PS_PostPlain, sizeof(PS_PostPlain), NULL, &s->pixelshader);
	}
	if (FAILED(hr))
		goto err;

	if (!createvertexshader(d3d, &s->vertexshader, &s->matrixbuffer, &s->layout))
		goto err;

	memset(&desc, 0, sizeof desc);
	desc.Width = s->width;
	desc.Height = s->height;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->texformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &s->texture);
	if (FAILED(hr)) {
		write_log(_T("CreateTexture2D (%dx%d) failed: %08x\n"), width, height, hr);
		goto err;
	}

	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Format = d3d->texformat;

	hr = d3d->m_device->CreateShaderResourceView(s->texture, &srvDesc, &s->texturerv);
	if (FAILED(hr)) {
		write_log(_T("CreateShaderResourceView (%dx%d) failed: %08x\n"), width, height, hr);
		goto err;
	}

	setsprite(d3d, s, 0, 0);

	return true;
err:
	freesprite(s);
	return false;
}

#if 0
static void erasetexture(struct d3d11struct *d3d)
{
	int pitch;
	uae_u8 *p = D3D_locktexture(d3d->num, &pitch, NULL, true);
	if (p) {
		for (int i = 0; i < d3d->m_bitmapHeight; i++) {
			memset(p, 255, d3d->m_bitmapWidth * d3d->texdepth / 8);
			p += pitch;
		}
		D3D_unlocktexture(d3d->num, -1, -1);
	}
}
#endif

static bool CreateTexture(struct d3d11struct *d3d)
{
	struct AmigaMonitor *mon = &AMonitors[d3d->num];
	D3D11_TEXTURE2D_DESC desc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	HRESULT hr;

	memset(&d3d->sr2, 0, sizeof(RECT));
	memset(&d3d->dr2, 0, sizeof(RECT));
	memset(&d3d->zr2, 0, sizeof(RECT));

	FreeTextures(d3d);

	memset(&desc, 0, sizeof desc);
	desc.Width = d3d->m_bitmapWidth;
	desc.Height = d3d->m_bitmapHeight;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->texformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_DEFAULT;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = 0;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &d3d->texture2d);
	if (FAILED(hr)) {
		write_log(_T("CreateTexture2D (main) failed: %08x\n"), hr);
		return false;
	}

	memset(&desc, 0, sizeof desc);
	desc.Width = d3d->m_bitmapWidth;
	desc.Height = d3d->m_bitmapHeight;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->texformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_STAGING;
	desc.BindFlags = 0;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &d3d->texture2dstaging);
	if (FAILED(hr)) {
		write_log(_T("CreateTexture2D (staging) failed: %08x\n"), hr);
		return false;
	}

	desc.Width = d3d->m_bitmapWidth;
	desc.Height = d3d->m_bitmapHeight;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
	hr = d3d->m_device->CreateTexture2D(&desc, nullptr, &d3d->screenshottexturetx);
	if (FAILED(hr)) {
		write_log(_T("CreateTexture2D (screenshot tx) failed: %08x\n"), hr);
	}

	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Format = d3d->texformat;

	hr = d3d->m_device->CreateShaderResourceView(d3d->texture2d, &srvDesc, &d3d->texture2drv);
	if (FAILED(hr)) {
		write_log(_T("CreateShaderResourceView MAIN failed: %08x\n"), hr);
		return false;
	}

	ID3D11Texture2D *pSurface;
	hr = d3d->m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&pSurface));
	if (SUCCEEDED(hr)) {
		memset(&desc, 0, sizeof desc);
		pSurface->GetDesc(&desc);
		desc.BindFlags = 0;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
		desc.Usage = D3D11_USAGE_STAGING;

		hr = d3d->m_device->CreateTexture2D(&desc, nullptr, &d3d->screenshottexturert);
		if (FAILED(hr)) {
			write_log(_T("CreateTexture2D (screenshot rt) failed: %08x\n"), hr);
		}
		pSurface->Release();
	}

	UpdateVertexArray(d3d, d3d->m_vertexBuffer, 0, 0, 0, 0, 0, 0, 0, 0);

	d3d->statusbar_hx = d3d->statusbar_vx = statusline_set_multiplier(mon->monitor_id, d3d->m_screenWidth, d3d->m_screenHeight) / 100;
	d3d->ledwidth = d3d->m_screenWidth;
	d3d->ledheight = TD_TOTAL_HEIGHT * d3d->statusbar_vx;
	allocsprite(d3d, &d3d->osd, d3d->ledwidth, d3d->ledheight, true, false, false);
	d3d->osd.enabled = true;

	d3d->cursor_v = false;
	d3d->cursor_scale = false;
	allocsprite(d3d, &d3d->hwsprite, CURSORMAXWIDTH, CURSORMAXHEIGHT, true, true, true);

	write_log(_T("D3D11 %dx%d main texture allocated\n"), d3d->m_bitmapWidth, d3d->m_bitmapHeight);

	return true;
}

static bool allocshadertex(struct d3d11struct *d3d, struct shadertex *t, int w, int h, int idx)
{
	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	D3D11_TEXTURE2D_DESC desc;
	HRESULT hr;

	memset(&desc, 0, sizeof desc);
	desc.Width = w;
	desc.Height = h;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->scrformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_DEFAULT;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
	desc.CPUAccessFlags = 0;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &t->tex);
	if (FAILED(hr)) {
		write_log(_T("D3D11 Failed to create working texture: %08x:%d\n"), hr, idx);
		return 0;
	}

	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Format = d3d->scrformat;

	hr = d3d->m_device->CreateShaderResourceView(t->tex, &srvDesc, &t->rv);
	if (FAILED(hr)) {
		write_log(_T("CreateShaderResourceView texture failed: %08x:%d\n"), hr, idx);
		return false;
	}

	hr = d3d->m_device->CreateRenderTargetView(t->tex, NULL, &t->rt);
	if (FAILED(hr))
	{
		write_log(_T("ID3D11Device CreateRenderTargetView %08x:%d\n"), hr, idx);
		return false;
	}

	return true;
}

static bool allocextratextures(struct d3d11struct *d3d, struct shaderdata11 *s, int w, int h)
{
	int scnt = addrdiff(s, &d3d->shaders[0]);
	if (!allocshadertex(d3d, &s->lpWorkTexture1, w, h, scnt))
		return false;
	if (!allocshadertex(d3d, &s->lpWorkTexture2, w, h, scnt))
		return false;

	write_log(_T("D3D11 %d*%d working texture:%d\n"), w, h, scnt);
	return true;
}

static bool createextratextures(struct d3d11struct *d3d, int ow, int oh, int win_w, int win_h)
{
	bool haveafter = false;

	int zw, zh;

	if (ow > win_w * d3d->dmultxh && oh > win_h * d3d->dmultxv) {
		zw = ow;
		zh = oh;
	} else {
		zw = win_w * d3d->dmultxh;
		zh = win_h * d3d->dmultxv;
	}
	for (int i = 0; i < MAX_SHADERS; i++) {
		struct shaderdata11 *s = &d3d->shaders[i];
		if (s->type == SHADERTYPE_BEFORE || s->type == SHADERTYPE_AFTER || s->type == SHADERTYPE_MIDDLE) {
			int w2, h2, w, h;
			if (s->type == SHADERTYPE_AFTER) {
				w2 = zw; h2 = zh;
				w = zw; h = zh;
				haveafter = true;
				if (!allocextratextures(d3d, &d3d->shaders[i], d3d->m_screenWidth, d3d->m_screenHeight))
					return 0;
			} else if (s->type == SHADERTYPE_MIDDLE) {
				// worktex_width = 800
				// extratex = amiga res
				w2 = zw; h2 = zh;
				w = zw; h = zh;
				if (!allocextratextures(d3d, &d3d->shaders[i], ow, oh))
					return 0;
			} else {
				w2 = ow;
				h2 = oh;
				w = ow;
				h = oh;
			}
			d3d->shaders[i].targettex_width = w2;
			d3d->shaders[i].targettex_height = h2;
			if (!allocshadertex(d3d, &s->lpTempTexture, w2, h2, (int)(s - &d3d->shaders[0])))
				return false;
			write_log(_T("D3D11 %d*%d temp texture:%d:%d\n"), w2, h2, i, d3d->shaders[i].type);
			d3d->shaders[i].worktex_width = w;
			d3d->shaders[i].worktex_height = h;
		}
	}
	if (haveafter) {
		if (!allocshadertex(d3d, &d3d->lpPostTempTexture, d3d->m_screenWidth, d3d->m_screenHeight, -1))
			return 0;
		write_log(_T("D3D11 %d*%d after texture\n"), d3d->m_screenWidth, d3d->m_screenHeight);
	}
	return 1;
}

static bool createsltexture(struct d3d11struct *d3d)
{
	D3D11_TEXTURE2D_DESC desc;
	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	HRESULT hr;

	FreeTexture2D(&d3d->sltexture, &d3d->sltexturerv);

	memset(&desc, 0, sizeof desc);
	desc.Width = d3d->m_screenWidth;
	desc.Height = d3d->m_screenHeight;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->texformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &d3d->sltexture);
	if (FAILED(hr)) {
		write_log(_T("CreateTexture2D (main) failed: %08x\n"), hr);
		return false;
	}

	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Format = d3d->texformat;

	hr = d3d->m_device->CreateShaderResourceView(d3d->sltexture, &srvDesc, &d3d->sltexturerv);
	if (FAILED(hr)) {
		write_log(_T("CreateShaderResourceView SL failed: %08x\n"), hr);
		FreeTexture2D(&d3d->sltexture, NULL);
		return false;
	}

	write_log(_T("SL %d*%d texture allocated\n"), desc.Width, desc.Height);
	return true;
}


static void createscanlines(struct d3d11struct *d3d, int force)
{
	HRESULT hr;
	D3D11_MAPPED_SUBRESOURCE map;
	int sl4, sl42;
	int l1, l2;
	int x, y, yy;
	uae_u8 *sld, *p;
	int bpp;

	if (d3d->scanline_osl1 == d3d->filterd3d->gfx_filter_scanlines &&
		d3d->scanline_osl3 == d3d->filterd3d->gfx_filter_scanlinelevel &&
		d3d->scanline_osl2 == d3d->filterd3d->gfx_filter_scanlineratio &&
		d3d->scanline_osl4 == d3d->filterd3d->gfx_filter_scanlineoffset &&
		!force)
		return;
	bpp = 4;
	d3d->scanline_osl1 = d3d->filterd3d->gfx_filter_scanlines;
	d3d->scanline_osl3 = d3d->filterd3d->gfx_filter_scanlinelevel;
	d3d->scanline_osl2 = d3d->filterd3d->gfx_filter_scanlineratio;
	d3d->scanline_osl4 = d3d->filterd3d->gfx_filter_scanlineoffset;
	sl4 = d3d->filterd3d->gfx_filter_scanlines * 16 / 100;
	sl42 = d3d->filterd3d->gfx_filter_scanlinelevel * 16 / 100;
	if (sl4 > 15)
		sl4 = 15;
	if (sl42 > 15)
		sl42 = 15;
	l1 = (d3d->filterd3d->gfx_filter_scanlineratio >> 0) & 15;
	l2 = (d3d->filterd3d->gfx_filter_scanlineratio >> 4) & 15;

	if (l1 + l2 <= 0)
		return;

	if (!d3d->sltexture) {
		if (d3d->scanline_osl1 == 0 && d3d->scanline_osl3 == 0)
			return;
		if (!createsltexture(d3d))
			return;
	}

	hr = d3d->m_deviceContext->Map(d3d->sltexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (FAILED(hr)) {
		write_log(_T("SL Map failed %08x\n"), hr);
		return;
	}
	sld = (uae_u8*)map.pData;
	for (y = 0; y < d3d->m_screenHeight; y++) {
		memset(sld + y * map.RowPitch, 0, d3d->m_screenWidth * bpp);
	}
	for (y = 0; y < d3d->m_screenHeight; y += l1 + l2) {
		int y2 = y + (d3d->filterd3d->gfx_filter_scanlineoffset % (l1 + 1));
		for (yy = 0; yy < l2 && y2 + yy < d3d->m_screenHeight; yy++) {
			for (x = 0; x < d3d->m_screenWidth; x++) {
				uae_u8 sll = sl42;
				p = &sld[(y2 + yy) * map.RowPitch + (x * bpp)];
				/* 32-bit, A8R8G8B8 */
				uae_u8 sll4 = sl4 | (sl4 << 4);
				uae_u8 sll2 = sll | (sll << 4);
				p[0] = sll2;
				p[1] = sll2;
				p[2] = sll2;
				p[3] = sll4;
			}
		}
	}
	d3d->m_deviceContext->Unmap(d3d->sltexture, 0);
}


#include "png.h"
#include <atlcomcli.h>

struct uae_image
{
	uae_u8 *data;
	int width, height, pitch;
};

struct png_cb
{
	uae_u8 *ptr;
	size_t size;
};

static void __cdecl readcallback(png_structp png_ptr, png_bytep out, png_size_t count)
{
	png_voidp io_ptr = png_get_io_ptr(png_ptr);

	if (!io_ptr)
		return;
	struct png_cb *cb = (struct png_cb*)io_ptr;
	if (count > cb->size)
		count = cb->size;
	memcpy(out, cb->ptr, count);
	cb->ptr += count;
	cb->size -= count;
}

static bool load_png_image(struct zfile *zf, struct uae_image *img)
{
	extern unsigned char test_card_png[];
	extern unsigned int test_card_png_len;
	uae_u8 *b = test_card_png;
	uae_u8 *bfree = NULL;
	png_structp png_ptr;
	png_infop info_ptr;
	png_uint_32 width, height;
	int depth, color_type;
	struct png_cb cb;
	png_bytepp row_pp;
	png_size_t cols;
	bool ok = false;

	memset(img, 0, sizeof(struct uae_image));
	int size;
	uae_u8 *bb = zfile_getdata(zf, 0, -1, &size);
	if (!bb)
		goto end;
	b = bb;
	bfree = bb;

	if (!png_check_sig(b, 8))
		goto end;

	png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
	if (!png_ptr)
		goto end;
	info_ptr = png_create_info_struct(png_ptr);
	if (!info_ptr) {
		png_destroy_read_struct(&png_ptr, 0, 0);
		goto end;
	}
	cb.ptr = b;
	cb.size = size;
	png_set_read_fn(png_ptr, &cb, readcallback);

	png_read_info(png_ptr, info_ptr);

	png_get_IHDR(png_ptr, info_ptr, &width, &height, &depth, &color_type, 0, 0, 0);

	if (color_type == PNG_COLOR_TYPE_PALETTE)
		png_set_expand(png_ptr);
	if (color_type == PNG_COLOR_TYPE_GRAY && depth < 8)
		png_set_expand(png_ptr);

	if (depth > 8)
		png_set_strip_16(png_ptr);
	if (depth < 8)
		png_set_packing(png_ptr);
	if (!(color_type & PNG_COLOR_MASK_ALPHA))
		png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER);

	png_set_bgr(png_ptr);

	cols = png_get_rowbytes(png_ptr, info_ptr);

	img->pitch = width * 4;
	img->width = width;
	img->height = height;

	row_pp = new png_bytep[height];

	img->data = xcalloc(uae_u8, width * height * 4);

	for (int i = 0; i < height; i++) {
		row_pp[i] = (png_bytep)&img->data[i * img->pitch];
	}

	png_read_image(png_ptr, row_pp);
	png_read_end(png_ptr, info_ptr);

	png_destroy_read_struct(&png_ptr, &info_ptr, 0);

	delete[] row_pp;

	ok = true;
end:
	xfree(bfree);

	return ok;
}

static void free_uae_image(struct uae_image *img)
{
	if (!img)
		return;
	xfree(img->data);
	img->data = NULL;
}

static int findedge(struct uae_image *img, int w, int h, int dx, int dy)
{
	int x = w / 2;
	int y = h / 2;

	if (dx != 0)
		x = dx < 0 ? 0 : w - 1;
	if (dy != 0)
		y = dy < 0 ? 0 : h - 1;

	for (;;) {
		uae_u32 *p = (uae_u32*)(img->data + y * img->pitch + x * 4);
		int alpha = (*p) >> 24;
		if (alpha != 255)
			break;
		x -= dx;
		y -= dy;
		if (x <= 0 || y <= 0)
			break;
		if (x >= w - 1 || y >= h - 1)
			break;
	}
	if (dx)
		return x;
	return y;
}

static void narrowimg(struct uae_image *img, int *xop, int *yop, const TCHAR *name)
{
	int x1, x2, y1, y2;

	for (y1 = 0; y1 < img->height; y1++) {
		bool tline = true;
		for (int x = 0; x < img->width; x++) {
			uae_u8 *p = img->data + y1 * img->pitch + x * 4;
			if (p[3] != 0x00)
				tline = false;
		}
		if (!tline)
			break;
	}

	for (y2 = img->height - 1; y2 >= y1; y2--) {
		bool tline = true;
		for (int x = 0; x < img->width; x++) {
			uae_u8 *p = img->data + y2 * img->pitch + x * 4;
			if (p[3] != 0x00)
				tline = false;
		}
		if (!tline)
			break;
	}

	for (x1 = 0; x1 < img->width; x1++) {
		bool tline = true;
		for (int y = y1; y <= y2; y++) {
			uae_u8 *p = img->data + y * img->pitch + x1 * 4;
			if (p[3] != 0x00)
				tline = false;
		}
		if (!tline)
			break;
	}

	for (x2 = img->width - 1; x2 >= x1; x2--) {
		bool tline = true;
		for (int y = y1; y <= y2; y++) {
			uae_u8 *p = img->data + y * img->pitch + x2 * 4;
			if (p[3] != 0x00)
				tline = false;
		}
		if (!tline)
			break;
	}

	int w = x2 - x1 + 1;
	int h = y2 - y1 + 1;
	int pitch = w * 4;

	*xop = x1;
	*yop = y1;

	uae_u8 *d = xcalloc(uae_u8, img->width * img->height * 4);
	for (int y = 0; y < h; y++) {
		uae_u8 *dp = d + y * pitch;
		uae_u8 *sp = img->data + (y + y1) * img->pitch + x1 * 4;
		memcpy(dp, sp, w * 4);
	}

	write_log(_T("Overlay LED: '%s' %d*%d -> %d*%d (%d*%d - %d*%d)\n"), name, img->width, img->height, w, h, x1, y1, x2, y2);

	xfree(img->data);
	img->width = w;
	img->height = h;
	img->pitch = pitch;
	img->data = d;

}

static uae_u8 dimming(uae_u8 v)
{
	return v * currprefs.power_led_dim / 100;
}

static int createmask2texture(struct d3d11struct *d3d, const TCHAR *filename)
{
	struct AmigaMonitor *mon = &AMonitors[d3d->num];
	struct zfile *zf;
	TCHAR tmp[MAX_DPATH];
	ID3D11Texture2D *tx = NULL;
	HRESULT hr;
	TCHAR filepath[MAX_DPATH];
	float xmult, ymult;

	freesprite(&d3d->mask2texture);
	for (int i = 0; overlayleds[i]; i++) {
		freesprite(&d3d->mask2textureleds[i]);
	}
	freesprite(&d3d->mask2textureled_power_dim);
	freesprite(&d3d->blanksprite);

	if (filename[0] == 0)
		return 0;

	zf = NULL;
	for (int i = 0; i < 2; i++) {
		if (i == 0) {
			get_plugin_path(tmp, sizeof tmp / sizeof(TCHAR), _T("overlays"));
			_tcscat(tmp, filename);
		} else {
			_tcscpy(tmp, filename);
		}
		TCHAR tmp2[MAX_DPATH], tmp3[MAX_DPATH];
		_tcscpy(tmp3, tmp);
		TCHAR *s = _tcsrchr(tmp3, '.');
		if (s) {
			TCHAR *s2 = s;
			while (s2 > tmp3) {
				TCHAR v = *s2;
				if (v == '_') {
					s = s2;
					break;
				}
				if (v == 'X' || v == 'x') {
					s2--;
					continue;
				}
				if (!_istdigit(v))
					break;
				s2--;
			}
			_tcscpy(tmp2, s);
			_stprintf(s, _T("_%dx%d%s"), d3d->m_screenWidth, d3d->m_screenHeight, tmp2);
			_tcscpy(filepath, tmp3);
			zf = zfile_fopen(tmp3, _T("rb"), ZFD_NORMAL);
			if (zf)
				break;
			float aspect = (float)d3d->m_screenWidth / d3d->m_screenHeight;
			int ax = -1, ay = -1;
			if (abs(aspect - 16.0 / 10.0) <= 0.1)
				ax = 16, ay = 10;
			if (abs(aspect - 16.0 / 9.0) <= 0.1)
				ax = 16, ay = 9;
			if (abs(aspect - 4.0 / 3.0) <= 0.1)
				ax = 4, ay = 3;
			if (ax > 0 && ay > 0) {
				_stprintf(s, _T("_%dx%d%s"), ax, ay, tmp2);
				_tcscpy(filepath, tmp3);
				zf = zfile_fopen(tmp3, _T("rb"), ZFD_NORMAL);
				if (zf)
					break;
			}
		}
		_tcscpy(filepath, tmp);
		zf = zfile_fopen(tmp, _T("rb"), ZFD_NORMAL);
		if (zf)
			break;
	}
	if (!zf) {
		write_log(_T("Couldn't open overlay '%s'\n"), filename);
		return 0;
	}
	struct uae_image img;
	if (!load_png_image(zf, &img)) {
		write_log(_T("Overlay texture '%s' load failed.\n"), filename);
		goto end;
	}
	d3d->mask2texture_w = (float)img.width;
	d3d->mask2texture_h = (float)img.height;
	if (!allocsprite(d3d, &d3d->mask2texture, img.width, img.height, true, true, false))
		goto end;

	D3D11_MAPPED_SUBRESOURCE map;
	hr = d3d->m_deviceContext->Map(d3d->mask2texture.texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (FAILED(hr))
		goto end;
	for (int i = 0; i < img.height; i++) {
		for (int j = 0; j < img.width; j++) {
			uae_u32 v;
			uae_u32 *sptr = (uae_u32*)((uae_u8*)img.data + i * img.pitch + j * 4);
			uae_u32 *ptr = (uae_u32*)((uae_u8*)map.pData + i * map.RowPitch + j * 4);
			v = *sptr;
			*ptr = v;
		}
	}
	d3d->m_deviceContext->Unmap(d3d->mask2texture.texture, 0);

	d3d->mask2rect.left = 0;
	d3d->mask2rect.top = 0;
	d3d->mask2rect.right = (LONG)d3d->mask2texture_w;
	d3d->mask2rect.bottom = (LONG)d3d->mask2texture_h;

	d3d->mask2rect.left = findedge(&img, (int)d3d->mask2texture_w, (int)d3d->mask2texture_h, -1, 0);
	d3d->mask2rect.right = findedge(&img, (int)d3d->mask2texture_w, (int)d3d->mask2texture_h, 1, 0);
	d3d->mask2rect.top = findedge(&img, (int)d3d->mask2texture_w, (int)d3d->mask2texture_h, 0, -1);
	d3d->mask2rect.bottom = findedge(&img, (int)d3d->mask2texture_w, (int)d3d->mask2texture_h, 0, 1);
	if (d3d->mask2rect.left >= d3d->mask2texture_w / 2 || d3d->mask2rect.top >= d3d->mask2texture_h / 2 ||
		d3d->mask2rect.right <= d3d->mask2texture_w / 2 || d3d->mask2rect.bottom <= d3d->mask2texture_h / 2) {
		d3d->mask2rect.left = 0;
		d3d->mask2rect.top = 0;
		d3d->mask2rect.right = (LONG)d3d->mask2texture_w;
		d3d->mask2rect.bottom = (LONG)d3d->mask2texture_h;
	}
	d3d->mask2texture_multx = (float)d3d->m_screenWidth / d3d->mask2texture_w;
	d3d->mask2texture_multy = (float)d3d->m_screenHeight / d3d->mask2texture_h;
	d3d->mask2texture_offsetw = 0;

	if (isfs(d3d) > 0) {
		struct MultiDisplay *md = getdisplay(&currprefs, mon->monitor_id);
		float deskw = (float)md->rect.right - md->rect.left;
		float deskh = (float)md->rect.bottom - md->rect.top;
		//deskw = 800; deskh = 600;
		float dstratio = deskw / deskh;
		float srcratio = d3d->mask2texture_w / d3d->mask2texture_h;
		d3d->mask2texture_multx *= srcratio / dstratio;
	} else {
		d3d->mask2texture_multx = d3d->mask2texture_multy;
	}

	d3d->mask2texture_wh = (float)d3d->m_screenHeight;
	d3d->mask2texture_ww = d3d->mask2texture_w * d3d->mask2texture_multx;

	d3d->mask2texture_offsetw = (d3d->m_screenWidth - d3d->mask2texture_ww) / 2;

	if (d3d->mask2texture_offsetw > 0) {
		allocsprite(d3d, &d3d->blanksprite, (int)(d3d->mask2texture_offsetw + 1), d3d->m_screenHeight, false, false, false);
	}

	xmult = d3d->mask2texture_multx;
	ymult = d3d->mask2texture_multy;

	d3d->mask2rect.left = (int)(d3d->mask2rect.left * xmult);
	d3d->mask2rect.right = (int)(d3d->mask2rect.right * xmult);
	d3d->mask2rect.top = (int)(d3d->mask2rect.top * ymult);
	d3d->mask2rect.bottom = (int)(d3d->mask2rect.bottom * ymult);
	d3d->mask2texture_wwx = d3d->mask2texture_w * xmult;
	if (d3d->mask2texture_wwx > d3d->m_screenWidth)
		d3d->mask2texture_wwx = (float)d3d->m_screenWidth;
	if (d3d->mask2texture_wwx < d3d->mask2rect.right - d3d->mask2rect.left)
		d3d->mask2texture_wwx = (float)d3d->mask2rect.right - d3d->mask2rect.left;
	if (d3d->mask2texture_wwx > d3d->mask2texture_ww)
		d3d->mask2texture_wwx = d3d->mask2texture_ww;

	d3d->mask2texture_minusx = (float)(-((d3d->m_screenWidth - d3d->mask2rect.right) + d3d->mask2rect.left));
	if (d3d->mask2texture_offsetw > 0)
		d3d->mask2texture_minusx += d3d->mask2texture_offsetw * xmult;

	d3d->mask2texture_minusy = (float)(-(d3d->m_screenHeight - (d3d->mask2rect.bottom - d3d->mask2rect.top)));

	d3d->mask2texture_hhx = d3d->mask2texture_h * ymult;

	write_log(_T("Overlay: '%s' %.0f*%.0f (%d*%d - %d*%d) (%d*%d)\n"),
		tmp, d3d->mask2texture_w, d3d->mask2texture_h,
		d3d->mask2rect.left, d3d->mask2rect.top, d3d->mask2rect.right, d3d->mask2rect.bottom,
		d3d->mask2rect.right - d3d->mask2rect.left, d3d->mask2rect.bottom - d3d->mask2rect.top);

	d3d->mask2texture.enabled = true;
	d3d->mask2texture.bilinear = true;

	for (int i = 0; overlayleds[i]; i++) {
		if (!overlayleds[i][0])
			continue;
		TCHAR tmp1[MAX_DPATH];
		_tcscpy(tmp1, filepath);
		_tcscpy(tmp1 + _tcslen(tmp1) - 4, _T("_"));
		_tcscpy(tmp1 + _tcslen(tmp1), overlayleds[i]);
		_tcscat(tmp1, _T("_led"));
		_tcscat(tmp1, filepath + _tcslen(filepath) - 4);
		zf = zfile_fopen(tmp1, _T("rb"), ZFD_NORMAL);
		if (zf) {
			struct uae_image ledimg;
			if (load_png_image(zf, &ledimg)) {
				if (ledimg.width == img.width && ledimg.height == img.height) {
					narrowimg(&ledimg, &d3d->mask2textureledoffsets[i * 2 + 0], &d3d->mask2textureledoffsets[i * 2 + 1], tmp1);
					if (allocsprite(d3d, &d3d->mask2textureleds[i], ledimg.width, ledimg.height, true, false, false)) {
						D3D11_MAPPED_SUBRESOURCE map;
						hr = d3d->m_deviceContext->Map(d3d->mask2textureleds[i].texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
						if (SUCCEEDED(hr)) {
							for (int j = 0; j < ledimg.height; j++) {
								memcpy((uae_u8*)map.pData + j * map.RowPitch, ledimg.data + j * ledimg.pitch, ledimg.width * 4);
							}
							d3d->m_deviceContext->Unmap(d3d->mask2textureleds[i].texture, 0);
						}
						d3d->mask2textureleds[i].enabled = true;
						d3d->mask2textureleds[i].bilinear = true;
						if (ledtypes[i] == LED_POWER) {
							if (allocsprite(d3d, &d3d->mask2textureled_power_dim, ledimg.width, ledimg.height, true, false, false)) {
								hr = d3d->m_deviceContext->Map(d3d->mask2textureled_power_dim.texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
								if (SUCCEEDED(hr)) {
									for (int j = 0; j < ledimg.height; j++) {
										uae_u8 *pd = (uae_u8*)map.pData + j * map.RowPitch;
										uae_u8 *ps = ledimg.data + j * ledimg.pitch;
										for (int k = 0; k < ledimg.width; k++) {
											pd[0] = dimming(ps[0]);
											pd[1] = dimming(ps[1]);
											pd[2] = dimming(ps[2]);
											pd[3] = ps[3];
											pd += 4;
											ps += 4;
										}
									}
									d3d->m_deviceContext->Unmap(d3d->mask2textureled_power_dim.texture, 0);
								}
								d3d->mask2textureled_power_dim.enabled = true;
								d3d->mask2textureled_power_dim.bilinear = true;
							}
						}
					}
				} else {
					write_log(_T("Overlay led '%s' size mismatch.\n"), tmp1);
				}
				free_uae_image(&ledimg);
			} else {
				write_log(_T("Overlay led '%s' load failed.\n"), tmp1);
			}
			zfile_fclose(zf);
		}
	}

	free_uae_image(&img);

	return 1;
end:
	free_uae_image(&img);
	freesprite(&d3d->mask2texture);
	freesprite(&d3d->blanksprite);
	return 0;
}

static int createmasktexture(struct d3d11struct *d3d, const TCHAR *filename, struct shaderdata11 *sd)
{
	struct zfile *zf;
	HRESULT hr;
	TCHAR tmp[MAX_DPATH];
	int maskwidth, maskheight;
	int idx = 0;// sd - &d3d->shaders[0];
	D3D11_MAPPED_SUBRESOURCE map;
	D3D11_TEXTURE2D_DESC desc;

	if (filename[0] == 0)
		return 0;
	get_plugin_path(tmp, sizeof tmp / sizeof(TCHAR), _T("masks"));
	_tcscat(tmp, filename);
	zf = zfile_fopen(tmp, _T("rb"), ZFD_NORMAL);
	if (!zf) {
		zf = zfile_fopen(filename, _T("rb"), ZFD_NORMAL);
		if (!zf) {
			write_log(_T("Couldn't open mask '%s':%d\n"), filename, idx);
			return 0;
		}
	}

	struct uae_image img;
	if (!load_png_image(zf, &img)) {
		write_log(_T("Temp mask texture '%s' load failed:%d\n"), filename, idx);
		goto end;
	}

	sd->masktexture_w = img.width;
	sd->masktexture_h = img.height;

	// both must be divisible by mask size
	maskwidth = ((d3d->m_screenWidth + sd->masktexture_w - 1) / sd->masktexture_w) * sd->masktexture_w;
	maskheight = ((d3d->m_screenHeight + sd->masktexture_h - 1) / sd->masktexture_h) * sd->masktexture_h;

	memset(&desc, 0, sizeof desc);
	desc.Width = maskwidth;
	desc.Height = maskheight;
	desc.MipLevels = 1;
	desc.ArraySize = 1;
	desc.Format = d3d->texformat;
	desc.SampleDesc.Count = 1;
	desc.SampleDesc.Quality = 0;
	desc.Usage = D3D11_USAGE_DYNAMIC;
	desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
	desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

	hr = d3d->m_device->CreateTexture2D(&desc, NULL, &sd->masktexture);
	if (FAILED(hr)) {
		write_log(_T("Mask texture creation failed: %s:%d\n"), hr, idx);
		return false;
	}

	hr = d3d->m_deviceContext->Map(sd->masktexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (SUCCEEDED(hr)) {
		int x, y, sx, sy;
		uae_u32 *sptr, *ptr;
		sy = 0;
		for (y = 0; y < maskheight; y++) {
			sx = 0;
			for (x = 0; x < maskwidth; x++) {
				uae_u32 v;
				sptr = (uae_u32*)((uae_u8*)img.data + sy * img.pitch + sx * 4);
				ptr = (uae_u32*)((uae_u8*)map.pData + y * map.RowPitch + x * 4);
				v = *sptr;
				*ptr = v;
				sx++;
				if (sx >= sd->masktexture_w)
					sx = 0;
			}
			sy++;
			if (sy >= sd->masktexture_h)
				sy = 0;
		}
		d3d->m_deviceContext->Unmap(sd->masktexture, 0);
	}
	sd->masktexture_w = maskwidth;
	sd->masktexture_h = maskheight;

	D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
	srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
	srvDesc.Texture2D.MostDetailedMip = 0;
	srvDesc.Texture2D.MipLevels = desc.MipLevels;
	srvDesc.Format = d3d->texformat;

	hr = d3d->m_device->CreateShaderResourceView(sd->masktexture, &srvDesc, &sd->masktexturerv);
	if (FAILED(hr)) {
		write_log(_T("CreateShaderResourceView MASK failed: %08x\n"), hr);
		goto end;
	}

	write_log(_T("Mask %d*%d (%d*%d) %d*%d ('%s':%d) texture allocated\n"), sd->masktexture_w, sd->masktexture_h,
		img.width, img.height, 0, 0, filename, idx);

	free_uae_image(&img);
	return 1;
end:
	FreeTexture2D(&sd->masktexture, NULL);
	free_uae_image(&img);
	return 0;
}

#if 0
static void OutputShaderErrorMessage(ID3D10Blob* errorMessage, HWND hwnd, TCHAR* shaderFilename)
{
	char *compileErrors;
	unsigned long bufferSize;

	if (errorMessage) {
		// Get a pointer to the error message text buffer.
		compileErrors = (char*)(errorMessage->GetBufferPointer());

		// Get the length of the message.
		bufferSize = errorMessage->GetBufferSize();

		TCHAR *s = au(compileErrors);

		write_log(_T("D3D11 Shader error: %s"), s);

		xfree(s);

		// Release the error message.
		errorMessage->Release();
	} else {

		write_log(_T("D3D11 Shader error: %08x"), GetLastError());

	}
}
#endif


static bool TextureShaderClass_InitializeShader(struct d3d11struct *d3d)
{
	HRESULT result;
	D3D11_SAMPLER_DESC samplerDesc;

	for (int i = 0; i < 3; i++) {
		ID3D11PixelShader **ps = NULL;
		const BYTE *Buffer = NULL;
		int BufferSize = 0;
		char *name;

		if (gfx_hdr) {
			switch (i)
			{
				case 0:
				name = "PS_PostPlain_HDR";
				ps = &d3d->m_pixelShader;
				Buffer = PS_PostPlain_HDR;
				BufferSize = sizeof(PS_PostPlain_HDR);
				break;
				case 1:
				name = "PS_PostMask_HDR";
				ps = &d3d->m_pixelShaderMask;
				Buffer = PS_PostMask_HDR;
				BufferSize = sizeof(PS_PostMask_HDR);
				break;
				case 2:
				name = "PS_PostAlpha_HDR";
				ps = &d3d->m_pixelShaderSL;
				Buffer = PS_PostAlpha_HDR;
				BufferSize = sizeof(PS_PostAlpha_HDR);
				break;
			}
		} else {
			switch (i)
			{
				case 0:
				name = "PS_PostPlain";
				ps = &d3d->m_pixelShader;
				Buffer = PS_PostPlain;
				BufferSize = sizeof(PS_PostPlain);
				break;
				case 1:
				name = "PS_PostMask";
				ps = &d3d->m_pixelShaderMask;
				Buffer = PS_PostMask;
				BufferSize = sizeof(PS_PostMask);
				break;
				case 2:
				name = "PS_PostAlpha";
				ps = &d3d->m_pixelShaderSL;
				Buffer = PS_PostAlpha;
				BufferSize = sizeof(PS_PostAlpha);
				break;
			}
		}
		// Create the pixel shader from the buffer.
		result = d3d->m_device->CreatePixelShader(Buffer, BufferSize, NULL, ps);
		if (FAILED(result))
		{
			write_log(_T("ID3D11Device CreatePixelShader %08x\n"), result);
			return false;
		}
#ifndef NDEBUG
		static const char psname[] = "shader";
		(*ps)->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(psname) - 1, psname);
#endif
	}

	D3D11_BUFFER_DESC psBufferDesc;
	psBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
	psBufferDesc.ByteWidth = sizeof(PSBufferType);
	psBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
	psBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
	psBufferDesc.MiscFlags = 0;
	psBufferDesc.StructureByteStride = 0;
	result = d3d->m_device->CreateBuffer(&psBufferDesc, NULL, &d3d->m_psBuffer);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateBuffer(ps) %08x\n"), result);
		return false;
	}
#ifndef NDEBUG
	static const char pname[] = "psbuffer";
	d3d->m_psBuffer->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(pname) - 1, pname);
#endif

	if (!createvertexshader(d3d, &d3d->m_vertexShader, &d3d->m_matrixBuffer, &d3d->m_layout))
		return false;

	// Create a texture sampler state description.
	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_POINT;
	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_CLAMP;
	samplerDesc.MipLODBias = 0.0f;
	samplerDesc.MaxAnisotropy = 1;
	samplerDesc.ComparisonFunc = D3D11_COMPARISON_ALWAYS;
	samplerDesc.BorderColor[0] = 0;
	samplerDesc.BorderColor[1] = 0;
	samplerDesc.BorderColor[2] = 0;
	samplerDesc.BorderColor[3] = 0;
	samplerDesc.MinLOD = 0;
	samplerDesc.MaxLOD = D3D11_FLOAT32_MAX;

	// Create the texture sampler state.
	result = d3d->m_device->CreateSamplerState(&samplerDesc, &d3d->m_sampleState_point_clamp);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateSamplerState1 %08x\n"), result);
		return false;
	}

	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;

	result = d3d->m_device->CreateSamplerState(&samplerDesc, &d3d->m_sampleState_linear_clamp);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateSamplerState2 %08x\n"), result);
		return false;
	}

	samplerDesc.AddressU = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressV = D3D11_TEXTURE_ADDRESS_WRAP;
	samplerDesc.AddressW = D3D11_TEXTURE_ADDRESS_WRAP;
	// Create the texture sampler state.
	result = d3d->m_device->CreateSamplerState(&samplerDesc, &d3d->m_sampleState_point_wrap);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateSamplerState1 %08x\n"), result);
		return false;
	}

	samplerDesc.Filter = D3D11_FILTER_MIN_MAG_MIP_LINEAR;

	result = d3d->m_device->CreateSamplerState(&samplerDesc, &d3d->m_sampleState_linear_wrap);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateSamplerState2 %08x\n"), result);
		return false;
	}

	return true;
}

static bool initd3d(struct d3d11struct *d3d, int monid)
{
	HRESULT result;
	ID3D11Texture2D *backBufferPtr;
	D3D11_RASTERIZER_DESC rasterDesc;

	if (d3d->d3dinit_done)
		return true;

	write_log(_T("D3D11 initd3d start\n"));

	// Get the pointer to the back buffer.
	result = d3d->m_swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (LPVOID*)&backBufferPtr);
	if (FAILED(result))
	{
		write_log(_T("IDXGISwapChain1 GetBuffer %08x\n"), result);
		return false;
	}

	// Create the render target view with the back buffer pointer.
	result = d3d->m_device->CreateRenderTargetView(backBufferPtr, NULL, &d3d->m_renderTargetView);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateRenderTargetView %08x\n"), result);
		backBufferPtr->Release();
		return false;
	}

	// Release pointer to the back buffer as we no longer need it.
	backBufferPtr->Release();
	backBufferPtr = NULL;

	// Setup the raster description which will determine how and what polygons will be drawn.
	rasterDesc.AntialiasedLineEnable = false;
	rasterDesc.CullMode = D3D11_CULL_NONE;
	rasterDesc.DepthBias = 0;
	rasterDesc.DepthBiasClamp = 0.0f;
	rasterDesc.DepthClipEnable = d3d11_feature_level < D3D10_FEATURE_LEVEL_10_0 ? true : false;
	rasterDesc.FillMode = D3D11_FILL_SOLID;
	rasterDesc.FrontCounterClockwise = false;
	rasterDesc.MultisampleEnable = false;
	rasterDesc.ScissorEnable = false;
	rasterDesc.SlopeScaledDepthBias = 0.0f;

	// Create the rasterizer state from the description we just filled out.
	result = d3d->m_device->CreateRasterizerState(&rasterDesc, &d3d->m_rasterState);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateRasterizerState %08x\n"), result);
		return false;
	}

	// Now set the rasterizer state.
	d3d->m_deviceContext->RSSetState(d3d->m_rasterState);
	d3d->m_deviceContext->OMSetDepthStencilState(0, 0);

	D3D11_BLEND_DESC blendStateDescription;
	// Clear the blend state description.
	ZeroMemory(&blendStateDescription, sizeof(D3D11_BLEND_DESC));
	// Create an alpha enabled blend state description.
	blendStateDescription.RenderTarget[0].BlendEnable = TRUE;
	//blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_ONE;
	blendStateDescription.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
	blendStateDescription.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_ONE;
	blendStateDescription.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_ZERO;
	blendStateDescription.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
	blendStateDescription.RenderTarget[0].RenderTargetWriteMask = 0x0f;

	// Create the blend state using the description.
	result = d3d->m_device->CreateBlendState(&blendStateDescription, &d3d->m_alphaEnableBlendingState);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateBlendState Alpha %08x\n"), result);
		return false;
	}
	// Modify the description to create an alpha disabled blend state description.
	blendStateDescription.RenderTarget[0].BlendEnable = FALSE;
	// Create the blend state using the description.
	result = d3d->m_device->CreateBlendState(&blendStateDescription, &d3d->m_alphaDisableBlendingState);
	if (FAILED(result))
	{
		write_log(_T("ID3D11Device CreateBlendState NoAlpha %08x\n"), result);
		return false;
	}

	// Setup the viewport for rendering.
	d3d->viewport.Width = (float)d3d->m_screenWidth;
	d3d->viewport.Height = (float)d3d->m_screenHeight;
	d3d->viewport.MinDepth = 0.0f;
	d3d->viewport.MaxDepth = 1.0f;
	d3d->viewport.TopLeftX = 0.0f;
	d3d->viewport.TopLeftY = 0.0f;

	// Create the viewport.
	d3d->m_deviceContext->RSSetViewports(1, &d3d->viewport);

	// Initialize the world matrix to the identity matrix.
	xD3DXMatrixIdentity(&d3d->m_worldMatrix);

	// Create an orthographic projection matrix for 2D rendering.
	xD3DXMatrixOrthoLH(&d3d->m_orthoMatrix, (float)d3d->m_screenWidth, (float)d3d->m_screenHeight, 0.0f, 1.0f);

	d3d->m_positionX = 0.0f;
	d3d->m_positionY = 0.0f;
	d3d->m_positionZ = 1.0f;

	d3d->m_rotationX = 0.0f;
	d3d->m_rotationY = 0.0f;
	d3d->m_rotationZ = 0.0f;

	if (!TextureShaderClass_InitializeShader(d3d))
		return false;
	if (!InitializeBuffers(d3d, &d3d->m_vertexBuffer, &d3d->m_indexBuffer))
		return false;
	if (!UpdateBuffers(d3d, monid))
		return false;

	settransform(d3d, NULL);

	d3d->d3dinit_done = true;

	write_log(_T("D3D11 initd3d end\n"));
	return true;
}

static void setswapchainmode(struct d3d11struct *d3d, int fs)
{
	struct amigadisplay *ad = &adisplays[d3d->num];
	struct apmode *apm = ad->picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];
	// It is recommended to always use the tearing flag when it is supported.
	d3d->swapChainDesc.Flags &= ~DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
	if (d3d->m_tearingSupport && (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD)) {
		d3d->swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
	}
	if (0 && os_win8 > 1 && fs <= 0) {
		d3d->swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
	}
	d3d->swapChainDesc.Flags &= ~DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
	// tearing flag is not fullscreen compatible
	if (fs > 0) {
		d3d->swapChainDesc.Flags |= DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
		d3d->swapChainDesc.Flags &= ~DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING;
	}

	d3d->fsSwapChainDesc.Windowed = TRUE;
}

static const TCHAR *d3dcompiler2 = _T("D3DCompiler_46.dll");
static const TCHAR *d3dcompiler1 = _T("D3DCompiler_47.dll");
static const TCHAR *d3dcompiler = NULL;

int can_D3D11(bool checkdevice)
{
	static bool detected;
	static int detected_val = 0;
	HRESULT hr;
	int ret = 0;

	if (detected && !checkdevice)
		return detected_val;

	detected = true;

	if (!hd3d11)
		hd3d11 = LoadLibrary(_T("D3D11.dll"));
	if (!hdxgi)
		hdxgi = LoadLibrary(_T("Dxgi.dll"));
	if (!dwmapi)
		dwmapi = LoadLibrary(_T("Dwmapi.dll"));

	if (!hd3dcompiler) {
		d3dcompiler = d3dcompiler1;
		hd3dcompiler = LoadLibrary(d3dcompiler);
		if (!hd3dcompiler) {
			d3dcompiler = d3dcompiler2;
			hd3dcompiler = LoadLibrary(d3dcompiler);
		}
		if (!hd3dcompiler) {
			d3dcompiler = NULL;
		}
	}
	if (!hd3d11 || !hdxgi) {
		write_log(_T("D3D11.dll=%p Dxgi.dll=%p\n"), hd3d11, hdxgi);
		return 0;
	}

	pD3D11CreateDevice = (PFN_D3D11_CREATE_DEVICE)GetProcAddress(GetModuleHandle(_T("D3D11.dll")), "D3D11CreateDevice");
	pCreateDXGIFactory1 = (CREATEDXGIFACTORY1)GetProcAddress(GetModuleHandle(_T("Dxgi.dll")), "CreateDXGIFactory1");
	if (hd3dcompiler && d3dcompiler) {
		HMODULE h = GetModuleHandle(d3dcompiler);
		pD3DCompileFromFile = (D3DCOMPILEFROMFILE)GetProcAddress(h, "D3DCompileFromFile");
		ppD3DCompile = (D3DCOMPILE)GetProcAddress(h, "D3DCompile");
		ppD3DCompile2 = (D3DCOMPILE2)GetProcAddress(h, "D3DCompile2");
		pD3DReflect = (D3DREFLECT)GetProcAddress(h, "D3DReflect");
		pD3DGetBlobPart = (D3DGETBLOBPART)GetProcAddress(h, "D3DGetBlobPart");
	}

	if (!pD3D11CreateDevice || !pCreateDXGIFactory1) {
		write_log(_T("pD3D11CreateDevice=%p pCreateDXGIFactory1=%p\n"),
			pD3D11CreateDevice, pCreateDXGIFactory1);
		return 0;
	}

	if (!pDwmGetCompositionTimingInfo && dwmapi) {
		pDwmGetCompositionTimingInfo = (DWMGETCOMPOSITIONTIMINGINFO)GetProcAddress(dwmapi, "DwmGetCompositionTimingInfo");
	}

	if (ppD3DCompile && pD3DReflect && pD3DGetBlobPart)
		ret |= 4;


	// Create a DirectX graphics interface factory.
	ComPtr<IDXGIFactory4> factory4;
	hr = pCreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&factory4);
	if (SUCCEEDED(hr)) {
		ComPtr<IDXGIFactory5> factory5;
		BOOL allowTearing = FALSE;
		hr = factory4.As(&factory5);
		if (SUCCEEDED(hr)) {
			hr = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing));
			if (SUCCEEDED(hr) && allowTearing) {
				ret |= 2;
			}
		}
	}

	if (checkdevice) {
		static const D3D_FEATURE_LEVEL levels[] = { D3D_FEATURE_LEVEL_9_1 };
		UINT cdflags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
		ID3D11Device *m_device;
		ID3D11DeviceContext *m_deviceContext;
		HRESULT hr = pD3D11CreateDevice(NULL, currprefs.gfx_api_options == 0 ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_WARP,
			NULL, cdflags, levels, 1, D3D11_SDK_VERSION, &m_device, NULL, &m_deviceContext);
		if (FAILED(hr)) {
			write_log(_T("D3D11 check failed. %08x\n"), hr);
			return 0;
		}
		m_deviceContext->Release();
		m_device->Release();
	}

	ret |= 1;
	detected_val = ret;
	return ret;
}

static bool device_error(struct d3d11struct *d3d)
{
	d3d->device_errors++;
	if (d3d->device_errors > 2) {
		d3d->delayedfs = -1;
		return true;
	}
	return false;
}

static void do_black(struct d3d11struct *d3d)
{
	float color[4];
	color[0] = 0;
	color[1] = 0;
	color[2] = 0;
	color[3] = 0;
	// Bind the render target view and depth stencil buffer to the output render pipeline.
	d3d->m_deviceContext->OMSetRenderTargets(1, &d3d->m_renderTargetView, NULL);
	// Clear the back buffer.
	d3d->m_deviceContext->ClearRenderTargetView(d3d->m_renderTargetView, color);
	d3d->m_deviceContext->Flush();
}

static void do_present(struct d3d11struct *d3d)
{
	struct amigadisplay *ad = &adisplays[d3d->num];
	struct apmode *apm = ad->picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];
	HRESULT hr;
	UINT presentFlags = 0;

	int vsync = isvsync();
	UINT syncinterval = d3d->vblankintervals;
	// only if no vsync or low latency vsync
	if (d3d->m_tearingSupport && (d3d->swapChainDesc.Flags & DXGI_SWAP_CHAIN_FLAG_ALLOW_TEARING) && (!vsync || apm->gfx_vsyncmode)) {
		if (apm->gfx_vsyncmode || d3d->num > 0 || currprefs.turbo_emulation || (currprefs.gfx_variable_sync > 0 || (isfs(d3d) <= 0) && currprefs.gfx_variable_sync >= 0)) {
			presentFlags |= DXGI_PRESENT_ALLOW_TEARING;
			syncinterval = 0;
		}
	}
	if (!vsync) {
		if (apm->gfx_backbuffers == 0 || (presentFlags & DXGI_PRESENT_ALLOW_TEARING) || (apm->gfx_vflip == 0 && isfs(d3d) <= 0) || (isfs(d3d) > 0 && apm->gfx_vsyncmode))
			syncinterval = 0;
	}
	d3d->syncinterval = syncinterval;
	if (currprefs.turbo_emulation) {
		static int skip;
		static int toggle;
		if (--skip > 0)
			return;
		skip = 10 + toggle;
		toggle = !toggle;
		if (os_win8)
			presentFlags |= DXGI_PRESENT_DO_NOT_WAIT;
		syncinterval = 0;
	}

	hr = d3d->m_swapChain->Present(syncinterval, presentFlags);
	if (currprefs.turbo_emulation && hr == DXGI_ERROR_WAS_STILL_DRAWING)
		hr = S_OK;
	if (FAILED(hr) && hr != DXGI_STATUS_OCCLUDED) {
		if (hr == DXGI_ERROR_DEVICE_REMOVED) {
			device_error(d3d);
		} else if (hr == E_OUTOFMEMORY) {
			d3d->invalidmode = true;
		}
		write_log(_T("D3D11 Present %08x\n"), hr);
	}
	slicecnt++;
}

static float xD3D_getrefreshrate(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	d3d->lastframe = 0;
	if (isfs(d3d) != 0 && d3d->fsSwapChainDesc.RefreshRate.Denominator) {
		d3d->vblank = (float)d3d->fsSwapChainDesc.RefreshRate.Numerator / d3d->fsSwapChainDesc.RefreshRate.Denominator;
		return d3d->vblank;
	}
	if (!pDwmGetCompositionTimingInfo)
		return 0;
	DWM_TIMING_INFO ti;
	ti.cbSize = sizeof ti;
	HRESULT hr = pDwmGetCompositionTimingInfo(NULL, &ti);
	if (FAILED(hr)) {
		write_log(_T("DwmGetCompositionTimingInfo1 %08x\n"), hr);
		return 0;
	}
	d3d->vblank = (float)ti.rateRefresh.uiNumerator / ti.rateRefresh.uiDenominator;
	return d3d->vblank;
}

static bool xD3D11_initvals(HWND ahwnd, int monid, int w_w, int w_h, int t_w, int t_h, int depth, int *freq, int mmulth, int mmultv, bool doalloc)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	bool changed = false;

	if (d3d->m_screenWidth != w_w || d3d->m_screenHeight != w_h) {
		changed = true;
	}
	d3d->m_screenWidth = w_w;
	d3d->m_screenHeight = w_h;
	d3d->dmultxh = mmulth;
	d3d->dmultxv = mmultv;
	if (d3d->m_device) {
		target_graphics_buffer_update(monid, false);
	}

	return changed;
}

static int xxD3D11_init2(HWND ahwnd, int monid, int w_w, int w_h, int t_w, int t_h, int depth, int *freq, int mmulth, int mmultv)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	struct amigadisplay *ad = &adisplays[monid];
	struct apmode *apm = ad->picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];

	HRESULT result;
	int ret = 0;
	ComPtr<IDXGIFactory2> factory2;
	ComPtr<IDXGIFactory4> factory4;
	ComPtr<IDXGIFactory5> factory5;
	IDXGIAdapter1 *adapter;
	IDXGIOutput *adapterOutput;
	DXGI_ADAPTER_DESC1 adesc;
	DXGI_OUTPUT_DESC odesc;
	unsigned int numModes = 0;
	DXGI_MODE_DESC1 *displayModeList;
	DXGI_ADAPTER_DESC adapterDesc;

	write_log(_T("D3D11 init start. (%d*%d) (%d*%d) RTG=%d Depth=%d.\n"), w_w, w_h, t_w, t_h, ad->picasso_on, depth);

	d3d->filterd3didx = ad->gf_index;
	d3d->filterd3d = &currprefs.gf[d3d->filterd3didx];

	d3d->delayedfs = 0;
	d3d->device_errors = 0;

	if (depth != 32) {
		return 0;
	}

	if (!can_D3D11(false)) {
		return 0;
	}

	xD3D11_initvals(ahwnd, monid, w_w, w_h, t_w, t_h, depth, freq, mmulth, mmultv, false);

	d3d->ahwnd = ahwnd;

	d3d->texformat = DXGI_FORMAT_B8G8R8A8_UNORM;
	d3d->scrformat = DXGI_FORMAT_B8G8R8A8_UNORM;

	HMONITOR winmon;
	struct MultiDisplay *md = NULL;
	if (isfullscreen() == 0) {
		winmon = MonitorFromWindow(ahwnd, MONITOR_DEFAULTTONEAREST);
	} else {
		md = getdisplay(&currprefs, monid);
		POINT pt;
		pt.x = (md->rect.right - md->rect.left) / 2 + md->rect.left;
		pt.y = (md->rect.bottom - md->rect.top) / 2 + md->rect.top;
		winmon = MonitorFromPoint(pt, MONITOR_DEFAULTTONEAREST);
	}

	// Create a DirectX graphics interface factory.
	result = pCreateDXGIFactory1(__uuidof(IDXGIFactory4), (void**)&factory4);
	if (FAILED(result))
	{
		write_log(_T("D3D11 CreateDXGIFactory4 %08x\n"), result);
		result = pCreateDXGIFactory1(__uuidof(IDXGIFactory2), (void**)&factory2);
		if (FAILED(result)) {
			write_log(_T("D3D11 CreateDXGIFactory2 %08x\n"), result);
			if (!os_win8) {
				gui_message(_T("WinUAE Direct3D 11 mode requires Windows 7 Platform Update (KB2670838). Check Windows Update optional updates or download it from: https://www.microsoft.com/en-us/download/details.aspx?id=36805"));
			}
			return 0;
		}
	} else {
		BOOL allowTearing = FALSE;
		result = factory4.As(&factory5);
		if (SUCCEEDED(result)) {
			factory2 = factory5;
			if (!d3d->m_tearingSupport) {
				result = factory5->CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING, &allowTearing, sizeof(allowTearing));
				d3d->m_tearingSupport = SUCCEEDED(result) && allowTearing;
				write_log(_T("CheckFeatureSupport(DXGI_FEATURE_PRESENT_ALLOW_TEARING) = %08x %d\n"), result, allowTearing);
			}
		} else {
			factory2 = factory4;
		}
	}

	// Use the factory to create an adapter for the primary graphics interface (video card).
	UINT adapterNum = 0;
	bool outputFound = false;
	for (;;) {
		adapterOutput = NULL;
		result = factory2->EnumAdapters1(adapterNum, &adapter);
		if (FAILED(result))
		{
			if (adapterNum > 0)
				break;
			write_log(_T("IDXGIFactory2 EnumAdapters1 %08x\n"), result);
			return 0;
		}
		result = adapter->GetDesc1(&adesc);

		UINT adapterOutNum = 0;
		// Enumerate the monitor
		for(;;) {
			result = adapter->EnumOutputs(adapterOutNum, &adapterOutput);
			if (FAILED(result))
				break;
			result = adapterOutput->GetDesc(&odesc);
			if (SUCCEEDED(result)) {
				if (odesc.Monitor == winmon || (md && !_tcscmp(odesc.DeviceName, md->adapterid))) {
					outputFound = true;
					break;
				}
			}
			adapterOutput->Release();
			adapterOutput = NULL;
			adapterOutNum++;
		}
		if (outputFound)
			break;
		adapter->Release();
		adapter = NULL;
		adapterNum++;
	}
	if (!outputFound) {
		if (adapter)
			adapter->Release();
		adapter = NULL;
		result = factory2->EnumAdapters1(0, &adapter);
		if (FAILED(result)) {
			write_log(_T("EnumAdapters1 Default %08x\n"), result);
			return 0;
		}
		result = adapter->EnumOutputs(0, &adapterOutput);
		if (FAILED(result)) {
			adapter->Release();
			adapter = NULL;
			write_log(_T("EnumOutputs Default %08x\n"), result);
			return 0;
		}
	}

	d3d->outputAdapter = adapterOutput;

	ComPtr<IDXGIOutput1> adapterOutputx;
	ComPtr<IDXGIOutput6> adapterOutput6;
	d3d->hdr = false;
	result = adapterOutput->QueryInterface(__uuidof(IDXGIOutput6), &adapterOutput6);
	if (FAILED(result)) {
		write_log(_T("IDXGIOutput6 QueryInterface %08x\n"), result);
		ComPtr<IDXGIOutput1> adapterOutput1;
		result = adapterOutput->QueryInterface(__uuidof(IDXGIOutput1), &adapterOutput1);
		if (FAILED(result)) {
			write_log(_T("IDXGIOutput QueryInterface %08x\n"), result);
			return 0;
		}
		adapterOutputx = std::move(adapterOutput1);
	} else {
		DXGI_OUTPUT_DESC1 desc1;
		result = adapterOutput6->GetDesc1(&desc1);
		if (SUCCEEDED(result)) {
			write_log(_T("Monitor Rotation=%d BPC=%d ColorSpace=%d\n"), desc1.Rotation, desc1.BitsPerColor, desc1.ColorSpace);
			write_log(_T("R=%fx%f  G=%fx%f B=%fx%f WP=%fx%f\n"),
				desc1.RedPrimary[0], desc1.RedPrimary[1],
				desc1.GreenPrimary[0], desc1.GreenPrimary[1],
				desc1.BluePrimary[0], desc1.BluePrimary[1],
				desc1.WhitePoint[0], desc1.WhitePoint[1]);
			write_log(_T("MinL=%f MaxL=%f MaxFFL=%f\n"),
				desc1.MinLuminance, desc1.MaxLuminance, desc1.MaxFullFrameLuminance);
			d3d->hdr = desc1.ColorSpace == DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
			if (currprefs.gfx_api < 3) {
				d3d->hdr = false;
			}
		}
		adapterOutputx = adapterOutput6;
	}

	if (d3d->hdr) {
		//d3d->scrformat = DXGI_FORMAT_R10G10B10A2_UNORM;
		d3d->scrformat = DXGI_FORMAT_R16G16B16A16_FLOAT;
		gfx_hdr = true;
	}

	// Get the number of modes that fit the display format for the adapter output (monitor).
	result = adapterOutputx->GetDisplayModeList1(d3d->scrformat, DXGI_ENUM_MODES_INTERLACED, &numModes, NULL);
	if (FAILED(result))
	{
		write_log(_T("IDXGIOutput1 GetDisplayModeList1 %08x\n"), result);
		return 0;
	}

	// Create a list to hold all the possible display modes for this monitor/video card combination.
	displayModeList = new DXGI_MODE_DESC1[numModes];
	if (!displayModeList)
	{
		write_log(_T("IDXGIAdapter1 GetDesc %08x\n"), result);
		return 0;
	}

	// Now fill the display mode list structures.
	result = adapterOutputx->GetDisplayModeList1(d3d->scrformat, DXGI_ENUM_MODES_INTERLACED, &numModes, displayModeList);
	if (FAILED(result))
	{
		write_log(_T("IDXGIAdapter1 GetDesc %08x\n"), result);
		return 0;
	}

	ZeroMemory(&d3d->fsSwapChainDesc, sizeof(d3d->fsSwapChainDesc));

	int hz = getrefreshrate(monid, w_w, w_h);

	// Now go through all the display modes and find the one that matches the screen width and height.
	// When a match is found store the numerator and denominator of the refresh rate for that monitor.
	d3d->fsSwapChainDesc.RefreshRate.Denominator = 0;
	d3d->fsSwapChainDesc.RefreshRate.Numerator = 0;
	d3d->fsSwapChainDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
	for (int i = 0; i < numModes; i++) {
		DXGI_MODE_DESC1 *m = &displayModeList[i];
		if (m->Format != d3d->scrformat)
			continue;
		if (apm->gfx_interlaced && m->ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST)
			continue;
		if (!apm->gfx_interlaced && m->ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE)
			continue;
		if (m->Width == w_w && m->Height == w_h) {
			d3d->fsSwapChainDesc.ScanlineOrdering = m->ScanlineOrdering;
			d3d->fsSwapChainDesc.Scaling = m->Scaling;
			if (!hz) {
				write_log(_T("D3D11 found matching fullscreen mode. SLO=%d S=%d. Default refresh rate.\n"), m->ScanlineOrdering, m->Scaling);
				break;
			}
			if (isfs(d3d) != 0 && m->RefreshRate.Numerator && m->RefreshRate.Denominator) {
				float mhz = (float)m->RefreshRate.Numerator / m->RefreshRate.Denominator;
				if ((int)(mhz + 0.5) == hz || (int)(mhz) == hz) {
					d3d->fsSwapChainDesc.RefreshRate.Denominator = m->RefreshRate.Denominator;
					d3d->fsSwapChainDesc.RefreshRate.Numerator = m->RefreshRate.Numerator;
					write_log(_T("D3D11 found matching refresh rate %d/%d=%.2f. SLO=%d\n"), m->RefreshRate.Numerator, m->RefreshRate.Denominator, (float)mhz, m->ScanlineOrdering);
					*freq = hz;
					break;
				}
			}
		}
	}
	if (isfs(d3d) > 0 && (hz == 0 || (d3d->fsSwapChainDesc.RefreshRate.Denominator == 0 && d3d->fsSwapChainDesc.RefreshRate.Numerator == 0))) {
		// find highest frequency for selected mode
		float ffreq = 0;
		for (int i = 0; i < numModes; i++) {
			DXGI_MODE_DESC1 *m = &displayModeList[i];
			if (m->Format != d3d->scrformat)
				continue;
			if (apm->gfx_interlaced && m->ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST)
				continue;
			if (!apm->gfx_interlaced && m->ScanlineOrdering != DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE)
				continue;
			if (m->Width != w_w || m->Height != w_h)
				continue;
			if (!m->RefreshRate.Numerator || !m->RefreshRate.Denominator)
				continue;
			float nfreq = (float)m->RefreshRate.Numerator / m->RefreshRate.Denominator;
			if (nfreq > ffreq) {
				ffreq = nfreq;
				d3d->fsSwapChainDesc.RefreshRate.Denominator = m->RefreshRate.Denominator;
				d3d->fsSwapChainDesc.RefreshRate.Numerator = m->RefreshRate.Numerator;
				if (!currprefs.gfx_variable_sync) {
					*freq = (int)nfreq;
				}
			}
		}
		write_log(_T("D3D11 Highest freq: %d/%d=%.2f W=%d H=%d\n"),
			d3d->fsSwapChainDesc.RefreshRate.Numerator, d3d->fsSwapChainDesc.RefreshRate.Denominator, ffreq, w_w, w_h);
		// then re-confirm with FindClosestMatchingMode1()
		DXGI_MODE_DESC1 md1 = { 0 }, md2;
		md1.Format = d3d->scrformat;
		md1.RefreshRate.Numerator = d3d->fsSwapChainDesc.RefreshRate.Numerator;
		md1.RefreshRate.Denominator = d3d->fsSwapChainDesc.RefreshRate.Denominator;
		md1.Width = w_w;
		md1.Height = w_h;
		md1.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
		md1.ScanlineOrdering = apm->gfx_interlaced ? DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST : DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE;
		result = adapterOutputx->FindClosestMatchingMode1(&md1, &md2, NULL);
		if (FAILED(result)) {
			write_log(_T("FindClosestMatchingMode1 %08x\n"), result);
		} else {
			d3d->fsSwapChainDesc.RefreshRate.Denominator = md2.RefreshRate.Denominator;
			d3d->fsSwapChainDesc.RefreshRate.Numerator = md2.RefreshRate.Numerator;
			if (!currprefs.gfx_variable_sync) {
				*freq = 0;
				if (md2.RefreshRate.Denominator && md2.RefreshRate.Numerator)
					*freq = md2.RefreshRate.Numerator / md2.RefreshRate.Denominator;
				write_log(_T("D3D11 FindClosestMatchingMode1() %d/%d=%.2f SLO=%d W=%d H=%d\n"),
					md2.RefreshRate.Numerator, md2.RefreshRate.Denominator,
					(float)md2.RefreshRate.Numerator / md2.RefreshRate.Denominator, md1.ScanlineOrdering,
					md2.Width, md2.Height);
			}
		}
	}

	if (isfs(d3d) <= 0 && !currprefs.gfx_variable_sync) {
		*freq = (int)xD3D_getrefreshrate(monid);
	}

	// Get the adapter (video card) description.
	result = adapter->GetDesc(&adapterDesc);
	if (FAILED(result)) {
		write_log(_T("IDXGIAdapter1 GetDesc %08x\n"), result);
		return 0;
	}

	result = adapterOutput->GetDesc(&odesc);
	if (FAILED(result)) {
		write_log(_T("IDXGIAdapter1 GetDesc %08x\n"), result);
		return 0;
	}

	write_log(_T("D3D11 Device: %s [%s] (%d,%d,%d,%d)\n"), adapterDesc.Description, odesc.DeviceName,
		odesc.DesktopCoordinates.left, odesc.DesktopCoordinates.top,
		odesc.DesktopCoordinates.right, odesc.DesktopCoordinates.bottom);

	// Release the display mode list.
	delete[] displayModeList;
	displayModeList = 0;

	// Release the adapter.
	adapter->Release();
	adapter = 0;

	UINT cdflags = D3D11_CREATE_DEVICE_BGRA_SUPPORT;
#ifndef NDEBUG
	cdflags |= D3D11_CREATE_DEVICE_DEBUG;
#endif
	static const D3D_FEATURE_LEVEL levels111[] = { D3D_FEATURE_LEVEL_11_1 };
	D3D_FEATURE_LEVEL outlevel;
	D3D_DRIVER_TYPE dt = currprefs.gfx_api_options == 0 ? D3D_DRIVER_TYPE_HARDWARE : D3D_DRIVER_TYPE_WARP;
	result = pD3D11CreateDevice(NULL, dt, NULL, cdflags, levels111, 1, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
	if (FAILED(result)) {
		write_log(_T("D3D11CreateDevice LEVEL_11_1: %08x\n"), result);
		if (result == E_INVALIDARG || result == DXGI_ERROR_UNSUPPORTED) {
			result = pD3D11CreateDevice(NULL, dt, NULL, cdflags, NULL, 0, D3D11_SDK_VERSION, &d3d->m_device, &outlevel, &d3d->m_deviceContext);
		}
		if (FAILED(result)) {
			write_log(_T("D3D11CreateDevice %08x. Hardware does not support Direct3D11 Level 9.1 or higher.\n"), result);
			return 0;
		}
	}
#ifndef NDEBUG
	static const char dname[] = "device";
	static const char cname[] = "context";
	d3d->m_device->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(dname) - 1, dname);
	d3d->m_deviceContext->SetPrivateData(WKPDID_D3DDebugObjectName, sizeof(cname) - 1, cname);
	d3d->m_device->QueryInterface(IID_ID3D11InfoQueue, (void **)&d3d->m_debugInfoQueue);
	if (0 && d3d->m_debugInfoQueue)
	{
		d3d->m_debugInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_CORRUPTION, TRUE);
		d3d->m_debugInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_ERROR, TRUE);
		d3d->m_debugInfoQueue->SetBreakOnSeverity(D3D11_MESSAGE_SEVERITY_WARNING, TRUE);
	}
	d3d->m_device->QueryInterface(IID_ID3D11Debug, (void **)&d3d->m_debug);
#endif

	write_log(_T("D3D11CreateDevice succeeded with level %d.%d. %s.\n"), outlevel >> 12, (outlevel >> 8) & 15,
		currprefs.gfx_api_options ? _T("Software WARP driver") : _T("Hardware accelerated"));
	d3d11_feature_level = outlevel;

	UINT flags = 0;
	result = d3d->m_device->CheckFormatSupport(d3d->texformat, &flags);
	if (FAILED(result) || !(flags & D3D11_FORMAT_SUPPORT_TEXTURE2D)) {
		if (depth != 32)
			write_log(_T("Direct3D11: 16-bit texture format is not supported %08x\n"), result);
		else
			write_log(_T("Direct3D11: 32-bit texture format is not supported!? %08x\n"), result);
		if (depth == 32)
			return 0;
		write_log(_T("Direct3D11: Retrying in 32-bit mode\n"), result);
		return -1;
	}

	// Initialize the swap chain description.
	ZeroMemory(&d3d->swapChainDesc, sizeof(d3d->swapChainDesc));

	// Set the width and height of the back buffer.
	d3d->swapChainDesc.Width = w_w;
	d3d->swapChainDesc.Height = w_h;

	// Set regular 32-bit surface for the back buffer.
	d3d->swapChainDesc.Format = d3d->scrformat;

	// Turn multisampling off.
	d3d->swapChainDesc.SampleDesc.Count = 1;
	d3d->swapChainDesc.SampleDesc.Quality = 0;

	// Set the usage of the back buffer.
	d3d->swapChainDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;

	d3d->swapChainDesc.BufferCount = apm->gfx_backbuffers + 1;
	if (d3d->swapChainDesc.BufferCount < 2)
		d3d->swapChainDesc.BufferCount = 2;

	if (d3d->swapChainDesc.BufferCount > 2 && isfullscreen() <= 0 && !apm->gfx_vsync) {
		write_log(_T("Switch from triple buffer to double buffer (%d).\n"), apm->gfx_vflip);
		d3d->swapChainDesc.BufferCount = 2;
		apm->gfx_vflip = 0;
	}

	d3d->swapChainDesc.SwapEffect = os_win8 ? (os_win10 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL) : DXGI_SWAP_EFFECT_SEQUENTIAL;
	if (apm->gfx_vsyncmode && isfs(d3d) > 0 && !os_win10) {
		d3d->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
	}

	d3d->vblankintervals = 0;
	if (!monid && apm->gfx_backbuffers > 2 && !isvsync())
		d3d->vblankintervals = 1;
	cannoclear = false;

	if (apm->gfx_vsyncmode) {
		cannoclear = true;
	}

	d3d->swapChainDesc.AlphaMode = DXGI_ALPHA_MODE_UNSPECIFIED;

	setswapchainmode(d3d, isfs(d3d));

	d3d->swapChainDesc.Scaling = (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL || d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD) ? DXGI_SCALING_NONE : DXGI_SCALING_STRETCH;

	d3d->blackscreen = false;
	if (!monid && isvsync()) {
		int vsync = isvsync();
		int hzmult = 0;
		getvsyncrate(monid, (float)*freq, &hzmult);
		if (hzmult < 0 && !currprefs.gfx_variable_sync && apm->gfx_vsyncmode == 0) {
			if (!apm->gfx_strobo) {
				d3d->vblankintervals = 2;
			} else {
				d3d->vblankintervals = 1;
				d3d->blackscreen = true;
			}
		}
		if (vsync > 0 && !apm->gfx_vsyncmode) {
			if (apm->gfx_strobo)
				d3d->blackscreen = true;
			d3d->vblankintervals = 1;
			int hzmult;
			getvsyncrate(monid, (float)hz, &hzmult);
			if (hzmult < 0) {
				d3d->vblankintervals = 1 + (-hzmult) - (d3d->blackscreen ? 1 : 0);
			}
		}
	}
	if (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_FLIP_DISCARD && d3d->vblankintervals > 0)
		d3d->swapChainDesc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_SEQUENTIAL;

	// Create the swap chain, Direct3D device, and Direct3D device context.
	result = factory2->CreateSwapChainForHwnd(d3d->m_device, ahwnd, &d3d->swapChainDesc, isfs(d3d) != 0 ? &d3d->fsSwapChainDesc : NULL, NULL, &d3d->m_swapChain);
	if (FAILED(result)) {
		write_log(_T("IDXGIFactory2 CreateSwapChainForHwnd %08x\n"), result);
		return 0;
	}

	{
		CComPtr<IDXGISwapChain3> m_swapChain3;
		if (SUCCEEDED(d3d->m_swapChain->QueryInterface(&m_swapChain3))) {
			if (d3d->hdr) {
				if (d3d->scrformat == DXGI_FORMAT_R10G10B10A2_UNORM) {
					DXGI_COLOR_SPACE_TYPE type = DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020;
					UINT cps;
					result = m_swapChain3->CheckColorSpaceSupport(type, &cps);
					if (SUCCEEDED(result)) {
						if (!(cps & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT))
							write_log(_T("CheckColorSpaceSupport(DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020) not supported!?\n"));
						result = m_swapChain3->SetColorSpace1(type);
						if (FAILED(result)) {
							write_log(_T("SetColorSpace1 failed %08x\n"), result);
						}
					} else {
						write_log(_T("CheckColorSpaceSupport failed %08x\n"), result);
					}
				} else {
					DXGI_COLOR_SPACE_TYPE type = DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709;;
					UINT cps;
					result = m_swapChain3->CheckColorSpaceSupport(type, &cps);
					if (SUCCEEDED(result)) {
						if (!(cps & DXGI_SWAP_CHAIN_COLOR_SPACE_SUPPORT_FLAG_PRESENT))
							write_log(_T("CheckColorSpaceSupport(DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709) not supported!?\n"));
						result = m_swapChain3->SetColorSpace1(type);
						if (FAILED(result)) {
							write_log(_T("SetColorSpace1 failed %08x\n"), result);
						}
					} else {
						write_log(_T("CheckColorSpaceSupport failed %08x\n"), result);
					}
				}
			}
			IDXGIOutput *dxgiOutput = NULL;
			result = m_swapChain3->GetContainingOutput(&dxgiOutput);
			if (SUCCEEDED(result)) {
				IDXGIOutput2 *dxgiOutput2 = NULL;
				result = dxgiOutput->QueryInterface(IID_PPV_ARGS(&dxgiOutput2));
				if (SUCCEEDED(result)) {
					BOOL mpo = dxgiOutput2->SupportsOverlays();
					if (mpo) {
						write_log(_T("MultiPlane Overlays supported\n"));
					}
					dxgiOutput2->Release();
				}
				dxgiOutput->Release();
			}
		}
	}


	{
		ComPtr<IDXGIDevice1> dxgiDevice;
		result = d3d->m_device->QueryInterface(__uuidof(IDXGIDevice1), &dxgiDevice);
		if (FAILED(result)) {
			write_log(_T("QueryInterface IDXGIDevice1 %08x\n"), result);
		} else {
			int f = apm->gfx_backbuffers <= 1 ? 1 : 2;
			if (d3d->blackscreen)
				f++;
			result = dxgiDevice->SetMaximumFrameLatency(f);
			if (FAILED(result)) {
				write_log(_T("IDXGIDevice1 SetMaximumFrameLatency %08x\n"), result);
			}
		}
	}

	IDXGIFactory1 *pFactory = NULL;
	result = d3d->m_swapChain->GetParent(__uuidof (IDXGIFactory1), (void **)&pFactory);
	if (SUCCEEDED(result)) {
		result = pFactory->MakeWindowAssociation(ahwnd, DXGI_MWA_NO_WINDOW_CHANGES | DXGI_MWA_NO_ALT_ENTER | DXGI_MWA_NO_PRINT_SCREEN);
		if (FAILED(result)) {
			write_log(_T("IDXGIFactory2 MakeWindowAssociation %08x\n"), result);
		}
		pFactory->Release();
	}

	result = d3d->m_swapChain->GetDesc1(&d3d->swapChainDesc);
	if (FAILED(result)) {
		write_log(_T("IDXGIFactory2 GetDesc1 %08x\n"), result);
	}

	d3d->invalidmode = false;
	d3d->fsmode = 0;
	clearcnt = 0;

	write_log(_T("D3D11 Buffers=%d Flags=%08x Format=%08x Scaling=%d SwapEffect=%d VBI=%d\n"),
		d3d->swapChainDesc.BufferCount, d3d->swapChainDesc.Flags, d3d->swapChainDesc.Format,
		d3d->swapChainDesc.Scaling, d3d->swapChainDesc.SwapEffect, d3d->vblankintervals);

	if (isfs(d3d) > 0)
		D3D_resize(monid, 1);
	D3D_resize(monid, 0);

	ret = 1;

	write_log(_T("D3D11 init end\n"));
	return ret;
}

static void freed3d(struct d3d11struct *d3d)
{
	write_log(_T("D3D11 freed3d start\n"));

	d3d->d3dinit_done = false;

	if (d3d->m_rasterState) {
		d3d->m_rasterState->Release();
		d3d->m_rasterState = NULL;
	}
	if (d3d->m_alphaEnableBlendingState) {
		d3d->m_alphaEnableBlendingState->Release();
		d3d->m_alphaEnableBlendingState = NULL;
	}
	if (d3d->m_alphaDisableBlendingState) {
		d3d->m_alphaDisableBlendingState->Release();
		d3d->m_alphaDisableBlendingState = NULL;
	}

	if (d3d->m_renderTargetView) {
		d3d->m_renderTargetView->Release();
		d3d->m_renderTargetView = NULL;
	}

	if (d3d->m_layout) {
		d3d->m_layout->Release();
		d3d->m_layout = NULL;
	}
	if (d3d->m_vertexShader) {
		d3d->m_vertexShader->Release();
		d3d->m_vertexShader = NULL;
	}
	if (d3d->m_pixelShader) {
		d3d->m_pixelShader->Release();
		d3d->m_pixelShader = NULL;
	}
	if (d3d->m_pixelShaderMask) {
		d3d->m_pixelShaderMask->Release();
		d3d->m_pixelShaderMask = NULL;
	}
	if (d3d->m_pixelShaderSL) {
		d3d->m_pixelShaderSL->Release();
		d3d->m_pixelShaderSL = NULL;
	}

	if (d3d->m_sampleState_point_wrap) {
		d3d->m_sampleState_point_wrap->Release();
		d3d->m_sampleState_point_wrap = NULL;
	}
	if (d3d->m_sampleState_linear_wrap) {
		d3d->m_sampleState_linear_wrap->Release();
		d3d->m_sampleState_linear_wrap = NULL;
	}
	if (d3d->m_sampleState_point_clamp) {
		d3d->m_sampleState_point_clamp->Release();
		d3d->m_sampleState_point_clamp = NULL;
	}
	if (d3d->m_sampleState_linear_clamp) {
		d3d->m_sampleState_linear_clamp->Release();
		d3d->m_sampleState_linear_clamp = NULL;
	}

	if (d3d->m_indexBuffer) {
		d3d->m_indexBuffer->Release();
		d3d->m_indexBuffer = 0;
	}
	if (d3d->m_vertexBuffer) {
		d3d->m_vertexBuffer->Release();
		d3d->m_vertexBuffer = 0;
	}
	if (d3d->m_matrixBuffer) {
		d3d->m_matrixBuffer->Release();
		d3d->m_matrixBuffer = 0;
	}
	if (d3d->m_psBuffer) {
		d3d->m_psBuffer->Release();
		d3d->m_psBuffer = 0;
	}

	FreeTextures(d3d);
	FreeTexture2D(&d3d->sltexture, &d3d->sltexturerv);
	FreeShaderTex(&d3d->lpPostTempTexture);

	for (int i = 0; i < MAX_SHADERS; i++) {
		struct shaderdata11 *s = &d3d->shaders[i];
		freeshaderdata(s);
	}

	if (d3d->filenotificationhandle)
		CloseHandle(d3d->filenotificationhandle);
	d3d->filenotificationhandle = NULL;

	if (d3d->m_deviceContext) {
		d3d->m_deviceContext->ClearState();
	}
	write_log(_T("D3D11 freed3d end\n"));
}

static void xD3D11_free(int monid, bool immediate)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	write_log(_T("D3D11 free start\n"));

	//freethread(d3d);

	freed3d(d3d);

	if (d3d->m_swapChain) {
		// Before shutting down set to windowed mode or when you release the swap chain it will throw an exception.
		d3d->m_swapChain->SetFullscreenState(false, NULL);
		d3d->m_swapChain->Release();
		d3d->m_swapChain = NULL;
	}
	if (d3d->m_deviceContext) {
		d3d->m_deviceContext->ClearState();
		d3d->m_deviceContext->Flush();
		d3d->m_deviceContext->Release();
		d3d->m_deviceContext = NULL;
	}

	if (d3d->m_device) {
		d3d->m_device->Release();
		d3d->m_device = NULL;
	}

	if (d3d->outputAdapter) {
		d3d->outputAdapter->Release();
		d3d->outputAdapter = NULL;
	}

#ifndef NDEBUG
	if (d3d->m_debugInfoQueue) {
		d3d->m_debugInfoQueue->Release();
		d3d->m_debugInfoQueue = NULL;
	}
	if (d3d->m_debug) {
		d3d->m_debug->ReportLiveDeviceObjects(D3D11_RLDO_SUMMARY | D3D11_RLDO_DETAIL);
		d3d->m_debug->Release();
		d3d->m_debug = NULL;
	}
#endif

	d3d->device_errors = 0;

	changed_prefs.leds_on_screen &= ~STATUSLINE_TARGET;
	currprefs.leds_on_screen &= ~STATUSLINE_TARGET;

	for (int i = 0; i < LED_MAX; i++) {
		leds[i] = 0;
	}

	write_log(_T("D3D11 free end\n"));
}

static int xxD3D11_init(HWND ahwnd, int monid, int w_w, int w_h, int depth, int *freq, int mmulth, int mmultv)
{
	return xxD3D11_init2(ahwnd, monid, w_w, w_h, w_w, w_h, depth, freq, mmulth, mmultv);
}

static const TCHAR *xD3D11_init(HWND ahwnd, int monid, int w_w, int w_h, int depth, int *freq, int mmulth, int mmultv, int *errp)
{
	bool reset = false;
	if (!can_D3D11(false)) {
		*errp = 1;
		return _T("D3D11 FAILED TO INIT");
	}
	struct d3d11struct *d3d = &d3d11data[monid];
	struct amigadisplay *ad = &adisplays[monid];
	if (!D3D_isenabled(monid)) {
		reset = true;
	}
	if (d3d->filterd3didx != ad->gf_index) {
		struct gfx_filterdata *f1 = &currprefs.gf[d3d->filterd3didx];
		if (!f1->enable) {
			f1 = &currprefs.gf[0];
		}
		struct gfx_filterdata *f2 = &currprefs.gf[ad->gf_index];
		if (!f2->enable) {
			f2 = &currprefs.gf[0];
		}
		if (f1 != f2) {
			if (f1->gfx_filter != f2->gfx_filter) {
				reset = true;
			}
			for (int i = 0; i < MAX_FILTERSHADERS * 2 + 1; i++) {
				if (_tcsicmp(f1->gfx_filtershader[i], f2->gfx_filtershader[i])) {
					reset = true;
				}
				if (_tcsicmp(f1->gfx_filtermask[i], f2->gfx_filtermask[i])) {
					reset = true;
				}
			}
			if (_tcsicmp(f1->gfx_filteroverlay, f2->gfx_filteroverlay)) {
				reset = true;
			}
			if (f1->gfx_filter_scanlines != f2->gfx_filter_scanlines) {
				reset = true;
			}
			if (f1->gfx_filter_scanlineratio != f2->gfx_filter_scanlineratio) {
				reset = true;
			}
			if (f1->gfx_filter_scanlinelevel != f2->gfx_filter_scanlinelevel) {
				reset = true;
			}
			if (f1->gfx_filter_scanlineoffset != f2->gfx_filter_scanlineoffset) {
				reset = true;
			}
		}
	}
	if (reset) {
		xD3D11_free(monid, true);
		int v = xxD3D11_init(ahwnd, monid, w_w, w_h, depth, freq, mmulth, mmultv);
		if (v > 0) {
			return NULL;
		}
		xD3D11_free(monid, true);
		*errp = 1;
		if (v <= 0) {
			return _T("");
		}
		return _T("D3D11 INITIALIZATION ERROR");
	} else {
		struct d3d11struct *d3d = &d3d11data[monid];
		if (xD3D11_initvals(ahwnd, monid, w_w, w_h, w_w, w_h, depth, freq, mmulth, mmultv, true)) {
			d3d->fsresizedo = true;
		} else {
			*errp = -1;
		}
		return NULL;
	}
}

static void setpsbuffer(struct d3d11struct *d3d, ID3D11Buffer *psbuffer)
{
	HRESULT result;
	PSBufferType *dataPtr;
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	// Lock the constant buffer so it can be written to.
	result = d3d->m_deviceContext->Map(psbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (FAILED(result))
	{
		write_log(_T("ID3D11DeviceContext map(ps) %08x\n"), result);
		return;
	}

	float bri = (currprefs.gfx_luminance) * (1.0f / 2000.0f) + 1.0f;
	float con = (currprefs.gfx_contrast + 2000.0f) / 2000.0f;

	// Get a pointer to the data in the constant buffer.
	dataPtr = (PSBufferType *)mappedResource.pData;
	dataPtr->brightness = bri;
	dataPtr->contrast = con;

	// Unlock the constant buffer.
	d3d->m_deviceContext->Unmap(psbuffer, 0);
}

static bool setmatrix(struct d3d11struct *d3d, ID3D11Buffer *matrixbuffer, D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	HRESULT result;
	D3DXMATRIX worldMatrix2, viewMatrix2, projectionMatrix2;
	MatrixBufferType *dataPtr;
	D3D11_MAPPED_SUBRESOURCE mappedResource;

	// Transpose the matrices to prepare them for the shader.
	xD3DXMatrixTranspose(&worldMatrix2, &worldMatrix);
	xD3DXMatrixTranspose(&viewMatrix2, &viewMatrix);
	xD3DXMatrixTranspose(&projectionMatrix2, &projectionMatrix);

	// Lock the constant buffer so it can be written to.
	result = d3d->m_deviceContext->Map(matrixbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
	if (FAILED(result))
	{
		write_log(_T("ID3D11DeviceContext map(matrix) %08x\n"), result);
		return false;
	}

	// Get a pointer to the data in the constant buffer.
	dataPtr = (MatrixBufferType*)mappedResource.pData;

	// Copy the matrices into the constant buffer.
	dataPtr->world = worldMatrix2;
	dataPtr->view = viewMatrix2;
	dataPtr->projection = projectionMatrix2;

	// Unlock the constant buffer.
	d3d->m_deviceContext->Unmap(matrixbuffer, 0);
	return true;
}

static void RenderBuffers(struct d3d11struct *d3d, ID3D11Buffer *vertexbuffer, ID3D11Buffer *indexbuffer)
{
	unsigned int stride;
	unsigned int offset;

	// Set vertex buffer stride and offset.
	stride = sizeof(VertexType);
	offset = 0;

	// Set the vertex buffer to active in the input assembler so it can be rendered.
	d3d->m_deviceContext->IASetVertexBuffers(0, 1, &vertexbuffer, &stride, &offset);

	// Set the index buffer to active in the input assembler so it can be rendered.
	d3d->m_deviceContext->IASetIndexBuffer(indexbuffer, d3d->index_buffer_bytes == 4 ? DXGI_FORMAT_R32_UINT : DXGI_FORMAT_R16_UINT, 0);

	// Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
	d3d->m_deviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
}

static void EndScene(struct d3d11struct *d3d)
{
#if 0
	if ((d3d->syncinterval || (d3d->swapChainDesc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD)) && d3d->flipped) {
		WaitForSingleObject(flipevent2, 100);
		d3d->flipped = false;
	}
	SetEvent(flipevent);
#endif
	do_present(d3d);
}

static void TextureShaderClass_RenderShader(struct d3d11struct *d3d)
{
	// Set the vertex input layout.
	d3d->m_deviceContext->IASetInputLayout(d3d->m_layout);

	// Set the vertex and pixel shaders that will be used to render this triangle.
	d3d->m_deviceContext->VSSetShader(d3d->m_vertexShader, NULL, 0);
	bool mask = false;
	for (int i = 0; i < MAX_SHADERS; i++) {
		if (d3d->shaders[i].type == SHADERTYPE_MASK_MIDDLE && d3d->shaders[i].masktexturerv) {
			mask = true;
		}
	}
	if (mask) {
		d3d->m_deviceContext->PSSetShader(d3d->m_pixelShaderMask, NULL, 0);
	} else if (d3d->sltexture) {
		d3d->m_deviceContext->PSSetShader(d3d->m_pixelShaderSL, NULL, 0);
	} else {
		d3d->m_deviceContext->PSSetShader(d3d->m_pixelShader, NULL, 0);
	}

	// Set the sampler state in the pixel shader.
	d3d->m_deviceContext->PSSetSamplers(0, 1, d3d->filterd3d->gfx_filter_bilinear ? &d3d->m_sampleState_linear_clamp : &d3d->m_sampleState_point_clamp);
	d3d->m_deviceContext->PSSetSamplers(1, 1, d3d->filterd3d->gfx_filter_bilinear ? &d3d->m_sampleState_linear_wrap : &d3d->m_sampleState_point_wrap);

	// Render the triangle.
	d3d->m_deviceContext->DrawIndexed(INDEXCOUNT, 0, 0);
}

static void RenderSprite(struct d3d11struct *d3d, struct d3d11sprite *spr, int monid)
{
	D3DXMATRIX scaling, rotation, translation, worldMatrix;
	float left, top;
	float w, h;
	float rot = 0;

	if (!spr->enabled)
		return;
	
	left = spr->x;
	top = -spr->y;

	if (spr->outwidth) {
		w = spr->outwidth;
	} else {
		w = (float)spr->width;
	}
	if (spr->outheight) {
		h = spr->outheight;
	} else {
		h = (float)spr->height;
	}

	if (spr->rotation) {
		rot = get_rotation(d3d, monid);
	}

	xD3DXMatrixScaling(&scaling, w, h, 1.0f);
	xD3DXMatrixRotationZ(&rotation, rot);

	left += (int)(w / 2 + 0.5f) - d3d->m_screenWidth / 2;
	top += (int)(-h / 2 - 0.5f) + d3d->m_screenHeight / 2;

	xD3DXMatrixTranslation(&translation, left, top, 0.0f);

	xD3DXMatrixMultiply(&worldMatrix, &scaling, &rotation);
	xD3DXMatrixMultiply(&worldMatrix, &worldMatrix, &translation);

	UpdateVertexArray(d3d, spr->vertexbuffer, -0.5f, 0.5f, 0.5f, -0.5f, 0, 0, 0, 0);

	RenderBuffers(d3d, spr->vertexbuffer, spr->indexbuffer);

	if (!setmatrix(d3d, spr->matrixbuffer, worldMatrix, d3d->m_viewMatrix, d3d->m_orthoMatrix))
		return;

	if (spr->alpha)
		TurnOnAlphaBlending(d3d);

	// Now set the constant buffer in the vertex shader with the updated values.
	d3d->m_deviceContext->VSSetConstantBuffers(0, 1, &spr->matrixbuffer);

	d3d->m_deviceContext->PSSetShaderResources(0, 1, &spr->texturerv);
	// Set the vertex input layout.
	d3d->m_deviceContext->IASetInputLayout(spr->layout);
	// Set the vertex and pixel shaders that will be used to render this triangle.
	d3d->m_deviceContext->VSSetShader(spr->vertexshader, NULL, 0);
	d3d->m_deviceContext->PSSetShader(spr->pixelshader, NULL, 0);
	// Set the sampler state in the pixel shader.
	d3d->m_deviceContext->PSSetSamplers(0, 1, spr->bilinear ? &d3d->m_sampleState_linear_clamp : &d3d->m_sampleState_point_clamp);
	// Render the triangle.
	d3d->m_deviceContext->DrawIndexed(INDEXCOUNT, 0, 0);

	if (spr->alpha)
		TurnOffAlphaBlending(d3d);
}

static void setspritescaling(struct d3d11sprite *spr, float w, float h)
{
	spr->outwidth = w * spr->width;
	spr->outheight = h * spr->height;
}

static void renderoverlay(struct d3d11struct *d3d, int monid)
{
	if (!d3d->mask2texture.enabled)
		return;

	struct d3d11sprite *spr = &d3d->mask2texture;

	float srcw = d3d->mask2texture_w;
	float srch = d3d->mask2texture_h;
	float aspectsrc = srcw / srch;
	float aspectdst = (float)d3d->m_screenWidth / d3d->m_screenHeight;

	setspritescaling(spr, d3d->mask2texture_multx, d3d->mask2texture_multy);

#if 0
	v.x = 0;
	if (filterd3d->gfx_filteroverlay_pos.x == -1)
		v.x = (d3d->m_screenWidth - (d3d->mask2texture_w * w)) / 2;
	else if (filterd3d->gfx_filteroverlay_pos.x > -24000)
		v.x = filterd3d->gfx_filteroverlay_pos.x;
	else
		v.x = (d3d->m_screenWidth - (d3d->mask2texture_w * w)) / 2 + (-filterd3d->gfx_filteroverlay_pos.x - 30100) * d3d->m_screenHeight / 100.0;

	v.y = 0;
	if (filterd3d->gfx_filteroverlay_pos.y == -1)
		v.y = (d3d->m_screenWidth - (d3d->mask2texture_h * h)) / 2;
	else if (filterd3d->gfx_filteroverlay_pos.y > -24000)
		v.y = filterd3d->gfx_filteroverlay_pos.y;
	else
		v.y = (d3d->m_screenWidth - (d3d->mask2texture_h * h)) / 2 + (-filterd3d->gfx_filteroverlay_pos.y - 30100) * d3d->m_screenHeight / 100.0;
#endif

	setsprite(d3d, spr, d3d->mask2texture_offsetw, 0);
	RenderSprite(d3d, spr, monid);

	for (int i = 0; overlayleds[i]; i++) {
		bool led = leds[ledtypes[i]] != 0;
		if (led || (ledtypes[i] == LED_POWER && currprefs.power_led_dim)) {
			struct d3d11sprite *sprled = &d3d->mask2textureleds[i];
			if (!led && ledtypes[i] == LED_POWER && currprefs.power_led_dim)
				sprled = &d3d->mask2textureled_power_dim;
			if (sprled) {
				setspritescaling(sprled, d3d->mask2texture_multx, d3d->mask2texture_multy);
				setsprite(d3d, sprled,
					d3d->mask2texture_offsetw + d3d->mask2textureledoffsets[i * 2 + 0] * d3d->mask2texture_multx,
					d3d->mask2textureledoffsets[i * 2 + 1] * d3d->mask2texture_multy);
				RenderSprite(d3d, sprled, monid);
			}
		}
	}

	if (d3d->mask2texture_offsetw > 0) {
		struct d3d11sprite *bspr = &d3d->blanksprite;
		setsprite(d3d, bspr, 0, 0);
		RenderSprite(d3d, bspr, monid);
		setsprite(d3d, bspr, d3d->mask2texture_offsetw + d3d->mask2texture_ww, 0);
		RenderSprite(d3d, bspr, monid);
	}
}

static void xD3D11_led(int led, int on, int brightness)
{
	struct d3d11struct *d3d = &d3d11data[0];
	leds[led] = on;
}

static int xD3D11_debug(int monid, int mode)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	int old = debugcolors ? 1 : 0;
	debugcolors = (mode & 1) != 0;
	noclear = debugcolors ? false : true;
	clearcnt = 0;
	return old;
}

static void clearrt(struct d3d11struct *d3d)
{
	// Setup the color to clear the buffer to.
	float color[4];
	color[0] = 0;
	color[1] = 0;
	color[2] = 0;
	color[3] = 0;

	if (noclear && cannoclear) {
		if (clearcnt > 3)
			return;
		clearcnt++;
	}

	if (!noclear && debugcolors && slicecnt > 0) {
		int cnt = slicecnt - 1;
		int v = cnt % 3;
		if (cnt / 3 == 1)
			color[(v + 1) % 3] = 0.3;
		color[v] = 0.3;
	}

	// Clear the back buffer.
	d3d->m_deviceContext->ClearRenderTargetView(d3d->m_renderTargetView, color);
}

static bool renderframe(struct d3d11struct *d3d)
{
	ID3D11ShaderResourceView *empty = NULL;
	struct shadertex st;
	st.tex = d3d->texture2d;
	st.rv = d3d->texture2drv;
	st.rt = NULL;

	TurnOffAlphaBlending(d3d);

	d3d->m_deviceContext->PSSetShaderResources(0, 1, &empty);

	for (int i = 0; i < MAX_SHADERS; i++) {
		struct shaderdata11 *s = &d3d->shaders[i];
		if (s->type == SHADERTYPE_BEFORE)
			settransform_pre(d3d, s);
		if (s->type == SHADERTYPE_MIDDLE) {
			d3d->m_matProj = d3d->m_matProj_out;
			d3d->m_matView = d3d->m_matView_out;
			d3d->m_matWorld = d3d->m_matWorld_out;
		}
		if (s->type == SHADERTYPE_BEFORE || s->type == SHADERTYPE_MIDDLE) {
			settransform(d3d, s);
			st.vp = &s->viewport;
			if (!processshader(d3d, &st, s, true))
				return false;
		}
	}

	d3d->m_matProj = d3d->m_matProj_out;
	d3d->m_matView = d3d->m_matView_out;
	d3d->m_matWorld = d3d->m_matWorld_out;

	d3d->m_deviceContext->RSSetViewports(1, &d3d->viewport);

	// Set shader texture resource in the pixel shader.
	d3d->m_deviceContext->PSSetShaderResources(0, 1, &st.rv);
	bool mask = false;
	int after = -1;
	for (int i = 0; i < MAX_SHADERS; i++) {
		struct shaderdata11 *s = &d3d->shaders[i];
		if (s->type == SHADERTYPE_MASK_MIDDLE && s->masktexturerv) {
			d3d->m_deviceContext->PSSetShaderResources(1, 1, &s->masktexturerv);
			mask = true;
		}
		if (s->type == SHADERTYPE_AFTER)
			after = i;
	}
	if (!mask && d3d->sltexturerv) {
		d3d->m_deviceContext->PSSetShaderResources(1, 1, &d3d->sltexturerv);
	}

	// Set the shader parameters that it will use for rendering.
	if (!setmatrix(d3d, d3d->m_matrixBuffer, d3d->m_worldMatrix, d3d->m_viewMatrix, d3d->m_orthoMatrix))
		return false;

	// Put the bitmap vertex and index buffers on the graphics pipeline to prepare them for drawing.
	RenderBuffers(d3d, d3d->m_vertexBuffer, d3d->m_indexBuffer);

	// Now set the constant buffer in the vertex shader with the updated values.
	d3d->m_deviceContext->VSSetConstantBuffers(0, 1, &d3d->m_matrixBuffer);

	setpsbuffer(d3d, d3d->m_psBuffer);

	d3d->m_deviceContext->PSSetConstantBuffers(0, 1, &d3d->m_psBuffer);

	// Bind the render target view and depth stencil buffer to the output render pipeline.
	if (after >= 0) {
		d3d->m_deviceContext->OMSetRenderTargets(1, &d3d->lpPostTempTexture.rt, NULL);
	} else {
		d3d->m_deviceContext->OMSetRenderTargets(1, &d3d->m_renderTargetView, NULL);
		clearrt(d3d);
	}

	// Now render the prepared buffers with the shader.
	TextureShaderClass_RenderShader(d3d);

	if (after >= 0) {
		d3d->m_deviceContext->OMSetRenderTargets(1, &d3d->m_renderTargetView, NULL);
		clearrt(d3d);
	}

	if (after >= 0) {
		memcpy(&st, &d3d->lpPostTempTexture, sizeof(struct shadertex));
		for (int i = 0; i < MAX_SHADERS; i++) {
			struct shaderdata11 *s = &d3d->shaders[i];
			if (s->type == SHADERTYPE_AFTER) {
				settransform2(d3d, s);
				if (i == after)
					st.vp = &d3d->viewport;
				else
					st.vp = &s->viewport;
				if (!processshader(d3d, &st, s, i != after))
					return false;
			}
		}
	}
	return true;
}

static bool TextureShaderClass_Render(struct d3d11struct *d3d, int monid)
{
	renderframe(d3d);

	RenderSprite(d3d, &d3d->hwsprite, monid);

	renderoverlay(d3d, monid);

	RenderSprite(d3d, &d3d->osd, monid);

	struct d3doverlay *ov = d3d->extoverlays;
	while (ov) {
		RenderSprite(d3d, &ov->s, monid);
		ov = ov->next;
	}

	return true;
}

static void CameraClass_Render(struct d3d11struct *d3d)
{
	D3DXVECTOR3 up, position, lookAt;
	float yaw, pitch, roll;
	D3DXMATRIX rotationMatrix;

	// Setup the vector that points upwards.
	up.x = 0.0f;
	up.y = 1.0f;
	up.z = 0.0f;

	// Setup the position of the camera in the world.
	position.x = d3d->m_positionX;
	position.y = d3d->m_positionY;
	position.z = d3d->m_positionZ;

	// Setup where the camera is looking by default.
	lookAt.x = 0.0f;
	lookAt.y = 0.0f;
	lookAt.z = 1.0f;

	// Set the yaw (Y axis), pitch (X axis), and roll (Z axis) rotations in radians.
	pitch = d3d->m_rotationX * 0.0174532925f;
	yaw = d3d->m_rotationY * 0.0174532925f;
	roll = d3d->m_rotationZ * 0.0174532925f;

	// Create the rotation matrix from the yaw, pitch, and roll values.
	xD3DXMatrixRotationYawPitchRoll(&rotationMatrix, yaw, pitch, roll);

	// Transform the lookAt and up vector by the rotation matrix so the view is correctly rotated at the origin.
	xD3DXVec3TransformCoord(&lookAt, &lookAt, &rotationMatrix);
	xD3DXVec3TransformCoord(&up, &up, &rotationMatrix);

	// Translate the rotated camera position to the location of the viewer.
	lookAt = position + lookAt;

	// Finally create the view matrix from the three updated vectors.
	xD3DXMatrixLookAtLH(&d3d->m_viewMatrix, &position, &lookAt, &up);
}

static bool GraphicsClass_Render(struct d3d11struct *d3d, bool normalrender, int monid)
{
	bool result;

	setupscenecoords(d3d, normalrender, monid);

	// Generate the view matrix based on the camera's position.
	CameraClass_Render(d3d);

	// Render the bitmap with the texture shader.
	result = TextureShaderClass_Render(d3d, monid);

	if (!result)
		return false;

	return true;
}

static struct shaderdata11 *allocshaderslot(struct d3d11struct *d3d, int type)
{
	for (int i = 0; i < MAX_SHADERS; i++) {
		if (d3d->shaders[i].type == 0) {
			d3d->shaders[i].type = type;
			return &d3d->shaders[i];
		}
	}
	return NULL;
}

static bool restore(struct d3d11struct *d3d)
{
	for (int i = 0; i < MAX_FILTERSHADERS; i++) {
		if (d3d->filterd3d->gfx_filtershader[i][0]) {
			struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_BEFORE);
			if (!psEffect_LoadEffect(d3d, d3d->filterd3d->gfx_filtershader[i], s, i)) {
				freeshaderdata(s);
				d3d->filterd3d->gfx_filtershader[i][0] = changed_prefs.gf[d3d->filterd3didx].gfx_filtershader[i][0] = 0;
				break;
			}
		}
		if (d3d->filterd3d->gfx_filtermask[i][0]) {
			struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_BEFORE);
			if (!createmasktexture(d3d, d3d->filterd3d->gfx_filtermask[i], s)) {
				freeshaderdata(s);
			}
		}
	}
	if (d3d->filterd3d->gfx_filtershader[2 * MAX_FILTERSHADERS][0]) {
		struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MIDDLE);
		if (!psEffect_LoadEffect(d3d, d3d->filterd3d->gfx_filtershader[2 * MAX_FILTERSHADERS], s, 2 * MAX_FILTERSHADERS)) {
			freeshaderdata(s);
			d3d->filterd3d->gfx_filtershader[2 * MAX_FILTERSHADERS][0] = changed_prefs.gf[d3d->filterd3didx].gfx_filtershader[2 * MAX_FILTERSHADERS][0] = 0;
		}
	}
	if (d3d->filterd3d->gfx_filtermask[2 * MAX_FILTERSHADERS][0]) {
		struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_MIDDLE);
		if (!createmasktexture(d3d, d3d->filterd3d->gfx_filtermask[2 * MAX_FILTERSHADERS], s)) {
			freeshaderdata(s);
		}
	}
	for (int i = 0; i < MAX_FILTERSHADERS; i++) {
		if (d3d->filterd3d->gfx_filtershader[i + MAX_FILTERSHADERS][0]) {
			struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_AFTER);
			if (!psEffect_LoadEffect(d3d, d3d->filterd3d->gfx_filtershader[i + MAX_FILTERSHADERS], s, i + MAX_FILTERSHADERS)) {
				freeshaderdata(s);
				d3d->filterd3d->gfx_filtershader[i + MAX_FILTERSHADERS][0] = changed_prefs.gf[d3d->filterd3didx].gfx_filtershader[i + MAX_FILTERSHADERS][0] = 0;
				break;
			}
		}
		if (d3d->filterd3d->gfx_filtermask[i + MAX_FILTERSHADERS][0]) {
			struct shaderdata11 *s = allocshaderslot(d3d, SHADERTYPE_MASK_AFTER);
			if (!createmasktexture(d3d, d3d->filterd3d->gfx_filtermask[i + MAX_FILTERSHADERS], s)) {
				freeshaderdata(s);
			}
		}
	}


	createscanlines(d3d, 1);
	createmask2texture(d3d, d3d->filterd3d->gfx_filteroverlay);

	int w = d3d->m_bitmapWidth;
	int h = d3d->m_bitmapHeight;

	if (!createextratextures(d3d, d3d->m_bitmapWidthX, d3d->m_bitmapHeightX, d3d->m_screenWidth, d3d->m_screenHeight))
		return false;

	for (int i = 0; i < MAX_SHADERS; i++) {
		int w2, h2;
		int type = d3d->shaders[i].type;
		if (type == SHADERTYPE_BEFORE) {
			w2 = d3d->shaders[i].worktex_width;
			h2 = d3d->shaders[i].worktex_height;
			if (!allocextratextures(d3d, &d3d->shaders[i], w, h))
				return 0;
		} else if (type == SHADERTYPE_MIDDLE) {
			w2 = d3d->shaders[i].worktex_width;
			h2 = d3d->shaders[i].worktex_height;
		} else {
			w2 = d3d->m_screenWidth;
			h2 = d3d->m_screenHeight;
		}
		if (type == SHADERTYPE_BEFORE || type == SHADERTYPE_AFTER || type == SHADERTYPE_MIDDLE) {
			D3D11_TEXTURE3D_DESC desc;
			D3D11_MAPPED_SUBRESOURCE map;
			HRESULT hr;
			memset(&desc, 0, sizeof(desc));
			desc.Width = 256;
			desc.Height = 16;
			desc.Depth = 256;
			desc.MipLevels = 1;
			desc.Format = d3d->texformat;
			desc.Usage = D3D11_USAGE_DYNAMIC;
			desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
			desc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
			hr = d3d->m_device->CreateTexture3D(&desc, NULL, &d3d->shaders[i].lpHq2xLookupTexture);
			if (FAILED(hr)) {
				write_log(_T("D3D11 Failed to create volume texture: %08x:%d\n"), hr, i);
				return false;
			}
			hr = d3d->m_deviceContext->Map(d3d->shaders[i].lpHq2xLookupTexture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
			if (FAILED(hr)) {
				write_log(_T("D3D11 Failed to lock box of volume texture: %08x:%d\n"), hr, i);
				return false;
			}
			write_log(_T("HQ2X texture (%dx%d) (%dx%d):%d\n"), w2, h2, w, h, i);
			BuildHq2xLookupTexture(w2, h2, w, h, (unsigned char*)map.pData);
			d3d->m_deviceContext->Unmap(d3d->shaders[i].lpHq2xLookupTexture, 0);
			D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc;
			memset(&srvDesc, 0, sizeof srvDesc);
			ID3D11ShaderResourceView *rv = NULL;
			srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
			srvDesc.Texture2D.MostDetailedMip = 0;
			srvDesc.Texture2D.MipLevels = 1;
			srvDesc.Format = d3d->texformat;
			hr = d3d->m_device->CreateShaderResourceView(d3d->shaders[i].lpHq2xLookupTexture, &srvDesc, &d3d->shaders[i].lpHq2xLookupTexturerv);
			if (FAILED(hr)) {
				write_log(_T("D3D11 Failed to create volume texture resource view: %08x:%d\n"), hr, i);
				return false;
			}
		}
	}

	write_log(_T("D3D11 Shader and extra textures restored\n"));

	return true;
}

static void resizemode(struct d3d11struct *d3d, int monid);

static bool xD3D11_renderframe(int monid, int mode, bool immediate)
{
	struct amigadisplay *ad = &adisplays[monid];
	struct apmode *apm = ad->picasso_on ? &currprefs.gfx_apmode[APMODE_RTG] : &currprefs.gfx_apmode[APMODE_NATIVE];
	struct d3d11struct *d3d = &d3d11data[monid];

	d3d->frames_since_init++;

	if (mode > 0 && (mode & 2))
		slicecnt = 0;
	else if (mode < 0)
		slicecnt = slicecnt == 2 ? 0 : slicecnt;

	if (!d3d->m_swapChain)
		return false;

	if (d3d->fsmodechange)
		D3D_resize(monid, 0);

	if (d3d->invalidmode)
		return false;

	if (d3d->delayedrestore) {
		d3d->delayedrestore = false;
		restore(d3d);
	}

	if (d3d->delayedfs || !d3d->texture2d || !d3d->d3dinit_done)
		return false;

	GraphicsClass_Render(d3d, mode < 0 || (mode & 1), monid);

	if (d3d->filenotificationhandle != NULL) {
		bool notify = false;
		while (WaitForSingleObject(d3d->filenotificationhandle, 0) == WAIT_OBJECT_0) {
			if (FindNextChangeNotification(d3d->filenotificationhandle)) {
				if (d3d->frames_since_init > 50)
					notify = true;
			}
		}
		if (notify) {
			write_log(_T("D3D11 shader file modification notification.\n"));
			D3D_resize(monid, 0);
		}
	}
	if (apm->gfx_vsyncmode)
		d3d->m_deviceContext->Flush();

	return true;
}

static void xD3D11_showframe_special(int monid, int mode)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	if (d3d->invalidmode || d3d->delayedfs || !d3d->texture2d || !d3d->d3dinit_done)
		return;
	if (!d3d->m_swapChain)
		return;
	if (mode == 1)
		do_present(d3d);
	if (mode == 2)
		do_black(d3d);
}

static void xD3D11_showframe(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	if (d3d->invalidmode || d3d->delayedfs || !d3d->texture2d || !d3d->d3dinit_done)
		return;
	if (!d3d->m_swapChain)
		return;
	// Present the rendered scene to the screen.
	EndScene(d3d);
}

static void xD3D11_clear(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	if (d3d->invalidmode)
		return;
	if (!d3d->m_swapChain)
		return;

	// Setup the color to clear the buffer to.
	float color[4];
	color[0] = 0;
	color[1] = 0;
	color[2] = 0;
	color[3] = 0;
	// Bind the render target view and depth stencil buffer to the output render pipeline.
	d3d->m_deviceContext->OMSetRenderTargets(1, &d3d->m_renderTargetView, NULL);
	// Clear the back buffer.
	d3d->m_deviceContext->ClearRenderTargetView(d3d->m_renderTargetView, color);
	d3d->m_deviceContext->Flush();
	clearcnt = 0;
}


static bool xD3D11_quit(struct d3d11struct *d3d)
{
	if (quit_program != -UAE_QUIT && quit_program != UAE_QUIT)
		return false;
	if (d3d->m_swapChain && (!d3d->invalidmode || d3d->fsmode > 0)) {
		d3d->m_swapChain->SetFullscreenState(FALSE, NULL);
		FreeTextures(d3d);
	}
	d3d->fsmode = 0;
	d3d->invalidmode = true;
	d3d->fsmodechange = 0;
	return true;
}

static void xD3D11_refresh(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	if (!d3d->m_swapChain)
		return;

	createscanlines(d3d, 0);
	for (int i = 0; i < 3; i++) {
		if (xD3D11_renderframe(monid, true, true)) {
			xD3D11_showframe(monid);
		}
	}
	clearcnt = 0;
}


static bool D3D11_resize_do(struct d3d11struct *d3d, int monid)
{
	HRESULT hr;

	if (!d3d->fsresizedo)
		return false;
	if (!d3d->m_swapChain)
		return false;

	d3d->fsresizedo = false;

	write_log(_T("D3D11 resize do %d %d\n"), d3d->fsmodechange, d3d->fsmode);

	if (d3d->fsmodechange && d3d->fsmode > 0) {
		write_log(_T("D3D11_resize -> fullscreen\n"));
		ShowWindow(d3d->ahwnd, SW_SHOWNORMAL);
		hr = d3d->m_swapChain->SetFullscreenState(TRUE, d3d->outputAdapter);
		if (FAILED(hr)) {
			write_log(_T("SetFullscreenState(TRUE) failed %08X\n"), hr);
			toggle_fullscreen(d3d->num, 10);
		} else {
			d3d->fsmode = 0;
		}
		d3d->fsmodechange = 0;
		d3d->invalidmode = false;
	} else if (d3d->fsmodechange && d3d->fsmode < 0) {
		write_log(_T("D3D11_resize -> window\n"));
		hr = d3d->m_swapChain->SetFullscreenState(FALSE, NULL);
		if (FAILED(hr))
			write_log(_T("SetFullscreenState(FALSE) failed %08X\n"), hr);
		ShowWindow(d3d->ahwnd, SW_MINIMIZE);
		d3d->fsmode = 0;
		d3d->invalidmode = true;
		d3d->fsmodechange = 0;
	} else {
		d3d->fsmode = 0;
		d3d->invalidmode = false;
		d3d->fsmodechange = 0;
		write_log(_T("D3D11_resize -> none\n"));
	}

	resizemode(d3d, monid);

	write_log(_T("D3D11 resize exit\n"));
	return true;
}


static bool recheck(struct d3d11struct *d3d, int monid)
{
	bool r = false;
	if (xD3D11_quit(d3d))
		return r;
	r = D3D11_resize_do(d3d, monid);
	if (d3d->resizeretry) {
		resizemode(d3d, monid);
		return r;
	}
	if (!d3d->delayedfs)
		return r;
	xD3D11_free(d3d->num, true);
	d3d->delayedfs = 0;
	ShowWindow(d3d->ahwnd, SW_SHOWNORMAL);
	int freq = 0;
	if (!xxD3D11_init2(d3d->ahwnd, d3d->num, d3d->m_screenWidth, d3d->m_screenHeight, d3d->m_bitmapWidth2, d3d->m_bitmapHeight2, 32, &freq, d3d->dmultxh, d3d->dmultxv))
		d3d->invalidmode = true;
	return false;
}

static bool xD3D11_alloctexture(int monid, int w, int h)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	bool v;

	if (w == 0 || h == 0) {
		return false;
	}
	if (w < 0 || h < 0) {
		if (d3d->m_bitmapWidth == -w && d3d->m_bitmapHeight == -h && d3d->texture2d) {
			return true;
		}
		return false;
	}

	recheck(d3d, monid);

	if (d3d->invalidmode)
		return false;

	d3d->m_bitmapWidth = w;
	d3d->m_bitmapHeight = h;
	d3d->m_bitmapWidth2 = d3d->m_bitmapWidth;
	d3d->m_bitmapHeight2 = d3d->m_bitmapHeight;
	d3d->dmult = S2X_getmult(monid);
	d3d->m_bitmapWidthX = d3d->m_bitmapWidth * d3d->dmultxh;
	d3d->m_bitmapHeightX = d3d->m_bitmapHeight * d3d->dmultxv;

	v = CreateTexture(d3d);
	if (!v)
		return false;

	if (d3d->reloadshaders) {
		d3d->reloadshaders = false;
		restore(d3d);
	} else {
		d3d->delayedrestore = true;
	}

	setupscenecoords(d3d, true, monid);

	changed_prefs.leds_on_screen |= STATUSLINE_TARGET;
	currprefs.leds_on_screen |= STATUSLINE_TARGET;

	return true;
}

static uae_u8 *xD3D11_locktexture(int monid, int *pitch, int *width, int *height, int fullupdate)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	// texture allocation must not cause side-effects

	if (d3d->invalidmode || !d3d->texture2d)
		return NULL;

	D3D11_MAPPED_SUBRESOURCE map;
	HRESULT hr = d3d->m_deviceContext->Map(d3d->texture2dstaging, 0, D3D11_MAP_WRITE, 0, &map);

	if (FAILED(hr)) {
		write_log(_T("D3D11 Map() %08x\n"), hr);
		return NULL;
	}
	*pitch = map.RowPitch;
	if (height)
		*height = d3d->m_bitmapHeight;
	if (width)
		*width = d3d->m_bitmapWidth;
	d3d->texturelocked++;
	return (uae_u8*)map.pData;
}

static void xD3D11_unlocktexture(int monid, int y_start, int y_end)
{
	struct AmigaMonitor *mon = &AMonitors[monid];
	struct d3d11struct *d3d = &d3d11data[monid];

	if (!d3d->texturelocked || d3d->invalidmode)
		return;
	d3d->texturelocked--;

	d3d->m_deviceContext->Unmap(d3d->texture2dstaging, 0);

	if (y_start < -1 || y_end < -1) {
		return;
	}

	bool rtg = WIN32GFX_IsPicassoScreen(mon);
	if (((currprefs.leds_on_screen & STATUSLINE_CHIPSET) && !rtg) || ((currprefs.leds_on_screen & STATUSLINE_RTG) && rtg)) {
		d3d->osd.enabled = true;
		updateleds(d3d);
	} else {
		d3d->osd.enabled = false;
	}
	if (y_start < 0) {
		d3d->m_deviceContext->CopyResource(d3d->texture2d, d3d->texture2dstaging);
	} else {
		D3D11_BOX box = { 0 };
		box.right = d3d->m_bitmapWidth;
		box.top = y_start;
		box.bottom = y_end;
		box.back = 1;
		d3d->m_deviceContext->CopySubresourceRegion(d3d->texture2d, 0, 0, y_start, 0, d3d->texture2dstaging, 0, &box);
	}
}

static void xD3D11_flushtexture(int monid, int miny, int maxy)
{
	struct d3d11struct *d3d = &d3d11data[monid];
}

static void xD3D11_restore(int monid, bool checkonly)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	recheck(d3d, monid);
}

static void xD3D11_vblank_reset(double freq)
{
}

static int xD3D11_canshaders(void)
{
	return (can_D3D11(false) & 4) != 0;
}

static int xD3D11_goodenough(void)
{
	return 1;
}

static void xD3D11_change(int monid, int temp)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	clearcnt = 0;
}

static void resizemode(struct d3d11struct *d3d, int monid)
{
	d3d->resizeretry = false;
	if (!d3d->invalidmode) {
		write_log(_T("D3D11 resizemode start\n"));
		freed3d(d3d);
		int fs = isfs(d3d);
		setswapchainmode(d3d, fs);
		write_log(_T("D3D11 resizemode %dx%d, %dx%d %d %08x FS=%d\n"), d3d->m_screenWidth, d3d->m_screenHeight, d3d->m_bitmapWidth, d3d->m_bitmapHeight,
			d3d->swapChainDesc.BufferCount, d3d->swapChainDesc.Flags, fs);
		HRESULT hr = d3d->m_swapChain->ResizeBuffers(d3d->swapChainDesc.BufferCount, d3d->m_screenWidth, d3d->m_screenHeight, d3d->scrformat, d3d->swapChainDesc.Flags);
		if (FAILED(hr)) {
			write_log(_T("ResizeBuffers %08x\n"), hr);
			if (!device_error(d3d)) {
				d3d->resizeretry = true;
			}
		}
		if (!d3d->invalidmode) {
			if (!initd3d(d3d, monid)) {
				xD3D11_free(d3d->num, true);
				gui_message(_T("D3D11 Resize failed."));
				d3d->invalidmode = true;
			} else {
				xD3D11_alloctexture(d3d->num, d3d->m_bitmapWidth, d3d->m_bitmapHeight);
			}
		}
		write_log(_T("D3D11 resizemode end\n"));
	}
}

static void xD3D11_resize(int monid, int activate)
{
	static int recursive;
	struct d3d11struct *d3d = &d3d11data[monid];

	write_log(_T("D3D11_resize %d %d %d (%d)\n"), activate, d3d->fsmodechange, d3d->fsmode, d3d->guimode);

	if (d3d->delayedfs)
		return;

	if (d3d->guimode && isfullscreen() > 0)
		return;

	if (quit_program == -UAE_QUIT || quit_program == UAE_QUIT)
		return;

	if (activate) {
		d3d->fsmode = activate;
		d3d->fsmodechange = true;
		ShowWindow(d3d->ahwnd, d3d->fsmode > 0 ? SW_SHOWNORMAL : SW_MINIMIZE);
		write_log(_T("D3D11 resize activate %d\n"), activate);
	}

	d3d->fsresizedo = true;
}

static void xD3D11_guimode(int monid, int guion)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	d3d->reloadshaders = true;

	if (isfullscreen() <= 0)
		return;
	
	write_log(_T("fs guimode %d\n"), guion);
	d3d->guimode = guion;
	if (guion > 0) {
		xD3D11_free(d3d->num, true);
		ShowWindow(d3d->ahwnd, SW_HIDE);
	} else if (guion == 0) {
		d3d->delayedfs = 1;
	}
	write_log(_T("fs guimode end\n"));
}

static int xD3D11_isenabled(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	return d3d->m_device != NULL ? 2 : 0;
}

static bool xD3D_getvblankpos(int *vp)
{
	*vp = 0;
	return false;
}

static HDC xD3D_getDC(int monid, HDC hdc)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	IDXGISurface1 *g_pSurface1 = NULL;
	HRESULT hr;

	if (hdc) {
		RECT empty = { 0 };
		g_pSurface1->ReleaseDC(&empty);
		g_pSurface1->Release();
		return NULL;
	} else {
		HDC g_hDC;
		//Setup the device and and swapchain
		hr = d3d->m_swapChain->GetBuffer(0, __uuidof(IDXGISurface1), (void**)&g_pSurface1);
		if (FAILED(hr)) {
			write_log(_T("GetDC GetBuffer() %08x\n"), hr);
			return NULL;
		}
		hr = g_pSurface1->GetDC(FALSE, &g_hDC);
		if (FAILED(hr)) {
			write_log(_T("GetDC GetDC() %08x\n"), hr);
			g_pSurface1->Release();
			return NULL;
		}
		d3d->hdc_surface = g_pSurface1;
		return g_hDC;
	}
}

bool D3D11_capture(int monid, void **data, int *w, int *h, int *d, int *pitch, bool rendertarget)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	HRESULT hr;
	ID3D11Texture2D *screenshottexture = rendertarget ? d3d->screenshottexturert : d3d->screenshottexturetx;

	if (!screenshottexture)
		return false;

	if (!w || !h) {
		d3d->m_deviceContext->Unmap(screenshottexture, 0);
		return true;
	} else {
		D3D11_MAPPED_SUBRESOURCE map;
		ID3D11Resource* pSurface = NULL;
		ID3D11Resource* pSurfaceRelease = NULL;
		if (rendertarget) {
			d3d->m_renderTargetView->GetResource(&pSurface);
			pSurfaceRelease = pSurface;
		} else {
			pSurface = d3d->texture2dstaging;
		}
		if (pSurface) {
			D3D11_TEXTURE2D_DESC desc;
			d3d->m_deviceContext->CopyResource(screenshottexture, pSurface);
			screenshottexture->GetDesc(&desc);
			hr = d3d->m_deviceContext->Map(screenshottexture, 0, D3D11_MAP_READ, 0, &map);
			if (FAILED(hr)) {
				write_log(_T("Screenshot DeviceContext->Map() failed %08x\n"), hr);
				return false;
			}
			if (pSurfaceRelease) {
				pSurfaceRelease->Release();
			}
			*data = map.pData;
			*pitch = map.RowPitch;
			*w = desc.Width;
			*h = desc.Height;
			*d = d3d->hdr ? 36 : 24;
			return true;
		} else {
			write_log(_T("Screenshot RenderTargetView->GetResource() failed\n"));
		}
	}
	return false;
}

static void updatecursorsurface(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	struct d3d11sprite *sp = &d3d->hwsprite;
	int cx = (int)d3d->cursor_x;
	int cy = (int)d3d->cursor_y;
	int width = sp->width;
	int height = sp->height;
	int bw = d3d->m_bitmapWidth;
	int bh = d3d->m_bitmapHeight;

	if (sp->texture == NULL) {
		return;
	}

	if (sp->updated && cx >= 0 && cy >= 0 && cx + width <= bw && cy + height <= bh) {
		return;
	}

	sp->updated = false;
	sp->empty = false;
	D3D11_MAPPED_SUBRESOURCE map;
	HRESULT hr = d3d->m_deviceContext->Map(sp->texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (SUCCEEDED(hr)) {
		int pitch = map.RowPitch;
		uae_u8 *b = (uae_u8 *)map.pData;
		uae_u8 *s = sp->texbuf;
		for (int h = 0; h < sp->height; h++) {
			int w = width;
			int x = 0;
			if (cx + w > bw) {
				w -= (cx + w) - bw;
			}
			if (cx < 0) {
				x = -cx;
				w -= -cx;
			}
			if (w <= 0 || cy + h > bh) {
				memset(b, 0, width * 4);
			} else {
				if (x > 0) {
					memset(b, 0, x * 4);
				}
				memcpy(b + x * 4, s + x * 4, w * 4);
				if (w < width) {
					memset(b + w * 4, 0, (width - w) * 4);
				}
			}
			b += pitch;
			s += width * 4;
		}
		d3d->m_deviceContext->Unmap(d3d->hwsprite.texture, 0);

		if (cx >= 0 && cy >= 0 && cx + width <= bw && cy + height <= bh) {
			sp->updated = true;
		}
	}
}

static bool xD3D_setcursor(int monid, int x, int y, int width, int height, float mx, float my, bool visible, bool noscale)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	struct d3d11sprite *sp = &d3d->hwsprite;

	//write_log(_T("setcursor %d %dx%d %dx%d %d %d\n"), monid, x, y, width, height, visible, noscale);

	if (width < 0 || height < 0)
		return true;

	if (width && height) {
		d3d->cursor_offset2_x = d3d->cursor_offset_x;
		d3d->cursor_offset2_y = d3d->cursor_offset_y;
		d3d->cursor_x = (float)x;
		d3d->cursor_y = (float)y;
	} else {
		d3d->cursor_x = d3d->cursor_y = 0;
		d3d->cursor_offset2_x = d3d->cursor_offset2_y = 0;
	}

	//write_log(_T("%.1fx%.1f %dx%d\n"), d3d->cursor_x, d3d->cursor_y, d3d->cursor_offset2_x, d3d->cursor_offset2_y);

	float multx = d3d->xmult;
	float multy = d3d->ymult;

	setspritescaling(sp, mx * multx, my * multy);

	sp->x = d3d->cursor_x * multx;
	sp->y = d3d->cursor_y * multy;
	sp->x += d3d->cursor_offset2_x * multx;
	sp->y += d3d->cursor_offset2_y * multy;

	//write_log(_T("-> %.1fx%.1f %.1f %.1f\n"), d3d->hwsprite.x, d3d->hwsprite.y, multx, multy);

	d3d->cursor_v = visible;
	sp->enabled = visible;
	sp->bilinear = d3d->filterd3d->gfx_filter_bilinear;

	updatecursorsurface(monid);

	return true;
}

static uae_u8 *xD3D_setcursorsurface(int monid, bool query, int *pitch)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	d3d->hwsprite.updated = false;
	if (query) {
		return d3d->hwsprite.empty ? NULL : d3d->hwsprite.texbuf;
	}
	if (!d3d->hwsprite.texbuf) {
		return NULL;
	}
	if (pitch) {
		*pitch = d3d->hwsprite.width * 4;
		return d3d->hwsprite.texbuf;
	}
	return NULL;
}

static bool xD3D11_getscalerect(int monid, float *mx, float *my, float *sx, float *sy, int width, int height)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	struct vidbuf_description *vidinfo = &adisplays[monid].gfxvidinfo;
	if (!d3d->mask2texture.enabled)
		return false;

	float mw = (float)(d3d->mask2rect.right - d3d->mask2rect.left);
	float mh = (float)(d3d->mask2rect.bottom - d3d->mask2rect.top);

	float mxt = (float)mw / width;
	float myt = (float)mh / height;

	*mx = d3d->mask2texture_minusx / mxt;
	*my = d3d->mask2texture_minusy / myt;

	*sx = -((d3d->mask2texture_ww - d3d->mask2rect.right) - (d3d->mask2rect.left)) / 2;
	*sy = -((d3d->mask2texture_wh - d3d->mask2rect.bottom) - (d3d->mask2rect.top)) / 2;

	*sx /= mxt;
	*sy /= myt;

	return true;
}

static bool xD3D11_run(int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];

	if (xD3D11_quit(d3d))
		return false;
	if (recheck(d3d, monid))
		return true;
	return D3D11_resize_do(d3d, monid);
}

static bool xD3D11_extoverlay(struct extoverlay *ext, int monid)
{
	struct d3d11struct *d3d = &d3d11data[monid];
	struct d3doverlay *ov, *ovprev, *ov2;
	struct d3d11sprite *s;
	D3D11_MAPPED_SUBRESOURCE map;
	HRESULT hr;

	s = NULL;
	ov = d3d->extoverlays;
	ovprev = NULL;
	while (ov) {
		if (ov->id == ext->idx) {
			s = &ov->s;
			break;
		}
		ovprev = ov;
		ov = ov->next;
	}

	if (!s && (ext->width <= 0 || ext->height <= 0))
		return false;

	if (!ext->data && s && (ext->width == 0 || ext->height == 0)) {
		s->x = (float)ext->xpos;
		s->y = (float)ext->ypos;
		return true;
	}

	if (ov && s) {
		if (ovprev) {
			ovprev->next = ov->next;
		} else {
			d3d->extoverlays = ov->next;
		}
		freesprite(s);
		xfree(ov);
		if (ext->width <= 0 || ext->height <= 0)
			return true;
	}

	if (ext->width <= 0 || ext->height <= 0)
		return false;

	ov = xcalloc(d3doverlay, 1);
	s = &ov->s;

	if (!allocsprite(d3d, s, ext->width, ext->height, true, false, false)) {
		xfree(ov);
		return false;
	}

	ov->id = ext->idx;

	ov2 = d3d->extoverlays;
	ovprev = NULL;
	for(;;) {
		if (ov2 == NULL || ov2->id >= ov->id) {
			if (ov2 == d3d->extoverlays) {
				d3d->extoverlays = ov;
				ov->next = ov2;
			} else {
				ov->next = ovprev->next;
				ovprev->next = ov;
			}
			break;
		}
		ovprev = ov2;
		ov2 = ov2->next;
	}

	s->enabled = true;
	s->x = (float)ext->xpos;
	s->y = (float)ext->ypos;

	hr = d3d->m_deviceContext->Map(s->texture, 0, D3D11_MAP_WRITE_DISCARD, 0, &map);
	if (SUCCEEDED(hr)) {
		if (d3d->hdr) {
			for (int y = 0; y < s->height; y++) {
				uae_u32 *dp = (uae_u32*)((uae_u8 *)map.pData + y * map.RowPitch);
				uae_u32 *sp = (uae_u32*)(ext->data + y * ext->width * 4);
				for (int x = 0; x < s->width; x++) {
					uae_u32 v = *sp++;
					uae_u16 t = (v >> 24) * 10 / 7;
					if (t > 255) {
						t = 255;
					}
					*dp++ = (v & 0xffffff) | (t << 24);
				}
			}

		} else {
			for (int y = 0; y < s->height; y++) {
				memcpy((uae_u8*)map.pData + y * map.RowPitch, ext->data + y * ext->width * 4, ext->width * 4);
			}
		}
		d3d->m_deviceContext->Unmap(s->texture, 0);
	}

	return true;
}

void d3d11_select(void)
{
	D3D_free = xD3D11_free;
	D3D_init = xD3D11_init;
	D3D_renderframe = xD3D11_renderframe;
	D3D_alloctexture = xD3D11_alloctexture;
	D3D_refresh = xD3D11_refresh;
	D3D_restore = xD3D11_restore;

	D3D_locktexture = xD3D11_locktexture;
	D3D_unlocktexture = xD3D11_unlocktexture;
	D3D_flushtexture = xD3D11_flushtexture;

	D3D_showframe = xD3D11_showframe;
	D3D_showframe_special = xD3D11_showframe_special;
	D3D_guimode = xD3D11_guimode;
	D3D_getDC = xD3D_getDC;
	D3D_isenabled = xD3D11_isenabled;
	D3D_clear = xD3D11_clear;
	D3D_canshaders = xD3D11_canshaders;
	D3D_goodenough = xD3D11_goodenough;
	D3D_setcursor = xD3D_setcursor;
	D3D_setcursorsurface = xD3D_setcursorsurface;
	D3D_getrefreshrate = xD3D_getrefreshrate;
	D3D_resize = xD3D11_resize;
	D3D_change = xD3D11_change;
	D3D_getscalerect = xD3D11_getscalerect;
	D3D_run = xD3D11_run;
	D3D_debug = xD3D11_debug;
	D3D_led = xD3D11_led;
	D3D_getscanline = NULL;
	D3D_extoverlay = xD3D11_extoverlay;
	D3D_paint = NULL;
}

void d3d_select(struct uae_prefs *p)
{
	for (int i = 0; i < MAX_AMIGAMONITORS; i++) {
		d3d11data[i].num = i;
	}
	if (p->gfx_api == 0)
		gdi_select();
	else if (p->gfx_api >= 2)
		d3d11_select();
	else
		d3d9_select();
}
