// SwiftShader Software Renderer
//
// Copyright(c) 2014 Google Inc.
//
// All rights reserved. No part of this software may be copied, distributed, transmitted,
// transcribed, stored in a retrieval system, translated into any human or computer
// language by any means, or disclosed to third parties without the explicit written
// agreement of Google Inc. Without such an agreement, no rights or licenses, express
// or implied, including but not limited to any patent rights, are granted to you.
//
// libRAD.cpp: Implements the exported Radiance functions.

#include "main.h"
#include "Context.h"
#include "Program.h"
#include "Shader.h"
#include "Texture.h"
#include "common/debug.h"
#include "Common/Version.h"
#include "Main/Register.hpp"
#include "Surface.h"

#define GL_APICALL
#include <RAD/rad.h>

#include <limits>
#include <deque>
#include <set>

namespace rad
{
class Object;
typedef std::set<const Object*> ObjectPool;

class Device
{
public:
	Device()
	{
		referenceCount = 1;
	}

	void reference()
	{
		sw::atomicIncrement(&referenceCount);
	}

	void release()
	{
		ASSERT(referenceCount > 0);

		if(referenceCount > 0)
		{
			sw::atomicDecrement(&referenceCount);
		}

		if(referenceCount == 0)
		{
			delete this;
		}
	}

	void addObject(const Object *object)
	{
		pool.insert(object);
	}

	void removeObject(const Object *object)
	{
		pool.erase(object);
	}

private:
	virtual ~Device()
	{
		ASSERT(referenceCount == 0);

		size_t leakedObjectCount = pool.size();

		if(leakedObjectCount != 0)
		{
			const Object *leakedObject = *pool.begin();
			ASSERT(!leakedObject);
		}
	}

	volatile int referenceCount;

	ObjectPool pool;
};

class Object
{
public:
	Object(Device *device) : device(device)
	{
		referenceCount = 1;

		device->addObject(this);
	}

	void reference()
	{
		sw::atomicIncrement(&referenceCount);
	}

	void release()
	{
		ASSERT(referenceCount > 0);

		if(referenceCount > 0)
		{
			sw::atomicDecrement(&referenceCount);
		}

		if(referenceCount == 0)
		{
			device->removeObject(this);

			delete this;
		}
	}

protected:
	virtual ~Object()
	{
		ASSERT(referenceCount == 0);
	}

	Device *const device;

private:
	volatile int referenceCount;
};

static void Release(Object *object)
{
	if(object)
	{
		object->release();
	}
}

class Buffer : public Object
{
public:
	Buffer(Device *device) : Object(device)
	{
		buffer = nullptr;
		access = 0;      // FIXME: default?
		mapAccess = 0;   // FIXME: default?
	}

	virtual ~Buffer()
	{
		if(buffer)
		{
			buffer->destruct();
		}
	}

	void storage(RADsizei size)
	{
		ASSERT(!buffer);
		const int padding = 1024;   // For SIMD processing of vertices
		buffer = new sw::Resource(size + padding);
	}

	void *map()
	{
		ASSERT(buffer);
		return const_cast<void*>(buffer->data());
	}

	sw::Resource *buffer;

	RADbitfield access;
	RADbitfield mapAccess;
};

class Sampler : public Object
{
public:
	Sampler(Device *device) : Object(device)
	{
		defaults();
	}

	virtual ~Sampler()
	{
	}

	void defaults()
	{
		minFilter = RAD_MIN_FILTER_NEAREST_MIPMAP_LINEAR;   // FIXME: default?
		magFilter = RAD_MAG_FILTER_LINEAR;                  // FIXME: default?

		wrapModeS = RAD_WRAP_MODE_REPEAT;   // FIXME: default?
		wrapModeT = RAD_WRAP_MODE_REPEAT;   // FIXME: default?
		wrapModeR = RAD_WRAP_MODE_REPEAT;   // FIXME: default?

		minLod = -1000.0f;   // FIXME: default?
		maxLod = 1000.0f;   // FIXME: default?
		lodBias = 0.0f;
		compareMode = RAD_COMPARE_MODE_NONE;
		compareFunc = RAD_COMPARE_FUNC_LEQUAL;   // FIXME: default?
		borderColor[0] = 0.0f;   // FIXME: default?
		borderColor[1] = 0.0f;   // FIXME: default?
		borderColor[2] = 0.0f;   // FIXME: default?
		borderColor[3] = 0.0f;   // FIXME: default?
		borderColorInt[0] = 0;   // FIXME: default?
		borderColorInt[1] = 0;   // FIXME: default?
		borderColorInt[2] = 0;   // FIXME: default?
		borderColorInt[3] = 0;   // FIXME: default?
	}

	RADminFilter minFilter;
	RADmagFilter magFilter;

	RADwrapMode wrapModeS;
	RADwrapMode wrapModeT;
	RADwrapMode wrapModeR;

	RADfloat minLod;
	RADfloat maxLod;
	RADfloat lodBias;
	RADcompareMode compareMode;
	RADcompareFunc compareFunc;
	RADfloat borderColor[4];
	RADuint borderColorInt[4];
};

class Texture;

struct TextureSampler
{
	TextureSampler(Texture *texture, Sampler *sampler, RADtextureTarget target, RADinternalFormat internalFormat, RADuint minLevel, RADuint numLevels, RADuint minLayer, RADuint numLayers);

	virtual ~TextureSampler();

	Texture *texture;
	Sampler *sampler;
	RADtextureTarget target;
	RADinternalFormat internalFormat;
	RADuint minLevel;
	RADuint numLevels;
	RADuint minLayer;
	RADuint numLayers;
};

class Texture : public Object
{
public:
	Texture(Device *device) : Object(device)
	{
		texture = nullptr;
		access = 0;   // FIXME: default?
		//target = 0;
		//levels = 0;
		//internalFormat = 0;
		//width = 0;
		//height = 0;
		//depth = 0;
		//samples = 0;
	}

	virtual ~Texture()
	{
		if(texture)
		{
			texture->release();
		}

		for(size_t i = 0; i < textureSamplers.size(); i++)
		{
			delete textureSamplers[i];
		}
	}

	es2::Texture *texture;

	RADbitfield access;

	RADtextureTarget target;
	RADsizei levels;
	RADinternalFormat internalFormat;
	RADsizei width;
	RADsizei height;
	RADsizei depth;
	RADsizei samples;

	std::vector<TextureSampler*> textureSamplers;
};

TextureSampler::TextureSampler(Texture *texture, Sampler *sampler, RADtextureTarget target, RADinternalFormat internalFormat, RADuint minLevel, RADuint numLevels, RADuint minLayer, RADuint numLayers)
{
	ASSERT(texture);
	ASSERT(sampler);
	//texture->reference();
	this->texture = texture;
	//sampler->reference();
	this->sampler = sampler;
	this->target = target;
	this->internalFormat = internalFormat;
	this->minLevel = minLevel;
	this->numLevels = numLevels;
	this->minLayer = minLayer;
	this->numLayers = numLayers;
}

TextureSampler::~TextureSampler()
{
	//texture->release();
	//sampler->release();
}

class Pass;

class Program : public Object
{
public:
	Program(Device *device) : Object(device)
	{
		program = new es2::Program(0);
		program->addRef();
	}

	virtual ~Program()
	{
		program->release();
	}

	es2::Program *program;
};

template<typename T, size_t n>
inline size_t arraySize(T(&)[n])
{
	return n;
}

const int RAD_MAX_COLOR_TARGETS = 1;

struct ColorState : public Object
{
	ColorState(Device *device) : Object(device)
	{
		defaults();
	}

	void defaults()
	{
		enable = RAD_FALSE;

		for(size_t i = 0; i < arraySize(blend); i++)
		{
			blend[i].defaults();
		}

		numTargets = 1;
		logicOpEnable = RAD_FALSE;
		logicOp = RAD_LOGIC_OP_COPY;
		alphaToCoverageEnable = RAD_FALSE;
		blendColor[0] = 0.0f;
		blendColor[1] = 0.0f;
		blendColor[2] = 0.0f;
		blendColor[3] = 0.0f;
	}

	RADboolean enable;
	
	struct Blend
	{
		Blend()
		{
			defaults();
		}

		void defaults()
		{
			enable = RAD_FALSE;
			srcFunc = RAD_BLEND_FUNC_ONE;
			dstFunc = RAD_BLEND_FUNC_ZERO;
			srcFuncAlpha = RAD_BLEND_FUNC_ONE;
			dstFuncAlpha = RAD_BLEND_FUNC_ZERO;
			modeRGB = RAD_BLEND_EQUATION_ADD;
			modeAlpha = RAD_BLEND_EQUATION_ADD;
			maskRGBA[0] = RAD_TRUE;
			maskRGBA[1] = RAD_TRUE;
			maskRGBA[2] = RAD_TRUE;
			maskRGBA[3] = RAD_TRUE;
		}

		RADboolean enable;
		RADblendFunc srcFunc;
		RADblendFunc dstFunc;
		RADblendFunc srcFuncAlpha;
		RADblendFunc dstFuncAlpha;
		RADblendEquation modeRGB;
		RADblendEquation modeAlpha;
		RADboolean maskRGBA[4];
	};

	Blend blend[RAD_MAX_COLOR_TARGETS];

	RADuint numTargets;
	RADboolean logicOpEnable;
	RADlogicOp logicOp;
	RADboolean alphaToCoverageEnable;
	RADfloat blendColor[4];
};

struct RasterState : public Object
{
	RasterState(Device *device) : Object(device)
	{
		defaults();
	}

