Browse Source

OpenGL/Vulkan: Nicer way of checking and dealing with (un)supported pixel formats

BearishSun 9 years ago
parent
commit
36139ced8f

+ 3 - 3
Source/BansheeCore/Include/BsPixelUtil.h

@@ -157,7 +157,7 @@ namespace bs
 		 * Returns the number of bits per each element in the provided pixel format. This will return all zero for 
 		 * compressed and depth/stencil formats.
 		 */
-        static void getBitDepths(PixelFormat format, int rgba[4]);
+        static void getBitDepths(PixelFormat format, int (&rgba)[4]);
 
 		/**
 		 * Returns bit masks that determine in what bit range is each channel stored.
@@ -166,7 +166,7 @@ namespace bs
 		 * For example if your color is stored in an UINT32 and you want to extract the red channel you should AND the color
 		 * UINT32 with the bit-mask for the red channel and then right shift it by the red channel bit shift amount.
 		 */
-        static void getBitMasks(PixelFormat format, UINT32 rgba[4]);
+        static void getBitMasks(PixelFormat format, UINT32 (&rgba)[4]);
 
 		/**
 		 * Returns number of bits you need to shift a pixel element in order to move it to the start of the data type.
@@ -175,7 +175,7 @@ namespace bs
 		 * For example if your color is stored in an UINT32 and you want to extract the red channel you should AND the color 
 		 * UINT32 with the bit-mask for the red channel and then right shift it by the red channel bit shift amount.
 		 */
-		static void getBitShifts(PixelFormat format, UINT8 rgba[4]);
+		static void getBitShifts(PixelFormat format, UINT8 (&rgba)[4]);
 
 		/**	Returns the name of the pixel format. */
         static String getFormatName(PixelFormat srcformat);

+ 3 - 3
Source/BansheeCore/Source/BsPixelUtil.cpp

@@ -1091,7 +1091,7 @@ namespace bs
 		}
 	}
 
-    void PixelUtil::getBitDepths(PixelFormat format, int rgba[4])
+    void PixelUtil::getBitDepths(PixelFormat format, int (&rgba)[4])
     {
         const PixelFormatDescription& des = getDescriptionFor(format);
         rgba[0] = des.rbits;
@@ -1100,7 +1100,7 @@ namespace bs
         rgba[3] = des.abits;
     }
 
-	void PixelUtil::getBitMasks(PixelFormat format, UINT32 rgba[4])
+	void PixelUtil::getBitMasks(PixelFormat format, UINT32 (&rgba)[4])
     {
         const PixelFormatDescription& des = getDescriptionFor(format);
         rgba[0] = des.rmask;
@@ -1109,7 +1109,7 @@ namespace bs
         rgba[3] = des.amask;
     }
 
-	void PixelUtil::getBitShifts(PixelFormat format, UINT8 rgba[4])
+	void PixelUtil::getBitShifts(PixelFormat format, UINT8 (&rgba)[4])
 	{
 		const PixelFormatDescription& des = getDescriptionFor(format);
 		rgba[0] = des.rshift;

+ 0 - 1
Source/BansheeD3D11RenderAPI/Include/BsD3D11Prerequisites.h

@@ -33,7 +33,6 @@ namespace bs
 {
 	class D3D11RenderAPI;
 	class D3D11RenderWindow;
-	class D3D11Texture;
 	class D3D11TextureManager;
 	class D3D11Driver;
 	class D3D11DriverList;

+ 3 - 3
Source/BansheeD3D11RenderAPI/Source/BsD3D11Texture.cpp

@@ -240,7 +240,7 @@ namespace bs
 		HRESULT hr;
 		DXGI_FORMAT d3dPF = D3D11Mappings::getPF(closestFormat, hwGamma);
 
-		if (format != D3D11Mappings::getPF(d3dPF))
+		if (format != closestFormat)
 		{
 			LOGWRN(StringUtil::format("Provided pixel format is not supported by the driver: {0}. Falling back on: {1}.",
 									  format, closestFormat));
@@ -363,7 +363,7 @@ namespace bs
 		HRESULT hr;
 		DXGI_FORMAT d3dPF = D3D11Mappings::getPF(closestFormat, hwGamma);
 
-		if (format != D3D11Mappings::getPF(d3dPF))
+		if (format != closestFormat)
 		{
 			LOGWRN(StringUtil::format("Provided pixel format is not supported by the driver: {0}. Falling back on: {1}.",
 									  format, closestFormat));
@@ -509,7 +509,7 @@ namespace bs
 		HRESULT hr;
 		DXGI_FORMAT d3dPF = D3D11Mappings::getPF(closestFormat, hwGamma);
 		
-		if (format != D3D11Mappings::getPF(d3dPF))
+		if (format != closestFormat)
 		{
 			LOGWRN(StringUtil::format("Provided pixel format is not supported by the driver: {0}. Falling back on: {1}.",
 									  format, closestFormat));

+ 2 - 27
Source/BansheeGLRenderAPI/Include/BsGLPixelBuffer.h

@@ -104,9 +104,6 @@ namespace bs
 		 */
 		virtual void bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers);
 
-		/**	Returns internal OpenGL pixel format used by the buffer. */
-		GLenum getGLFormat() const { return mGLInternalFormat; }
-
 		/**
 		 * Blits the contents of the provided buffer into this pixel buffer. Data is bilinearily interpolated in case buffer
 		 * sizes don't match.
@@ -138,7 +135,6 @@ namespace bs
 		PixelVolume mLockedBox;
 
 		PixelData mBuffer;
-		GLenum mGLInternalFormat;
 		GpuLockOptions mCurrentLockOptions;
 	};
 
@@ -154,11 +150,11 @@ namespace bs
 		 * @param[in]	face				Face index of the texture in the case of cube textures or texture arrays.
 		 * @param[in]	level				Mip level of the texture.
 		 * @param[in]	usage				Usage signaling the render system how we plan on using the buffer.
-		 * @param[in]	writeGamma			True if the parent texture was created with SRGB support.
+		 * @param[in]	format				Format of each pixel in the buffer.
 		 * @param[in]	multisampleCount	Number of samples the parent texture was created with.
 		 */
 		GLTextureBuffer(GLenum target, GLuint id, GLint face, 
-			GLint level, GpuBufferUsage usage, bool writeGamma, UINT32 multisampleCount);
+			GLint level, GpuBufferUsage usage, PixelFormat format, UINT32 multisampleCount);
         ~GLTextureBuffer();
         
 		/** @copydoc GLPixelBuffer::bindToFramebuffer */
@@ -189,26 +185,5 @@ namespace bs
 		UINT32 mMultisampleCount;
     };
 
-	/**	Pixel buffer specialization that represents a render buffer. */
-    class GLRenderBuffer : public GLPixelBuffer
-	{
-    public:
-		/**
-		 * Initializes a new render buffer.
-		 *
-		 * @param[in]	format		OpenGL pixel format.
-		 * @param[in]	width		Width of the render buffer in pixels.
-		 * @param[in]	height		Height of the render buffer in pixels.
-		 * @param[in]	numSamples	Number of samples to support.
-		 */
-        GLRenderBuffer(GLenum format, UINT32 width, UINT32 height, GLsizei numSamples);
-        ~GLRenderBuffer();
-        
-		/** @copydoc	GLPixelBuffer::bindToFramebuffer */
-        void bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers) override;
-    protected:
-        GLuint mRenderbufferID;
-    };
-
 	/** @} */
 };

