/**************************************************************************
 *
 * Copyright 2010 Luca Barbieri
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Software, and to
 * permit persons to whom the Software is furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice (including the
 * next paragraph) shall be included in all copies or substantial
 * portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 **************************************************************************/

DEBUG_GET_ONCE_BOOL_OPTION(dump_shaders, "D3D1X_DUMP_SHADERS", FALSE);

/* These cap sets are much more correct than the ones in u_caps.c */
/* TODO: it seems cube levels should be the same as 2D levels */

/* DX 9_1 */
static unsigned caps_dx_9_1[] = {
	UTIL_CHECK_INT(MAX_RENDER_TARGETS, 1),
	UTIL_CHECK_INT(MAX_TEXTURE_2D_LEVELS, 12),	/* 2048 */
	UTIL_CHECK_INT(MAX_TEXTURE_3D_LEVELS, 8),	 /* 256 */
	UTIL_CHECK_INT(MAX_TEXTURE_CUBE_LEVELS, 10), /* 512 */
	UTIL_CHECK_TERMINATE
};

/* DX 9_2 */
static unsigned caps_dx_9_2[] = {
	UTIL_CHECK_CAP(OCCLUSION_QUERY),
	UTIL_CHECK_CAP(TWO_SIDED_STENCIL),
	UTIL_CHECK_CAP(TEXTURE_MIRROR_CLAMP),
	UTIL_CHECK_CAP(BLEND_EQUATION_SEPARATE),
	UTIL_CHECK_INT(MAX_RENDER_TARGETS, 1),
	UTIL_CHECK_INT(MAX_TEXTURE_2D_LEVELS, 12),	/* 2048 */
	UTIL_CHECK_INT(MAX_TEXTURE_3D_LEVELS, 9),	 /* 256 */
	UTIL_CHECK_INT(MAX_TEXTURE_CUBE_LEVELS, 10), /* 512 */
	UTIL_CHECK_TERMINATE
};

/* DX 9_3 */
static unsigned caps_dx_9_3[] = {
	UTIL_CHECK_CAP(OCCLUSION_QUERY),
	UTIL_CHECK_CAP(TWO_SIDED_STENCIL),
	UTIL_CHECK_CAP(TEXTURE_MIRROR_CLAMP),
	UTIL_CHECK_CAP(BLEND_EQUATION_SEPARATE),
	UTIL_CHECK_CAP(SM3),
	//UTIL_CHECK_CAP(INSTANCING),
	UTIL_CHECK_CAP(OCCLUSION_QUERY),
	UTIL_CHECK_INT(MAX_RENDER_TARGETS, 4),
	UTIL_CHECK_INT(MAX_TEXTURE_2D_LEVELS, 13),	/* 4096 */
	UTIL_CHECK_INT(MAX_TEXTURE_3D_LEVELS, 9),	 /* 256 */
	UTIL_CHECK_INT(MAX_TEXTURE_CUBE_LEVELS, 10), /* 512 */
	UTIL_CHECK_TERMINATE
};


// this is called "screen" because in the D3D10 case it's only part of the device
template<bool threadsafe>
struct GalliumD3D11ScreenImpl : public GalliumD3D11Screen
{
	D3D_FEATURE_LEVEL feature_level;
	int format_support[PIPE_FORMAT_COUNT];
	unsigned creation_flags;
	unsigned exception_mode;
	maybe_mutex_t<threadsafe> mutex;

/* TODO: Direct3D 11 specifies that fine-grained locking should be used if the driver supports it.
 * Right now, I don't trust Gallium drivers to get this right.
 */
#define SYNCHRONIZED lock_t<maybe_mutex_t<threadsafe> > lock_(mutex)

	GalliumD3D11ScreenImpl(struct pipe_screen* screen, struct pipe_context* immediate_pipe, BOOL owns_immediate_pipe,unsigned creation_flags, IDXGIAdapter* adapter)
	: GalliumD3D11Screen(screen, immediate_pipe, adapter), creation_flags(creation_flags)
	{
		memset(&screen_caps, 0, sizeof(screen_caps));
		screen_caps.gs = screen->get_shader_param(screen, PIPE_SHADER_GEOMETRY, PIPE_SHADER_CAP_MAX_INSTRUCTIONS) > 0;
		screen_caps.so = !!screen->get_param(screen, PIPE_CAP_STREAM_OUTPUT);
		screen_caps.queries = screen->get_param(screen, PIPE_CAP_OCCLUSION_QUERY);
		screen_caps.render_condition = screen_caps.queries;
		for(unsigned i = 0; i < PIPE_SHADER_TYPES; ++i)
			screen_caps.constant_buffers[i] = screen->get_shader_param(screen, i, PIPE_SHADER_CAP_MAX_CONST_BUFFERS);
		screen_caps.stages = 0;
		for(unsigned i = 0; i < PIPE_SHADER_TYPES; ++i)
		{
			if(!screen->get_shader_param(screen, i, PIPE_SHADER_CAP_MAX_INSTRUCTIONS))
				break;
			screen_caps.stages = i + 1;
		}

		screen_caps.stages_with_sampling = (1 << screen_caps.stages) - 1;
		if(!screen->get_param(screen, PIPE_CAP_MAX_VERTEX_TEXTURE_UNITS))
			screen_caps.stages_with_sampling &=~ (1 << PIPE_SHADER_VERTEX);

		memset(format_support, 0xff, sizeof(format_support));

		float default_level;
		/* don't even attempt to autodetect D3D10 level support, since it's just not fully implemented yet */
		if(util_check_caps(screen, caps_dx_9_3))
			default_level = 9.3;
		else if(util_check_caps(screen, caps_dx_9_2))
			default_level = 9.2;
		else if(util_check_caps(screen, caps_dx_9_1))
			default_level = 9.1;
		else
		{
			_debug_printf("Warning: driver does not even meet D3D_FEATURE_LEVEL_9_1 features, advertising it anyway!\n");
			default_level = 9.1;
		}

		char default_level_name[64];
		sprintf(default_level_name, "%.1f", default_level);
		float feature_level_number = atof(debug_get_option("D3D11_FEATURE_LEVEL", default_level_name));
		if(!feature_level_number)
			feature_level_number = default_level;

#if API >= 11
		if(feature_level_number >= 11.0f)
			feature_level = D3D_FEATURE_LEVEL_11_0;
		else
#endif
		if(feature_level_number >= 10.1f)
			feature_level = D3D_FEATURE_LEVEL_10_1;
		else if(feature_level_number >= 10.0f)
			feature_level = D3D_FEATURE_LEVEL_10_0;
		else if(feature_level_number >= 9.3f)
			feature_level = D3D_FEATURE_LEVEL_9_3;
		else if(feature_level_number >= 9.2f)
			feature_level = D3D_FEATURE_LEVEL_9_2;
		else
			feature_level = D3D_FEATURE_LEVEL_9_1;

#if API >= 11
		immediate_context = GalliumD3D11ImmediateDeviceContext_Create(this, immediate_pipe, owns_immediate_pipe);
		// release to the reference to ourselves that the immediate context took, to avoid a garbage cycle
		immediate_context->Release();
#endif
	}

	~GalliumD3D11ScreenImpl()
	{
#if API >= 11
		GalliumD3D11ImmediateDeviceContext_Destroy(immediate_context);
#endif
	}

	virtual D3D_FEATURE_LEVEL STDMETHODCALLTYPE GetFeatureLevel(void)
	{
		return feature_level;
	}

	virtual unsigned STDMETHODCALLTYPE GetCreationFlags(void)
	{
		return creation_flags;
	}

