text: /*
   * Copyright (c) 2014-2017, NVIDIA CORPORATION.  All rights reserved.
   *
   * NVIDIA CORPORATION and its licensors retain all intellectual property
   * and proprietary rights in and to this software, related documentation
   * and any modifications thereto.  Any use, reproduction, disclosure or
   * distribution of this software and related documentation without an express
   * license agreement from NVIDIA CORPORATION is strictly prohibited.
   */
  
  #include "imguiGraphD3D11.h"
  
  //direct3d headers
  #include <d3d11.h>
  
  #include "../d3d/shaders/imguiVS.hlsl.h"
  #include "../d3d/shaders/imguiPS.hlsl.h"
  
  namespace
  {
  	template <class T>
  	void inline COMRelease(T& t)
  	{
  		if (t) t->Release();
  		t = nullptr;
  	}
  }
  
  namespace
  {
  	ID3D11Device* m_device = nullptr;
  	ID3D11DeviceContext* m_deviceContext = nullptr;
  	int m_winW = 0;
  	int m_winH = 0;
  
  	uint32_t m_maxVertices = 0;
  
  	struct Vertex
  	{
  		float x, y;
  		float u, v;
  		uint8_t rgba[4];
  	};
  
  	ID3D11RasterizerState* m_rasterizerState = nullptr;
  	ID3D11SamplerState* m_samplerState = nullptr;
  	ID3D11InputLayout* m_inputLayout = nullptr;
  	ID3D11BlendState* m_blendState = nullptr;
  	ID3D11VertexShader* m_imguiVS = nullptr;
  	ID3D11PixelShader* m_imguiPS = nullptr;
  	ID3D11Buffer* m_constantBuffer = nullptr;
  	ID3D11Buffer* m_vertexBuffer = nullptr;
  	Vertex* m_vertexBufferData = nullptr;
  
  	struct Scissor
  	{
  		int beginIdx;
  		int stopIdx;
  		int x;
  		int y;
  		int width;
  		int height;
  	};
  	Scissor m_stateScissor = {};
  
  	ID3D11Texture2D* m_texture = nullptr;
  	ID3D11ShaderResourceView* m_textureSRV = nullptr;
  
  	Vertex m_stateVert;
  	uint32_t m_stateVertIdx = 0u;
  
  	struct Params
  	{
  		float projection[16];
  
  		float padding[64 - 16];
  	};
  }
  
  void imguiGraphContextDestroyD3D11()
  {
  	COMRelease(m_rasterizerState);
  	COMRelease(m_samplerState);
  	COMRelease(m_inputLayout);
  	COMRelease(m_blendState);
  	COMRelease(m_imguiVS);
  	COMRelease(m_imguiPS);
  	COMRelease(m_constantBuffer);
  	COMRelease(m_vertexBuffer);
  }
  
  void imguiGraphContextInitD3D11(const ImguiGraphDesc* descIn)
  {
  	const auto desc = cast_to_imguiGraphDescD3D11(descIn);
  
  	m_device = desc->device;
  	m_deviceContext = desc->deviceContext;
  	m_winW = desc->winW;
  	m_winH = desc->winH;
  
  	m_maxVertices = desc->maxVertices;
  
  	// create the rastersizer state
  	{
  		D3D11_RASTERIZER_DESC desc = {};
  		desc.FillMode = D3D11_FILL_SOLID;
  		desc.CullMode = D3D11_CULL_BACK;
  		desc.FrontCounterClockwise = TRUE;	// This is non-default
  		desc.DepthBias = 0;
  		desc.DepthBiasClamp = 0.f;
  		desc.SlopeScaledDepthBias = 0.f;
  		desc.DepthClipEnable = TRUE;
  		desc.ScissorEnable = TRUE;		// This is non-default
  		desc.MultisampleEnable = FALSE;
  		desc.AntialiasedLineEnable = FALSE;
  
  		m_device->CreateRasterizerState(&desc, &m_rasterizerState);
  	}
  
  	// create the sampler state
  	{
  		D3D11_SAMPLER_DESC sampler = {};
  		sampler.Filter = D3D11_FILTER_MIN_MAG_LINEAR_MIP_POINT;
  		sampler.AddressU = D3D11_TEXTURE_ADDRESS_BORDER;
  		sampler.AddressV = D3D11_TEXTURE_ADDRESS_BORDER;
  		sampler.AddressW = D3D11_TEXTURE_ADDRESS_BORDER;
  		sampler.MipLODBias = 0;
  		sampler.MaxAnisotropy = 0;
  		sampler.ComparisonFunc = D3D11_COMPARISON_NEVER;
  		//sampler.BorderColor = D3D11_BORDER_COLOR_TRANSPARENT_BLACK;
  		sampler.MinLOD = 0.f;
  		sampler.MaxLOD = D3D11_FLOAT32_MAX;
  
  		m_device->CreateSamplerState(&sampler, &m_samplerState);
  	}
  
  	// create the input layout
  	{
  		D3D11_INPUT_ELEMENT_DESC inputElementDescs[] =
  		{
  			{ "POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  			{ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  			{ "COLOR", 0, DXGI_FORMAT_R8G8B8A8_UNORM, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
  		};
  
  		m_device->CreateInputLayout(inputElementDescs, 3, g_imguiVS, sizeof(g_imguiVS), &m_inputLayout);
  	}
  
  	// create the blend state
  	{
  		D3D11_BLEND_DESC blendDesc = {};
  		blendDesc.AlphaToCoverageEnable = false;
  		blendDesc.IndependentBlendEnable = false;
  		blendDesc.RenderTarget[0].BlendEnable = true;
  		blendDesc.RenderTarget[0].SrcBlend = D3D11_BLEND_SRC_ALPHA;
  		blendDesc.RenderTarget[0].DestBlend = D3D11_BLEND_INV_SRC_ALPHA;
  		blendDesc.RenderTarget[0].BlendOp = D3D11_BLEND_OP_ADD;
  		blendDesc.RenderTarget[0].SrcBlendAlpha = D3D11_BLEND_SRC_ALPHA;
  		blendDesc.RenderTarget[0].DestBlendAlpha = D3D11_BLEND_INV_SRC_ALPHA;
  		blendDesc.RenderTarget[0].BlendOpAlpha = D3D11_BLEND_OP_ADD;
  		blendDesc.RenderTarget[0].RenderTargetWriteMask = D3D11_COLOR_WRITE_ENABLE_ALL;
  
  		m_device->CreateBlendState(&blendDesc, &m_blendState);
  	}
  
  	// create the shaders
  	m_device->CreateVertexShader(g_imguiVS, sizeof(g_imguiVS), nullptr, &m_imguiVS);
  	m_device->CreatePixelShader(g_imguiPS, sizeof(g_imguiPS), nullptr, &m_imguiPS);
  
  	// create a constant buffer
  	{
  		Params params = {
  			1.f, 0.f, 0.f, 0.f,
  			0.f, 1.f, 0.f, 0.f,
  			0.f, 0.f, 1.f, 0.f,
  			0.f, 0.f, 0.f, 1.f
  		};
  
  		D3D11_BUFFER_DESC bufDesc;
  		bufDesc.ByteWidth = sizeof(params);
  		bufDesc.Usage = D3D11_USAGE_DYNAMIC;
  		bufDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
  		bufDesc.CPUAccessFlags  = D3D11_CPU_ACCESS_WRITE;
  		bufDesc.MiscFlags = 0;
  
  		D3D11_SUBRESOURCE_DATA data = {};
  		data.pSysMem = &params;
  
  		m_device->CreateBuffer(&bufDesc, &data, &m_constantBuffer);
  	}
  
  	// create a vertex buffer
  	{
  		UINT bufferSize = (UINT)(m_maxVertices) * sizeof(Vertex);
  
  		D3D11_BUFFER_DESC bufDesc;
  		bufDesc.ByteWidth = bufferSize;
  		bufDesc.Usage = D3D11_USAGE_DYNAMIC;
  		bufDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
  		bufDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
  		bufDesc.MiscFlags = 0;
  
  		m_device->CreateBuffer(&bufDesc, nullptr, &m_vertexBuffer);
  	}
  }
  
  void imguiGraphContextUpdateD3D11(const ImguiGraphDesc* descIn)
  {
  	const auto desc = cast_to_imguiGraphDescD3D11(descIn);
  
  	m_device = desc->device;
  	m_deviceContext = desc->deviceContext;
  	m_winW = desc->winW;
  	m_winH = desc->winH;
  }
  
  void imguiGraphRecordBeginD3D11()
  {
  	Params params = {
  		2.f / float(m_winW), 0.f, 0.f, -1.f,
  		0.f, 2.f / float(m_winH), 0.f, -1.f,
  		0.f, 0.f, 1.f, 0.f,
  		0.f, 0.f, 0.f, 1.f
  	};
  
  	ID3D11DeviceContext* context = m_deviceContext;
  
  	// update constant buffer
  	{
  		D3D11_MAPPED_SUBRESOURCE mappedResource = {};
  		if (S_OK == context->Map(m_constantBuffer, 0u, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource))
  		{
  			memcpy(mappedResource.pData, &params, sizeof(Params));
  
  			context->Unmap(m_constantBuffer, 0u);
  		}
  	}
  
  	// clear state
  	m_stateVert = Vertex{ 0.f, 0.f, -1.f, -1.f, 0,0,0,0 };
  	m_stateVertIdx = 0u;
  
  	m_stateScissor = Scissor { 0, 0, 0, 0, m_winW, m_winH };
  
  	// configure for triangle renderering
  	context->VSSetShader(m_imguiVS, nullptr, 0u);
  	context->GSSetShader(nullptr, nullptr, 0u);
  	context->PSSetShader(m_imguiPS, nullptr, 0u);
  
  	context->IASetInputLayout(m_inputLayout);
  	context->OMSetBlendState(m_blendState, nullptr, 0xFFFFFFFF);
  	context->PSSetSamplers(0, 1, &m_samplerState);
  
  	context->VSSetConstantBuffers(0, 1, &m_constantBuffer);
  
  	context->PSSetShaderResources(0, 1, &m_textureSRV);
  
  	context->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
  
  	context->RSSetState(m_rasterizerState);
  
  	// trigger allocation of new vertex buffer as needed
  	{
  		D3D11_MAPPED_SUBRESOURCE mappedResource = {};
  		if (S_OK == context->Map(m_vertexBuffer, 0u, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource))
  		{
  			context->Unmap(m_vertexBuffer, 0u);
  		}
  	}
  
  	UINT vertexStride = sizeof(Vertex);
  	UINT offset = 0u;
  	context->IASetVertexBuffers(0, 1, &m_vertexBuffer, &vertexStride, &offset);
  
  	// map allocated vertex buffer
  	{
  		D3D11_MAPPED_SUBRESOURCE mappedResource = {};
  		if (S_OK == context->Map(m_vertexBuffer, 0u, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource))
  		{
  			m_vertexBufferData = (Vertex*)mappedResource.pData;
  		}
  		else
  		{
  			m_vertexBufferData = nullptr;
  		}
  	}
  }
  
  static void imguiGraphFlush()
  {
  	ID3D11DeviceContext* context = m_deviceContext;
  
  	// unmap vertex buffer
  	context->Unmap(m_vertexBuffer, 0u);
  
  	Scissor& p = m_stateScissor;
  	if (p.beginIdx < p.stopIdx)
  	{
  		int winH = m_winH;
  		D3D11_RECT rect;
  		rect.left = p.x;
  		rect.right = p.x + p.width;
  		rect.top = (winH) - (p.y + p.height);
  		rect.bottom = (winH) - (p.y);
  		context->RSSetScissorRects(1, &rect);
  
  		UINT vertexCount = (p.stopIdx - p.beginIdx);
  		UINT startIndex = p.beginIdx;
  		context->DrawInstanced(vertexCount, 1, startIndex, 0);
  	}
  
  	// map allocated vertex buffer
  	{
  		D3D11_MAPPED_SUBRESOURCE mappedResource = {};
  		if (S_OK == context->Map(m_vertexBuffer, 0u, D3D11_MAP_WRITE_NO_OVERWRITE, 0, &mappedResource))
  		{
  			m_vertexBufferData = (Vertex*)mappedResource.pData;
  		}
  		else
  		{
  			m_vertexBufferData = nullptr;
  		}
  	}
  }
  
  void imguiGraphRecordEndD3D11()
  {
  	ID3D11DeviceContext* context = m_deviceContext;
  
  	context->OMSetBlendState(nullptr,nullptr,0xFFFFFFFF);
  
  	// unmap vertex buffer
  	context->Unmap(m_vertexBuffer, 0u);
  
  	// restore scissor
  	Scissor& p = m_stateScissor;
  	int winH = m_winH;
  	D3D11_RECT rect;
  	rect.left = p.x;
  	rect.right = p.x + p.width;
  	rect.top = (winH) - (p.y + p.height);
  	rect.bottom = (winH) - (p.y);
  	context->RSSetScissorRects(1, &rect);
  
  	context->RSSetState(nullptr);
  }
  
  void imguiGraphEnableScissorD3D11(int x, int y, int width, int height)
  {
  	// mark end of last region
  	m_stateScissor.stopIdx = m_stateVertIdx;
  
  	imguiGraphFlush();
  
  	m_stateScissor.beginIdx = m_stateVertIdx;
  	m_stateScissor.stopIdx = m_stateVertIdx;
  	m_stateScissor.x = x;
  	m_stateScissor.y = y;
  	m_stateScissor.width = width;
  	m_stateScissor.height = height;
  }
  
  void imguiGraphDisableScissorD3D11()
  {
  	if (m_stateVertIdx == 0) return;
  
  	// mark end of last region
  	m_stateScissor.stopIdx = m_stateVertIdx;
  
  	imguiGraphFlush();
  
  	m_stateScissor.beginIdx = m_stateVertIdx;
  	m_stateScissor.stopIdx = m_stateVertIdx;
  	m_stateScissor.x = 0;
  	m_stateScissor.y = 0;
  	m_stateScissor.width = m_winW;
  	m_stateScissor.height = m_winH;
  }
  
  void imguiGraphVertex2fD3D11(float x, float y)
  {
  	float v[2] = { x,y };
  	imguiGraphVertex2fvD3D11(v);
  }
  
  void imguiGraphVertex2fvD3D11(const float* v)
  {
  	// update state
  	m_stateVert.x = v[0];
  	m_stateVert.y = v[1];
  
  	Vertex* vdata = m_vertexBufferData;
  
  	// push vertex
  	if ((m_stateVertIdx) < m_maxVertices)
  	{
  		vdata[m_stateVertIdx++] = m_stateVert;
  	}
  }
  
  void imguiGraphTexCoord2fD3D11(float u, float v)
  {
  	m_stateVert.u = u;
  	m_stateVert.v = v;
  }
  
  void imguiGraphColor4ubD3D11(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
  {
  	m_stateVert.rgba[0] = red;
  	m_stateVert.rgba[1] = green;
  	m_stateVert.rgba[2] = blue;
  	m_stateVert.rgba[3] = alpha;
  }
  
  void imguiGraphColor4ubvD3D11(const uint8_t* v)
  {
  	m_stateVert.rgba[0] = v[0];
  	m_stateVert.rgba[1] = v[1];
  	m_stateVert.rgba[2] = v[2];
  	m_stateVert.rgba[3] = v[3];
  }
  
  void imguiGraphFontTextureEnableD3D11()
  {
  
  }
  
  void imguiGraphFontTextureDisableD3D11()
  {
  	m_stateVert.u = -1.f;
  	m_stateVert.v = -1.f;
  }
  
  void imguiGraphFontTextureInitD3D11(unsigned char* data)
  {
  	ID3D11DeviceContext* context = m_deviceContext;
  
  	// create texture
  	{
  		UINT width = 512;
  		UINT height = 512;
  
  		D3D11_TEXTURE2D_DESC texDesc = {};
  		texDesc.Width = width;
  		texDesc.Height = height;
  		texDesc.MipLevels = 1;
  		texDesc.ArraySize = 1;
  		texDesc.Format = DXGI_FORMAT_R8_UNORM;
  		texDesc.SampleDesc.Count = 1;
  		texDesc.SampleDesc.Quality = 0u;
  		texDesc.Usage = D3D11_USAGE_IMMUTABLE;
  		texDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
  		texDesc.CPUAccessFlags = 0;
  		texDesc.MiscFlags = 0;
  
  		D3D11_SUBRESOURCE_DATA initData = {};
  		initData.pSysMem = data;
  		initData.SysMemPitch = width;
  
  		if (m_device->CreateTexture2D(&texDesc, &initData, &m_texture))
  		{
  			return;
  		}
  
  		if (m_device->CreateShaderResourceView(m_texture, nullptr, &m_textureSRV))
  		{
  			return;
  		}
  	}
  
  }
  
  void imguiGraphFontTextureReleaseD3D11()
  {
  	COMRelease(m_texture);
  	COMRelease(m_textureSRV);
  }