+ 3 - 15
Source/BansheeGLRenderAPI/Include/BsGLPixelFormat.h

@@ -15,6 +15,9 @@ namespace bs
 	class BS_RSGL_EXPORT GLPixelUtil
 	{
 	public:
+		/**	Finds the closest pixel format that OpenGL supports. */
+		static PixelFormat getClosestSupportedPF(PixelFormat format, TextureType texType, int usage);
+
 		/**	Returns matching OpenGL base pixel format type if one is found, zero otherwise. */
 		static GLenum getGLOriginFormat(PixelFormat mFormat);
 	
@@ -27,24 +30,9 @@ namespace bs
 		 */
 		static GLenum getGLInternalFormat(PixelFormat mFormat, bool hwGamma = false);
 	
-		/**
-		 * Returns matching OpenGL internal pixel format type if one is found, otherwise it returns the closest matching
-		 * format. Optionally returns an SRGB format if @p hwGamma is specified and such format exists.
-		 */
-		static GLenum getClosestGLInternalFormat(PixelFormat mFormat, bool hwGamma = false);
-		
 		/** Returns an OpenGL type that should be used for creating a buffer for the specified depth/stencil format. */
 		static GLenum getDepthStencilTypeFromFormat(PixelFormat mFormat);
 
-		/**	Returns pixel format closest to the provided internal OpenGL format. */
-		static PixelFormat getClosestEngineFormat(GLenum fmt);
-
-		/**	Returns closest supported format to the provided format. */
-		static PixelFormat getClosestValidFormat(PixelFormat fmt);
-
-		/**	Converts an OpenGL internal format into base format. */
-		static GLenum getBaseFormatFromCompressedInternalFormat(GLenum internalFormat);
-
 		/** Converts engine GPU buffer format to OpenGL GPU buffer format. */
 		static GLenum getBufferFormat(GpuBufferFormat format);
 

+ 1 - 0
Source/BansheeGLRenderAPI/Include/BsGLTexture.h

@@ -72,6 +72,7 @@ namespace bs
     private:
         GLuint mTextureID;
 		GLenum mGLFormat;
+		PixelFormat mInternalFormat;
         GLSupport& mGLSupport;
 		SPtr<GLPixelBuffer> mLockedBuffer;
 		

+ 30 - 55
Source/BansheeGLRenderAPI/Source/BsGLPixelBuffer.cpp

@@ -13,7 +13,7 @@ namespace bs
 {
 	GLPixelBuffer::GLPixelBuffer(UINT32 inWidth, UINT32 inHeight, UINT32 inDepth, PixelFormat inFormat, GpuBufferUsage usage)
 		: mUsage(usage), mIsLocked(false), mWidth(inWidth), mHeight(inHeight), mDepth(inDepth), mFormat(inFormat)
-		, mBuffer(inWidth, inHeight, inDepth, inFormat), mGLInternalFormat(GL_NONE)
+		, mBuffer(inWidth, inHeight, inDepth, inFormat)
 	{
 		mSizeInBytes = mHeight*mWidth*PixelUtil::getNumElemBytes(mFormat);
 		mCurrentLockOptions = (GpuLockOptions)0;
@@ -114,8 +114,8 @@ namespace bs
 
 	GLTextureBuffer::GLTextureBuffer(GLenum target, GLuint id, 
 									 GLint face, GLint level, GpuBufferUsage usage, 
-									 bool writeGamma, UINT32 multisampleCount):
-		GLPixelBuffer(0, 0, 0, PF_UNKNOWN, usage),
+									 PixelFormat format, UINT32 multisampleCount):
+		GLPixelBuffer(0, 0, 0, format, usage),
 		mTarget(target), mFaceTarget(0), mTextureID(id), mFace(face), mLevel(level), mMultisampleCount(multisampleCount)
 	{
 		GLint value = 0;
@@ -145,11 +145,6 @@ namespace bs
 			glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_DEPTH, &value);
 		mDepth = value;
 
-		// Get format
-		glGetTexLevelParameteriv(mFaceTarget, level, GL_TEXTURE_INTERNAL_FORMAT, &value);
-		mGLInternalFormat = value;
-		mFormat = GLPixelUtil::getClosestEngineFormat(value);
-	
 		// Default
 		mSizeInBytes = PixelUtil::getMemorySize(mWidth, mHeight, mDepth, mFormat);
 	
@@ -162,20 +157,28 @@ namespace bs
 
 	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_RENDERTARGET) != 0)