	virtual HRESULT STDMETHODCALLTYPE GetDeviceRemovedReason(void)
	{
		return S_OK;
	}

#if API >= 11
	virtual void STDMETHODCALLTYPE GetImmediateContext(
		ID3D11DeviceContext **out_immediate_context)
	{
		immediate_context->AddRef();
		*out_immediate_context = immediate_context;
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE SetExceptionMode(unsigned RaiseFlags)
	{
		exception_mode = RaiseFlags;
		return S_OK;
	}

	virtual unsigned STDMETHODCALLTYPE GetExceptionMode(void)
	{
		return exception_mode;
	}

	virtual HRESULT STDMETHODCALLTYPE CheckCounter(
		const D3D11_COUNTER_DESC *desc,
		D3D11_COUNTER_TYPE *type,
		unsigned *active_counters,
		LPSTR sz_name,
		unsigned *name_length,
		LPSTR sz_units,
		unsigned *units_length,
		LPSTR sz_description,
		unsigned *description_length)
	{
		return E_NOTIMPL;
	}

	virtual void STDMETHODCALLTYPE CheckCounterInfo(
		D3D11_COUNTER_INFO *counter_info)
	{
		/* none supported at the moment */
		counter_info->LastDeviceDependentCounter = (D3D11_COUNTER)0;
		counter_info->NumDetectableParallelUnits = 1;
		counter_info->NumSimultaneousCounters = 0;
	}

#if API >= 11
	virtual HRESULT STDMETHODCALLTYPE CheckFeatureSupport(
		D3D11_FEATURE feature,
		void *out_feature_support_data,
		unsigned feature_support_data_size)
	{
		SYNCHRONIZED;

		switch(feature)
		{
			case D3D11_FEATURE_THREADING:
			{
				D3D11_FEATURE_DATA_THREADING* data = (D3D11_FEATURE_DATA_THREADING*)out_feature_support_data;
				if(feature_support_data_size != sizeof(*data))
					return E_INVALIDARG;

				data->DriverCommandLists = FALSE;
				data->DriverConcurrentCreates = FALSE;
				return S_OK;
			}
			case D3D11_FEATURE_DOUBLES:
			{
				D3D11_FEATURE_DATA_DOUBLES* data = (D3D11_FEATURE_DATA_DOUBLES*)out_feature_support_data;
				if(feature_support_data_size != sizeof(*data))
					return E_INVALIDARG;

				data->DoublePrecisionFloatShaderOps = FALSE;
				return S_OK;
			}
			case D3D11_FEATURE_FORMAT_SUPPORT:
			{
				D3D11_FEATURE_DATA_FORMAT_SUPPORT* data = (D3D11_FEATURE_DATA_FORMAT_SUPPORT*)out_feature_support_data;
				if(feature_support_data_size != sizeof(*data))
					return E_INVALIDARG;

				return this->CheckFormatSupport(data->InFormat, &data->OutFormatSupport);
			}
			case D3D11_FEATURE_FORMAT_SUPPORT2:
			{
				D3D11_FEATURE_DATA_FORMAT_SUPPORT* data = (D3D11_FEATURE_DATA_FORMAT_SUPPORT*)out_feature_support_data;
				if(feature_support_data_size != sizeof(*data))
					return E_INVALIDARG;

				data->OutFormatSupport = 0;
				/* TODO: should this be S_OK? */
				return E_INVALIDARG;
			}
			case D3D11_FEATURE_D3D10_X_HARDWARE_OPTIONS:
			{
				D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS* data = (D3D11_FEATURE_DATA_D3D10_X_HARDWARE_OPTIONS*)out_feature_support_data;
				if(feature_support_data_size != sizeof(*data))
					return E_INVALIDARG;

				data->ComputeShaders_Plus_RawAndStructuredBuffers_Via_Shader_4_x = FALSE;
				return S_OK;
			}
			default:
				return E_INVALIDARG;
		}
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE CheckFormatSupport(
		DXGI_FORMAT dxgi_format,
		unsigned *out_format_support
	)
	{
		SYNCHRONIZED;

		/* TODO: MSAA, advanced features */
		pipe_format format = dxgi_to_pipe_format[dxgi_format];
		if(!format)
			return E_INVALIDARG;

		int support = format_support[format];
		if(support < 0)
		{
			support = 0;
			unsigned buffer = D3D11_FORMAT_SUPPORT_BUFFER | D3D11_FORMAT_SUPPORT_IA_VERTEX_BUFFER | D3D11_FORMAT_SUPPORT_IA_INDEX_BUFFER;
			unsigned sampler_view = D3D11_FORMAT_SUPPORT_SHADER_SAMPLE | D3D11_FORMAT_SUPPORT_MIP | D3D11_FORMAT_SUPPORT_MIP_AUTOGEN;
			if(util_format_is_depth_or_stencil(format))
				sampler_view |= D3D11_FORMAT_SUPPORT_SHADER_SAMPLE_COMPARISON;

			/* TODO: do this properly when Gallium drivers actually support index/vertex format queries */
                        if(screen->is_format_supported(screen, format, PIPE_BUFFER, 0, PIPE_BIND_VERTEX_BUFFER)
                                || (screen->is_format_supported(screen, format, PIPE_BUFFER, 0, PIPE_BIND_INDEX_BUFFER)
				|| format == PIPE_FORMAT_R8_UNORM))
				support |= buffer;
                        if(screen->is_format_supported(screen, format, PIPE_BUFFER, 0, PIPE_BIND_STREAM_OUTPUT))
				support |= buffer | D3D11_FORMAT_SUPPORT_SO_BUFFER;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_1D, 0, PIPE_BIND_SAMPLER_VIEW))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE1D | sampler_view;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_2D, 0, PIPE_BIND_SAMPLER_VIEW))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE2D | sampler_view;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_CUBE, 0, PIPE_BIND_SAMPLER_VIEW))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE2D | sampler_view;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_3D, 0, PIPE_BIND_SAMPLER_VIEW))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE3D | sampler_view;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_2D, 0, PIPE_BIND_RENDER_TARGET))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_RENDER_TARGET | D3D11_FORMAT_SUPPORT_BLENDABLE;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_2D, 0, PIPE_BIND_DEPTH_STENCIL))
				support |= D3D11_FORMAT_SUPPORT_TEXTURE2D | D3D11_FORMAT_SUPPORT_DEPTH_STENCIL;
                        if(screen->is_format_supported(screen, format, PIPE_TEXTURE_2D, 0, PIPE_BIND_DISPLAY_TARGET))
				support |= D3D11_FORMAT_SUPPORT_DISPLAY;
			format_support[format] = support;
		}
		*out_format_support = support;
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CheckMultisampleQualityLevels(
		DXGI_FORMAT format,
		unsigned sample_count,
		unsigned *pcount
	)
	{
		SYNCHRONIZED;

		if(sample_count == 1)
			*pcount = 1;
		else
			*pcount = 0;
		return S_OK;
	}

	template<typename T, typename U>
	bool convert_blend_state(T& to, const U& from, unsigned BlendEnable, unsigned RenderTargetWriteMask)
	{
		if(invalid(0
			|| from.SrcBlend >= D3D11_BLEND_COUNT
			|| from.SrcBlendAlpha >= D3D11_BLEND_COUNT
			|| from.DestBlend >= D3D11_BLEND_COUNT
			|| from.DestBlendAlpha >= D3D11_BLEND_COUNT
			|| from.BlendOp >= 6
			|| from.BlendOpAlpha >= 6
			|| !from.BlendOp
			|| !from.BlendOpAlpha
		))
			return false;

		to.blend_enable = BlendEnable;

		to.rgb_func = from.BlendOp - 1;
		to.alpha_func = from.BlendOpAlpha - 1;

		to.rgb_src_factor = d3d11_to_pipe_blend[from.SrcBlend];
		to.alpha_src_factor = d3d11_to_pipe_blend[from.SrcBlendAlpha];
		to.rgb_dst_factor = d3d11_to_pipe_blend[from.DestBlend];
		to.alpha_dst_factor = d3d11_to_pipe_blend[from.DestBlendAlpha];

		to.colormask = RenderTargetWriteMask & 0xf;
		return true;
	}