	void defaults()
	{
		pointSize = 1.0f;
		lineWidth = 1.0f;
		cullFace = RAD_FACE_NONE;
		frontFace = RAD_FRONT_FACE_CW;
		polygonMode = RAD_POLYGON_MODE_FILL;
	
		offsetFactor = 0.0f;
		offsetUnits = 0.0f;
		offsetClamp = 0.0f;

		polygonOffsetEnables = RAD_POLYGON_OFFSET_NONE;
		discardEnable = RAD_FALSE;
		multisampleEnable = RAD_TRUE;

		samples = 0;
		sampleMask = ~0;
	}

	RADfloat pointSize;
	RADfloat lineWidth;
	RADfaceBitfield cullFace;
	RADfrontFace frontFace;
	RADpolygonMode polygonMode;
	
	RADfloat offsetFactor;
	RADfloat offsetUnits;
	RADfloat offsetClamp;

	RADpolygonOffsetEnables polygonOffsetEnables;
	RADboolean discardEnable;
	RADboolean multisampleEnable;

	RADuint samples;
	RADuint sampleMask;
};

struct DepthStencilState : public Object
{
	DepthStencilState(Device *device) : Object(device)
	{
		defaults();
	}

	void defaults()
	{
		depthTestEnable = RAD_FALSE;
		depthWriteEnable = RAD_FALSE;
		depthFunc = RAD_DEPTH_FUNC_LESS;
		stencilTestEnable = RAD_FALSE;

		stencilFuncFront = RAD_STENCIL_FUNC_ALWAYS;
		stencilRefFront = 0;
		stencilMaskFront = ~0;

		stencilFuncBack = RAD_STENCIL_FUNC_ALWAYS;
		stencilRefBack = 0;
		stencilMaskBack = ~0;

		stencilFailOpFront = RAD_STENCIL_OP_KEEP;
		depthFailOpFront = RAD_STENCIL_OP_KEEP;
		depthPassOpFront = RAD_STENCIL_OP_KEEP;

		stencilFailOpBack = RAD_STENCIL_OP_KEEP;
		depthFailOpBack = RAD_STENCIL_OP_KEEP;
		depthPassOpBack = RAD_STENCIL_OP_KEEP;

		stencilWriteMaskFront = ~0;
		stencilWriteMaskBack = ~0;
	}

	RADboolean depthTestEnable;
	RADboolean depthWriteEnable;
	RADdepthFunc depthFunc;
	RADboolean stencilTestEnable;
	
	RADstencilFunc stencilFuncFront;
	RADint stencilRefFront;
	RADuint stencilMaskFront;

	RADstencilFunc stencilFuncBack;
	RADint stencilRefBack;
	RADuint stencilMaskBack;

	RADstencilOp stencilFailOpFront;
	RADstencilOp depthFailOpFront;
	RADstencilOp depthPassOpFront;

	RADstencilOp stencilFailOpBack;
	RADstencilOp depthFailOpBack;
	RADstencilOp depthPassOpBack;

	RADuint stencilWriteMaskFront;
	RADuint stencilWriteMaskBack;
};

const int RAD_MAX_VERTEX_ATTRIB = 16;
const int RAD_MAX_VERTEX_BINDING = 16;

struct VertexState : public Object
{
	VertexState(Device *device) : Object(device)
	{
		defaults();
	}

	void defaults()
	{
		for(size_t i = 0; i < arraySize(attrib); i++)
		{
			attrib[i].defaults();
		}

		for(size_t i = 0; i < arraySize(binding); i++)
		{
			binding[i].defaults();
		}
	}

	struct Attribute
	{
		Attribute()
		{
			defaults();
		}

		void defaults()
		{
			enable = RAD_FALSE;
			bindingIndex = 0;
			numComponents = 0;
			bytesPerComponent = 0;
			type = RAD_ATTRIB_TYPE_SNORM;
			relativeOffset = 0;
		}

		RADboolean enable;
		
		RADint bindingIndex;
		
		RADint numComponents;
		RADint bytesPerComponent;
		RADattribType type;
		RADuint relativeOffset;
	};

	Attribute attrib[RAD_MAX_VERTEX_ATTRIB];

	struct Binding
	{
		Binding()
		{
			defaults();
		}

		void defaults()
		{
			group = 0;
			index = 0;
			stride = 0;
		}

		RADint group;
		RADint index;

		RADuint stride;
	};

	Binding binding[RAD_MAX_VERTEX_BINDING];
};

struct FormatState : public Object
{
	FormatState(Device *device) : Object(device)
	{
		defaults();
	}

	void defaults()
	{
		for(size_t i = 0; i < arraySize(colorFormat); i++)
		{
			colorFormat[i] = RAD_FORMAT_NONE;
		}

		depthFormat = RAD_FORMAT_NONE;
		stencilFormat = RAD_FORMAT_NONE;
		colorSamples = 0;
		depthStencilSamples = 0;
	}

	RADinternalFormat colorFormat[RAD_MAX_COLOR_TARGETS];
	RADinternalFormat depthFormat;
	RADinternalFormat stencilFormat;
	RADuint colorSamples;
	RADuint depthStencilSamples;
};

class Pipeline : public Object
{
public:
	Pipeline(Device *device) : Object(device)
	{
		vertexProgram = nullptr;
		fragmentProgram = nullptr;
		vertexState = nullptr;
		colorState = nullptr;
		rasterState = nullptr;
		depthStencilState = nullptr;
		formatState = nullptr;
		primitiveType = RAD_TRIANGLES;
	}

	virtual ~Pipeline()
	{
		Release(vertexProgram);
		Release(fragmentProgram);
		Release(vertexState);
		Release(colorState);
		Release(rasterState);
		Release(depthStencilState);
		Release(formatState);
	}

	Program *vertexProgram;
	Program *fragmentProgram;
	VertexState *vertexState;
	ColorState *colorState;
	RasterState *rasterState;
	DepthStencilState *depthStencilState;
	FormatState *formatState;
	RADprimitiveType primitiveType;
};

const int RAD_MAX_ATTACHMENTS = 1 /*depth*/ + 1 /*stencil*/ + RAD_MAX_COLOR_TARGETS;

class Pass : public Object
{
public:
	Pass(Device *device) : Object(device)
	{
		defaults();
	}

	virtual ~Pass()
	{
		for(size_t i = 0; i < arraySize(colorTarget); i++)
		{
			if(colorTarget[i])
			{
				colorTarget[i]->release();
			}
		}

		if(depthTarget)
		{
			depthTarget->release();
		}

		if(stencilTarget)
		{
			stencilTarget->release();
		}
	}

	void defaults()
	{
		numColors = 0;

		for(size_t i = 0; i < arraySize(colorTarget); i++)
		{
			colorTarget[i] = nullptr;
		}

		depthTarget = nullptr;
		stencilTarget = nullptr;

		for(size_t i = 0; i < arraySize(preserveEnable); i++)
		{
			preserveEnable[i] = RAD_TRUE;
		}
	}

	RADuint numColors;
	es2::Image *colorTarget[RAD_MAX_COLOR_TARGETS];
	es2::Image *depthTarget;
	es2::Image *stencilTarget;

	RADboolean preserveEnable[RAD_MAX_ATTACHMENTS];

	//RADuint numDiscardTextures;
	//const RADtexture *discardTextures;
	//const RADoffset2D *discardOffsets;
	//
	//RADtexture resolveTexture[RAD_MAX_ATTACHMENTS];

	//RADuint numStoreTextures;
	//const RADtexture *storeTextures;
	//const RADoffset2D *storeOffsets;
	//
	//const RADrect2D *clipRect;

	//RADuint numDependencies;
	//const RADpass *dependentPasses;
	//const RADbitfield *srcMask;
	//const RADbitfield *dstMask;
	//const RADbitfield *flushMask;
	//const RADbitfield *invalidateMask;

	//RADboolean tilingBoundary;

	//RADuint tileFilterWidth;
	//RADuint tileTilterHeight;

	//RADuint bytesPerPixel;
	//RADuint footprintFilterWidth;
	//RADuint footprintFilterHeight;
};

class Command
{
public:
	Command()
	{
		//pipeline = nullptr;
		//pass = nullptr;
	}

	virtual ~Command()
	{
		//Release(pipeline);
		//Release(pass);
	}

	virtual void execute(Pipeline *pipeline, Pass *pass) = 0;   // FIXME: Just queue as parameter?

	virtual bool isPresent() const
	{
		return false;
	}

	//void setPipeline(Pipeline *pipeline)
	//{
	//	if(pipeline)
	//	{
	//		pipeline->reference();
	//		this->pipeline = pipeline;
	//	}
	//}

	//void setPass(Pass *pass)
	//{
	//	if(pass)
	//	{
	//		pass->reference();
	//		this->pass = pass;
	//	}
	//}

protected:
	//Pipeline *pipeline;   // FIXME: Command-specific state captured in their constructor?
	//Pass *pass;
};

class CopyBufferToImage : public Command
{
public:
	CopyBufferToImage(Buffer *buffer, RADintptr bufferOffset, Texture *texture, RADint level, RADuint xoffset, RADuint yoffset, RADuint zoffset, RADsizei width, RADsizei height, RADsizei depth)
	{
		ASSERT(buffer);
		ASSERT(texture);
		buffer->reference();
		this->buffer = buffer;
		this->bufferOffset = bufferOffset;
		texture->reference();
		this->texture = texture;
		this->level = level;
		this->xoffset = xoffset;
		this->yoffset = yoffset;
		this->zoffset = zoffset;
		this->width = width;
		this->height = height;
		this->depth = depth;
	}

