#include "common.h"

#include "kernel/vc4_packet.h"
#include "brcm/cle/v3d_decoder.h"
#include "brcm/clif/clif_dump.h"

uint32_t getFormatBpp(VkFormat f)
{
	switch(f)
	{
	case VK_FORMAT_R64G64B64_UINT: //padded to 256
	case VK_FORMAT_R64G64B64_SINT:
	case VK_FORMAT_R64G64B64_SFLOAT:
	case VK_FORMAT_R64G64B64A64_UINT:
	case VK_FORMAT_R64G64B64A64_SINT:
	case VK_FORMAT_R64G64B64A64_SFLOAT:
		return 256;
	case VK_FORMAT_R32G32B32_UINT: //padded to 128
	case VK_FORMAT_R32G32B32_SINT:
	case VK_FORMAT_R32G32B32_SFLOAT:
	case VK_FORMAT_R32G32B32A32_UINT:
	case VK_FORMAT_R32G32B32A32_SINT:
	case VK_FORMAT_R32G32B32A32_SFLOAT:
	case VK_FORMAT_R64G64_UINT:
	case VK_FORMAT_R64G64_SINT:
	case VK_FORMAT_R64G64_SFLOAT:
		return 128;
	case VK_FORMAT_R16G16B16A16_SFLOAT:
	case VK_FORMAT_R16G16B16_UNORM: //padded to 64
	case VK_FORMAT_R16G16B16_SNORM: //padded to 64
	case VK_FORMAT_R16G16B16_USCALED: //padded to 64
	case VK_FORMAT_R16G16B16_SSCALED: //padded to 64
	case VK_FORMAT_R16G16B16_UINT: //padded to 64
	case VK_FORMAT_R16G16B16_SINT: //padded to 64
	case VK_FORMAT_R16G16B16_SFLOAT: //padded to 64
	case VK_FORMAT_R16G16B16A16_UNORM:
	case VK_FORMAT_R16G16B16A16_SNORM:
	case VK_FORMAT_R16G16B16A16_USCALED:
	case VK_FORMAT_R16G16B16A16_SSCALED:
	case VK_FORMAT_R16G16B16A16_UINT:
	case VK_FORMAT_R16G16B16A16_SINT:
	case VK_FORMAT_R32G32_UINT:
	case VK_FORMAT_R32G32_SINT:
	case VK_FORMAT_R32G32_SFLOAT:
	case VK_FORMAT_R64_UINT:
	case VK_FORMAT_R64_SINT:
	case VK_FORMAT_R64_SFLOAT:
	case VK_FORMAT_D32_SFLOAT_S8_UINT: //padded to 64
		return 64;
	case VK_FORMAT_R8G8B8_UNORM: //padded to 32
	case VK_FORMAT_R8G8B8A8_UNORM:
	case VK_FORMAT_R32_UINT:
	case VK_FORMAT_R8G8B8A8_UINT:
	case VK_FORMAT_D32_SFLOAT:
	case VK_FORMAT_R8G8B8_SNORM: //padded to 32
	case VK_FORMAT_R8G8B8_USCALED: //padded to 32
	case VK_FORMAT_R8G8B8_SSCALED: //padded to 32
	case VK_FORMAT_R8G8B8_UINT: //padded to 32
	case VK_FORMAT_R8G8B8_SINT: //padded to 32
	case VK_FORMAT_R8G8B8_SRGB: //padded to 32
	case VK_FORMAT_B8G8R8_UNORM: //padded to 32
	case VK_FORMAT_B8G8R8_SNORM: //padded to 32
	case VK_FORMAT_B8G8R8_USCALED: //padded to 32
	case VK_FORMAT_B8G8R8_SSCALED: //padded to 32
	case VK_FORMAT_B8G8R8_UINT: //padded to 32
	case VK_FORMAT_B8G8R8_SINT: //padded to 32
	case VK_FORMAT_B8G8R8_SRGB: //padded to 32
	case VK_FORMAT_R8G8B8A8_SNORM:
	case VK_FORMAT_R8G8B8A8_USCALED:
	case VK_FORMAT_R8G8B8A8_SSCALED:
	case VK_FORMAT_R8G8B8A8_SINT:
	case VK_FORMAT_R8G8B8A8_SRGB:
	case VK_FORMAT_B8G8R8A8_UNORM:
	case VK_FORMAT_B8G8R8A8_SNORM:
	case VK_FORMAT_B8G8R8A8_USCALED:
	case VK_FORMAT_B8G8R8A8_SSCALED:
	case VK_FORMAT_B8G8R8A8_UINT:
	case VK_FORMAT_B8G8R8A8_SINT:
	case VK_FORMAT_B8G8R8A8_SRGB:
	case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
	case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
	case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
	case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
	case VK_FORMAT_A8B8G8R8_UINT_PACK32:
	case VK_FORMAT_A8B8G8R8_SINT_PACK32:
	case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
	case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
	case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
	case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
	case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
	case VK_FORMAT_A2R10G10B10_UINT_PACK32:
	case VK_FORMAT_A2R10G10B10_SINT_PACK32:
	case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
	case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
	case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
	case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
	case VK_FORMAT_A2B10G10R10_UINT_PACK32:
	case VK_FORMAT_A2B10G10R10_SINT_PACK32:
	case VK_FORMAT_R16G16_UNORM:
	case VK_FORMAT_R16G16_SNORM:
	case VK_FORMAT_R16G16_USCALED:
	case VK_FORMAT_R16G16_SSCALED:
	case VK_FORMAT_R16G16_UINT:
	case VK_FORMAT_R16G16_SINT:
	case VK_FORMAT_R16G16_SFLOAT:
	case VK_FORMAT_R32_SINT:
	case VK_FORMAT_R32_SFLOAT:
	case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
	case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
	case VK_FORMAT_X8_D24_UNORM_PACK32:
	case VK_FORMAT_D16_UNORM_S8_UINT: //padded to 32
	case VK_FORMAT_D24_UNORM_S8_UINT:
		return 32;
	case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
	case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
	case VK_FORMAT_R5G6B5_UNORM_PACK16:
	case VK_FORMAT_R8G8_UNORM:
	case VK_FORMAT_R16_SFLOAT:
	case VK_FORMAT_R16_SINT:
	case VK_FORMAT_D16_UNORM:
	case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
	case VK_FORMAT_B5G6R5_UNORM_PACK16:
	case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
	case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
	case VK_FORMAT_R8G8_SNORM:
	case VK_FORMAT_R8G8_USCALED:
	case VK_FORMAT_R8G8_SSCALED:
	case VK_FORMAT_R8G8_UINT:
	case VK_FORMAT_R8G8_SINT:
	case VK_FORMAT_R8G8_SRGB:
	case VK_FORMAT_R16_UNORM:
	case VK_FORMAT_R16_SNORM:
	case VK_FORMAT_R16_USCALED:
	case VK_FORMAT_R16_SSCALED:
	case VK_FORMAT_R16_UINT:
		return 16;
	case VK_FORMAT_R8_UNORM:
	case VK_FORMAT_R8_SINT:
	case VK_FORMAT_S8_UINT:
	case VK_FORMAT_R4G4_UNORM_PACK8:
	case VK_FORMAT_R8_SNORM:
	case VK_FORMAT_R8_USCALED:
	case VK_FORMAT_R8_SSCALED:
	case VK_FORMAT_R8_UINT:
	case VK_FORMAT_R8_SRGB:
	case VK_FORMAT_UNDEFINED: //TODO
		return 8;
	default://
		fprintf(stderr, "format %i\n", f);
		assert(!"Unknown format.");
		return 0;
	}
}

