void D3D11TextureCore::readData(PixelData& dest, UINT32 mipLevel, UINT32 face)
	{
		if (mProperties.getMultisampleCount() > 1)
			BS_EXCEPT(InvalidStateException, "Multisampled textures cannot be accessed from the CPU directly.");

		PixelData myData = lock(GBL_READ_ONLY, mipLevel, face);

#if BS_DEBUG_MODE
		if(dest.getConsecutiveSize() != myData.getConsecutiveSize())
		{
			unlock();
			BS_EXCEPT(InternalErrorException, "Buffer sizes don't match");
		}
#endif

		PixelUtil::bulkPixelConversion(myData, dest);

		unlock();
	}
	void GLTextureBuffer::upload(const PixelData &data, const PixelVolume &dest)
	{
		if((mUsage & TU_RENDERTARGET) != 0)
			BS_EXCEPT(NotImplementedException, "Writing to render texture from CPU not supported.");

		if ((mUsage & TU_DEPTHSTENCIL) != 0)
			BS_EXCEPT(NotImplementedException, "Writing to depth stencil texture from CPU not supported.");

		glBindTexture( mTarget, mTextureID );
		if(PixelUtil::isCompressed(data.getFormat()))
		{
			if(data.getFormat() != mFormat || !data.isConsecutive())
				BS_EXCEPT(InvalidParametersException, 
				"Compressed images must be consecutive, in the source format");

			GLenum format = GLPixelUtil::getClosestGLInternalFormat(mFormat);
			switch(mTarget) {
				case GL_TEXTURE_1D:
					if (dest.left == 0)
					{
						glCompressedTexImage1D(GL_TEXTURE_1D, mLevel,
							format,
							dest.getWidth(),
							0,
							data.getConsecutiveSize(),
							data.getData());
					}
					else
					{
						glCompressedTexSubImage1D(GL_TEXTURE_1D, mLevel, 
							dest.left,
							dest.getWidth(),
							format, data.getConsecutiveSize(),
							data.getData());
					}
					break;
				case GL_TEXTURE_2D:
				case GL_TEXTURE_CUBE_MAP:
					if (dest.left == 0 && dest.top == 0)
					{
						glCompressedTexImage2D(mFaceTarget, mLevel,
							format,
							dest.getWidth(),
							dest.getHeight(),
							0,
							data.getConsecutiveSize(),
							data.getData());
					}
					else
					{
						glCompressedTexSubImage2D(mFaceTarget, mLevel, 
							dest.left, dest.top, 
							dest.getWidth(), dest.getHeight(),
							format, data.getConsecutiveSize(),
							data.getData());
					}
					break;
				case GL_TEXTURE_3D:
					if (dest.left == 0 && dest.top == 0 && dest.front == 0)
					{
						glCompressedTexImage3D(GL_TEXTURE_3D, mLevel,
							format,
							dest.getWidth(),
							dest.getHeight(),
							dest.getDepth(),
							0,
							data.getConsecutiveSize(),
							data.getData());
					}
					else
					{			
						glCompressedTexSubImage3D(GL_TEXTURE_3D, mLevel, 
							dest.left, dest.top, dest.front,
							dest.getWidth(), dest.getHeight(), dest.getDepth(),
							format, data.getConsecutiveSize(),
							data.getData());
					}
					break;
			}
		
		} 
		else
		{
			if(data.getWidth() != data.getRowPitch())
				glPixelStorei(GL_UNPACK_ROW_LENGTH, data.getRowPitch());

			if(data.getHeight()*data.getWidth() != data.getSlicePitch())
				glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.getSlicePitch()/data.getWidth()));

			if(data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
				glPixelStorei(GL_UNPACK_SKIP_PIXELS, data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());

			if((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
				glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

			switch(mTarget) {
				case GL_TEXTURE_1D:
					glTexSubImage1D(GL_TEXTURE_1D, mLevel, 
						dest.left,
						dest.getWidth(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					break;
				case GL_TEXTURE_2D:
				case GL_TEXTURE_CUBE_MAP:
					glTexSubImage2D(mFaceTarget, mLevel, 
						dest.left, dest.top, 
						dest.getWidth(), dest.getHeight(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					break;
				case GL_TEXTURE_3D:
					glTexSubImage3D(
						GL_TEXTURE_3D, mLevel, 
						dest.left, dest.top, dest.front,
						dest.getWidth(), dest.getHeight(), dest.getDepth(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					break;
			}	
		}

		// Restore defaults
		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
		glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
		glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);

		BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
	}
	void GLTextureBuffer::upload(const PixelData& data, const PixelVolume& dest)
	{
		if ((mUsage & TU_DEPTHSTENCIL) != 0)
		{
			LOGERR("Writing to depth stencil texture from CPU not supported.");
			return;
		}

		glBindTexture(mTarget, mTextureID);
		BS_CHECK_GL_ERROR();

		if(PixelUtil::isCompressed(data.getFormat()))
		{
			// Block-compressed data cannot be smaller than 4x4, and must be a multiple of 4
			const UINT32 actualWidth = Math::divideAndRoundUp(std::max(mWidth, 4U), 4U) * 4U;
			const UINT32 actualHeight = Math::divideAndRoundUp(std::max(mHeight, 4U), 4U) * 4U;

			const UINT32 expectedRowPitch = actualWidth;
			const UINT32 expectedSlicePitch = actualWidth * actualHeight;

			const bool isConsecutive = data.getRowPitch() == expectedRowPitch && data.getSlicePitch() == expectedSlicePitch;
			if (data.getFormat() != mFormat || !isConsecutive)
			{
				LOGERR("Compressed images must be consecutive, in the source format");
				return;
			}

			GLenum format = GLPixelUtil::getGLInternalFormat(mFormat, mHwGamma);
			switch(mTarget) 
			{
				case GL_TEXTURE_1D:
					glCompressedTexSubImage1D(GL_TEXTURE_1D, mLevel,
						dest.left,
						dest.getWidth(),
						format, data.getConsecutiveSize(),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
				case GL_TEXTURE_2D:
				case GL_TEXTURE_CUBE_MAP:
					glCompressedTexSubImage2D(mFaceTarget, mLevel,
						dest.left, dest.top,
						dest.getWidth(), dest.getHeight(),
						format, data.getConsecutiveSize(),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
				case GL_TEXTURE_3D:
					glCompressedTexSubImage3D(GL_TEXTURE_3D, mLevel,
						dest.left, dest.top, dest.front,
						dest.getWidth(), dest.getHeight(), dest.getDepth(),
						format, data.getConsecutiveSize(),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
				default:
					break;
			}
		
		} 
		else
		{
			if (data.getWidth() != data.getRowPitch())
			{
				glPixelStorei(GL_UNPACK_ROW_LENGTH, data.getRowPitch());
				BS_CHECK_GL_ERROR();
			}

			if (data.getHeight()*data.getWidth() != data.getSlicePitch())
			{
				glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, (data.getSlicePitch() / data.getWidth()));
				BS_CHECK_GL_ERROR();
			}

			if (data.getLeft() > 0 || data.getTop() > 0 || data.getFront() > 0)
			{
				glPixelStorei(
					GL_UNPACK_SKIP_PIXELS, 
					data.getLeft() + data.getRowPitch() * data.getTop() + data.getSlicePitch() * data.getFront());
				BS_CHECK_GL_ERROR();
			}

			if ((data.getWidth()*PixelUtil::getNumElemBytes(data.getFormat())) & 3)
			{
				glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
				BS_CHECK_GL_ERROR();
			}

			switch(mTarget) {
				case GL_TEXTURE_1D:
					glTexSubImage1D(GL_TEXTURE_1D, mLevel, 
						dest.left,
						dest.getWidth(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
				case GL_TEXTURE_2D:
				case GL_TEXTURE_CUBE_MAP:
					glTexSubImage2D(mFaceTarget, mLevel, 
						dest.left, dest.top, 
						dest.getWidth(), dest.getHeight(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
				case GL_TEXTURE_2D_ARRAY:
				case GL_TEXTURE_3D:
					glTexSubImage3D(
						mTarget, mLevel,
						dest.left, dest.top, dest.front,
						dest.getWidth(), dest.getHeight(), dest.getDepth(),
						GLPixelUtil::getGLOriginFormat(data.getFormat()), GLPixelUtil::getGLOriginDataType(data.getFormat()),
						data.getData());
					BS_CHECK_GL_ERROR();
					break;
			}	
		}

		// Restore defaults
		glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
		BS_CHECK_GL_ERROR();

		glPixelStorei(GL_UNPACK_IMAGE_HEIGHT, 0);
		BS_CHECK_GL_ERROR();

		glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
		BS_CHECK_GL_ERROR();

		glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
		BS_CHECK_GL_ERROR();

		BS_INC_RENDER_STAT_CAT(ResWrite, RenderStatObject_Texture);
	}