	virtual ~CopyBufferToImage()
	{
		buffer->release();
		texture->release();
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		ASSERT(depth == 1);   // FIXME: Unimplemented
		egl::Image *image = texture->texture->getRenderTarget(GL_TEXTURE_2D, level);
		uint8_t *output = static_cast<uint8_t*>(image->lock(xoffset, yoffset, sw::LOCK_WRITEONLY));   // FIXME: Discard if whole image
		unsigned int pitch = image->getPitch();
		const uint8_t *input = static_cast<const uint8_t*>(buffer->map()) + bufferOffset;   // FIXME: Necessary to lock?

		ASSERT(texture->internalFormat == RAD_RGBA8);   // FIXME: Unimplemented
		int bytesPerTexel = 4;

		for(int y = 0; y < height; y++)
		{
			const uint8_t *source = input + y * (width * bytesPerTexel);
			uint8_t *dest = output + y * pitch;

			memcpy(dest, source, width * bytesPerTexel);
		}

		image->unlock();
		image->release();
	}

	Buffer *buffer;
	RADintptr bufferOffset;
	Texture *texture;
	RADint level;
	RADuint xoffset;
	RADuint yoffset;
	RADuint zoffset;
	RADsizei width;
	RADsizei height;
	RADsizei depth;
};

class Scissor : public Command
{
public:
	Scissor(RADint x, RADint y, RADint w, RADint h)
	{
		this->x = x;
		this->y = y;
		this->w = w;
		this->h = h;
	}

	virtual ~Scissor()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		es2::Context *context = es2::getContext();
		context->setScissorParams(x, y, w, h);
	}

	RADint x;
	RADint y;
	RADint w;
	RADint h;
};

class Viewport : public Command
{
public:
	Viewport(RADint x, RADint y, RADint w, RADint h)
	{
		this->x = x;
		this->y = y;
		this->w = w;
		this->h = h;
	}

	virtual ~Viewport()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		es2::Context *context = es2::getContext();
		context->setViewportParams(x, y, w, h);
	}

	RADint x;
	RADint y;
	RADint w;
	RADint h;
};

class ClearColor : public Command
{
public:
	ClearColor(RADuint index, const RADfloat *color)
	{
		this->index = index;
		this->color[0] = color[0];
		this->color[1] = color[1];
		this->color[2] = color[2];
		this->color[3] = color[3];
	}

	virtual ~ClearColor()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		ASSERT(pass);   // FIXME: Error if no beginPass
		ASSERT(index < pass->numColors);
		es2::Image *image = pass->colorTarget[index];

		GLenum format = image->getFormat();
		GLenum type = image->getType();
		ASSERT(format == GL_RGBA);   // FIXME
		ASSERT(type == GL_UNSIGNED_BYTE);   //  FIXME

		es2::Context *context = es2::getContext();
		int x0 = context->mState.scissorX;
		int y0 = context->mState.scissorY;
		int width = context->mState.scissorWidth;
		int height = context->mState.scissorHeight;

		image->clearColorBuffer(sw::Color<float>(color[0], color[1], color[2], color[3]), 0xF, x0, y0, width, height);
	}

	RADuint index;
	RADfloat color[4];
};

class ClearDepth : public Command
{
public:
	ClearDepth(RADfloat depth)
	{
		this->depth = depth;
	}

	virtual ~ClearDepth()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		ASSERT(pass);   // FIXME: Error if no beginPass
		es2::Image *image = pass->depthTarget;
		
		es2::Context *context = es2::getContext();
		int x0 = context->mState.scissorX;
		int y0 = context->mState.scissorY;
		int width = context->mState.scissorWidth;
		int height = context->mState.scissorHeight;

		image->clearDepthBuffer(depth, x0, y0, width, height);
	}

	RADfloat depth;
};

class Present : public Command
{
public:
	Present(Texture *texture)
	{
		texture->reference();
		this->texture = texture;
	}

	virtual ~Present()
	{
		Release(texture);
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		es2::Context *context = es2::getContext();
		sw::FrameBuffer *frameBuffer = (*es2::getDisplay()->mSurfaceSet.begin())->frameBuffer;

		egl::Image *image = texture->texture->getRenderTarget(GL_TEXTURE_2D, 0);
		void *source = image->lockInternal(0, 0, 0, sw::LOCK_READONLY, sw::PUBLIC);
		frameBuffer->flip(source, image->getInternalFormat());
		image->unlockInternal();
		image->release();
	}

	virtual bool isPresent() const
	{
		return true;
	}

	Texture *texture;
};

class DrawElements : public Command
{
public:
	DrawElements(RADprimitiveType mode, RADindexType type, RADsizei count, sw::Resource *indexBuffer, RADuint offset)
	{
		this->mode = mode;
		this->type = type;
		this->count = count;
		this->indexBuffer = indexBuffer;
		this->offset = offset;
	}

	~DrawElements()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		es2::Context *context = es2::getContext();

		ASSERT(pipeline->vertexProgram == pipeline->fragmentProgram);   // FIXME
		context->mState.program = pipeline->vertexProgram->program;

		GLenum glType = GL_UNSIGNED_SHORT;
		switch(type)
		{
		case RAD_INDEX_UNSIGNED_BYTE:  glType = GL_UNSIGNED_BYTE;  break;
		case RAD_INDEX_UNSIGNED_SHORT: glType = GL_UNSIGNED_SHORT; break;
		case RAD_INDEX_UNSIGNED_INT:   glType = GL_UNSIGNED_INT;   break;
		default: UNREACHABLE();
		}

		GLenum glMode = GL_TRIANGLES;
		switch(mode)
		{
		case RAD_TRIANGLES: glMode = GL_TRIANGLES; break;
		default: UNREACHABLE();
		}

		context->mState.colorBuffer = pass->colorTarget[0];
		context->mState.depthBuffer = pass->depthTarget;

		context->mState.depthTest = pipeline->depthStencilState->depthTestEnable;

		GLenum glDepth = GL_LESS;
		switch(pipeline->depthStencilState->depthFunc)
		{
		case RAD_DEPTH_FUNC_NEVER:    glDepth = GL_NEVER;    break;
		case RAD_DEPTH_FUNC_LESS:     glDepth = GL_LESS;     break;
		case RAD_DEPTH_FUNC_EQUAL:    glDepth = GL_EQUAL;    break;
		case RAD_DEPTH_FUNC_LEQUAL:   glDepth = GL_LEQUAL;   break;
		case RAD_DEPTH_FUNC_GREATER:  glDepth = GL_GREATER;  break;
		case RAD_DEPTH_FUNC_NOTEQUAL: glDepth = GL_NOTEQUAL; break;
		case RAD_DEPTH_FUNC_GEQUAL:   glDepth = GL_GEQUAL;   break;
		case RAD_DEPTH_FUNC_ALWAYS:   glDepth = GL_ALWAYS;   break;
		default: UNREACHABLE();
		}

		context->mState.elementArrayBuffer = indexBuffer;
		context->drawElements(glMode, count, glType, 0);
	}

	RADprimitiveType mode;
	RADindexType type;
	RADsizei count;
	sw::Resource *indexBuffer;
	RADuint offset;
};

class BindGroup : public Command
{
public:
	BindGroup(RADbitfield stages, RADuint group, RADuint count, sw::Resource *buffer, RADuint offset)
	{
		this->stages = stages;
		this->group = group;
		this->count = count;
		this->buffer = buffer;
		this->offset = offset;
	}

	virtual ~BindGroup()
	{
	}

	virtual void execute(Pipeline *pipeline, Pass *pass)
	{
		es2::Context *context = es2::getContext();

		const RADbindGroupElement *groupElements = static_cast<const RADbindGroupElement*>(buffer->data());

		// FIXME: Should parse the layout out of the shaders
		es2::Program *program = pipeline->vertexProgram->program;

		sw::Resource *element0 = reinterpret_cast<sw::Resource*>(groupElements[0].handle);
		uintptr_t offset0 = static_cast<uintptr_t>(groupElements[0].offset);
		int position = program->getAttributeLocation("position");
		context->setVertexAttribState(position, element0, 3, GL_FLOAT, GL_TRUE, 0, offset0);
		context->setEnableVertexAttribArray(position, true);

		sw::Resource *element1 = reinterpret_cast<sw::Resource*>(groupElements[1].handle);
		uintptr_t offset1 = static_cast<uintptr_t>(groupElements[1].offset);
		int tc = program->getAttributeLocation("tc");
		context->setVertexAttribState(tc, element1, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, offset1);
		context->setEnableVertexAttribArray(tc, true);

		sw::Resource *element2 = reinterpret_cast<sw::Resource*>(groupElements[2].handle);
		const void *uniform = static_cast<const uint8_t*>(element2->data()) + groupElements[2].offset;
		int scale = program->getUniformLocation("scale");
		program->setUniform4fv(scale, 1, (const GLfloat*)uniform);

		rad::TextureSampler *element3 = reinterpret_cast<rad::TextureSampler*>(groupElements[3].handle);
		int tex = program->getUniformLocation("tex");
		int sampler = 0;
		program->setUniform1iv(tex, 1, &sampler);
		context->applyTexture(sw::SAMPLER_PIXEL, sampler, element3->texture->texture);
	}

	RADbitfield stages;
	RADuint group;
	RADuint count;
	sw::Resource *buffer;
	RADuint offset;
};