uint32_t packVec4IntoABGR8(const float rgba[4])
{
	uint8_t r, g, b, a;
	r = rgba[0] * 255.0;
	g = rgba[1] * 255.0;
	b = rgba[2] * 255.0;
	a = rgba[3] * 255.0;

	uint32_t res = 0 |
			(a << 24) |
			(b << 16) |
			(g << 8) |
			(r << 0);

	return res;
}

int findInstanceExtension(char* name)
{
	for(int c = 0; c < numInstanceExtensions; ++c)
	{
		if(strcmp(instanceExtensions[c].extensionName, name) == 0)
		{
			return c;
		}
	}

	return -1;
}

int findDeviceExtension(char* name)
{
	for(int c = 0; c < numDeviceExtensions; ++c)
	{
		if(strcmp(deviceExtensions[c].extensionName, name) == 0)
		{
			return c;
		}
	}

	return -1;
}

//Textures in T format:
//formed out of 4KB tiles, which have 1KB subtiles (see page 105 in VC4 arch guide)
//1KB subtiles have 512b microtiles.
//Width/height of the 512b microtiles is the following:
// 64bpp: 2x4
// 32bpp: 4x4
// 16bpp: 8x4
// 8bpp:  8x8
// 4bpp:  16x8
// 1bpp:  32x16
//Therefore width/height of 1KB subtiles is the following:
// 64bpp: 8x16
// 32bpp: 16x16
// 16bpp: 32x16
// 8bpp:  32x32
// 4bpp:  64x32
// 1bpp:  128x64
//Finally width/height of the 4KB tiles:
// 64bpp: 16x32
// 32bpp: 32x32
// 16bpp: 64x32
// 8bpp:  64x64
// 4bpp:  128x64
// 1bpp:  256x128
void getPaddedTextureDimensionsT(uint32_t width, uint32_t height, uint32_t bpp, uint32_t* paddedWidth, uint32_t* paddedHeight)
{
	assert(paddedWidth);
	assert(paddedHeight);
	uint32_t tileW = 0;
	uint32_t tileH = 0;

	switch(bpp)
	{
	case 256:
	{
		tileW = 8;
		tileH = 16;
		break;
	}
	case 128:
	{
		tileW = 16;
		tileH = 16;
		break;
	}
	case 64:
	{
		tileW = 16;
		tileH = 32;
		break;
	}
	case 32:
	{
		tileW = 32;
		tileH = 32;
		break;
	}
	case 16:
	{
		tileW = 64;
		tileH = 32;
		break;
	}
	case 8:
	{
		tileW = 64;
		tileH = 64;
		break;
	}
	case 4:
	{
		tileW = 128;
		tileH = 64;
		break;
	}
	case 1:
	{
		tileW = 256;
		tileH = 128;
		break;
	}
	default:
	{
		fprintf(stderr, "bpp: %i\n", bpp);
		assert(!"Unsupported texture bpp.");
	}
	}

	*paddedWidth = ((tileW - (width % tileW)) % tileW) + width;
	*paddedHeight = ((tileH - (height % tileH)) % tileH) + height;
}