+		{
+			LOGERR("Writing to render texture from CPU not supported.");
+			return;
+		}
 
 		if ((mUsage & TU_DEPTHSTENCIL) != 0)
-			BS_EXCEPT(NotImplementedException, "Writing to depth stencil texture from CPU not supported.");
+		{
+			LOGERR("Writing to depth stencil texture from CPU not supported.");
+			return;
+		}
 
 		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");
+			if (data.getFormat() != mFormat || !data.isConsecutive())
+			{
+				LOGERR("Compressed images must be consecutive, in the source format");
+				return;
+			}
 
-			GLenum format = GLPixelUtil::getClosestGLInternalFormat(mFormat);
+			GLenum format = GLPixelUtil::getGLInternalFormat(mFormat);
 			switch(mTarget) {
 				case GL_TEXTURE_1D:
 					if (dest.left == 0)
@@ -293,15 +296,20 @@ namespace bs
 
 	void GLTextureBuffer::download(const PixelData &data)
 	{
-		if(data.getWidth() != getWidth() || data.getHeight() != getHeight() || data.getDepth() != getDepth())
-			BS_EXCEPT(InvalidParametersException, "only download of entire buffer is supported by GL");
+		if (data.getWidth() != getWidth() || data.getHeight() != getHeight() || data.getDepth() != getDepth())
+		{
+			LOGERR("Only download of entire buffer is supported by OpenGL.");
+			return;
+		}
 
-		glBindTexture( mTarget, mTextureID );
+		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");
+			if (data.getFormat() != mFormat || !data.isConsecutive())
+			{
+				LOGERR("Compressed images must be consecutive, in the source format");
+				return;
+			}
 
 			// Data must be consecutive and at beginning of buffer as PixelStorei not allowed
 			// for compressed formate
@@ -443,37 +451,4 @@ namespace bs
 			}
 		}		
 	}
-	
-	GLRenderBuffer::GLRenderBuffer(GLenum format, UINT32 width, UINT32 height, GLsizei numSamples):
-		GLPixelBuffer(width, height, 1, GLPixelUtil::getClosestEngineFormat(format), GBU_DYNAMIC),
-		mRenderbufferID(0)
-	{
-		mGLInternalFormat = format;
-		glGenRenderbuffers(1, &mRenderbufferID);
-		glBindRenderbuffer(GL_RENDERBUFFER, mRenderbufferID);
-    
-		/// Allocate storage for depth buffer
-		if (numSamples > 0)
-		{
-			glRenderbufferStorageMultisample(GL_RENDERBUFFER, 
-				numSamples, format, width, height);
-		}
-		else
-		{
-			glRenderbufferStorage(GL_RENDERBUFFER, format,
-								width, height);
-		}
-	}
-
-	GLRenderBuffer::~GLRenderBuffer()
-	{
-		glDeleteRenderbuffers(1, &mRenderbufferID);
-	}
-
-	void GLRenderBuffer::bindToFramebuffer(GLenum attachment, UINT32 zoffset, bool allLayers)
-	{
-		assert(zoffset < mDepth);
-		glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment,
-							GL_RENDERBUFFER, mRenderbufferID);
-	}
-};
+}

+ 10 - 174
Source/BansheeGLRenderAPI/Source/BsGLPixelFormat.cpp

@@ -7,6 +7,16 @@
 
 namespace bs  
 {
+	PixelFormat GLPixelUtil::getClosestSupportedPF(PixelFormat pf, TextureType texType, int usage)
+	{
+		// Check for any obvious issues first
+		PixelUtil::checkFormat(pf, texType, usage);
+		
+		// We don't check for any platform-specific format issues, assumed all are supported
+
+		return pf;
+	}
+
     GLenum GLPixelUtil::getGLOriginFormat(PixelFormat mFormat)
     {
         switch(mFormat)
@@ -89,35 +99,6 @@ namespace bs
                 return 0;
         }
     }