class Queue : public Object
{
public:
	Queue(Device *device) : Object(device)
	{
		graphicsPipeline = nullptr;
		pass = nullptr;
	}

	virtual ~Queue()
	{
		for(size_t i = 0; i < commands.size(); i++)
		{
			delete commands[i];
		}

		Release(graphicsPipeline);

		ASSERT(!pass);   // FIXME: No matching endPass
		Release(pass);
	}

	void submit(Command *command)
	{
		// FIXME: Make BeginPass/EndPass/BindPipeline commands too?
		//command->setPipeline(graphicsPipeline);
		//command->setPass(pass);

		if(false)   // Queued execution
		{
			commands.push_back(command);

			if(command->isPresent())
			{
				// FIXME: Flush
			}
		}
		else   // Immediate execution
		{
			command->execute(graphicsPipeline, pass);

			delete command;
		}
	}

	void bindPipeline(RADpipelineType pipelineType, Pipeline *pipeline)
	{
		if(pipelineType == RAD_PIPELINE_TYPE_GRAPHICS)
		{
			pipeline->reference();

			if(graphicsPipeline)
			{
				graphicsPipeline->release();
			}

			graphicsPipeline = pipeline;
		}
		else UNREACHABLE();
	}

	void beginPass(rad::Pass *pass)
	{
		ASSERT(!this->pass);   // FIXME: Can be nested?
		pass->reference();
		this->pass = pass;
	}

	void endPass(rad::Pass *pass)
	{
		ASSERT(this->pass == pass);   // FIXME: Can be nested?
		this->pass->release();
		this->pass = nullptr;
	}

private:
	std::deque<Command*> commands;
	Pipeline *graphicsPipeline;
	Pass *pass;
};
}

extern "C"
{
RADdevice RADAPIENTRY radCreateDevice(void)
{
	static rad::Device *device = new rad::Device();
	return reinterpret_cast<RADdevice>(device);
}

void RADAPIENTRY radReferenceDevice(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	return radDevice->reference();
}
void RADAPIENTRY radReleaseDevice(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	return radDevice->release();
}

RADuint RADAPIENTRY radGetTokenHeader(RADdevice device, RADtokenName name) {UNIMPLEMENTED(); return 0;}

RADqueue RADAPIENTRY radCreateQueue(RADdevice device, RADqueueType queuetype)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Queue *queue = new rad::Queue(radDevice);
	return reinterpret_cast<RADqueue>(queue);
}

void RADAPIENTRY radReferenceQueue(RADqueue queue)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	return radQueue->reference();
}

void RADAPIENTRY radReleaseQueue(RADqueue queue)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	return radQueue->release();
}

void RADAPIENTRY radQueueTagBuffer(RADqueue queue, RADbuffer buffer) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueTagTexture(RADqueue queue, RADtexture texture) {UNIMPLEMENTED();}

void RADAPIENTRY radQueueSubmitCommands(RADqueue queue, RADuint numCommands, const RADcommandHandle *handles)
{
	return;
}

void RADAPIENTRY radFlushQueue(RADqueue queue) {UNIMPLEMENTED();}
void RADAPIENTRY radFinishQueue(RADqueue queue) {UNIMPLEMENTED();}

void RADAPIENTRY radQueueViewport(RADqueue queue, RADint x, RADint y, RADint w, RADint h)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Viewport *command = new rad::Viewport(x, y, w, h);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueScissor(RADqueue queue, RADint x, RADint y, RADint w, RADint h)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Scissor *command = new rad::Scissor(x, y, w, h);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueCopyBufferToImage(RADqueue queue, RADbuffer buffer, RADintptr bufferOffset, RADtexture texture, RADint level, RADuint xoffset, RADuint yoffset, RADuint zoffset, RADsizei width, RADsizei height, RADsizei depth)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	rad::CopyBufferToImage *command = new rad::CopyBufferToImage(radBuffer, bufferOffset, radTexture, level, xoffset, yoffset, zoffset, width, height, depth);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueCopyImageToBuffer(RADqueue queue, RADbuffer buffer, RADintptr bufferOffset, RADtexture texture, RADint level, RADuint xoffset, RADuint yoffset, RADuint zoffset, RADsizei width, RADsizei height, RADsizei depth) {UNIMPLEMENTED();}

void RADAPIENTRY radQueueCopyBuffer(RADqueue queue, RADbuffer srcBuffer, RADintptr srcOffset, RADbuffer dstBuffer, RADintptr dstOffset, RADsizei size)
{
	return;
}

void RADAPIENTRY radQueueClearColor(RADqueue queue, RADuint index, const RADfloat *color)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::ClearColor *command = new rad::ClearColor(index, color);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueClearDepth(RADqueue queue, RADfloat depth)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::ClearDepth *command = new rad::ClearDepth(depth);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueClearStencil(RADqueue queue, RADuint stencil) {UNIMPLEMENTED();}

void RADAPIENTRY radQueuePresent(RADqueue queue, RADtexture texture)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	rad::Present *command = new rad::Present(radTexture);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueDrawArrays(RADqueue queue, RADprimitiveType mode, RADint first, RADsizei count) {UNIMPLEMENTED();}

void RADAPIENTRY radQueueDrawElements(RADqueue queue, RADprimitiveType mode, RADindexType type, RADsizei count, RADindexHandle indexHandle, RADuint offset)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	sw::Resource *indexBuffer = reinterpret_cast<sw::Resource*>(indexHandle);
	rad::DrawElements *command = new rad::DrawElements(mode, type, count, indexBuffer, offset);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueBindPipeline(RADqueue queue, RADpipelineType pipelineType, RADpipelineHandle pipelineHandle)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipelineHandle);
	radQueue->bindPipeline(pipelineType, radPipeline);
}

void RADAPIENTRY radQueueBindGroup(RADqueue queue, RADbitfield stages, RADuint group, RADuint count, RADbindGroupHandle groupHandle, RADuint offset)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	sw::Resource *groupBuffer = reinterpret_cast<sw::Resource*>(groupHandle);
	rad::BindGroup *command = new rad::BindGroup(stages, group, count, groupBuffer, offset);
	radQueue->submit(command);
}

void RADAPIENTRY radQueueBeginPass(RADqueue queue, RADpass pass)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radQueue->beginPass(radPass);
}

void RADAPIENTRY radQueueEndPass(RADqueue queue, RADpass pass)
{
	rad::Queue *radQueue = reinterpret_cast<rad::Queue*>(queue);
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radQueue->endPass(radPass);
}

void RADAPIENTRY radQueueSubmitDynamic(RADqueue queue, const void *dynamic, RADsizei length) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueStencilValueMask(RADqueue queue, RADfaceBitfield faces, RADuint mask) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueStencilMask(RADqueue queue, RADfaceBitfield faces, RADuint mask) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueStencilRef(RADqueue queue, RADfaceBitfield faces, RADint ref) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueBlendColor(RADqueue queue, const RADfloat *blendColor) {UNIMPLEMENTED();}
void RADAPIENTRY radQueuePointSize(RADqueue queue, RADfloat pointSize) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueLineWidth(RADqueue queue, RADfloat lineWidth) {UNIMPLEMENTED();}
void RADAPIENTRY radQueuePolygonOffsetClamp(RADqueue queue, RADfloat factor, RADfloat units, RADfloat clamp) {UNIMPLEMENTED();}
void RADAPIENTRY radQueueSampleMask(RADqueue queue, RADuint mask) {UNIMPLEMENTED();}

RADprogram RADAPIENTRY radCreateProgram(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Program *program = new rad::Program(radDevice);
	return reinterpret_cast<RADprogram>(program);
}

void RADAPIENTRY radReferenceProgram(RADprogram program)
{
	rad::Program *radProgram = reinterpret_cast<rad::Program*>(program);
	radProgram->reference();
}

void RADAPIENTRY radReleaseProgram(RADprogram program)
{
	rad::Program *radProgram = reinterpret_cast<rad::Program*>(program);
	radProgram->release();
}

void RADAPIENTRY radProgramSource(RADprogram program, RADprogramFormat format, RADsizei length, const void *source)
{
	rad::Program *radProgram = reinterpret_cast<rad::Program*>(program);

	// FIXME: Assumes first source is vertex shader, second is fragment shader
	ASSERT(length == 2);
	const char *vertexSource = static_cast<const char* const*>(source)[0];
	const char *fragmentSource = static_cast<const char* const*>(source)[1];
	GLint vertexLength = strlen(vertexSource);
	GLint fragmentLength = strlen(fragmentSource);

	es2::VertexShader *vertexShader = new es2::VertexShader(0);
	vertexShader->addRef();
	es2::FragmentShader *fragmentShader = new es2::FragmentShader(0);
	fragmentShader->addRef();

	vertexShader->setSource(1, &vertexSource, &vertexLength);
	fragmentShader->setSource(1, &fragmentSource, &fragmentLength);

	vertexShader->compile();
	fragmentShader->compile();

	radProgram->program->attachShader(vertexShader);
	radProgram->program->attachShader(fragmentShader);
	radProgram->program->link();

	vertexShader->release();     // Still referenced by program
	fragmentShader->release();   // Still referenced by program
}

RADbuffer RADAPIENTRY radCreateBuffer(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Buffer *buffer = new rad::Buffer(radDevice);
	return reinterpret_cast<RADbuffer>(buffer);
}

void RADAPIENTRY radReferenceBuffer(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	radBuffer->reference();
}