/*static inline void util_pack_color(const float rgba[4], enum pipe_format format, union util_color *uc)
{
   ubyte r = 0;
   ubyte g = 0;
   ubyte b = 0;
   ubyte a = 0;

   if (util_format_get_component_bits(format, UTIL_FORMAT_COLORSPACE_RGB, 0) <= 8) {
	  r = float_to_ubyte(rgba[0]);
	  g = float_to_ubyte(rgba[1]);
	  b = float_to_ubyte(rgba[2]);
	  a = float_to_ubyte(rgba[3]);
   }

   switch (format) {
   case PIPE_FORMAT_ABGR8888_UNORM:
	  {
		 uc->ui[0] = (r << 24) | (g << 16) | (b << 8) | a;
	  }
	  return;
   case PIPE_FORMAT_XBGR8888_UNORM:
	  {
		 uc->ui[0] = (r << 24) | (g << 16) | (b << 8) | 0xff;
	  }
	  return;
   case PIPE_FORMAT_BGRA8888_UNORM:
	  {
		 uc->ui[0] = (a << 24) | (r << 16) | (g << 8) | b;
	  }
	  return;
   case PIPE_FORMAT_BGRX8888_UNORM:
	  {
		 uc->ui[0] = (0xffu << 24) | (r << 16) | (g << 8) | b;
	  }
	  return;
   case PIPE_FORMAT_ARGB8888_UNORM:
	  {
		 uc->ui[0] = (b << 24) | (g << 16) | (r << 8) | a;
	  }
	  return;
   case PIPE_FORMAT_XRGB8888_UNORM:
	  {
		 uc->ui[0] = (b << 24) | (g << 16) | (r << 8) | 0xff;
	  }
	  return;
   case PIPE_FORMAT_B5G6R5_UNORM:
	  {
		 uc->us = ((r & 0xf8) << 8) | ((g & 0xfc) << 3) | (b >> 3);
	  }
	  return;
   case PIPE_FORMAT_B5G5R5X1_UNORM:
	  {
		 uc->us = ((0x80) << 8) | ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | (b >> 3);
	  }
	  return;
   case PIPE_FORMAT_B5G5R5A1_UNORM:
	  {
		 uc->us = ((a & 0x80) << 8) | ((r & 0xf8) << 7) | ((g & 0xf8) << 2) | (b >> 3);
	  }
	  return;
   case PIPE_FORMAT_B4G4R4A4_UNORM:
	  {
		 uc->us = ((a & 0xf0) << 8) | ((r & 0xf0) << 4) | ((g & 0xf0) << 0) | (b >> 4);
	  }
	  return;
   case PIPE_FORMAT_A8_UNORM:
	  {
		 uc->ub = a;
	  }
	  return;
   case PIPE_FORMAT_L8_UNORM:
   case PIPE_FORMAT_I8_UNORM:
	  {
		 uc->ub = r;
	  }
	  return;
   case PIPE_FORMAT_R32G32B32A32_FLOAT:
	  {
		 uc->f[0] = rgba[0];
		 uc->f[1] = rgba[1];
		 uc->f[2] = rgba[2];
		 uc->f[3] = rgba[3];
	  }
	  return;
   case PIPE_FORMAT_R32G32B32_FLOAT:
	  {
		 uc->f[0] = rgba[0];
		 uc->f[1] = rgba[1];
		 uc->f[2] = rgba[2];
	  }
	  return;

   default:
	  util_format_write_4f(format, rgba, 0, uc, 0, 0, 0, 1, 1);
   }
}*/