-
-	GLenum GLPixelUtil::getBaseFormatFromCompressedInternalFormat(GLenum internalFormat)
-	{
-		 switch(internalFormat) 
-		 {
-		 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
-		 case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-		 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
-		 case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-		 case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
-		 case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-		 case GL_COMPRESSED_RGBA_BPTC_UNORM:
-		 case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
-			 return GL_RGBA;
-		 case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-		 case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
-		 case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
-		 case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
-			 return GL_RGB;
-		 case GL_COMPRESSED_RED_RGTC1:
-		 case GL_COMPRESSED_SIGNED_RED_RGTC1:
-			 return GL_RED;
-		 case GL_COMPRESSED_RG_RGTC2:
-		 case GL_COMPRESSED_SIGNED_RG_RGTC2:
-			 return GL_RG;
-		 }
-
-		 return GL_RGBA;
-	}
     
     GLenum GLPixelUtil::getGLInternalFormat(PixelFormat mFormat, bool hwGamma)
     {
@@ -198,20 +179,6 @@ namespace bs
         }
     }
 
-    GLenum GLPixelUtil::getClosestGLInternalFormat(PixelFormat mFormat, bool hwGamma)
-    {
-        GLenum format = getGLInternalFormat(mFormat, hwGamma);
-        if(format == GL_NONE)
-		{
-			if (hwGamma)
-				return GL_SRGB8;
-			else
-				return GL_RGBA8;
-		}
-        else
-            return format;
-    }
-
 	GLenum GLPixelUtil::getDepthStencilTypeFromFormat(PixelFormat mFormat)
 	{
 		switch(mFormat)
@@ -232,137 +199,6 @@ namespace bs
 		return PF_D24S8;
 	}
 		