void RADAPIENTRY radReleaseBuffer(RADbuffer buffer, RADtagMode tagMode)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	radBuffer->release();
}

void RADAPIENTRY radBufferAccess(RADbuffer buffer, RADbitfield access)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	radBuffer->access = access;
}

void RADAPIENTRY radBufferMapAccess(RADbuffer buffer, RADbitfield mapAccess)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	radBuffer->mapAccess = mapAccess;
}

void RADAPIENTRY radBufferStorage(RADbuffer buffer, RADsizei size)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	radBuffer->storage(size);
}

void* RADAPIENTRY radMapBuffer(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	return radBuffer->map();
}

RADvertexHandle RADAPIENTRY radGetVertexHandle(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	return reinterpret_cast<RADvertexHandle>(radBuffer->buffer);
}

RADindexHandle RADAPIENTRY radGetIndexHandle(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	return reinterpret_cast<RADvertexHandle>(radBuffer->buffer);
}

RADuniformHandle RADAPIENTRY radGetUniformHandle(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	return reinterpret_cast<RADuniformHandle>(radBuffer->buffer);
}

RADbindGroupHandle RADAPIENTRY radGetBindGroupHandle(RADbuffer buffer)
{
	rad::Buffer *radBuffer = reinterpret_cast<rad::Buffer*>(buffer);
	return reinterpret_cast<RADbindGroupHandle>(radBuffer->buffer);
}

RADtexture RADAPIENTRY radCreateTexture(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Texture *colorState = new rad::Texture(radDevice);
	return reinterpret_cast<RADtexture>(colorState);
}

void RADAPIENTRY radReferenceTexture(RADtexture texture)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	radTexture->reference();
}

void RADAPIENTRY radReleaseTexture(RADtexture texture, RADtagMode tagMode)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	radTexture->release();
}

void RADAPIENTRY radTextureAccess(RADtexture texture, RADbitfield access)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	radTexture->access = access;
}

void RADAPIENTRY radTextureStorage(RADtexture texture, RADtextureTarget target, RADsizei levels, RADinternalFormat internalFormat, RADsizei width, RADsizei height, RADsizei depth, RADsizei samples)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	ASSERT(!radTexture->texture);

	radTexture->target = target;
	radTexture->levels = levels;
	radTexture->internalFormat = internalFormat;
	radTexture->width = width;
	radTexture->height = height;
	radTexture->depth = depth;
	radTexture->samples = samples;

	GLenum format = GL_NONE;
	GLenum type = GL_NONE;
	switch(internalFormat)
	{
	case RAD_RGBA8:
		format = GL_RGBA;
		type = GL_UNSIGNED_BYTE;
		break;
	case RAD_DEPTH24_STENCIL8:
		format = GL_DEPTH_STENCIL_OES;
		type = GL_UNSIGNED_INT_24_8_OES;
		break;
	default:
		UNIMPLEMENTED();   // FIXME
	}

	switch(target)
	{
	case RAD_TEXTURE_2D:
		{
			es2::Texture2D *tex = new es2::Texture2D();
			for(int level = 0; level < levels; level++)
			{
				tex->setImage(level, width >> level, height >> level, format, type, 1, nullptr);
			}
			tex->addRef();
			radTexture->texture = tex;
		}
		break;
	default:
		UNIMPLEMENTED();   // FIXME
	}
}

RADtextureHandle RADAPIENTRY radGetTextureSamplerHandle(RADtexture texture, RADsampler sampler, RADtextureTarget target, RADinternalFormat internalFormat, RADuint minLevel, RADuint numLevels, RADuint minLayer, RADuint numLayers)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	rad::TextureSampler *textureSampler = new rad::TextureSampler(radTexture, radSampler, target, internalFormat, minLevel, numLevels, minLayer, numLayers);
	radTexture->textureSamplers.push_back(textureSampler);   // FIXME: Check for matching existing textureSampler
	return reinterpret_cast<RADtextureHandle>(textureSampler);
}

RADrenderTargetHandle RADAPIENTRY radGetTextureRenderTargetHandle(RADtexture texture, RADtextureTarget target, RADinternalFormat internalFormat, RADuint level, RADuint minLayer, RADuint numLayers)
{
	rad::Texture *radTexture = reinterpret_cast<rad::Texture*>(texture);
	ASSERT(radTexture->texture);
	ASSERT(radTexture->internalFormat == internalFormat);
	ASSERT(minLayer == 0);
	ASSERT(numLayers == 1);
	GLenum glTarget = GL_NONE;
	switch(target)
	{
	case RAD_TEXTURE_2D:
		glTarget = GL_TEXTURE_2D;
		break;
	default:
		UNREACHABLE();
	}
	egl::Image *image = radTexture->texture->getRenderTarget(glTarget, level);
	image->release();   // FIXME: Handles are weak pointers
	return reinterpret_cast<RADrenderTargetHandle>(image);
}

RADsampler RADAPIENTRY radCreateSampler(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Sampler *sampler = new rad::Sampler(radDevice);
	return reinterpret_cast<RADsampler>(sampler);
}

void RADAPIENTRY radReferenceSampler(RADsampler sampler)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->reference();
}

void RADAPIENTRY radReleaseSampler(RADsampler sampler)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->release();
}

void RADAPIENTRY radSamplerDefault(RADsampler sampler)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->defaults();
}

void RADAPIENTRY radSamplerMinMagFilter(RADsampler sampler, RADminFilter min, RADmagFilter mag)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->minFilter = min;
	radSampler->magFilter = mag;
}

void RADAPIENTRY radSamplerWrapMode(RADsampler sampler, RADwrapMode s, RADwrapMode t, RADwrapMode r)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->wrapModeS = s;
	radSampler->wrapModeT = t;
	radSampler->wrapModeR = r;
}

void RADAPIENTRY radSamplerLodClamp(RADsampler sampler, RADfloat min, RADfloat max)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->minLod = min;
	radSampler->maxLod = max;
}

void RADAPIENTRY radSamplerLodBias(RADsampler sampler, RADfloat bias)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->lodBias = bias;
}

void RADAPIENTRY radSamplerCompare(RADsampler sampler, RADcompareMode mode, RADcompareFunc func)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->compareMode = mode;
	radSampler->compareFunc = func;
}

void RADAPIENTRY radSamplerBorderColorFloat(RADsampler sampler, const RADfloat *borderColor)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->borderColor[0] = borderColor[0];
	radSampler->borderColor[1] = borderColor[1];
	radSampler->borderColor[2] = borderColor[2];
	radSampler->borderColor[3] = borderColor[3];
}

void RADAPIENTRY radSamplerBorderColorInt(RADsampler sampler, const RADuint *borderColor)
{
	rad::Sampler *radSampler = reinterpret_cast<rad::Sampler*>(sampler);
	radSampler->borderColorInt[0] = borderColor[0];
	radSampler->borderColorInt[1] = borderColor[1];
	radSampler->borderColorInt[2] = borderColor[2];
	radSampler->borderColorInt[3] = borderColor[3];
}

RADcolorState RADAPIENTRY radCreateColorState(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::ColorState *colorState = new rad::ColorState(radDevice);
	return reinterpret_cast<RADcolorState>(colorState);
}

void RADAPIENTRY radReferenceColorState(RADcolorState color)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->reference();
}

void RADAPIENTRY radReleaseColorState(RADcolorState color)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->release();
}

void RADAPIENTRY radColorDefault(RADcolorState color)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->defaults();
}

void RADAPIENTRY radColorBlendEnable(RADcolorState color, RADuint index, RADboolean enable)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->blend[index].enable = enable;
}

void RADAPIENTRY radColorBlendFunc(RADcolorState color, RADuint index, RADblendFunc srcFunc, RADblendFunc dstFunc, RADblendFunc srcFuncAlpha, RADblendFunc dstFuncAlpha)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->blend[index].srcFunc = srcFunc;
	colorState->blend[index].dstFunc = dstFunc;
	colorState->blend[index].srcFuncAlpha = srcFuncAlpha;
	colorState->blend[index].dstFuncAlpha = dstFuncAlpha;
}

void RADAPIENTRY radColorBlendEquation(RADcolorState color, RADuint index, RADblendEquation modeRGB, RADblendEquation modeAlpha)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->blend[index].modeRGB = modeRGB;
	colorState->blend[index].modeAlpha = modeAlpha;
}

void RADAPIENTRY radColorMask(RADcolorState color, RADuint index, RADboolean r, RADboolean g, RADboolean b, RADboolean a)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->blend[index].maskRGBA[0] = r;
	colorState->blend[index].maskRGBA[1] = g;
	colorState->blend[index].maskRGBA[2] = b;
	colorState->blend[index].maskRGBA[3] = a;
}

void RADAPIENTRY radColorNumTargets(RADcolorState color, RADuint numTargets)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->numTargets = numTargets;
}

void RADAPIENTRY radColorLogicOpEnable(RADcolorState color, RADboolean enable)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->logicOpEnable = enable;
}

void RADAPIENTRY radColorLogicOp(RADcolorState color, RADlogicOp logicOp)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->logicOp = logicOp;
}

void RADAPIENTRY radColorAlphaToCoverageEnable(RADcolorState color, RADboolean enable)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->alphaToCoverageEnable = enable;
}

void RADAPIENTRY radColorBlendColor(RADcolorState color, const RADfloat *blendColor)
{
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	colorState->blendColor[0] = blendColor[0];
	colorState->blendColor[1] = blendColor[1];
	colorState->blendColor[2] = blendColor[2];
	colorState->blendColor[3] = blendColor[3];
}