int isDepthStencilFormat(VkFormat format)
{
	switch(format)
	{
	case VK_FORMAT_D16_UNORM:
	case VK_FORMAT_X8_D24_UNORM_PACK32:
	case VK_FORMAT_D32_SFLOAT:
	case VK_FORMAT_S8_UINT:
	case VK_FORMAT_D16_UNORM_S8_UINT:
	case VK_FORMAT_D24_UNORM_S8_UINT:
	case VK_FORMAT_D32_SFLOAT_S8_UINT:
		return 1;
	default:
		return 0;
	}
}

uint32_t getCompareOp(VkCompareOp op)
{
	switch(op)
	{
	case VK_COMPARE_OP_NEVER:
		return V3D_COMPARE_FUNC_NEVER;
	case VK_COMPARE_OP_LESS:
		return V3D_COMPARE_FUNC_LESS;
	case VK_COMPARE_OP_EQUAL:
		return V3D_COMPARE_FUNC_EQUAL;
	case VK_COMPARE_OP_LESS_OR_EQUAL:
		return V3D_COMPARE_FUNC_LEQUAL;
	case VK_COMPARE_OP_GREATER:
		return V3D_COMPARE_FUNC_GREATER;
	case VK_COMPARE_OP_NOT_EQUAL:
		return V3D_COMPARE_FUNC_NOTEQUAL;
	case VK_COMPARE_OP_GREATER_OR_EQUAL:
		return V3D_COMPARE_FUNC_GEQUAL;
	case VK_COMPARE_OP_ALWAYS:
		return V3D_COMPARE_FUNC_ALWAYS;
	default:
		return -1;
	}
}