-	PixelFormat GLPixelUtil::getClosestEngineFormat(GLenum fmt)
-	{
-		switch(fmt) 
-		{
-		case GL_R8:
-			return PF_R8;
-		case GL_RG8:
-			return PF_R8G8;
-		case GL_RGB8:
-		case GL_SRGB8:
-			return PF_R8G8B8;
-		case GL_RGBA8:
-		case GL_SRGB8_ALPHA8:
-			return PF_R8G8B8A8;
-		case GL_R16F:
-			return PF_FLOAT16_R;
-		case GL_RG16F:
-			return PF_FLOAT16_RG;
-		case GL_RG32F:
-			return PF_FLOAT32_RG;
-		case GL_R32F:
-			return PF_FLOAT32_R;
-		case GL_RGB16F:
-			return PF_FLOAT16_RGB;
-		case GL_RGBA16F:
-			return PF_FLOAT16_RGBA;
-		case GL_RGB32F:
-			return PF_FLOAT32_RGB;
-		case GL_RGBA32F:
-			return PF_FLOAT32_RGBA;
-		case GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-		case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-		case GL_COMPRESSED_SRGB_S3TC_DXT1_EXT:
-		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
-			return PF_BC1;
-		case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
-			return PF_BC2;
-		case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-		case GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
-			return PF_BC3;
-		case GL_COMPRESSED_RED_RGTC1:
-		case GL_COMPRESSED_SIGNED_RED_RGTC1:
-			return PF_BC4;
-		case GL_COMPRESSED_RG_RGTC2:
-		case GL_COMPRESSED_SIGNED_RG_RGTC2:
-			return PF_BC5;
-		case GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT:
-		case GL_COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT:
-			return PF_BC6H;
-		case GL_COMPRESSED_RGBA_BPTC_UNORM:
-		case GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM:
-			return PF_BC7;
-		case GL_DEPTH_COMPONENT16:
-			return PF_D16;
-		case GL_DEPTH_COMPONENT32F:
-			return PF_D32;
-		case GL_DEPTH24_STENCIL8:
-			return PF_D24S8;
-		case GL_DEPTH32F_STENCIL8:
-			return PF_D32_S8X24;
-		case GL_RGB10_A2:
-			return PF_UNORM_R10G10B10A2;
-		case GL_R11F_G11F_B10F:
-			return PF_FLOAT_R11G11B10;
-		default:
-			return PF_R8G8B8A8;
-		};
-	}
-
-	PixelFormat GLPixelUtil::getClosestValidFormat(PixelFormat fmt)
-	{
-		switch(fmt) 
-		{
-		case PF_R8:
-			return PF_R8;
-		case PF_R8G8:
-			return PF_R8G8;
-		case PF_R8G8B8:
-		case PF_B8G8R8:
-			return PF_R8G8B8;
-		case PF_B8G8R8A8:
-		case PF_R8G8B8A8:
-			return PF_R8G8B8A8;
-		case PF_FLOAT16_R:
-			return PF_FLOAT16_R;
-		case PF_FLOAT16_RGB:
-			return PF_FLOAT16_RGB;
-		case PF_FLOAT16_RG: 
-			return PF_FLOAT16_RG;
-		case PF_FLOAT16_RGBA:
-			return PF_FLOAT16_RGBA;
-		case PF_FLOAT32_R:
-			return PF_FLOAT32_R;
-		case PF_FLOAT32_RG:
-			return PF_FLOAT32_RG;
-		case PF_FLOAT32_RGB:
-			return PF_FLOAT32_RGB;
-		case PF_FLOAT32_RGBA:
-			return PF_FLOAT32_RGBA;
-		case PF_BC1:
-			return PF_BC1;
-		case PF_BC1a:
-			return PF_BC1;
-		case PF_BC3:
-			return PF_BC3;
-		case PF_BC4:
-			return PF_BC4;
-		case PF_BC5:
-			return PF_BC5;
-		case PF_BC6H:
-			return PF_BC6H;
-		case PF_BC7:
-			return PF_BC7;
-		case PF_D16:
-			return PF_D16;
-		case PF_D32:
-			return PF_D32;
-		case PF_D24S8:
-			return PF_D24S8;
-		case PF_D32_S8X24:
-			return PF_D32_S8X24;
-		case PF_UNORM_R10G10B10A2:
-			return PF_UNORM_R10G10B10A2;
-		case PF_FLOAT_R11G11B10:
-			return PF_FLOAT_R11G11B10;
-		default:
-			return PF_UNKNOWN;
-		}
-	}
-
 	GLenum GLPixelUtil::getBufferFormat(GpuBufferFormat format)
 	{
 		static bool lookupInitialized = false;

+ 56 - 28
Source/BansheeGLRenderAPI/Source/BsGLTexture.cpp

@@ -16,7 +16,7 @@ namespace bs
 	GLTextureCore::GLTextureCore(GLSupport& support, const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData, 
 		GpuDeviceFlags deviceMask)
 		: TextureCore(desc, initialData, deviceMask),
-		mTextureID(0), mGLFormat(0), mGLSupport(support)
+		mTextureID(0), mGLFormat(0), mInternalFormat(PF_UNKNOWN), mGLSupport(support)
 	{
 		assert((deviceMask == GDF_DEFAULT || deviceMask == GDF_PRIMARY) && "Multiple GPUs not supported natively on OpenGL.");
 	}
@@ -37,23 +37,34 @@ namespace bs
 		UINT32 height = mProperties.getHeight();
 		UINT32 depth = mProperties.getDepth();
 		TextureType texType = mProperties.getTextureType();
-		PixelFormat pixFormat = mProperties.getFormat();
 		int usage = mProperties.getUsage();
 		UINT32 numMips = mProperties.getNumMipmaps();
 		UINT32 numFaces = mProperties.getNumFaces();
 
+		PixelFormat pixFormat = mProperties.getFormat();
+		mInternalFormat = GLPixelUtil::getClosestSupportedPF(pixFormat, texType, usage);
+
+		if (pixFormat != mInternalFormat)
+		{
+			LOGWRN(StringUtil::format("Provided pixel format is not supported by the driver: {0}. Falling back on: {1}.",
+									  pixFormat, mInternalFormat));
+		}
+
 		// Check requested number of mipmaps
 		UINT32 maxMips = PixelUtil::getMaxMipmaps(width, height, depth, mProperties.getFormat());
 		if (numMips > maxMips)
-			BS_EXCEPT(InvalidParametersException, "Invalid number of mipmaps. Maximum allowed is: " + toString(maxMips));
+		{
+			LOGERR("Invalid number of mipmaps. Maximum allowed is: " + toString(maxMips));
+			numMips = maxMips;
+		}
 
 		if ((usage & TU_DEPTHSTENCIL) != 0)
 		{
 			if (texType != TEX_TYPE_2D)
-				BS_EXCEPT(NotImplementedException, "Only 2D depth stencil targets are supported at the moment");
-
-			if (!PixelUtil::isDepth(pixFormat))
-				BS_EXCEPT(NotImplementedException, "Supplied format is not a depth stencil format. Format: " + toString(pixFormat));
+			{
+				LOGERR("Only 2D depth stencil targets are supported at the moment");
+				usage &= ~TU_DEPTHSTENCIL;
+			}
 		}
 
 		// Generate texture handle
@@ -66,16 +77,7 @@ namespace bs
 		glTexParameteri(getGLTextureTarget(), GL_TEXTURE_MAX_LEVEL, numMips);
 
 		// Allocate internal buffer so that glTexSubImageXD can be used
-		mGLFormat = GLPixelUtil::getClosestGLInternalFormat(pixFormat, mProperties.isHardwareGammaEnabled());
-
-		if (PixelUtil::isCompressed(pixFormat))
-		{
-			if((usage & TU_RENDERTARGET) != 0)
-				BS_EXCEPT(InvalidParametersException, "Cannot use a compressed format for a render target.");
-
-			if ((usage & TU_DEPTHSTENCIL) != 0)
-				BS_EXCEPT(InvalidParametersException, "Cannot use a compressed format for a depth stencil target.");
-		}
+		mGLFormat = GLPixelUtil::getGLInternalFormat(mInternalFormat, mProperties.isHardwareGammaEnabled());
 
 		UINT32 sampleCount = mProperties.getNumSamples();
 		if((usage & (TU_RENDERTARGET | TU_DEPTHSTENCIL)) != 0 && mProperties.getTextureType() == TEX_TYPE_2D && sampleCount > 1)
@@ -87,7 +89,7 @@ namespace bs
 		}
 		else if((usage & TU_DEPTHSTENCIL) != 0 && mProperties.getTextureType() == TEX_TYPE_2D)
 		{
-			GLenum depthStencilFormat = GLPixelUtil::getDepthStencilTypeFromFormat(pixFormat);
+			GLenum depthStencilFormat = GLPixelUtil::getDepthStencilTypeFromFormat(mInternalFormat);
 
 			if (numFaces <= 1)
 			{
@@ -102,8 +104,8 @@ namespace bs
 		}
 		else
 		{
-			GLenum baseFormat = GLPixelUtil::getGLOriginFormat(pixFormat);
-			GLenum baseDataType = GLPixelUtil::getGLOriginDataType(pixFormat);
+			GLenum baseFormat = GLPixelUtil::getGLOriginFormat(mInternalFormat);
+			GLenum baseDataType = GLPixelUtil::getGLOriginDataType(mInternalFormat);
 
 			// Run through this process to pre-generate mipmap pyramid
 			for (UINT32 mip = 0; mip <= numMips; mip++)
@@ -232,8 +234,11 @@ namespace bs
 
 	void GLTextureCore::unlockImpl()
 	{
-		if(mLockedBuffer == nullptr)
-			BS_EXCEPT(InternalErrorException, "Trying to unlock a buffer that's not locked.");
+		if (mLockedBuffer == nullptr)
+		{
+			LOGERR("Trying to unlock a buffer that's not locked.");
+			return;
+		}
 
 		mLockedBuffer->unlock();
 		mLockedBuffer = nullptr;
@@ -242,18 +247,42 @@ namespace bs
 	void GLTextureCore::readDataImpl(PixelData& dest, UINT32 mipLevel, UINT32 face, UINT32 deviceIdx, UINT32 queueIdx)
 	{
 		if (mProperties.getNumSamples() > 1)
-			BS_EXCEPT(InvalidStateException, "Multisampled textures cannot be accessed from the CPU directly.");
+		{
+			LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
+			return;
+		}
 
-		getBuffer(face, mipLevel)->download(dest);
+		if(dest.getFormat() != mInternalFormat)
+		{
+			PixelData temp(dest.getExtents(), mInternalFormat);
+			temp.allocateInternalBuffer();
+
+			getBuffer(face, mipLevel)->download(temp);
+			PixelUtil::bulkPixelConversion(temp, dest);
+		}
+		else
+			getBuffer(face, mipLevel)->download(dest);
 	}
 
 	void GLTextureCore::writeDataImpl(const PixelData& src, UINT32 mipLevel, UINT32 face, bool discardWholeBuffer,
 								  UINT32 queueIdx)
 	{
 		if (mProperties.getNumSamples() > 1)
-			BS_EXCEPT(InvalidStateException, "Multisampled textures cannot be accessed from the CPU directly.");
+		{
+			LOGERR("Multisampled textures cannot be accessed from the CPU directly.");
+			return;
+		}
 
-		getBuffer(face, mipLevel)->upload(src, src.getExtents());
+		if (src.getFormat() != mInternalFormat)
+		{
+			PixelData temp(src.getExtents(), mInternalFormat);
+			temp.allocateInternalBuffer();
+
+			PixelUtil::bulkPixelConversion(src, temp);
+			getBuffer(face, mipLevel)->upload(temp, temp.getExtents());
+		}
+		else
+			getBuffer(face, mipLevel)->upload(src, src.getExtents());
 	}
 
 	void GLTextureCore::copyImpl(UINT32 srcFace, UINT32 srcMipLevel, UINT32 destFace, UINT32 destMipLevel,
@@ -274,8 +303,7 @@ namespace bs
 			for (UINT32 mip = 0; mip <= mProperties.getNumMipmaps(); mip++)
 			{
                 GLPixelBuffer *buf = bs_new<GLTextureBuffer>(getGLTextureTarget(), mTextureID, face, mip,
-					static_cast<GpuBufferUsage>(mProperties.getUsage()), 
-					mProperties.isHardwareGammaEnabled(), mProperties.getNumSamples());
+					static_cast<GpuBufferUsage>(mProperties.getUsage()), mInternalFormat, mProperties.getNumSamples());
 
 				mSurfaceList.push_back(bs_shared_ptr<GLPixelBuffer>(buf));
                 if(buf->getWidth() == 0 || buf->getHeight() == 0 || buf->getDepth() == 0)

+ 1 - 1
Source/BansheeGLRenderAPI/Source/BsGLTextureManager.cpp

@@ -31,7 +31,7 @@ namespace bs
 		if(usage & TU_RENDERTARGET)
             return GLRTTManager::instance().getSupportedAlternative(format);
 
-		return GLPixelUtil::getClosestValidFormat(format);
+		return GLPixelUtil::getClosestSupportedPF(format, ttype, usage);
 	}
 
 	GLTextureCoreManager::GLTextureCoreManager(GLSupport& support)

+ 2 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanTexture.h

@@ -218,7 +218,7 @@ namespace bs
 
 	private:
 		/** Creates a new image for the specified device, matching the current properties. */
-		VulkanImage* createImage(VulkanDevice& device);
+		VulkanImage* createImage(VulkanDevice& device, PixelFormat format);
 
 		/** 
 		 * Creates a staging buffer that can be used for texture transfer operations.
@@ -239,6 +239,7 @@ namespace bs
 			VkImageLayout srcFinalLayout, VkImageLayout dstFinalLayout);
 
 		VulkanImage* mImages[BS_MAX_DEVICES];
+		PixelFormat mInternalFormats[BS_MAX_DEVICES];
 		GpuDeviceFlags mDeviceMask;
 
 		VulkanBuffer* mStagingBuffer;

+ 4 - 0
Source/BansheeVulkanRenderAPI/Include/BsVulkanUtility.h

@@ -16,6 +16,10 @@ namespace bs
 	class VulkanUtility
 	{
 	public:
+		/**	Finds the closest pixel format that a specific Vulkan device supports. */
+		static PixelFormat getClosestSupportedPixelFormat(VulkanDevice& device, PixelFormat format, TextureType texType, 
+			int usage, bool optimalTiling, bool hwGamma);
+
 		/** Converts between Banshee and Vulkan pixel format. */
 		static VkFormat getPixelFormat(PixelFormat format, bool sRGB = false);
 

+ 21 - 25
Source/BansheeVulkanRenderAPI/Source/BsVulkanTexture.cpp

@@ -383,11 +383,11 @@ namespace bs
 	{ }
 
 	VulkanTextureCore::VulkanTextureCore(const TEXTURE_DESC& desc, const SPtr<PixelData>& initialData,
-		GpuDeviceFlags deviceMask)
+										 GpuDeviceFlags deviceMask)
 		: TextureCore(desc, initialData, deviceMask), mImages(), mDeviceMask(deviceMask), mStagingBuffer(nullptr)
 		, mMappedDeviceIdx(-1), mMappedGlobalQueueIdx(-1), mMappedMip(0), mMappedFace(0), mMappedRowPitch(false)
-		, mMappedSlicePitch(false), mMappedLockOptions(GBL_WRITE_ONLY), mDirectlyMappable(false), mSupportsGPUWrites(false)
-		, mIsMapped(false)
+		, mMappedSlicePitch(false), mMappedLockOptions(GBL_WRITE_ONLY), mInternalFormats()
+		, mDirectlyMappable(false), mSupportsGPUWrites(false), mIsMapped(false)
 	{
 		
 	}
@@ -481,7 +481,6 @@ namespace bs
 			}
 		}
 