void RADAPIENTRY radColorDynamic(RADcolorState color, RADcolorDynamic dynamic, RADboolean enable) {UNIMPLEMENTED();}

RADrasterState RADAPIENTRY radCreateRasterState(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::RasterState *rasterState = new rad::RasterState(radDevice);
	return reinterpret_cast<RADrasterState>(rasterState);
}

void RADAPIENTRY radReferenceRasterState(RADrasterState raster)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->reference();
}

void RADAPIENTRY radReleaseRasterState(RADrasterState raster)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->release();
}

void RADAPIENTRY radRasterDefault(RADrasterState raster)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->defaults();
}

void RADAPIENTRY radRasterPointSize(RADrasterState raster, RADfloat pointSize)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->pointSize = pointSize;
}

void RADAPIENTRY radRasterLineWidth(RADrasterState raster, RADfloat lineWidth)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->lineWidth = lineWidth;
}

void RADAPIENTRY radRasterCullFace(RADrasterState raster, RADfaceBitfield face)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->cullFace = face;
}

void RADAPIENTRY radRasterFrontFace(RADrasterState raster, RADfrontFace face)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->frontFace = face;
}

void RADAPIENTRY radRasterPolygonMode(RADrasterState raster, RADpolygonMode polygonMode)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->polygonMode = polygonMode;
}

void RADAPIENTRY radRasterPolygonOffsetClamp(RADrasterState raster, RADfloat factor, RADfloat units, RADfloat clamp)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->offsetFactor = factor;
	rasterState->offsetUnits = units;
	rasterState->offsetClamp = clamp;
}

void RADAPIENTRY radRasterPolygonOffsetEnables(RADrasterState raster, RADpolygonOffsetEnables enables)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->polygonOffsetEnables = enables;
}

void RADAPIENTRY radRasterDiscardEnable(RADrasterState raster, RADboolean enable)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->discardEnable = enable;
}

void RADAPIENTRY radRasterMultisampleEnable(RADrasterState raster, RADboolean enable)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->multisampleEnable = enable;
}

void RADAPIENTRY radRasterSamples(RADrasterState raster, RADuint samples)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->samples = samples;
}

void RADAPIENTRY radRasterSampleMask(RADrasterState raster, RADuint mask)
{
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	rasterState->sampleMask = mask;
}

void RADAPIENTRY radRasterDynamic(RADrasterState raster, RADrasterDynamic dynamic, RADboolean enable)
{
	UNIMPLEMENTED();
}

RADdepthStencilState RADAPIENTRY radCreateDepthStencilState(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::DepthStencilState *depthStencilState = new rad::DepthStencilState(radDevice);
	return reinterpret_cast<RADdepthStencilState>(depthStencilState);
}

void RADAPIENTRY radReferenceDepthStencilState(RADdepthStencilState depthStencil)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->reference();
}

void RADAPIENTRY radReleaseDepthStencilState(RADdepthStencilState depthStencil)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->release();
}

void RADAPIENTRY radDepthStencilDefault(RADdepthStencilState depthStencil)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->defaults();
}

void RADAPIENTRY radDepthStencilDepthTestEnable(RADdepthStencilState depthStencil, RADboolean enable)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->depthTestEnable = enable;
}

void RADAPIENTRY radDepthStencilDepthWriteEnable(RADdepthStencilState depthStencil, RADboolean enable)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->depthWriteEnable = enable;
}

void RADAPIENTRY radDepthStencilDepthFunc(RADdepthStencilState depthStencil, RADdepthFunc func)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->depthFunc = func;
}

void RADAPIENTRY radDepthStencilStencilTestEnable(RADdepthStencilState depthStencil, RADboolean enable)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	depthStencilState->stencilTestEnable = enable;
}

void RADAPIENTRY radDepthStencilStencilFunc(RADdepthStencilState depthStencil, RADfaceBitfield faces, RADstencilFunc func, RADint ref, RADuint mask)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);

	if(faces & RAD_FACE_FRONT)
	{
		depthStencilState->stencilFuncFront = func;
		depthStencilState->stencilRefFront = ref;
		depthStencilState->stencilMaskFront = mask;
	}

	if(faces & RAD_FACE_BACK)
	{
		depthStencilState->stencilFuncBack = func;
		depthStencilState->stencilRefBack = ref;
		depthStencilState->stencilMaskBack = mask;
	}
}

void RADAPIENTRY radDepthStencilStencilOp(RADdepthStencilState depthStencil, RADfaceBitfield faces, RADstencilOp fail, RADstencilOp depthFail, RADstencilOp depthPass)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	
	if(faces & RAD_FACE_FRONT)
	{
		depthStencilState->stencilFailOpFront = fail;
		depthStencilState->depthFailOpFront = depthFail;
		depthStencilState->depthPassOpFront = depthPass;
	}

	if(faces & RAD_FACE_BACK)
	{
		depthStencilState->stencilFailOpBack = fail;
		depthStencilState->depthFailOpBack = depthFail;
		depthStencilState->depthPassOpBack = depthPass;
	}
}

void RADAPIENTRY radDepthStencilStencilMask(RADdepthStencilState depthStencil, RADfaceBitfield faces, RADuint mask)
{
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	
	if(faces & RAD_FACE_FRONT)
	{
		depthStencilState->stencilMaskFront = mask;
		
	}

	if(faces & RAD_FACE_BACK)
	{
		depthStencilState->stencilMaskBack = mask;
	}
}

void RADAPIENTRY radDepthStencilDynamic(RADdepthStencilState depthStencil, RADdepthStencilDynamic dynamic, RADboolean enable)
{
	UNIMPLEMENTED();
}

RADvertexState RADAPIENTRY radCreateVertexState(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::VertexState *vertexState = new rad::VertexState(radDevice);
	return reinterpret_cast<RADvertexState>(vertexState);
}

void RADAPIENTRY radReferenceVertexState(RADvertexState vertex)
{
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->reference();
}

void RADAPIENTRY radReleaseVertexState(RADvertexState vertex)
{
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->release();
}

void RADAPIENTRY radVertexDefault(RADvertexState vertex) {UNIMPLEMENTED();}

void RADAPIENTRY radVertexAttribFormat(RADvertexState vertex, RADint attribIndex, RADint numComponents, RADint bytesPerComponent, RADattribType type, RADuint relativeOffset)
{
	ASSERT(attribIndex >= 0 && attribIndex < GL_MAX_VERTEX_ATTRIBS);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->attrib[attribIndex].numComponents = numComponents;
	vertexState->attrib[attribIndex].bytesPerComponent = bytesPerComponent;
	vertexState->attrib[attribIndex].type = type;
	vertexState->attrib[attribIndex].relativeOffset = relativeOffset;
}

void RADAPIENTRY radVertexAttribBinding(RADvertexState vertex, RADint attribIndex, RADint bindingIndex)
{
	ASSERT(attribIndex >= 0 && attribIndex < GL_MAX_VERTEX_ATTRIBS);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->attrib[attribIndex].bindingIndex = bindingIndex;
}

void RADAPIENTRY radVertexBindingGroup(RADvertexState vertex, RADint bindingIndex, RADint group, RADint index)
{
	ASSERT(bindingIndex >= 0 && bindingIndex < GL_MAX_VERTEX_ATTRIBS);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->binding[bindingIndex].group = group;
	vertexState->binding[bindingIndex].index = index;
}

void RADAPIENTRY radVertexAttribEnable(RADvertexState vertex, RADint attribIndex, RADboolean enable)
{
	ASSERT(attribIndex >= 0 && attribIndex < GL_MAX_VERTEX_ATTRIBS);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->attrib[attribIndex].bindingIndex = enable;
}

void RADAPIENTRY radVertexBindingStride(RADvertexState vertex, RADint bindingIndex, RADuint stride)
{
	ASSERT(bindingIndex >= 0 && bindingIndex < GL_MAX_VERTEX_ATTRIBS);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	vertexState->binding[bindingIndex].stride = stride;
}

RADrtFormatState RADAPIENTRY radCreateRtFormatState(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::FormatState *formatState = new rad::FormatState(radDevice);
	return reinterpret_cast<RADrtFormatState>(formatState);
}

void RADAPIENTRY radReferenceRtFormatState(RADrtFormatState rtFormat)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->reference();
}

void RADAPIENTRY radReleaseRtFormatState(RADrtFormatState rtFormat)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->release();
}

void RADAPIENTRY radRtFormatDefault(RADrtFormatState rtFormat)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->defaults();
}

void RADAPIENTRY radRtFormatColorFormat(RADrtFormatState rtFormat, RADuint index, RADinternalFormat format)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->colorFormat[index] = format;
}

void RADAPIENTRY radRtFormatDepthFormat(RADrtFormatState rtFormat, RADinternalFormat format)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->depthFormat = format;
}

void RADAPIENTRY radRtFormatStencilFormat(RADrtFormatState rtFormat, RADinternalFormat format)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->stencilFormat = format;
}

void RADAPIENTRY radRtFormatColorSamples(RADrtFormatState rtFormat, RADuint samples)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->colorSamples = samples;
}

void RADAPIENTRY radRtFormatDepthStencilSamples(RADrtFormatState rtFormat, RADuint samples)
{
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	formatState->depthStencilSamples = samples;
}

RADpipeline RADAPIENTRY radCreatePipeline(RADdevice device, RADpipelineType pipelineType)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Pipeline *pipeline = new rad::Pipeline(radDevice);
	return reinterpret_cast<RADpipeline>(pipeline);
}