uint32_t getStencilOp(VkStencilOp op)
{
	switch(op)
	{
	case VK_STENCIL_OP_ZERO:
		return 0;
	case VK_STENCIL_OP_KEEP:
		return 1;
	case VK_STENCIL_OP_REPLACE:
		return 2;
	case VK_STENCIL_OP_INCREMENT_AND_CLAMP:
		return 3;
	case VK_STENCIL_OP_DECREMENT_AND_CLAMP:
		return 4;
	case VK_STENCIL_OP_INVERT:
		return 5;
	case VK_STENCIL_OP_INCREMENT_AND_WRAP:
		return 6;
	case VK_STENCIL_OP_DECREMENT_AND_WRAP:
		return 7;
	default:
		return -1;
	};
}

uint32_t getTopology(VkPrimitiveTopology topology)
{
	switch(topology)
	{
	case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
		return 0;
	case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
	case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
		return 1;
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
		return 2;
	default:
		return -1;
	}
}

uint32_t getPrimitiveMode(VkPrimitiveTopology topology)
{
	switch(topology)
	{
	case VK_PRIMITIVE_TOPOLOGY_POINT_LIST:
		return V3D_PRIM_POINTS;
	case VK_PRIMITIVE_TOPOLOGY_LINE_LIST:
		return V3D_PRIM_LINES;
	case VK_PRIMITIVE_TOPOLOGY_LINE_STRIP:
		return V3D_PRIM_LINE_STRIP;
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST:
		return V3D_PRIM_TRIANGLES;
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP:
		return V3D_PRIM_TRIANGLE_STRIP;
	case VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN:
		return V3D_PRIM_TRIANGLE_FAN;
	default:
		return -1;
	}
}

uint32_t getFormatByteSize(VkFormat format)
{
	switch(format)
	{
	case VK_FORMAT_R16_SFLOAT:
		return 2;
	case VK_FORMAT_R16G16_SFLOAT:
		return 4;
	case VK_FORMAT_R16G16B16_SFLOAT:
		return 6;
	case VK_FORMAT_R16G16B16A16_SFLOAT:
		return 8;
	case VK_FORMAT_R32_SFLOAT:
		return 4;
	case VK_FORMAT_R32G32_SFLOAT:
		return 8;
	case VK_FORMAT_R32G32B32_SFLOAT:
		return 12;
	case VK_FORMAT_R32G32B32A32_SFLOAT:
		return 16;
	default:
		return -1;
	}
}

uint32_t ulog2(uint32_t v)
{
	uint32_t ret = 0;
	while(v >>= 1) ret++;
	return ret;
}

void clFit(VkCommandBuffer cb, ControlList* cl, uint32_t commandSize)
{
	if(!clHasEnoughSpace(cl, commandSize))
	{
		uint32_t currSize = cl->nextFreeByte - cl->buffer;
		cl->buffer = consecutivePoolReAllocate(&cb->cp->cpa, cl->buffer, cl->numBlocks); assert(cl->buffer);
		cl->nextFreeByte = cl->buffer + currSize;
	}
}

void clDump(void* cl, uint32_t size)
{
		struct v3d_device_info devinfo = {
				/* While the driver supports V3D 2.1 and 2.6, we haven't split
				 * off a 2.6 XML yet (there are a couple of fields different
				 * in render target formatting)
				 */
				.ver = 21,
		};
		struct v3d_spec* spec = v3d_spec_load(&devinfo);

		struct clif_dump *clif = clif_dump_init(&devinfo, stderr, true);

		uint32_t offset = 0, hw_offset = 0;
		uint8_t *p = cl;

		while (offset < size) {
				struct v3d_group *inst = v3d_spec_find_instruction(spec, p);
				uint8_t header = *p;
				uint32_t length;

				if (inst == NULL) {
						fprintf(stderr, "0x%08x 0x%08x: Unknown packet 0x%02x (%d)!\n",
								offset, hw_offset, header, header);
						return;
				}

				length = v3d_group_get_length(inst);

				printf("0x%08x 0x%08x: 0x%02x %s\n",
						offset, hw_offset, header, v3d_group_get_name(inst));

				v3d_print_group(clif, inst, offset, p);

				switch (header) {
				case VC4_PACKET_HALT:
				case VC4_PACKET_STORE_MS_TILE_BUFFER_AND_EOF:
						return;
				default:
						break;
				}

				offset += length;
				if (header != VC4_PACKET_GEM_HANDLES)
						hw_offset += length;
				p += length;
		}

		clif_dump_destroy(clif);
}