-		mImageCI.format = VulkanUtility::getPixelFormat(props.getFormat(), props.isHardwareGammaEnabled());
 		mImageCI.extent = { props.getWidth(), props.getHeight(), props.getDepth() };
 		mImageCI.mipLevels = props.getNumMipmaps() + 1;
 		mImageCI.arrayLayers = props.getNumFaces();
@@ -502,14 +501,21 @@ namespace bs
 			if (devices[i] == nullptr)
 				continue;
 
-			mImages[i] = createImage(*devices[i]);
+			bool optimalTiling = tiling == VK_IMAGE_TILING_OPTIMAL;
+
+			mInternalFormats[i] = VulkanUtility::getClosestSupportedPixelFormat(
+				*devices[i], props.getFormat(), props.getTextureType(), props.getUsage(), optimalTiling,
+				props.isHardwareGammaEnabled());
+
+			mImages[i] = createImage(*devices[i], mInternalFormats[i]);
+			
 		}
 
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_Texture);
 		TextureCore::initialize();
 	}
 
-	VulkanImage* VulkanTextureCore::createImage(VulkanDevice& device)
+	VulkanImage* VulkanTextureCore::createImage(VulkanDevice& device, PixelFormat format)
 	{
 		bool directlyMappable = mImageCI.tiling == VK_IMAGE_TILING_LINEAR;
 		VkMemoryPropertyFlags flags = directlyMappable ?
@@ -518,6 +524,8 @@ namespace bs
 
 		VkDevice vkDevice = device.getLogical();
 
+		mImageCI.format = VulkanUtility::getPixelFormat(format, mProperties.isHardwareGammaEnabled());;
+
 		VkImage image;
 		VkResult result = vkCreateImage(vkDevice, &mImageCI, gVulkanAllocator, &image);
 		assert(result == VK_SUCCESS);
@@ -758,7 +766,7 @@ namespace bs
 			bool isBoundWithoutUse = boundCount > useCount;
 			if (isBoundWithoutUse)
 			{
-				VulkanImage* newImage = createImage(device);
+				VulkanImage* newImage = createImage(device, mInternalFormats[i]);
 
 				// Avoid copying original contents if the image only has one sub-resource, which we'll overwrite anyway
 				if (dstProps.getNumMipmaps() > 0 || dstProps.getNumFaces() > 1)
@@ -854,7 +862,7 @@ namespace bs
 		UINT32 mipHeight = std::max(1u, props.getHeight() >> mipLevel);
 		UINT32 mipDepth = std::max(1u, props.getDepth() >> mipLevel);
 
-		PixelData lockedArea(mipWidth, mipHeight, mipDepth, props.getFormat());
+		PixelData lockedArea(mipWidth, mipHeight, mipDepth, mInternalFormats[deviceIdx]);
 
 		VulkanImage* image = mImages[deviceIdx];
 
@@ -899,7 +907,7 @@ namespace bs
 				// image so we don't modify the previous use of the image
 				if (subresource->isBound())
 				{
-					VulkanImage* newImage = createImage(device);
+					VulkanImage* newImage = createImage(device, mInternalFormats[deviceIdx]);
 
 					// Copy contents of the current image to the new one, unless caller explicitly specifies he doesn't
 					// care about the current contents
@@ -940,7 +948,7 @@ namespace bs
 				// We need to discard the entire image, even though we're only writing to a single sub-resource
 				image->destroy();
 
-				image = createImage(device);
+				image = createImage(device, mInternalFormats[deviceIdx]);
 				mImages[deviceIdx] = image;
 
 				image->map(face, mipLevel, lockedArea);
@@ -968,7 +976,7 @@ namespace bs
 				// create a new image so we don't modify the previous use of the image
 				if (options == GBL_READ_WRITE && subresource->isBound())
 				{
-					VulkanImage* newImage = createImage(device);
+					VulkanImage* newImage = createImage(device, mInternalFormats[deviceIdx]);
 
 					VkMemoryRequirements memReqs;
 					vkGetImageMemoryRequirements(device.getLogical(), image->getHandle(), &memReqs);
@@ -1136,7 +1144,7 @@ namespace bs
 						// We need to discard the entire image, even though we're only writing to a single sub-resource
 						image->destroy();
 
-						image = createImage(device);
+						image = createImage(device, mInternalFormats[mMappedDeviceIdx]);
 						mImages[mMappedDeviceIdx] = image;
 
 						subresource = image->getSubresource(mMappedFace, mMappedMip);
@@ -1164,7 +1172,7 @@ namespace bs
 					// avoid modifying its use in the previous operation
 					if (isBoundWithoutUse)
 					{
-						VulkanImage* newImage = createImage(device);
+						VulkanImage* newImage = createImage(device, mInternalFormats[mMappedDeviceIdx]);
 
 						// Avoid copying original contents if the image only has one sub-resource, which we'll overwrite anyway
 						if (props.getNumMipmaps() > 0 || props.getNumFaces() > 1)
@@ -1249,17 +1257,7 @@ namespace bs
 		}
 
 		PixelData myData = lock(GBL_READ_ONLY, mipLevel, face, deviceIdx, queueIdx);
-
-#if BS_DEBUG_MODE
-		if (dest.getConsecutiveSize() != myData.getConsecutiveSize())
-		{
-			unlock();
-			BS_EXCEPT(InternalErrorException, "Buffer sizes don't match");
-		}
-#endif
-
 		PixelUtil::bulkPixelConversion(myData, dest);
-
 		unlock();
 
 		BS_INC_RENDER_STAT_CAT(ResRead, RenderStatObject_Texture);
@@ -1274,8 +1272,6 @@ namespace bs
 			return;
 		}
 
-		PixelFormat format = mProperties.getFormat();
-
 		mipLevel = Math::clamp(mipLevel, (UINT32)mipLevel, mProperties.getNumMipmaps());
 		face = Math::clamp(face, (UINT32)0, mProperties.getNumFaces() - 1);
 

+ 68 - 0
Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

@@ -7,6 +7,74 @@
 
 namespace bs
 {
+	PixelFormat VulkanUtility::getClosestSupportedPixelFormat(VulkanDevice& device, PixelFormat format, TextureType texType,
+		int usage, bool optimalTiling, bool hwGamma)
+	{
+		// Check for any obvious issues first
+		PixelUtil::checkFormat(format, texType, usage);
+
+		// Check actual device for format support
+		VkFormatFeatureFlagBits featureFlagBit;
+		if ((usage & TU_RENDERTARGET) != 0)
+			featureFlagBit = VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
+		else if ((usage & TU_DEPTHSTENCIL) != 0)
+			featureFlagBit = VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
+		else if ((usage & TU_LOADSTORE) != 0)
+			featureFlagBit = VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT;
+		else
+			featureFlagBit = VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
+
+		VkFormatProperties props;
+		auto isSupported = [&](VkFormat vkFmt)
+		{
+			vkGetPhysicalDeviceFormatProperties(device.getPhysical(), vkFmt, &props);
+			VkFormatFeatureFlags featureFlags = optimalTiling ? props.optimalTilingFeatures : props.linearTilingFeatures;
+
+			return (featureFlags & featureFlagBit) != 0;
+		};
+
+		VkFormat vkFormat = getPixelFormat(format, hwGamma);
+		if(!isSupported(vkFormat))
+		{
+			if ((usage & TU_DEPTHSTENCIL) != 0)
+			{
+				bool hasStencil = format == PF_D24S8 || format == PF_D32_S8X24;
+
+				// Spec guarantees at least one depth-only, and one depth-stencil format to be supported
+				if(hasStencil)
+				{
+					if (isSupported(VK_FORMAT_D32_SFLOAT_S8_UINT))
+						format = PF_D32_S8X24;
+					else
+						format = PF_D24S8;
+
+					// We ignore 8-bit stencil-only, and 16/8 depth/stencil combo buffers as engine doesn't expose them,
+					// and spec guarantees one of the above must be implemented.
+				}
+				else
+				{
+					// The only format that could have failed is 32-bit depth, so we must use the alternative 16-bit.
+					// Spec guarantees it is always supported.
+					format = PF_D16;
+				}
+			}
+			else
+			{
+				int bitDepths[4];
+				PixelUtil::getBitDepths(format, bitDepths);
+
+				if (bitDepths[0] == 16) // 16-bit format, fall back to 4-channel 16-bit, guaranteed to be supported
+					format = PF_FLOAT16_RGBA;
+				else if(format == PF_BC6H) // Fall back to uncompressed alternative
+					format = PF_FLOAT16_RGBA;
+				else // Must be 8-bit per channel format, compressed format or some uneven format
+					format = PF_R8G8B8A8;
+			}
+		}
+
+		return format;
+	}
+
 	VkFormat VulkanUtility::getPixelFormat(PixelFormat format, bool sRGB)
 	{
 		switch (format)