#if API >= 11
	virtual HRESULT STDMETHODCALLTYPE CreateBlendState(
		const D3D11_BLEND_DESC *blend_state_desc,
		ID3D11BlendState **out_blend_state
	)
#else
	virtual HRESULT STDMETHODCALLTYPE CreateBlendState1(
		const D3D10_BLEND_DESC1 *blend_state_desc,
		ID3D10BlendState1 **out_blend_state
	)
#endif
	{
		SYNCHRONIZED;

		pipe_blend_state state;
		memset(&state, 0, sizeof(state));
		state.alpha_to_coverage = !!blend_state_desc->AlphaToCoverageEnable;
		state.independent_blend_enable = !!blend_state_desc->IndependentBlendEnable;
		assert(PIPE_MAX_COLOR_BUFS >= 8);
		for(unsigned i = 0; i < 8; ++i)
		{
			 if(!convert_blend_state(
					 state.rt[i],
					 blend_state_desc->RenderTarget[i],
					 blend_state_desc->RenderTarget[i].BlendEnable,
					 blend_state_desc->RenderTarget[i].RenderTargetWriteMask))
				 return E_INVALIDARG;
		}

		if(!out_blend_state)
			return S_FALSE;

		void* object = immediate_pipe->create_blend_state(immediate_pipe, &state);
		if(!object)
			return E_FAIL;

		*out_blend_state = new GalliumD3D11BlendState(this, object, *blend_state_desc);
		return S_OK;
	}