void encodeTextureUniform(uint32_t* params, //array of 4 uint32_t
						  //num mip levels - 1
						  uint8_t numMipLevels,
						  ///0:  rgba8
						  ///1:  rgbx8 (a=1)
						  ///2:  rgba4
						  ///3:  rgb5a1
						  ///4:  r5g6b5 (a=1)
						  ///5:  luminance (8 bit, a=1)
						  //6:  alpha (8 bit, rga=0)
						  ///7:  lumalpha
						  ///8:  etc1
						  ///9:  s16f (blending supported)
						  ///10: s8 (blending supported)
						  ///11: s16 (point sampling only)
						  //12: bw1 (1 bit black and white)
						  //13: a4
						  //14: a1
						  ///15: rgba16f
						  //16: rgba8r (raster format = not in T format)
						  ///17: yuyv422r (raster format = not in T format, yuyv)
						  uint8_t textureDataType,
						  uint8_t isCubeMap,
						  uint32_t cubemapStride, //in multiples of 4k bytes
						  uint32_t textureBasePtr, //in multiples of 4k bytes
						  //0 = 2048
						  uint16_t height,
						  uint16_t width,
						  //0: linear
						  //1: nearest
						  //2: near_mip_near
						  //3: near_mip_lin
						  //4: lin_mip_near
						  //5: lin_mip_lin
						  uint8_t minFilter,
						  //0: linear
						  //1: nearest
						  uint8_t magFilter,
						  //0: repeat
						  //1: clamp
						  //2: mirror
						  //3: border
						  uint8_t wrapT,
						  uint8_t wrapS,
						  uint8_t noAutoLod //disable automatic LOD, use bias only
						  )
{
	assert(params);

	params[0] = 0
			| numMipLevels & 0xf
			| (uint32_t)(textureDataType & 0xf) << 4
			| (uint32_t)(isCubeMap ? 1 : 0) << 9
			| (uint32_t)(textureBasePtr & 0xfffff) << 12;

	params[1] = 0
			| wrapS & 0x3
			| (uint32_t)(wrapT & 0x3) << 2
			| (uint32_t)(minFilter & 0x7) << 4
			| (uint32_t)(magFilter & 0x1) << 7
			| (uint32_t)(width & 0x7ff) << 8
			| (uint32_t)(height & 0x7ff) << 20
			| (uint32_t)((textureDataType & 0x10) >> 4) << 31;

	params[2] = 0
			| noAutoLod & 0x1
			| (uint32_t)(cubemapStride & 0x3ffff) << 12
			| (uint32_t)(isCubeMap ? 1 : 0) << 30;

	//TODO
	//child images
	params[3] = 0;
}