void RADAPIENTRY radReferencePipeline(RADpipeline pipeline)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	radPipeline->reference();
}

void RADAPIENTRY radReleasePipeline(RADpipeline pipeline)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	radPipeline->release();
}

void RADAPIENTRY radPipelineProgramStages(RADpipeline pipeline, RADbitfield stages, RADprogram program)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::Program *radProgram = reinterpret_cast<rad::Program*>(program);

	if(stages & RAD_VERTEX_SHADER_BIT)
	{
		ASSERT(!radPipeline->vertexProgram);
		radProgram->reference();   // FIXME: here or at compile?
		radPipeline->vertexProgram = radProgram;
	}

	if(stages & RAD_FRAGMENT_SHADER_BIT)
	{
		ASSERT(!radPipeline->fragmentProgram);
		radProgram->reference();   // FIXME: here or at compile?
		radPipeline->fragmentProgram = radProgram;
	}
}

void RADAPIENTRY radPipelineVertexState(RADpipeline pipeline, RADvertexState vertex)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::VertexState *vertexState = reinterpret_cast<rad::VertexState*>(vertex);
	ASSERT(!radPipeline->vertexState);
	vertexState->reference();   // FIXME: here or at compile?
	radPipeline->vertexState = vertexState;
}

void RADAPIENTRY radPipelineColorState(RADpipeline pipeline, RADcolorState color)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::ColorState *colorState = reinterpret_cast<rad::ColorState*>(color);
	ASSERT(!radPipeline->colorState);
	colorState->reference();   // FIXME: here or at compile?
	radPipeline->colorState = colorState;
}

void RADAPIENTRY radPipelineRasterState(RADpipeline pipeline, RADrasterState raster)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::RasterState *rasterState = reinterpret_cast<rad::RasterState*>(raster);
	ASSERT(!radPipeline->rasterState);
	rasterState->reference();   // FIXME: here or at compile?
	radPipeline->rasterState = rasterState;
}

void RADAPIENTRY radPipelineDepthStencilState(RADpipeline pipeline, RADdepthStencilState depthStencil)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::DepthStencilState *depthStencilState = reinterpret_cast<rad::DepthStencilState*>(depthStencil);
	ASSERT(!radPipeline->depthStencilState);
	depthStencilState->reference();   // FIXME: here or at compile?
	radPipeline->depthStencilState = depthStencilState;
}

void RADAPIENTRY radPipelineRtFormatState(RADpipeline pipeline, RADrtFormatState rtFormat)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	rad::FormatState *formatState = reinterpret_cast<rad::FormatState*>(rtFormat);
	ASSERT(!radPipeline->formatState);
	formatState->reference();   // FIXME: here or at compile?
	radPipeline->formatState = formatState;
}

void RADAPIENTRY radPipelinePrimitiveType(RADpipeline pipeline, RADprimitiveType mode)
{
	rad::Pipeline *radPipeline = reinterpret_cast<rad::Pipeline*>(pipeline);
	radPipeline->primitiveType = mode;
}

void RADAPIENTRY radCompilePipeline(RADpipeline pipeline)
{
	// FIXME: Reference state objects here or when set?
	return;
}

RADpipelineHandle RADAPIENTRY radGetPipelineHandle(RADpipeline pipeline)
{
	return reinterpret_cast<RADpipelineHandle>(pipeline);
}

RADcommandBuffer RADAPIENTRY radCreateCommandBuffer(RADdevice device, RADqueueType queueType)
{
	return 0;
}

void RADAPIENTRY radReferenceCommandBuffer(RADcommandBuffer cmdBuf) {UNIMPLEMENTED();}

void RADAPIENTRY radReleaseCommandBuffer(RADcommandBuffer cmdBuf)
{
	return;
}

void RADAPIENTRY radCmdBindPipeline(RADcommandBuffer cmdBuf, RADpipelineType pipelineType, RADpipelineHandle pipelineHandle)
{
	return;
}

void RADAPIENTRY radCmdBindGroup(RADcommandBuffer cmdBuf, RADbitfield stages, RADuint group, RADuint count, RADbindGroupHandle groupHandle, RADuint offset)
{
	
}

void RADAPIENTRY radCmdDrawArrays(RADcommandBuffer cmdBuf, RADprimitiveType mode, RADint first, RADsizei count) {UNIMPLEMENTED();}

void RADAPIENTRY radCmdDrawElements(RADcommandBuffer cmdBuf, RADprimitiveType mode, RADindexType type, RADsizei count, RADindexHandle indexHandle, RADuint offset)
{
	return;
}

RADboolean RADAPIENTRY radCompileCommandBuffer(RADcommandBuffer cmdBuf)
{
	return false;
}

RADcommandHandle RADAPIENTRY radGetCommandHandle(RADcommandBuffer cmdBuf)
{
	return 0;
}

void RADAPIENTRY radCmdStencilValueMask(RADcommandBuffer cmdBuf, RADfaceBitfield faces, RADuint mask) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdStencilMask(RADcommandBuffer cmdBuf, RADfaceBitfield faces, RADuint mask) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdStencilRef(RADcommandBuffer cmdBuf, RADfaceBitfield faces, RADint ref) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdBlendColor(RADcommandBuffer cmdBuf, const RADfloat *blendColor) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdPointSize(RADcommandBuffer cmdBuf, RADfloat pointSize) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdLineWidth(RADcommandBuffer cmdBuf, RADfloat lineWidth) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdPolygonOffsetClamp(RADcommandBuffer cmdBuf, RADfloat factor, RADfloat units, RADfloat clamp) {UNIMPLEMENTED();}
void RADAPIENTRY radCmdSampleMask(RADcommandBuffer cmdBuf, RADuint mask) {UNIMPLEMENTED();}

RADpass RADAPIENTRY radCreatePass(RADdevice device)
{
	rad::Device *radDevice = reinterpret_cast<rad::Device*>(device);
	rad::Pass *pass = new rad::Pass(radDevice);
	return reinterpret_cast<RADpass>(pass);
}

void RADAPIENTRY radReferencePass(RADpass pass)
{
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radPass->reference();
}

void RADAPIENTRY radReleasePass(RADpass pass)
{
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radPass->release();
}

void RADAPIENTRY radPassDefault(RADpass pass)
{
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radPass->defaults();
}

void RADAPIENTRY radCompilePass(RADpass pass)
{
	return;
}

void RADAPIENTRY radPassRenderTargets(RADpass pass, RADuint numColors, const RADrenderTargetHandle *colors, RADrenderTargetHandle depth, RADrenderTargetHandle stencil)
{
	rad::Pass *radPass = reinterpret_cast<rad::Pass*>(pass);
	radPass->numColors = numColors;
	for(unsigned int i = 0; i < numColors; i++)
	{
		ASSERT(colors[i]);
		es2::Image *colorTarget = reinterpret_cast<es2::Image*>(colors[i]);
		colorTarget->addRef();   // FIXME: here or at compile?
		radPass->colorTarget[i] = colorTarget;
	}

	if(depth)
	{
		es2::Image *depthTarget = reinterpret_cast<es2::Image*>(depth);
		depthTarget->addRef();   // FIXME: here or at compile?
		radPass->depthTarget = depthTarget;
	}

	if(stencil)
	{
		es2::Image *stencilTarget = reinterpret_cast<es2::Image*>(stencil);
		stencilTarget->addRef();   // FIXME: here or at compile?
		radPass->stencilTarget = stencilTarget;
	}
}

void RADAPIENTRY radPassPreserveEnable(RADpass pass, RADrtAttachment attachment, RADboolean enable) {UNIMPLEMENTED();}
void RADAPIENTRY radPassDiscard(RADpass pass, RADuint numTextures, const RADtexture *textures, const RADoffset2D *offsets) {UNIMPLEMENTED();}

void RADAPIENTRY radPassResolve(RADpass pass, RADrtAttachment attachment, RADtexture texture)
{
	return;
}

void RADAPIENTRY radPassStore(RADpass pass, RADuint numTextures, const RADtexture *textures, const RADoffset2D *offsets) {UNIMPLEMENTED();}
void RADAPIENTRY radPassClip(RADpass pass, const RADrect2D *rect) {UNIMPLEMENTED();}
void RADAPIENTRY radPassDependencies(RADpass pass, RADuint numPasses, const RADpass *otherPasses, const RADbitfield *srcMask, const RADbitfield *dstMask, const RADbitfield *flushMask, const RADbitfield *invalidateMask) {UNIMPLEMENTED();}
void RADAPIENTRY radPassTilingBoundary(RADpass pass, RADboolean boundary) {UNIMPLEMENTED();}
void RADAPIENTRY radPassTileFilterWidth(RADpass pass, RADuint filterWidth, RADuint filterHeight) {UNIMPLEMENTED();}
void RADAPIENTRY radPassTileFootprint(RADpass pass, RADuint bytesPerPixel, RADuint maxFilterWidth, RADuint maxFilterHeight) {UNIMPLEMENTED();}

RADsync RADAPIENTRY radCreateSync(RADdevice device)
{
	return 0;
}

void RADAPIENTRY radReferenceSync(RADsync sync) {UNIMPLEMENTED();}

void RADAPIENTRY radReleaseSync(RADsync sync)
{
	return;
}

void RADAPIENTRY radQueueFenceSync(RADqueue queue, RADsync sync, RADsyncCondition condition, RADbitfield flags)
{
	return;
}

RADwaitSyncResult RADAPIENTRY radWaitSync(RADsync sync, RADuint64 timeout) {UNIMPLEMENTED(); return RAD_WAIT_SYNC_FAILED;}