#if API < 11
	virtual HRESULT STDMETHODCALLTYPE CreateBlendState(
		const D3D10_BLEND_DESC *blend_state_desc,
		ID3D10BlendState **out_blend_state
	)
	{
		SYNCHRONIZED;

		pipe_blend_state state;
		memset(&state, 0, sizeof(state));
		state.alpha_to_coverage = !!blend_state_desc->AlphaToCoverageEnable;
		assert(PIPE_MAX_COLOR_BUFS >= 8);
		for(unsigned i = 0; i < 8; ++i)
		{
			if(!convert_blend_state(
				state.rt[i],
				*blend_state_desc,
				blend_state_desc->BlendEnable[i],
				blend_state_desc->RenderTargetWriteMask[i]))
				return E_INVALIDARG;
		}

		for(unsigned i = 1; i < 8; ++i)
		{
			if(memcmp(&state.rt[0], &state.rt[i], sizeof(state.rt[0])))
			{
				state.independent_blend_enable = TRUE;
				break;
			}
		}

		void* object = immediate_pipe->create_blend_state(immediate_pipe, &state);
		if(!object)
			return E_FAIL;

		*out_blend_state = new GalliumD3D11BlendState(this, object, *blend_state_desc);
		return S_OK;
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE CreateDepthStencilState(
		const D3D11_DEPTH_STENCIL_DESC *depth_stencil_state_desc,
		ID3D11DepthStencilState **depth_stencil_state
	)
	{
		SYNCHRONIZED;

		pipe_depth_stencil_alpha_state state;
		memset(&state, 0, sizeof(state));
		state.depth.enabled = !!depth_stencil_state_desc->DepthEnable;
		state.depth.writemask = depth_stencil_state_desc->DepthWriteMask;
		state.depth.func = depth_stencil_state_desc->DepthFunc - 1;
		state.stencil[0].enabled = !!depth_stencil_state_desc->StencilEnable;
		state.stencil[0].writemask = depth_stencil_state_desc->StencilWriteMask;
		state.stencil[0].valuemask = depth_stencil_state_desc->StencilReadMask;
		state.stencil[0].zpass_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->FrontFace.StencilPassOp];
		state.stencil[0].fail_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->FrontFace.StencilFailOp];
		state.stencil[0].zfail_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->FrontFace.StencilDepthFailOp];
		state.stencil[0].func = depth_stencil_state_desc->FrontFace.StencilFunc - 1;
		state.stencil[1].enabled = !!depth_stencil_state_desc->StencilEnable;
		state.stencil[1].writemask = depth_stencil_state_desc->StencilWriteMask;
		state.stencil[1].valuemask = depth_stencil_state_desc->StencilReadMask;
		state.stencil[1].zpass_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->BackFace.StencilPassOp];
		state.stencil[1].fail_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->BackFace.StencilFailOp];
		state.stencil[1].zfail_op = d3d11_to_pipe_stencil_op[depth_stencil_state_desc->BackFace.StencilDepthFailOp];
		state.stencil[1].func = depth_stencil_state_desc->BackFace.StencilFunc - 1;

		if(!depth_stencil_state)
			return S_FALSE;

		void* object = immediate_pipe->create_depth_stencil_alpha_state(immediate_pipe, &state);
		if(!object)
			return E_FAIL;

		*depth_stencil_state = new GalliumD3D11DepthStencilState(this, object, *depth_stencil_state_desc);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateRasterizerState(
		const D3D11_RASTERIZER_DESC *rasterizer_desc,
		ID3D11RasterizerState **out_rasterizer_state)
	{
		SYNCHRONIZED;

		pipe_rasterizer_state state;
		memset(&state, 0, sizeof(state));
		state.gl_rasterization_rules = 1; /* D3D10/11 use GL rules */
		state.fill_front = state.fill_back = (rasterizer_desc->FillMode == D3D11_FILL_WIREFRAME) ? PIPE_POLYGON_MODE_LINE : PIPE_POLYGON_MODE_FILL;
		if(rasterizer_desc->CullMode == D3D11_CULL_FRONT)
			state.cull_face = PIPE_FACE_FRONT;
		else if(rasterizer_desc->CullMode == D3D11_CULL_BACK)
			state.cull_face = PIPE_FACE_BACK;
		else
			state.cull_face = PIPE_FACE_NONE;
		state.front_ccw = !!rasterizer_desc->FrontCounterClockwise;
		/* TODO: is this correct? */
		/* TODO: we are ignoring depthBiasClamp! */
		state.offset_tri = state.offset_line = state.offset_point = rasterizer_desc->SlopeScaledDepthBias || rasterizer_desc->DepthBias;
		state.offset_scale = rasterizer_desc->SlopeScaledDepthBias;
		state.offset_units = rasterizer_desc->DepthBias;
		state.scissor = !!rasterizer_desc->ScissorEnable;
		state.multisample = !!rasterizer_desc->MultisampleEnable;
		state.line_smooth = !!rasterizer_desc->AntialiasedLineEnable;

		/* TODO: is this correct? */
		state.point_quad_rasterization = 1;

		if(!out_rasterizer_state)
			return S_FALSE;

		void* object = immediate_pipe->create_rasterizer_state(immediate_pipe, &state);
		if(!object)
			return E_FAIL;

		*out_rasterizer_state = new GalliumD3D11RasterizerState(this, object, *rasterizer_desc, !rasterizer_desc->DepthClipEnable);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateSamplerState(
		const D3D11_SAMPLER_DESC *sampler_desc,
		ID3D11SamplerState **out_sampler_state)
	{
		SYNCHRONIZED;

		pipe_sampler_state state;
		memset(&state, 0, sizeof(state));
		state.normalized_coords = 1;
		state.min_mip_filter = (sampler_desc->Filter & 1);
		state.mag_img_filter = ((sampler_desc->Filter >> 2) & 1);
		state.min_img_filter = ((sampler_desc->Filter >> 4) & 1);
		if(sampler_desc->Filter & 0x40)
			state.max_anisotropy = sampler_desc->MaxAnisotropy;
		if(sampler_desc->Filter & 0x80)
		{
			state.compare_mode = PIPE_TEX_COMPARE_R_TO_TEXTURE;
			state.compare_func = sampler_desc->ComparisonFunc;
		}
		state.wrap_s = d3d11_to_pipe_wrap[sampler_desc->AddressU];
		state.wrap_t = d3d11_to_pipe_wrap[sampler_desc->AddressV];
		state.wrap_r = d3d11_to_pipe_wrap[sampler_desc->AddressW];
		state.lod_bias = sampler_desc->MipLODBias;
		memcpy(state.border_color, sampler_desc->BorderColor, sizeof(state.border_color));
		state.min_lod = sampler_desc->MinLOD;
		state.max_lod = sampler_desc->MaxLOD;

		if(!out_sampler_state)
			return S_FALSE;

		void* object = immediate_pipe->create_sampler_state(immediate_pipe, &state);
		if(!object)
			return E_FAIL;

		*out_sampler_state = new GalliumD3D11SamplerState(this, object, *sampler_desc);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateInputLayout(
		const D3D11_INPUT_ELEMENT_DESC *input_element_descs,
		unsigned count,
		const void *shader_bytecode_with_input_signature,
		SIZE_T bytecode_length,
		ID3D11InputLayout **out_input_layout)
	{
		SYNCHRONIZED;

		if(count > D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT)
			return E_INVALIDARG;
		assert(D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT <= PIPE_MAX_ATTRIBS);

		// putting semantics matching in the core API seems to be a (minor) design mistake

		struct dxbc_chunk_signature* sig = dxbc_find_signature(shader_bytecode_with_input_signature, bytecode_length, false);
		D3D11_SIGNATURE_PARAMETER_DESC* params;
		unsigned num_params = dxbc_parse_signature(sig, &params);

		typedef std::unordered_map<std::pair<c_string, unsigned>, unsigned> semantic_to_idx_map_t;
		semantic_to_idx_map_t semantic_to_idx_map;
		for(unsigned i = 0; i < count; ++i)
			semantic_to_idx_map[std::make_pair(c_string(input_element_descs[i].SemanticName), input_element_descs[i].SemanticIndex)] = i;

		struct pipe_vertex_element elements[D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT];

		unsigned num_params_to_use = std::min(num_params, (unsigned)D3D11_IA_VERTEX_INPUT_STRUCTURE_ELEMENT_COUNT);
		for(unsigned i = 0; i < num_params_to_use; ++i)
		{
			int idx = -1;
			semantic_to_idx_map_t::iterator iter = semantic_to_idx_map.find(std::make_pair(c_string(params[i].SemanticName), params[i].SemanticIndex));
			if(iter != semantic_to_idx_map.end())
				idx = iter->second;

			// TODO: I kind of doubt Gallium drivers will like null elements; should we do something about it, either here, in the interface, or in the drivers?
			// TODO: also, in which cases should we return errors? (i.e. duplicate semantics in vs, duplicate semantics in layout, unmatched semantic in vs, unmatched semantic in layout)
			memset(&elements[i], 0, sizeof(elements[i]));
			if(idx >= 0)
			{
				elements[i].src_format = dxgi_to_pipe_format[input_element_descs[idx].Format];
				elements[i].src_offset = input_element_descs[idx].AlignedByteOffset;
				elements[i].vertex_buffer_index = input_element_descs[idx].InputSlot;
				elements[i].instance_divisor = input_element_descs[idx].InstanceDataStepRate;
			}
		}

		free(params);

		if(!out_input_layout)
			return S_FALSE;

		void* object = immediate_pipe->create_vertex_elements_state(immediate_pipe, num_params_to_use, elements);
		if(!object)
			return E_FAIL;

		*out_input_layout = new GalliumD3D11InputLayout(this, object);
		return S_OK;
	}

	static unsigned d3d11_to_pipe_bind_flags(unsigned bind_flags)
	{
		unsigned bind = 0;
		if(bind_flags & D3D11_BIND_VERTEX_BUFFER)
			bind |= PIPE_BIND_VERTEX_BUFFER;
		if(bind_flags & D3D11_BIND_INDEX_BUFFER)
			bind |= PIPE_BIND_INDEX_BUFFER;
		if(bind_flags & D3D11_BIND_CONSTANT_BUFFER)
			bind |= PIPE_BIND_CONSTANT_BUFFER;
		if(bind_flags & D3D11_BIND_SHADER_RESOURCE)
			bind |= PIPE_BIND_SAMPLER_VIEW;
		if(bind_flags & D3D11_BIND_STREAM_OUTPUT)
			bind |= PIPE_BIND_STREAM_OUTPUT;
		if(bind_flags & D3D11_BIND_RENDER_TARGET)
			bind |= PIPE_BIND_RENDER_TARGET;
		if(bind_flags & D3D11_BIND_DEPTH_STENCIL)
			bind |= PIPE_BIND_DEPTH_STENCIL;
		return bind;
	}

	inline HRESULT create_resource(
		pipe_texture_target target,
		unsigned width,
		unsigned height,
		unsigned depth,
		unsigned mip_levels,
		unsigned array_size,
		DXGI_FORMAT format,
		const DXGI_SAMPLE_DESC* SampleDesc,
		D3D11_USAGE usage,
		unsigned bind_flags,
		unsigned c_p_u_access_flags,
		unsigned misc_flags,
		const D3D11_SUBRESOURCE_DATA *initial_data,
		DXGI_USAGE dxgi_usage,
		struct pipe_resource** ppresource
	)
	{
		if(invalid(format >= DXGI_FORMAT_COUNT))
			return E_INVALIDARG;
		if(misc_flags & D3D11_RESOURCE_MISC_TEXTURECUBE)
		{
			if(target != PIPE_TEXTURE_2D)
				return E_INVALIDARG;
			target = PIPE_TEXTURE_CUBE;

			if(array_size != 6)
				return E_NOTIMPL;
		}
		else
		{
			if(array_size > 1)
				return E_NOTIMPL;
			array_size = 1;
		}
		/* TODO: msaa */
		struct pipe_resource templat;
		memset(&templat, 0, sizeof(templat));
		templat.target = target;
		templat.width0 = width;
		templat.height0 = height;
		templat.depth0 = depth;
		if(mip_levels)
			templat.last_level = mip_levels - 1;
		else
			templat.last_level = MAX2(MAX2(util_logbase2(templat.width0), util_logbase2(templat.height0)), util_logbase2(templat.depth0));
		templat.format = dxgi_to_pipe_format[format];
		templat.bind = d3d11_to_pipe_bind_flags(bind_flags);
		if(c_p_u_access_flags & D3D11_CPU_ACCESS_READ)
			templat.bind |= PIPE_BIND_TRANSFER_READ;
		if(c_p_u_access_flags & D3D11_CPU_ACCESS_WRITE)
			templat.bind |= PIPE_BIND_TRANSFER_WRITE;
		if(misc_flags & D3D11_RESOURCE_MISC_SHARED)
			templat.bind |= PIPE_BIND_SHARED;
		if(misc_flags & D3D11_RESOURCE_MISC_GDI_COMPATIBLE)
			templat.bind |= PIPE_BIND_TRANSFER_READ | PIPE_BIND_TRANSFER_WRITE;
		if(dxgi_usage & DXGI_USAGE_BACK_BUFFER)
			templat.bind |= PIPE_BIND_DISPLAY_TARGET;
		templat.usage = d3d11_to_pipe_usage[usage];
		if(invalid(!templat.format))
			return E_NOTIMPL;

		if(!ppresource)
			return S_FALSE;

		struct pipe_resource* resource = screen->resource_create(screen, &templat);
		if(!resource)
			return E_FAIL;
		if(initial_data)
		{
			for(unsigned slice = 0; slice < array_size; ++slice)
			{
				for(unsigned level = 0; level <= templat.last_level; ++level)
				{
					struct pipe_box box;
					box.x = box.y = 0;
					box.z = slice;
					box.width = u_minify(width, level);
					box.height = u_minify(height, level);
					box.depth = 1;
					immediate_pipe->transfer_inline_write(immediate_pipe, resource, level, PIPE_TRANSFER_WRITE | PIPE_TRANSFER_DISCARD | PIPE_TRANSFER_UNSYNCHRONIZED, &box, initial_data->pSysMem, initial_data->SysMemPitch, initial_data->SysMemSlicePitch);
					++initial_data;
				}
			}
		}
		*ppresource = resource;
		return S_OK;
	}

	static unsigned d3d_to_dxgi_usage(unsigned bind, unsigned misc)
	{
		unsigned dxgi_usage = 0;
		if(bind |= D3D11_BIND_RENDER_TARGET)
			dxgi_usage |= DXGI_USAGE_RENDER_TARGET_OUTPUT;
		if(bind & D3D11_BIND_SHADER_RESOURCE)
			dxgi_usage |= DXGI_USAGE_SHADER_INPUT;
#if API >= 11
		if(bind & D3D11_BIND_UNORDERED_ACCESS)
			dxgi_usage |= DXGI_USAGE_UNORDERED_ACCESS;
#endif
		if(misc & D3D11_RESOURCE_MISC_SHARED)
			dxgi_usage |= DXGI_USAGE_SHARED;
		return dxgi_usage;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateTexture1D(
		const D3D11_TEXTURE1D_DESC *desc,
		const D3D11_SUBRESOURCE_DATA *initial_data,
		ID3D11Texture1D **out_texture1d)
	{
		SYNCHRONIZED;

		struct pipe_resource* resource;
		DXGI_USAGE dxgi_usage = d3d_to_dxgi_usage(desc->BindFlags, desc->MiscFlags);
		HRESULT hr = create_resource(PIPE_TEXTURE_1D, desc->Width, 1, 1, desc->MipLevels, desc->ArraySize, desc->Format, 0, desc->Usage, desc->BindFlags, desc->CPUAccessFlags, desc->MiscFlags, initial_data, dxgi_usage, out_texture1d ? &resource : 0);
		if(hr != S_OK)
			return hr;
		D3D11_TEXTURE1D_DESC cdesc = *desc;
		cdesc.MipLevels = resource->last_level + 1;
		*out_texture1d = new GalliumD3D11Texture1D(this, resource, cdesc, dxgi_usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateTexture2D(
		const D3D11_TEXTURE2D_DESC *desc,
		const D3D11_SUBRESOURCE_DATA *initial_data,
		ID3D11Texture2D **out_texture2d)
	{
		SYNCHRONIZED;

		struct pipe_resource* resource;
		DXGI_USAGE dxgi_usage = d3d_to_dxgi_usage(desc->BindFlags, desc->MiscFlags);
		HRESULT hr = create_resource(PIPE_TEXTURE_2D, desc->Width, desc->Height, 1, desc->MipLevels, desc->ArraySize, desc->Format, &desc->SampleDesc, desc->Usage, desc->BindFlags, desc->CPUAccessFlags, desc->MiscFlags, initial_data, dxgi_usage, out_texture2d ? &resource : 0);
		if(hr != S_OK)
			return hr;
		D3D11_TEXTURE2D_DESC cdesc = *desc;
		cdesc.MipLevels = resource->last_level + 1;
		if(cdesc.MipLevels == 1 && cdesc.ArraySize == 1)
			*out_texture2d = new GalliumD3D11Surface(this, resource, cdesc, dxgi_usage);
		else
			*out_texture2d = new GalliumD3D11Texture2D(this, resource, cdesc, dxgi_usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateTexture3D(
		const D3D11_TEXTURE3D_DESC *desc,
		const D3D11_SUBRESOURCE_DATA *initial_data,
		ID3D11Texture3D **out_texture3d)
	{
		SYNCHRONIZED;

		struct pipe_resource* resource;
		DXGI_USAGE dxgi_usage = d3d_to_dxgi_usage(desc->BindFlags, desc->MiscFlags);
		HRESULT hr = create_resource(PIPE_TEXTURE_3D, desc->Width, desc->Height, desc->Depth, desc->MipLevels, 1, desc->Format, 0, desc->Usage, desc->BindFlags, desc->CPUAccessFlags, desc->MiscFlags, initial_data, dxgi_usage, out_texture3d ? &resource : 0);
		if(hr != S_OK)
			return hr;
		D3D11_TEXTURE3D_DESC cdesc = *desc;
		cdesc.MipLevels = resource->last_level + 1;
		*out_texture3d = new GalliumD3D11Texture3D(this, resource, cdesc, dxgi_usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateBuffer(
		const D3D11_BUFFER_DESC *desc,
		const D3D11_SUBRESOURCE_DATA *initial_data,
		ID3D11Buffer **out_buffer)
	{
		SYNCHRONIZED;

		struct pipe_resource* resource;
		DXGI_USAGE dxgi_usage = d3d_to_dxgi_usage(desc->BindFlags, desc->MiscFlags);
		HRESULT hr = create_resource(PIPE_BUFFER, desc->ByteWidth, 1, 1, 1, 1, DXGI_FORMAT_R8_UNORM, 0, desc->Usage, desc->BindFlags, desc->CPUAccessFlags, desc->MiscFlags, initial_data, dxgi_usage, out_buffer ? &resource : 0);
		if(hr != S_OK)
			return hr;
		*out_buffer = new GalliumD3D11Buffer(this, resource, *desc, dxgi_usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE OpenGalliumResource(
		struct pipe_resource* resource,
		IUnknown** dxgi_resource)
	{
		SYNCHRONIZED;

		/* TODO: maybe support others */
		assert(resource->target == PIPE_TEXTURE_2D);
		*dxgi_resource = 0;
		D3D11_TEXTURE2D_DESC desc;
		memset(&desc, 0, sizeof(desc));
		desc.Width = resource->width0;
		desc.Height = resource->height0;
		init_pipe_to_dxgi_format();
		desc.Format = pipe_to_dxgi_format[resource->format];
		desc.SampleDesc.Count = resource->nr_samples;
		desc.SampleDesc.Quality = 0;
		desc.ArraySize = 1;
		desc.MipLevels = resource->last_level + 1;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
		if(resource->bind & PIPE_BIND_RENDER_TARGET)
			desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
		if(resource->bind & PIPE_BIND_DEPTH_STENCIL)
			desc.BindFlags |= D3D11_BIND_DEPTH_STENCIL;
		if(resource->bind & PIPE_BIND_SAMPLER_VIEW)
			desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
		if(resource->bind & PIPE_BIND_SHARED)
			desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
		DXGI_USAGE dxgi_usage = d3d_to_dxgi_usage(desc.BindFlags, desc.MiscFlags);
		if(desc.MipLevels == 1 && desc.ArraySize == 1)
			*dxgi_resource = (ID3D11Texture2D*)new GalliumD3D11Surface(this, resource, desc, dxgi_usage);
		else
			*dxgi_resource = (ID3D11Texture2D*)new GalliumD3D11Texture2D(this, resource, desc, dxgi_usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateSurface(
		const DXGI_SURFACE_DESC *dxgi_desc,
		unsigned count,
		DXGI_USAGE usage,
		const DXGI_SHARED_RESOURCE *shared_resource,
		IDXGISurface **out_surface)
	{
		SYNCHRONIZED;

		D3D11_TEXTURE2D_DESC desc;
		memset(&desc, 0, sizeof(desc));

		struct pipe_resource* resource;
		desc.Width = dxgi_desc->Width;
		desc.Height = dxgi_desc->Height;
		desc.Format = dxgi_desc->Format;
		desc.SampleDesc = dxgi_desc->SampleDesc;
		desc.ArraySize = count;
		desc.MipLevels = 1;
		desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
		if(usage & DXGI_USAGE_RENDER_TARGET_OUTPUT)
			desc.BindFlags |= D3D11_BIND_RENDER_TARGET;
		if(usage & DXGI_USAGE_SHADER_INPUT)
			desc.BindFlags |= D3D11_BIND_SHADER_RESOURCE;
#if API >= 11
		if(usage & DXGI_USAGE_UNORDERED_ACCESS)
			desc.BindFlags |= D3D11_BIND_UNORDERED_ACCESS;
#endif
		if(usage & DXGI_USAGE_SHARED)
			desc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
		HRESULT hr = create_resource(PIPE_TEXTURE_2D, dxgi_desc->Width, dxgi_desc->Height, 1, 1, count, dxgi_desc->Format, &dxgi_desc->SampleDesc, D3D11_USAGE_DEFAULT, desc.BindFlags, D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE, desc.MiscFlags, 0, usage, &resource);
		if(hr != S_OK)
			return hr;
		*out_surface = new GalliumD3D11Surface(this, resource, desc, usage);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateShaderResourceView(
		ID3D11Resource *iresource,
		const D3D11_SHADER_RESOURCE_VIEW_DESC *desc,
		ID3D11ShaderResourceView **out_srv)
	{
#if API >= 11
		D3D11_SHADER_RESOURCE_VIEW_DESC def_desc;
#else
		if(desc->ViewDimension == D3D10_1_SRV_DIMENSION_TEXTURECUBEARRAY)
			return E_INVALIDARG;
		D3D10_SHADER_RESOURCE_VIEW_DESC1 desc1;
		memset(&desc1, 0, sizeof(desc1));
		memcpy(&desc1, desc, sizeof(*desc));
		return CreateShaderResourceView1(iresource, &desc1, (ID3D10ShaderResourceView1**)out_srv);
	}

	virtual HRESULT STDMETHODCALLTYPE CreateShaderResourceView1(
			ID3D11Resource *iresource,
			const D3D10_SHADER_RESOURCE_VIEW_DESC1 *desc,
			ID3D10ShaderResourceView1 **out_srv)
	{
		D3D10_SHADER_RESOURCE_VIEW_DESC1 def_desc;
#endif
		SYNCHRONIZED;

		if(!desc)
		{
			struct pipe_resource* resource = ((GalliumD3D11Resource<>*)iresource)->resource;
			init_pipe_to_dxgi_format();
			memset(&def_desc, 0, sizeof(def_desc));
			def_desc.Format = pipe_to_dxgi_format[resource->format];
			switch(resource->target)
			{
			case PIPE_BUFFER:
				def_desc.ViewDimension = D3D11_SRV_DIMENSION_BUFFER;
				def_desc.Buffer.ElementWidth = resource->width0;
				break;
			case PIPE_TEXTURE_1D:
				def_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE1D;
				def_desc.Texture1D.MipLevels = resource->last_level + 1;
				break;
			case PIPE_TEXTURE_2D:
			case PIPE_TEXTURE_RECT:
				def_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
				def_desc.Texture2D.MipLevels = resource->last_level + 1;
				break;
			case PIPE_TEXTURE_3D:
				def_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE3D;
				def_desc.Texture3D.MipLevels = resource->last_level + 1;
				break;
			case PIPE_TEXTURE_CUBE:
				def_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURECUBE;
				def_desc.TextureCube.MipLevels = resource->last_level + 1;
				break;
			default:
				return E_INVALIDARG;
			}
			desc = &def_desc;
		}

		struct pipe_sampler_view templat;
		memset(&templat, 0, sizeof(templat));
		if(invalid(format >= DXGI_FORMAT_COUNT))
			return E_INVALIDARG;
		templat.format = dxgi_to_pipe_format[desc->Format];
		if(!templat.format)
			return E_NOTIMPL;
		templat.swizzle_r = PIPE_SWIZZLE_RED;
		templat.swizzle_g = PIPE_SWIZZLE_GREEN;
		templat.swizzle_b = PIPE_SWIZZLE_BLUE;
		templat.swizzle_a = PIPE_SWIZZLE_ALPHA;

		templat.texture = ((GalliumD3D11Resource<>*)iresource)->resource;
		switch(desc->ViewDimension)
		{
		case D3D11_SRV_DIMENSION_TEXTURE1D:
		case D3D11_SRV_DIMENSION_TEXTURE2D:
		case D3D11_SRV_DIMENSION_TEXTURE3D:
		case D3D11_SRV_DIMENSION_TEXTURE1DARRAY:
		case D3D11_SRV_DIMENSION_TEXTURE2DARRAY:
			/* yes, this works for all of these types (but TODO: texture arrays) */
			templat.u.tex.first_level = desc->Texture1D.MostDetailedMip;
			templat.u.tex.last_level = templat.u.tex.first_level + desc->Texture1D.MipLevels - 1;
			break;
		case D3D11_SRV_DIMENSION_BUFFER:
		case D3D11_SRV_DIMENSION_TEXTURE2DMS:
		case D3D11_SRV_DIMENSION_TEXTURE2DMSARRAY:
			return E_NOTIMPL;
		default:
			return E_INVALIDARG;
		}

		if(!out_srv)
			return S_FALSE;

		struct pipe_sampler_view* view = immediate_pipe->create_sampler_view(immediate_pipe, templat.texture, &templat);
		if(!view)
			return E_FAIL;
		*out_srv = new GalliumD3D11ShaderResourceView(this, (GalliumD3D11Resource<>*)iresource, view, *desc);
		return S_OK;
	}

#if API >= 11
	virtual HRESULT STDMETHODCALLTYPE CreateUnorderedAccessView(
		ID3D11Resource *resource,
		const D3D11_UNORDERED_ACCESS_VIEW_DESC *desc,
		ID3D11UnorderedAccessView **out_uav)
	{
		SYNCHRONIZED;

		return E_NOTIMPL;

		// remember to return S_FALSE and not crash if out_u_a_view == 0 and parameters are valid
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE CreateRenderTargetView(
		ID3D11Resource *iresource,
		const D3D11_RENDER_TARGET_VIEW_DESC *desc,
		ID3D11RenderTargetView **out_rtv)
	{
		SYNCHRONIZED;

		D3D11_RENDER_TARGET_VIEW_DESC def_desc;
		if(!desc)
		{
			struct pipe_resource* resource = ((GalliumD3D11Resource<>*)iresource)->resource;
			init_pipe_to_dxgi_format();
			memset(&def_desc, 0, sizeof(def_desc));
			def_desc.Format = pipe_to_dxgi_format[resource->format];
			switch(resource->target)
			{
			case PIPE_BUFFER:
				def_desc.ViewDimension = D3D11_RTV_DIMENSION_BUFFER;
				def_desc.Buffer.ElementWidth = resource->width0;
				break;
			case PIPE_TEXTURE_1D:
				def_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE1D;
				break;
			case PIPE_TEXTURE_2D:
			case PIPE_TEXTURE_RECT:
				def_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
				break;
			case PIPE_TEXTURE_3D:
				def_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE3D;
				def_desc.Texture3D.WSize = resource->depth0;
				break;
			case PIPE_TEXTURE_CUBE:
				def_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
				def_desc.Texture2DArray.ArraySize = 6;
				break;
			default:
				return E_INVALIDARG;
			}
			desc = &def_desc;
		}

		struct pipe_surface templat;
		memset(&templat, 0, sizeof(templat));
		if(invalid(desc->format >= DXGI_FORMAT_COUNT))
			return E_INVALIDARG;
		templat.format = dxgi_to_pipe_format[desc->Format];
		if(!templat.format)
			return E_NOTIMPL;
		templat.usage = PIPE_BIND_RENDER_TARGET;
		templat.texture = ((GalliumD3D11Resource<>*)iresource)->resource;

		switch(desc->ViewDimension)
		{
		case D3D11_RTV_DIMENSION_TEXTURE1D:
		case D3D11_RTV_DIMENSION_TEXTURE2D:
			templat.u.tex.level = desc->Texture1D.MipSlice;
			break;
		case D3D11_RTV_DIMENSION_TEXTURE3D:
			templat.u.tex.level = desc->Texture3D.MipSlice;
			templat.u.tex.first_layer = desc->Texture3D.FirstWSlice;
			/* XXX FIXME */
			templat.u.tex.last_layer = desc->Texture3D.FirstWSlice;
			break;
		case D3D11_RTV_DIMENSION_TEXTURE1DARRAY:
		case D3D11_RTV_DIMENSION_TEXTURE2DARRAY:
			templat.u.tex.level = desc->Texture1DArray.MipSlice;
			templat.u.tex.first_layer = desc->Texture1DArray.FirstArraySlice;
			/* XXX FIXME */
			templat.u.tex.last_layer = desc->Texture1DArray.FirstArraySlice;
			break;
		case D3D11_RTV_DIMENSION_BUFFER:
		case D3D11_RTV_DIMENSION_TEXTURE2DMS:
		case D3D11_RTV_DIMENSION_TEXTURE2DMSARRAY:
			return E_NOTIMPL;
		default:
			return E_INVALIDARG;
		}

		if(!out_rtv)
			return S_FALSE;

		struct pipe_surface* surface = immediate_pipe->create_surface(immediate_pipe, templat.texture, &templat);
		if(!surface)
			return E_FAIL;
		*out_rtv = new GalliumD3D11RenderTargetView(this, (GalliumD3D11Resource<>*)iresource, surface, *desc);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreateDepthStencilView(
		ID3D11Resource *iresource,
		const D3D11_DEPTH_STENCIL_VIEW_DESC *desc,
		ID3D11DepthStencilView **out_depth_stencil_view)
	{
		SYNCHRONIZED;

		D3D11_DEPTH_STENCIL_VIEW_DESC def_desc;
		if(!desc)
		{
			struct pipe_resource* resource = ((GalliumD3D11Resource<>*)iresource)->resource;
			init_pipe_to_dxgi_format();
			memset(&def_desc, 0, sizeof(def_desc));
			def_desc.Format = pipe_to_dxgi_format[resource->format];
			switch(resource->target)
			{
			case PIPE_TEXTURE_1D:
				def_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE1D;
				break;
			case PIPE_TEXTURE_2D:
			case PIPE_TEXTURE_RECT:
				def_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2D;
				break;
			case PIPE_TEXTURE_CUBE:
				def_desc.ViewDimension = D3D11_DSV_DIMENSION_TEXTURE2DARRAY;
				def_desc.Texture2DArray.ArraySize = 6;
				break;
			default:
				return E_INVALIDARG;
			}
			desc = &def_desc;
		}

		struct pipe_surface templat;
		memset(&templat, 0, sizeof(templat));
		if(invalid(desc->format >= DXGI_FORMAT_COUNT))
			return E_INVALIDARG;
		templat.format = dxgi_to_pipe_format[desc->Format];
		if(!templat.format)
			return E_NOTIMPL;
		templat.usage = PIPE_BIND_DEPTH_STENCIL;
		templat.texture = ((GalliumD3D11Resource<>*)iresource)->resource;

		switch(desc->ViewDimension)
		{
		case D3D11_DSV_DIMENSION_TEXTURE1D:
		case D3D11_DSV_DIMENSION_TEXTURE2D:
			templat.u.tex.level = desc->Texture1D.MipSlice;
			break;
		case D3D11_DSV_DIMENSION_TEXTURE1DARRAY:
		case D3D11_DSV_DIMENSION_TEXTURE2DARRAY:
			templat.u.tex.level = desc->Texture1DArray.MipSlice;
			templat.u.tex.first_layer = desc->Texture1DArray.FirstArraySlice;
			/* XXX FIXME */
			templat.u.tex.last_layer = desc->Texture1DArray.FirstArraySlice;
			break;
		case D3D11_DSV_DIMENSION_TEXTURE2DMS:
		case D3D11_DSV_DIMENSION_TEXTURE2DMSARRAY:
			return E_NOTIMPL;
		default:
			return E_INVALIDARG;
		}

		if(!out_depth_stencil_view)
			return S_FALSE;

		struct pipe_surface* surface = immediate_pipe->create_surface(immediate_pipe, templat.texture, &templat);
		if(!surface)
			return E_FAIL;
		*out_depth_stencil_view = new GalliumD3D11DepthStencilView(this, (GalliumD3D11Resource<>*)iresource, surface, *desc);
		return S_OK;
	}

	GalliumD3D11Shader<>* create_stage_shader(unsigned type, const void* shader_bytecode, SIZE_T bytecode_length
#if API >= 11
			, ID3D11ClassLinkage *class_linkage
#endif
			)
	{
		bool dump = debug_get_option_dump_shaders();

		dxbc_chunk_header* sm4_chunk = dxbc_find_shader_bytecode(shader_bytecode, bytecode_length);
		if(!sm4_chunk)
			return 0;

		std::auto_ptr<sm4_program> sm4(sm4_parse(sm4_chunk + 1, bswap_le32(sm4_chunk->size)));
		if(!sm4.get())
			return 0;

		if(dump)
			sm4->dump();

		struct pipe_shader_state tgsi_shader;
		memset(&tgsi_shader, 0, sizeof(tgsi_shader));
		tgsi_shader.tokens = (const tgsi_token*)sm4_to_tgsi(*sm4);
		if(!tgsi_shader.tokens)
			return 0;

		if(dump)
			tgsi_dump(tgsi_shader.tokens, 0);

		void* shader_cso;
		GalliumD3D11Shader<>* shader;

		switch(type)
		{
		case PIPE_SHADER_VERTEX:
			shader_cso = immediate_pipe->create_vs_state(immediate_pipe, &tgsi_shader);
			shader = (GalliumD3D11Shader<>*)new GalliumD3D11VertexShader(this, shader_cso);
			break;
		case PIPE_SHADER_FRAGMENT:
			shader_cso = immediate_pipe->create_fs_state(immediate_pipe, &tgsi_shader);
			shader = (GalliumD3D11Shader<>*)new GalliumD3D11PixelShader(this, shader_cso);
			break;
		case PIPE_SHADER_GEOMETRY:
			shader_cso = immediate_pipe->create_gs_state(immediate_pipe, &tgsi_shader);
			shader = (GalliumD3D11Shader<>*)new GalliumD3D11GeometryShader(this, shader_cso);
			break;
		default:
			shader_cso = 0;
			shader = 0;
			break;
		}

		if(shader)
		{
			shader->slot_to_resource = sm4->slot_to_resource;
			shader->slot_to_sampler = sm4->slot_to_sampler;
		}

		free((void*)tgsi_shader.tokens);
		return shader;
	}

#if API >= 11
#define CREATE_SHADER_ARGS \
	const void *shader_bytecode, \
	SIZE_T bytecode_length, \
	ID3D11ClassLinkage *class_linkage
#define PASS_SHADER_ARGS shader_bytecode, bytecode_length, class_linkage
#else
#define CREATE_SHADER_ARGS \
	const void *shader_bytecode, \
	SIZE_T bytecode_length
#define PASS_SHADER_ARGS shader_bytecode, bytecode_length
#endif

#define IMPLEMENT_CREATE_SHADER(Stage, GALLIUM) \
	virtual HRESULT STDMETHODCALLTYPE Create##Stage##Shader( \
		CREATE_SHADER_ARGS, \
		ID3D11##Stage##Shader **out_shader) \
	{ \
		SYNCHRONIZED; \
		GalliumD3D11##Stage##Shader* shader = (GalliumD3D11##Stage##Shader*)create_stage_shader(PIPE_SHADER_##GALLIUM, PASS_SHADER_ARGS); \
		if(!shader) \
			return E_FAIL; \
		if(out_shader) \
		{ \
			*out_shader = shader; \
			return S_OK; \
		} \
		else \
		{ \
			shader->Release(); \
			return S_FALSE; \
		} \
	}

#define IMPLEMENT_NOTIMPL_CREATE_SHADER(Stage) \
	virtual HRESULT STDMETHODCALLTYPE Create##Stage##Shader( \
		CREATE_SHADER_ARGS, \
		ID3D11##Stage##Shader **out_shader) \
	{ \
		return E_NOTIMPL; \
	}

	IMPLEMENT_CREATE_SHADER(Vertex, VERTEX)
	IMPLEMENT_CREATE_SHADER(Pixel, FRAGMENT)
	IMPLEMENT_CREATE_SHADER(Geometry, GEOMETRY)
#if API >= 11
	IMPLEMENT_NOTIMPL_CREATE_SHADER(Hull)
	IMPLEMENT_NOTIMPL_CREATE_SHADER(Domain)
	IMPLEMENT_NOTIMPL_CREATE_SHADER(Compute)
#endif

	virtual HRESULT STDMETHODCALLTYPE CreateGeometryShaderWithStreamOutput(
		const void *shader_bytecode,
		SIZE_T bytecode_length,
		const D3D11_SO_DECLARATION_ENTRY *so_declaration,
		unsigned num_entries,
#if API >= 11
		const unsigned *buffer_strides,
		unsigned num_strides,
		unsigned rasterized_stream,
		ID3D11ClassLinkage *class_linkage,
#else
		UINT output_stream_stride,
#endif
		ID3D11GeometryShader **out_geometry_shader)
	{
		SYNCHRONIZED;

		return E_NOTIMPL;

		// remember to return S_FALSE if ppGeometyShader == NULL and the shader is OK
	}

#if API >= 11
	virtual HRESULT STDMETHODCALLTYPE CreateClassLinkage(
		ID3D11ClassLinkage **out_linkage)
	{
		SYNCHRONIZED;

		return E_NOTIMPL;
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE CreateQuery(
		const D3D11_QUERY_DESC *query_desc,
		ID3D11Query **out_query)
	{
		SYNCHRONIZED;

		if(invalid(query_desc->Query >= D3D11_QUERY_COUNT))
			return E_INVALIDARG;
		unsigned query_type = d3d11_to_pipe_query[query_desc->Query];
		if(!query_type)
			return E_NOTIMPL;

		if(out_query)
			return S_FALSE;

		struct pipe_query* query = immediate_pipe->create_query(immediate_pipe, query_type);
		if(!query)
			return E_FAIL;

		*out_query = new GalliumD3D11Query(this, query, d3d11_query_size[query_desc->Query], *query_desc);
		return S_OK;
	}

	virtual HRESULT STDMETHODCALLTYPE CreatePredicate(
		const D3D11_QUERY_DESC *predicate_desc,
		ID3D11Predicate **out_predicate)
	{
		SYNCHRONIZED;

		unsigned query_type;
		switch(predicate_desc->Query)
		{
		case D3D11_QUERY_SO_OVERFLOW_PREDICATE:
			return E_NOTIMPL;
		case D3D11_QUERY_OCCLUSION_PREDICATE:
			query_type = PIPE_QUERY_OCCLUSION_COUNTER;
			break;
		default:
			return E_INVALIDARG;
		}

		if(out_predicate)
			return S_FALSE;

		struct pipe_query* query = immediate_pipe->create_query(immediate_pipe, query_type);
		if(!query)
			return E_FAIL;

		*out_predicate = new GalliumD3D11Predicate(this, query, sizeof(BOOL), *predicate_desc);
		return S_OK;
	}


	virtual HRESULT STDMETHODCALLTYPE CreateCounter(
		const D3D11_COUNTER_DESC *counter_desc,
		ID3D11Counter **out_counter)
	{
		SYNCHRONIZED;

		return E_NOTIMPL;

		// remember to return S_FALSE if out_counter == NULL and everything is OK
	}

#if API >= 11
	virtual HRESULT STDMETHODCALLTYPE CreateDeferredContext(
		unsigned context_flags,
		ID3D11DeviceContext **out_deferred_context)
	{
		SYNCHRONIZED;

		// TODO: this will have to be implemented using a new Gallium util module
		return E_NOTIMPL;

		// remember to return S_FALSE if out_counter == NULL and everything is OK
	}
#endif

	virtual HRESULT STDMETHODCALLTYPE OpenSharedResource(
			HANDLE resource,
			REFIID iid,
			void **out_resource)
	{
		SYNCHRONIZED;

		// TODO: the problem here is that we need to communicate dimensions somehow
		return E_NOTIMPL;

		// remember to return S_FALSE if out_counter == NULL and everything is OK
#if 0
		struct pipe_resou	rce templat;
		struct winsys_handle handle;
		handle.stride = 0;
		handle.handle = resource;
		handle.type = DRM_API_HANDLE_TYPE_SHARED;
		screen->resource_from_handle(screen, &templat, &handle);
#endif
	}

#if API < 11
	/* these are documented as "Not implemented".
	 * According to the UMDDI documentation, they apparently turn on a
	 * (width + 1) x (height + 1) convolution filter for 1-bit textures.
	 * Probably nothing uses these, assuming it has ever been implemented anywhere.
	 */
	void STDMETHODCALLTYPE SetTextFilterSize(
		UINT width,
		UINT height
	)
	{}

	virtual void STDMETHODCALLTYPE GetTextFilterSize(
		UINT *width,
		UINT *height
	)
	{}
#endif

#if API >= 11
	virtual void STDMETHODCALLTYPE RestoreGalliumState()
	{
		GalliumD3D11ImmediateDeviceContext_RestoreGalliumState(immediate_context);
	}

	virtual void STDMETHODCALLTYPE RestoreGalliumStateBlitOnly()
	{
		GalliumD3D11ImmediateDeviceContext_RestoreGalliumStateBlitOnly(immediate_context);
	}
#endif

	virtual struct pipe_context* STDMETHODCALLTYPE GetGalliumContext(void)
	{
		return immediate_pipe;
	}

#undef SYNCHRONIZED
};