void encodeStencilValue(uint32_t *values, uint32_t* numValues, VkStencilOpState front, VkStencilOpState back, uint8_t stencilTestEnable)
{
	assert(values);
	assert(numValues);

	if(!stencilTestEnable)
	{
		front.compareOp = back.compareOp = VK_COMPARE_OP_ALWAYS;
	}

	if(front.compareMask == back.compareMask &&
	   front.compareOp == back.compareOp &&
	   front.depthFailOp == back.depthFailOp &&
	   front.failOp == back.failOp &&
	   front.passOp == back.passOp &&
	   front.reference == back.reference &&
	   front.writeMask == back.writeMask
	   )
	{
		*numValues = 1;

		values[0] = 0
				| (front.compareMask & 0xff)
				| (front.reference & 0xff) << 0x8
				| (getCompareOp(front.compareOp) & 0x7) << 16
				| (getStencilOp(front.failOp) & 0x7) << 19
				| (getStencilOp(front.passOp) & 0x7) << 22
				| (getStencilOp(front.depthFailOp) & 0x7) << 25
				| 3 << 30; //front and back

		switch(front.writeMask)
		{
		case 0x1:
			values[0] |= 0 << 28;
			break;
		case 0x3:
			values[0] |= 1 << 28;
			break;
		case 0xf:
			values[0] |= 2 << 28;
			break;
		case 0xff:
			values[0] |= 3 << 28;
			break;
		default:
			values[1] = 0
					| front.writeMask & 0xff
					| (front.writeMask & 0xff) << 8;
			*numValues = 2;
			break;
		};
	}
	else
	{
		*numValues = 2;

		values[0] = 0
				| (front.compareMask & 0xff)
				| (front.reference & 0xff) << 0x8
				| (getCompareOp(front.compareOp) & 0x7) << 16
				| (getStencilOp(front.failOp) & 0x7) << 19
				| (getStencilOp(front.passOp) & 0x7) << 22
				| (getStencilOp(front.depthFailOp) & 0x7) << 25
				| 1 << 30; //front

		values[1] = 0
				| (back.compareMask & 0xff)
				| (back.reference & 0xff) << 0x8
				| (getCompareOp(back.compareOp) & 0x7) << 16
				| (getStencilOp(back.failOp) & 0x7) << 19
				| (getStencilOp(back.passOp) & 0x7) << 22
				| (getStencilOp(back.depthFailOp) & 0x7) << 25
				| 2 << 30; //front

		if((front.writeMask == 0x1 ||
		   front.writeMask == 0x3 ||
		   front.writeMask == 0xf ||
		   front.writeMask == 0xff) &&
		   (back.writeMask == 0x1 ||
		   back.writeMask == 0x3 ||
		   back.writeMask == 0xf ||
		   back.writeMask == 0xff))
		{
			switch(front.writeMask)
			{
			case 0x1:
				values[0] |= 0 << 28;
				break;
			case 0x3:
				values[0] |= 1 << 28;
				break;
			case 0xf:
				values[0] |= 2 << 28;
				break;
			case 0xff:
				values[0] |= 3 << 28;
				break;
			};

			switch(back.writeMask)
			{
			case 0x1:
				values[1] |= 0 << 28;
				break;
			case 0x3:
				values[1] |= 1 << 28;
				break;
			case 0xf:
				values[1] |= 2 << 28;
				break;
			case 0xff:
				values[1] |= 3 << 28;
				break;
			};
		}
		else
		{
			values[2] = 0
					| front.writeMask & 0xff
					| (back.writeMask & 0xff) << 8;
			*numValues = 3;
		}
	}
}

uint8_t getTextureDataType(VkFormat format)
{
	switch(format)
	{
	case VK_FORMAT_R16G16B16A16_SFLOAT:
		return 15; //rgba16f
	case VK_FORMAT_R8G8B8_UNORM:
		return 1; //rgbx8 (a=1)
	case VK_FORMAT_R8G8B8A8_UNORM:
		return 0; //rgba8
	case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
		return 3; //rgb5a1
	case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
		return 2; //rgba4
	case VK_FORMAT_R5G6B5_UNORM_PACK16:
		return 4; //r5g6b5 (a=1)
	case VK_FORMAT_R8G8_UNORM:
		return 7; //lumalpha
	case VK_FORMAT_R16_SFLOAT:
		return 9; //s16f (blending supported)
	case VK_FORMAT_R16_SINT:
		return 11; //s16 (point sampling only)
	case VK_FORMAT_R8_UNORM:
		return 5; //luminance (8 bit, a=1)
	case VK_FORMAT_R8_SINT:
		return 10; //s8 (blending supported)
	case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
		return 8; //etc1
	case VK_FORMAT_G8B8G8R8_422_UNORM:
		return 17; //yuyv422r (raster format = not in T format, yuyv)
	case VK_FORMAT_UNDEFINED: //TODO
		return -1;
	default://
		fprintf(stderr, "format %i\n", format);
		assert(!"Unsupported format.");
		return -1;
	}
}