RADboolean RADAPIENTRY radQueueWaitSync(RADqueue queue, RADsync sync)
{
	return 0;
}

RADPROC RADAPIENTRY radGetProcAddress(const RADchar *procname)
{
	struct Extension
    {
        const char *name;
        RADPROC address;
    };

    static const Extension glExtensions[] =
    {
        #define EXTENSION(name) {#name, (RADPROC)name}
		
		EXTENSION(radGetProcAddress),
		EXTENSION(radCreateDevice),
		EXTENSION(radReferenceDevice),
		EXTENSION(radReleaseDevice),
		EXTENSION(radGetTokenHeader),
		EXTENSION(radCreateQueue),
		EXTENSION(radReferenceQueue),
		EXTENSION(radReleaseQueue),
		EXTENSION(radQueueTagBuffer),
		EXTENSION(radQueueTagTexture),
		EXTENSION(radQueueSubmitCommands),
		EXTENSION(radFlushQueue),
		EXTENSION(radFinishQueue),
		EXTENSION(radQueueViewport),
		EXTENSION(radQueueScissor),
		EXTENSION(radQueueCopyBufferToImage),
		EXTENSION(radQueueCopyImageToBuffer),
		EXTENSION(radQueueCopyBuffer),
		EXTENSION(radQueueClearColor),
		EXTENSION(radQueueClearDepth),
		EXTENSION(radQueueClearStencil),
		EXTENSION(radQueuePresent),
		EXTENSION(radQueueDrawArrays),
		EXTENSION(radQueueDrawElements),
		EXTENSION(radQueueBindPipeline),
		EXTENSION(radQueueBindGroup),
		EXTENSION(radQueueBeginPass),
		EXTENSION(radQueueEndPass),
		EXTENSION(radQueueSubmitDynamic),
		EXTENSION(radQueueStencilValueMask),
		EXTENSION(radQueueStencilMask),
		EXTENSION(radQueueStencilRef),
		EXTENSION(radQueueBlendColor),
		EXTENSION(radQueuePointSize),
		EXTENSION(radQueueLineWidth),
		EXTENSION(radQueuePolygonOffsetClamp),
		EXTENSION(radQueueSampleMask),
		EXTENSION(radCreateProgram),
		EXTENSION(radReferenceProgram),
		EXTENSION(radReleaseProgram),
		EXTENSION(radProgramSource),
		EXTENSION(radCreateBuffer),
		EXTENSION(radReferenceBuffer),
		EXTENSION(radReleaseBuffer),
		EXTENSION(radBufferAccess),
		EXTENSION(radBufferMapAccess),
		EXTENSION(radBufferStorage),
		EXTENSION(radMapBuffer),
		EXTENSION(radGetVertexHandle),
		EXTENSION(radGetIndexHandle),
		EXTENSION(radGetUniformHandle),
		EXTENSION(radGetBindGroupHandle),
		EXTENSION(radCreateTexture),
		EXTENSION(radReferenceTexture),
		EXTENSION(radReleaseTexture),
		EXTENSION(radTextureAccess),
		EXTENSION(radTextureStorage),
		EXTENSION(radGetTextureSamplerHandle),
		EXTENSION(radGetTextureRenderTargetHandle),
		EXTENSION(radCreateSampler),
		EXTENSION(radReferenceSampler),
		EXTENSION(radReleaseSampler),
		EXTENSION(radSamplerDefault),
		EXTENSION(radSamplerMinMagFilter),
		EXTENSION(radSamplerWrapMode),
		EXTENSION(radSamplerLodClamp),
		EXTENSION(radSamplerLodBias),
		EXTENSION(radSamplerCompare),
		EXTENSION(radSamplerBorderColorFloat),
		EXTENSION(radSamplerBorderColorInt),
		EXTENSION(radCreateColorState),
		EXTENSION(radReferenceColorState),
		EXTENSION(radReleaseColorState),
		EXTENSION(radColorDefault),
		EXTENSION(radColorBlendEnable),
		EXTENSION(radColorBlendFunc),
		EXTENSION(radColorBlendEquation),
		EXTENSION(radColorMask),
		EXTENSION(radColorNumTargets),
		EXTENSION(radColorLogicOpEnable),
		EXTENSION(radColorLogicOp),
		EXTENSION(radColorAlphaToCoverageEnable),
		EXTENSION(radColorBlendColor),
		EXTENSION(radColorDynamic),
		EXTENSION(radCreateRasterState),
		EXTENSION(radReferenceRasterState),
		EXTENSION(radReleaseRasterState),
		EXTENSION(radRasterDefault),
		EXTENSION(radRasterPointSize),
		EXTENSION(radRasterLineWidth),
		EXTENSION(radRasterCullFace),
		EXTENSION(radRasterFrontFace),
		EXTENSION(radRasterPolygonMode),
		EXTENSION(radRasterPolygonOffsetClamp),
		EXTENSION(radRasterPolygonOffsetEnables),
		EXTENSION(radRasterDiscardEnable),
		EXTENSION(radRasterMultisampleEnable),
		EXTENSION(radRasterSamples),
		EXTENSION(radRasterSampleMask),
		EXTENSION(radRasterDynamic),
		EXTENSION(radCreateDepthStencilState),
		EXTENSION(radReferenceDepthStencilState),
		EXTENSION(radReleaseDepthStencilState),
		EXTENSION(radDepthStencilDefault),
		EXTENSION(radDepthStencilDepthTestEnable),
		EXTENSION(radDepthStencilDepthWriteEnable),
		EXTENSION(radDepthStencilDepthFunc),
		EXTENSION(radDepthStencilStencilTestEnable),
		EXTENSION(radDepthStencilStencilFunc),
		EXTENSION(radDepthStencilStencilOp),
		EXTENSION(radDepthStencilStencilMask),
		EXTENSION(radDepthStencilDynamic),
		EXTENSION(radCreateVertexState),
		EXTENSION(radReferenceVertexState),
		EXTENSION(radReleaseVertexState),
		EXTENSION(radVertexDefault),
		EXTENSION(radVertexAttribFormat),
		EXTENSION(radVertexAttribBinding),
		EXTENSION(radVertexBindingGroup),
		EXTENSION(radVertexAttribEnable),
		EXTENSION(radVertexBindingStride),
		EXTENSION(radCreateRtFormatState),
		EXTENSION(radReferenceRtFormatState),
		EXTENSION(radReleaseRtFormatState),
		EXTENSION(radRtFormatDefault),
		EXTENSION(radRtFormatColorFormat),
		EXTENSION(radRtFormatDepthFormat),
		EXTENSION(radRtFormatStencilFormat),
		EXTENSION(radRtFormatColorSamples),
		EXTENSION(radRtFormatDepthStencilSamples),
		EXTENSION(radCreatePipeline),
		EXTENSION(radReferencePipeline),
		EXTENSION(radReleasePipeline),
		EXTENSION(radPipelineProgramStages),
		EXTENSION(radPipelineVertexState),
		EXTENSION(radPipelineColorState),
		EXTENSION(radPipelineRasterState),
		EXTENSION(radPipelineDepthStencilState),
		EXTENSION(radPipelineRtFormatState),
		EXTENSION(radPipelinePrimitiveType),
		EXTENSION(radCompilePipeline),
		EXTENSION(radGetPipelineHandle),
		EXTENSION(radCreateCommandBuffer),
		EXTENSION(radReferenceCommandBuffer),
		EXTENSION(radReleaseCommandBuffer),
		EXTENSION(radCmdBindPipeline),
		EXTENSION(radCmdBindGroup),
		EXTENSION(radCmdDrawArrays),
		EXTENSION(radCmdDrawElements),
		EXTENSION(radCompileCommandBuffer),
		EXTENSION(radGetCommandHandle),
		EXTENSION(radCmdStencilValueMask),
		EXTENSION(radCmdStencilMask),
		EXTENSION(radCmdStencilRef),
		EXTENSION(radCmdBlendColor),
		EXTENSION(radCmdPointSize),
		EXTENSION(radCmdLineWidth),
		EXTENSION(radCmdPolygonOffsetClamp),
		EXTENSION(radCmdSampleMask),
		EXTENSION(radCreatePass),
		EXTENSION(radReferencePass),
		EXTENSION(radReleasePass),
		EXTENSION(radPassDefault),
		EXTENSION(radCompilePass),
		EXTENSION(radPassRenderTargets),
		EXTENSION(radPassPreserveEnable),
		EXTENSION(radPassDiscard),
		EXTENSION(radPassResolve),
		EXTENSION(radPassStore),
		EXTENSION(radPassClip),
		EXTENSION(radPassDependencies),
		EXTENSION(radPassTilingBoundary),
		EXTENSION(radPassTileFilterWidth),
		EXTENSION(radPassTileFootprint),
		EXTENSION(radCreateSync),
		EXTENSION(radReferenceSync),
		EXTENSION(radReleaseSync),
		EXTENSION(radQueueFenceSync),
		EXTENSION(radWaitSync),
		EXTENSION(radQueueWaitSync),

		#undef EXTENSION
    };

    for(int ext = 0; ext < sizeof(glExtensions) / sizeof(Extension); ext++)
    {
        if(strcmp(procname, glExtensions[ext].name) == 0)
        {
            return (RADPROC)glExtensions[ext].address;
        }
    }

    return NULL;
}

void GL_APIENTRY Register(const char *licenseKey)
{
	RegisterLicenseKey(licenseKey);
}

}