uint8_t getMinFilterType(VkFilter minFilter, VkSamplerMipmapMode mipFilter, float maxLod)
{
	if(minFilter == VK_FILTER_NEAREST)
	{
		if(maxLod < 0.0001f)
		{
			return 1; //no mip filtering
		}

		if(mipFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST)
		{
			return 2;
		}
		else if(mipFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
		{
			return 3;
		}
	}
	else if(minFilter == VK_FILTER_LINEAR)
	{
		if(maxLod < 0.0001f)
		{
			return 0; //no mip filtering
		}

		if(mipFilter == VK_SAMPLER_MIPMAP_MODE_NEAREST)
		{
			return 4;
		}
		else if(mipFilter == VK_SAMPLER_MIPMAP_MODE_LINEAR)
		{
			return 5;
		}
	}
}

uint8_t getWrapMode(VkSamplerAddressMode mode)
{
	if(mode == VK_SAMPLER_ADDRESS_MODE_REPEAT)
	{
		return 0;
	}
	else if(mode == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE)
	{
		return 1;
	}
	else if(mode == VK_SAMPLER_ADDRESS_MODE_MIRRORED_REPEAT)
	{
		return 2;
	}
	else if(mode == VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_BORDER)
	{
		return 3;
	}
	else
	{
		fprintf(stderr, "wrap mode: %i\n", mode);
		assert(!"Unsupported wrap mode.");
		return -1;
	}
}

uint32_t getRenderTargetFormatVC4(VkFormat format)
{
	//TODO dithered BGR565
	switch(format)
	{
		case VK_FORMAT_R16G16B16A16_SFLOAT: //HDR mode set in tile binning config mode, so just return a valid format
		case VK_FORMAT_R8G8B8A8_UNORM:
			return VC4_RENDER_CONFIG_FORMAT_RGBA8888;
		case VK_FORMAT_B5G6R5_UNORM_PACK16:
			return VC4_RENDER_CONFIG_FORMAT_BGR565;
		default:
			fprintf(stderr, "rendertarget format: %i\n", format);
			assert(!"Unsupported render target format");
			return -1;
	}
}

////////////////////////////////////////////////////
////////////////////////////////////////////////////
/// just so we can return a function pointer
////////////////////////////////////////////////////
////////////////////////////////////////////////////

VKAPI_ATTR void VKAPI_CALL rpi_vkGetPhysicalDeviceExternalBufferProperties(
	VkPhysicalDevice                            physicalDevice,
	const VkPhysicalDeviceExternalBufferInfo*   pExternalBufferInfo,
	VkExternalBufferProperties*                 pExternalBufferProperties)
{
	UNSUPPORTED(rpi_vkGetPhysicalDeviceExternalBufferProperties);
}

VKAPI_ATTR void VKAPI_CALL rpi_vkGetPhysicalDeviceExternalFenceProperties(
	VkPhysicalDevice                            physicalDevice,
	const VkPhysicalDeviceExternalFenceInfo*    pExternalFenceInfo,
	VkExternalFenceProperties*                  pExternalFenceProperties)
{
	UNSUPPORTED(rpi_vkGetPhysicalDeviceExternalFenceProperties);
}


VKAPI_ATTR void VKAPI_CALL rpi_vkGetPhysicalDeviceExternalSemaphoreProperties(
	VkPhysicalDevice                            physicalDevice,
	const VkPhysicalDeviceExternalSemaphoreInfo* pExternalSemaphoreInfo,
	VkExternalSemaphoreProperties*              pExternalSemaphoreProperties)
{
	UNSUPPORTED(rpi_vkGetPhysicalDeviceExternalSemaphoreProperties);
}

VKAPI_ATTR void VKAPI_CALL rpi_vkGetDeviceGroupPeerMemoryFeatures(
	VkDevice                                    device,
	uint32_t                                    heapIndex,
	uint32_t                                    localDeviceIndex,
	uint32_t                                    remoteDeviceIndex,
	VkPeerMemoryFeatureFlags*                   pPeerMemoryFeatures)
{
	UNSUPPORTED(rpi_vkGetDeviceGroupPeerMemoryFeatures);
}
