Просмотр исходного кода

Fixing OpenGL texture and image binding
Fixing OpenGL 3D render targets

BearishSun 9 лет назад
Родитель
Сommit
9555982752

BIN
Data/Engine/GUISkin.asset


BIN
Data/Engine/ResourceManifest.asset


BIN
Data/Engine/Shaders/Diffuse.bsl.asset


BIN
Data/Engine/Timestamp.asset


BIN
Data/Engine/arial.ttf.asset


+ 2 - 4
Data/Raw/Engine/Shaders/Diffuse.bsl

@@ -55,8 +55,6 @@ Technique : base("Surface") =
 			uniform sampler2D gAlbedoTex;
 			uniform sampler2D gNormalTex;
 			
-			out vec4[3] fragColor;
-			
 			void main()
 			{
 				vec3 normal = normalize(texture2D(gNormalTex, uv0).xyz * 2.0f - vec3(1, 1, 1));
@@ -66,10 +64,10 @@ Technique : base("Surface") =
 				gbufferData.albedo = texture2D(gAlbedoTex, uv0);
 				gbufferData.worldNormal.xyz = worldNormal;
 				
-				encodeGBuffer(gbufferData, fragColor[1], fragColor[2]);
+				encodeGBuffer(gbufferData, gl_FragData[1], gl_FragData[2]);
 				
 				// TODO - Just returning a simple ambient term, use better environment lighting later
-				fragColor[0] = vec4(gbufferData.albedo.rgb, 1.0f) * 0.01f; 
+				gl_FragData[0] = vec4(gbufferData.albedo.rgb, 1.0f) * 0.01f; 
 			}	
 		};
 	};

+ 3 - 4
Source/BansheeCore/Include/BsRenderAPI.h

@@ -46,7 +46,7 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	accessor	Accessor on which will this command be queued for execution.
 		 */
-		static void setLoadStoreTexture(CoreAccessor& accessor, GpuProgramType gptype, UINT16 texUnit, bool enabled, 
+		static void setLoadStoreTexture(CoreAccessor& accessor, GpuProgramType gptype, UINT16 texUnit,
 			const SPtr<Texture>& texture, const TextureSurface& surface);
 
 		/**  
@@ -379,12 +379,11 @@ namespace BansheeEngine
 		 *
 		 * @param[in]	gptype			Determines to which GPU program slot to bind the texture.
 		 * @param[in]	texUnit			Texture unit index to bind the texture to.
-		 * @param[in]	enabled			True to bind the texture at the specified unit, false to unbind.
 		 * @param[in]	texture			Texture to bind.
 		 * @param[in]	surface			Determines which surface of the texture to bind.
 		 */
-		virtual void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, bool enabled,
-			const SPtr<TextureCore>& texture, const TextureSurface& surface) = 0;
+		virtual void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, const SPtr<TextureCore>& texture, 
+			const TextureSurface& surface) = 0;
 
 		/**
 		 * Binds a buffer that can be used for read or write operations on the GPU.

+ 32 - 0
Source/BansheeCore/Include/BsRenderAPICapabilities.h

@@ -174,6 +174,18 @@ namespace BansheeEngine
 			mNumCombinedTextureUnits = num;
 		}
 
+		/**	Sets the maximum number of load-store texture units per pipeline stage. */
+		void setNumLoadStoreTextureUnits(GpuProgramType type, UINT16 num)
+		{
+			mNumLoadStoreTextureUnitsPerStage[type] = num;
+		}
+
+		/** Sets the maximum number of load-store texture units in all pipeline stages. */
+		void setNumCombinedLoadStoreTextureUnits(UINT16 num)
+		{
+			mNumCombinedLoadStoreTextureUnits = num;
+		}
+
 		/**	Sets the maximum number of GPU param block buffers per pipeline stage. */
 		void setNumGpuParamBlockBuffers(GpuProgramType type, UINT16 num)
 		{
@@ -220,6 +232,22 @@ namespace BansheeEngine
 			return mNumCombinedTextureUnits;
 		}
 
+		/**	Returns the number of load-store texture units supported per pipeline stage. */
+		UINT16 getNumLoadStoreTextureUnits(GpuProgramType type) const
+		{
+			auto iterFind = mNumLoadStoreTextureUnitsPerStage.find(type);
+			if (iterFind != mNumLoadStoreTextureUnitsPerStage.end())
+				return iterFind->second;
+			else
+				return 0;
+		}
+
+		/**	Returns the number of load-store texture units supported in all pipeline stages. */
+		UINT16 getNumCombinedLoadStoreTextureUnits() const
+		{
+			return mNumCombinedLoadStoreTextureUnits;
+		}
+
 		/**	Returns the maximum number of bound GPU program param block buffers per pipeline stage. */
 		UINT16 getNumGpuParamBlockBuffers(GpuProgramType type) const
 		{
@@ -489,6 +517,10 @@ namespace BansheeEngine
 		Map<GpuProgramType, UINT16> mNumGpuParamBlocksPerStage;
 		// Total number of uniform blocks available
 		UINT16 mNumCombinedUniformBlocks = 0;
+		// The number of load-store texture unitss available per stage
+		Map<GpuProgramType, UINT16> mNumLoadStoreTextureUnitsPerStage;
+		// Total number of load-store texture units available
+		UINT16 mNumCombinedLoadStoreTextureUnits = 0;
 		// The stencil buffer bit depth
 		UINT16 mStencilBufferBitDepth = 0;
 		// Maximum number of vertex buffers we can bind at once

+ 4 - 1
Source/BansheeCore/Source/BsAnimationManager.cpp

@@ -16,10 +16,13 @@ namespace BansheeEngine
 	AnimationManager::AnimationManager()
 		: mNextId(1), mUpdateRate(1.0f / 60.0f), mAnimationTime(0.0f), mLastAnimationUpdateTime(0.0f)
 		, mNextAnimationUpdateTime(0.0f), mPaused(false), mWorkerStarted(false), mPoseReadBufferIdx(1)
-		, mPoseWriteBufferIdx(0), mDataReadyCount(0), mWorkerState(WorkerState::Inactive), mDataReady(false)
+		, mPoseWriteBufferIdx(0), mDataReady(false)
 	{
 		mAnimationWorker = Task::create("Animation", std::bind(&AnimationManager::evaluateAnimation, this));
 
+		mDataReadyCount.store(0, std::memory_order_release);
+		mWorkerState.store(WorkerState::Inactive, std::memory_order_release);
+
 		mBlendShapeVertexDesc = VertexDataDesc::create();
 		mBlendShapeVertexDesc->addVertElem(VET_FLOAT3, VES_POSITION, 1, 1);
 		mBlendShapeVertexDesc->addVertElem(VET_UBYTE4_NORM, VES_NORMAL, 1, 1);

+ 4 - 4
Source/BansheeCore/Source/BsRenderAPI.cpp

@@ -27,11 +27,11 @@ namespace BansheeEngine
 		accessor.queueCommand(std::bind(&RenderAPICore::setTexture, RenderAPICore::instancePtr(), gptype, unit, texPtr->getCore()));
 	}
 
-	void RenderAPI::setLoadStoreTexture(CoreAccessor& accessor, GpuProgramType gptype, UINT16 unit, bool enabled, const SPtr<Texture>& texPtr,
-		const TextureSurface& surface)
+	void RenderAPI::setLoadStoreTexture(CoreAccessor& accessor, GpuProgramType gptype, UINT16 unit, 
+		const SPtr<Texture>& texPtr, const TextureSurface& surface)
 	{
-		accessor.queueCommand(std::bind(&RenderAPICore::setLoadStoreTexture, RenderAPICore::instancePtr(), gptype, unit, enabled, texPtr->getCore(),
-			surface));
+		accessor.queueCommand(std::bind(&RenderAPICore::setLoadStoreTexture, RenderAPICore::instancePtr(), gptype, unit, 
+			texPtr->getCore(), surface));
 	}
 
 	void RenderAPI::setBuffer(CoreAccessor& accessor, GpuProgramType gptype, UINT16 unit, const SPtr<GpuBuffer>& buffer,

+ 7 - 0
Source/BansheeCore/Source/BsRenderAPICapabilities.cpp

@@ -27,6 +27,13 @@ namespace BansheeEngine
 		mNumGpuParamBlocksPerStage[GPT_HULL_PROGRAM] = 0;
 		mNumGpuParamBlocksPerStage[GPT_DOMAIN_PROGRAM] = 0;
 		mNumGpuParamBlocksPerStage[GPT_COMPUTE_PROGRAM] = 0;
+
+		mNumLoadStoreTextureUnitsPerStage[GPT_VERTEX_PROGRAM] = 0;
+		mNumLoadStoreTextureUnitsPerStage[GPT_FRAGMENT_PROGRAM] = 0;
+		mNumLoadStoreTextureUnitsPerStage[GPT_GEOMETRY_PROGRAM] = 0;
+		mNumLoadStoreTextureUnitsPerStage[GPT_HULL_PROGRAM] = 0;
+		mNumLoadStoreTextureUnitsPerStage[GPT_DOMAIN_PROGRAM] = 0;
+		mNumLoadStoreTextureUnitsPerStage[GPT_COMPUTE_PROGRAM] = 0;
 	}
 
 	RenderAPICapabilities::~RenderAPICapabilities()

+ 1 - 1
Source/BansheeD3D11RenderAPI/Include/BsD3D11RenderAPI.h

@@ -40,7 +40,7 @@ namespace BansheeEngine
 		void setTexture(GpuProgramType gptype, UINT16 texUnit, const SPtr<TextureCore>& texture) override;
 
 		/** @copydoc RenderAPICore::setLoadStoreTexture */
-		void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, bool enabled, const SPtr<TextureCore>& texture,
+		void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, const SPtr<TextureCore>& texture,
 			const TextureSurface& surface) override;
 
 		/** @copydoc RenderAPICore::setBuffer */

+ 12 - 6
Source/BansheeD3D11RenderAPI/Source/BsD3D11RenderAPI.cpp

@@ -328,13 +328,13 @@ namespace BansheeEngine
 		BS_INC_RENDER_STAT(NumTextureBinds);
 	}
 
-	void D3D11RenderAPI::setLoadStoreTexture(GpuProgramType gptype, UINT16 unit, bool enabled, const SPtr<TextureCore>& texPtr,
+	void D3D11RenderAPI::setLoadStoreTexture(GpuProgramType gptype, UINT16 unit, const SPtr<TextureCore>& texPtr,
 		const TextureSurface& surface)
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
 		ID3D11UnorderedAccessView* viewArray[1];
-		if (texPtr != nullptr && enabled)
+		if (texPtr != nullptr)
 		{
 			D3D11TextureCore* d3d11Texture = static_cast<D3D11TextureCore*>(texPtr.get());
 			SPtr<TextureView> texView = TextureCore::requestView(texPtr, surface.mipLevel, 1, 
@@ -368,7 +368,7 @@ namespace BansheeEngine
 			mDevice->getImmediateContext()->CSSetUnorderedAccessViews(unit, 1, viewArray, nullptr);
 		}
 		else
-			BS_EXCEPT(InvalidParametersException, "Unsupported gpu program type: " + toString(gptype));
+			LOGERR("Unsupported gpu program type: " + toString(gptype));
 
 		BS_INC_RENDER_STAT(NumTextureBinds);
 	}
@@ -945,9 +945,9 @@ namespace BansheeEngine
 			rsc->addGpuProgramProfile(GPP_HS_5_0, "hs_5_0");
 			rsc->addGpuProgramProfile(GPP_DS_5_0, "ds_5_0");
 
-			rsc->setNumTextureUnits(GPT_HULL_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
-			rsc->setNumTextureUnits(GPT_DOMAIN_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
-			rsc->setNumTextureUnits(GPT_COMPUTE_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_SLOT_COUNT);
+			rsc->setNumTextureUnits(GPT_HULL_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT);
+			rsc->setNumTextureUnits(GPT_DOMAIN_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT);
+			rsc->setNumTextureUnits(GPT_COMPUTE_PROGRAM, D3D11_COMMONSHADER_INPUT_RESOURCE_REGISTER_COUNT);
 
 			rsc->setNumCombinedTextureUnits(rsc->getNumTextureUnits(GPT_FRAGMENT_PROGRAM)
 				+ rsc->getNumTextureUnits(GPT_VERTEX_PROGRAM) + rsc->getNumTextureUnits(GPT_VERTEX_PROGRAM)
@@ -963,6 +963,12 @@ namespace BansheeEngine
 				+ rsc->getNumGpuParamBlockBuffers(GPT_HULL_PROGRAM) + rsc->getNumGpuParamBlockBuffers(GPT_DOMAIN_PROGRAM)
 				+ rsc->getNumGpuParamBlockBuffers(GPT_COMPUTE_PROGRAM));
 
+			rsc->setNumLoadStoreTextureUnits(GPT_FRAGMENT_PROGRAM, D3D11_PS_CS_UAV_REGISTER_COUNT);
+			rsc->setNumLoadStoreTextureUnits(GPT_COMPUTE_PROGRAM, D3D11_PS_CS_UAV_REGISTER_COUNT);
+
+			rsc->setNumCombinedLoadStoreTextureUnits(rsc->getNumLoadStoreTextureUnits(GPT_FRAGMENT_PROGRAM)
+				+ rsc->getNumLoadStoreTextureUnits(GPT_COMPUTE_PROGRAM));
+
 			rsc->setCapability(RSC_SHADER_SUBROUTINE);
 		}
 

+ 2 - 2
Source/BansheeEngine/Source/BsRendererUtility.cpp

@@ -229,9 +229,9 @@ namespace BansheeEngine
 			const TextureSurface& surface = params->getLoadStoreSurface(iter->second.slot);
 
 			if (texture == nullptr)
-				rapi.setLoadStoreTexture(type, iter->second.slot, false, nullptr, surface);
+				rapi.setLoadStoreTexture(type, iter->second.slot, nullptr, surface);
 			else
-				rapi.setLoadStoreTexture(type, iter->second.slot, true, texture, surface);
+				rapi.setLoadStoreTexture(type, iter->second.slot, texture, surface);
 		}
 
 		for (auto iter = paramDesc.buffers.begin(); iter != paramDesc.buffers.end(); ++iter)

+ 42 - 18
Source/BansheeGLRenderAPI/Include/BsGLRenderAPI.h

@@ -49,7 +49,7 @@ namespace BansheeEngine
 		void setTexture(GpuProgramType gptype, UINT16 texUnit, const SPtr<TextureCore>& texture) override;
 
 		/** @copydoc RenderAPICore::setLoadStoreTexture */
-		void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, bool enabled, const SPtr<TextureCore>& texture,
+		void setLoadStoreTexture(GpuProgramType gptype, UINT16 texUnit, const SPtr<TextureCore>& texture,
 			const TextureSurface& surface) override;
         
 		/** @copydoc RenderAPICore::setBuffer */
@@ -170,10 +170,24 @@ namespace BansheeEngine
 		GLuint getCombinedMinMipFilter() const;
 
 		/**
-		 * OpenGL shares all texture slots, but the engine prefers to keep textures separate per-stage. This will convert
-		 * texture unit that is set per stage into a global texture unit usable by OpenGL.
+		 * Calculates a global texture unit slot for a sampler specific to a GPU program. 
+		 * 
+		 * @param[in]	gptype		Type of the GPU program the sampler is a part of.
+		 * @param[in]	samplerIdx	Index of the sampler uniform.
+		 * @return					Unique global texture unit index that can be used for binding a texture to the specified
+		 *							sampler.
 		 */
-		UINT32 getGLTextureUnit(GpuProgramType gptype, UINT32 unit);
+		UINT32 getGLTextureUnit(GpuProgramType gptype, UINT32 samplerIdx);
+
+		/**
+		 * Calculates a global image unit slot based on a uniform index of the image in a GPU program. 
+		 * 
+		 * @param[in]	gptype		Type of the GPU program the uniform is a part of.
+		 * @param[in]	uniformIdx	Index of the image uniform.
+		 * @return					Unique global image unit index that can be used for binding a load-store texture to the
+		 *							specified uniform.
+		 */
+		UINT32 getGLImageUnit(GpuProgramType gptype, UINT32 uniformIdx);
 
 		/**
 		 * OpenGL shares all buffer bindings, but the engine prefers to keep buffers separate per-stage. This will convert
@@ -204,13 +218,13 @@ namespace BansheeEngine
 		 * Sets the texture addressing mode for a texture unit. This determines how are UV address values outside of [0, 1]
 		 * range handled when sampling from texture.
 		 */
-        void setTextureAddressingMode(UINT16 stage, const UVWAddressingMode& uvw);
+        void setTextureAddressingMode(UINT16 unit, const UVWAddressingMode& uvw);
 
 		/**
 		 * Sets the texture border color for a texture unit. Border color determines color returned by the texture sampler
 		 * when border addressing mode is used and texture address is outside of [0, 1] range.
 		 */
-        void setTextureBorderColor(UINT16 stage, const Color& color);
+        void setTextureBorderColor(UINT16 unit, const Color& color);
 
 		/**
 		 * Sets the mipmap bias value for a given texture unit. Bias allows	you to adjust the mipmap selection calculation.
@@ -383,6 +397,19 @@ namespace BansheeEngine
 		bool checkForErrors() const;
 
 	private:
+		/** Information about a currently bound texture. */
+		struct TextureInfo
+		{
+			UINT32 samplerIdx;
+			GLenum type;
+		};
+
+		/** Information about a currently bound load-store texture (image in OpenGL lingo). */
+		struct ImageInfo
+		{
+			UINT32 uniformIdx;
+		};
+
 		Rect2 mViewportNorm;
 		UINT32 mScissorTop, mScissorBottom, mScissorLeft, mScissorRight;
 		UINT32 mViewportLeft, mViewportTop, mViewportWidth, mViewportHeight;
@@ -401,8 +428,10 @@ namespace BansheeEngine
 		FilterOptions mMipFilter;
 
 		// Holds texture type settings for every stage
-		UINT32	mNumTextureTypes;
-		GLenum* mTextureTypes;
+		UINT32 mNumTextureUnits;
+		TextureInfo* mTextureInfos;
+		UINT32 mNumImageUnits;
+		ImageInfo* mImageInfos;
 
 		bool mDepthWrite;
 		bool mColorWrite[4];
@@ -422,16 +451,11 @@ namespace BansheeEngine
 
 		const GLSLProgramPipeline* mActivePipeline;
 
-		UINT32 mFragmentTexOffset;
-		UINT32 mVertexTexOffset;
-		UINT32 mGeometryTexOffset;
-
-		UINT32 mFragmentUBOffset;
-		UINT32 mVertexUBOffset;
-		UINT32 mGeometryUBOffset;
-		UINT32 mHullUBOffset;
-		UINT32 mDomainUBOffset;
-		UINT32 mComputeUBOffset;
+		UINT32 mTextureUnitOffsets[6];
+		UINT32 mMaxBoundTexUnits[6];
+		UINT32 mImageUnitOffsets[6];
+		UINT32 mMaxBoundImageUnits[6];
+		UINT32 mUBOffsets[6];
 
 		Vector<SPtr<VertexBufferCore>> mBoundVertexBuffers;
 		SPtr<VertexDeclarationCore> mBoundVertexDeclaration;

+ 221 - 162
Source/BansheeGLRenderAPI/Source/BsGLRenderAPI.cpp

@@ -45,21 +45,19 @@ namespace BansheeEngine
 		, mStencilRefValue(0)
 		, mStencilCompareFront(CMPF_ALWAYS_PASS)
 		, mStencilCompareBack(CMPF_ALWAYS_PASS)
-		, mNumTextureTypes(0)
-		, mTextureTypes(nullptr)
+		, mNumTextureUnits(0)
+		, mTextureInfos(nullptr)
+		, mNumImageUnits(0)
+		, mImageInfos(nullptr)
 		, mDepthWrite(true)
 		, mGLSLProgramFactory(nullptr)
 		, mProgramPipelineManager(nullptr)
 		, mActivePipeline(nullptr)
-		, mFragmentTexOffset(0)
-		, mVertexTexOffset(0)
-		, mGeometryTexOffset(0)
-		, mFragmentUBOffset(0)
-		, mVertexUBOffset(0)
-		, mGeometryUBOffset(0)
-		, mHullUBOffset(0)
-		, mDomainUBOffset(0)
-		, mComputeUBOffset(0)
+		, mTextureUnitOffsets {}
+		, mMaxBoundTexUnits {}
+		, mImageUnitOffsets {}
+		, mMaxBoundImageUnits {}
+		, mUBOffsets {}
 		, mCurrentDrawOperation(DOT_TRIANGLE_LIST)
 		, mDrawCallInProgress(false)
 		, mActiveTextureUnit(0)
@@ -205,36 +203,21 @@ namespace BansheeEngine
 		if(mGLSupport)
 			bs_delete(mGLSupport);
 
-		if(mTextureTypes != nullptr)
-			bs_deleteN(mTextureTypes, mNumTextureTypes);
+		if(mTextureInfos != nullptr)
+			bs_deleteN(mTextureInfos, mNumTextureUnits);
+
+		if (mImageInfos != nullptr)
+			bs_deleteN(mImageInfos, mNumImageUnits);
 	}
 
 	void GLRenderAPI::bindGpuProgram(const SPtr<GpuProgramCore>& prg)
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
+		GpuProgramType type = prg->getProperties().getType();
 		SPtr<GLSLGpuProgramCore> glprg = std::static_pointer_cast<GLSLGpuProgramCore>(prg);
 
-		switch (glprg->getProperties().getType())
-		{
-		case GPT_VERTEX_PROGRAM:
-			mCurrentVertexProgram = glprg;
-			break;
-		case GPT_FRAGMENT_PROGRAM:
-			mCurrentFragmentProgram = glprg;
-			break;
-		case GPT_GEOMETRY_PROGRAM:
-			mCurrentGeometryProgram = glprg;
-			break;
-		case GPT_DOMAIN_PROGRAM:
-			mCurrentDomainProgram = glprg;
-			break;
-		case GPT_HULL_PROGRAM:
-			mCurrentHullProgram = glprg;
-			break;
-		case GPT_COMPUTE_PROGRAM:
-			mCurrentComputeProgram = glprg;
-		}
+		setActiveProgram(type, glprg);
 
 		RenderAPICore::bindGpuProgram(prg);
 	}
@@ -374,20 +357,37 @@ namespace BansheeEngine
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
-		unit = getGLTextureUnit(gptype, unit);
-		if (!activateGLTextureUnit(unit))
+		UINT32 texUnit = getGLTextureUnit(gptype, unit);
+		if (!activateGLTextureUnit(texUnit))
 			return;
 
 		SPtr<GLTextureCore> tex = std::static_pointer_cast<GLTextureCore>(texPtr);
 		if (tex != nullptr)
 		{
-			mTextureTypes[unit] = tex->getGLTextureTarget();
-			glBindTexture(mTextureTypes[unit], tex->getGLID());
+			GLenum newTextureType = tex->getGLTextureTarget();
+
+			if(mTextureInfos[texUnit].type != newTextureType)
+				glBindTexture(mTextureInfos[texUnit].type, 0);
+
+			mTextureInfos[texUnit].type = newTextureType;
+			mTextureInfos[texUnit].samplerIdx = unit;
+
+			mMaxBoundTexUnits[gptype] = std::max(mMaxBoundTexUnits[gptype], texUnit + 1);
+
+			glBindTexture(newTextureType, tex->getGLID());
+
+			SPtr<GLSLGpuProgramCore> activeProgram = getActiveProgram(gptype);
+			if (activeProgram != nullptr)
+			{
+				GLuint glProgram = activeProgram->getGLHandle();
+
+				glProgramUniform1i(glProgram, unit, texUnit);
+			}
 		}
 		else
 		{
-			// Note: This should probably clear all targets instead of just 2D
-			glBindTexture(GL_TEXTURE_2D, 0); 
+			glBindTexture(mTextureInfos[texUnit].type, 0);
+			mTextureInfos[texUnit].samplerIdx = (UINT32)-1;
 		}
 
 		activateGLTextureUnit(0);
@@ -401,48 +401,59 @@ namespace BansheeEngine
 
 		const SamplerProperties& stateProps = state->getProperties();
 
-		unit = getGLTextureUnit(gptype, unit);
+		UINT16 texUnit = getGLTextureUnit(gptype, unit);
 
 		// Set texture layer filtering
-		setTextureFiltering(unit, FT_MIN, stateProps.getTextureFiltering(FT_MIN));
-		setTextureFiltering(unit, FT_MAG, stateProps.getTextureFiltering(FT_MAG));
-		setTextureFiltering(unit, FT_MIP, stateProps.getTextureFiltering(FT_MIP));
+		setTextureFiltering(texUnit, FT_MIN, stateProps.getTextureFiltering(FT_MIN));
+		setTextureFiltering(texUnit, FT_MAG, stateProps.getTextureFiltering(FT_MAG));
+		setTextureFiltering(texUnit, FT_MIP, stateProps.getTextureFiltering(FT_MIP));
 
 		// Set texture anisotropy
-		setTextureAnisotropy(unit, stateProps.getTextureAnisotropy());
+		setTextureAnisotropy(texUnit, stateProps.getTextureAnisotropy());
 
 		// Set mipmap biasing
-		setTextureMipmapBias(unit, stateProps.getTextureMipmapBias());
+		setTextureMipmapBias(texUnit, stateProps.getTextureMipmapBias());
 
 		// Texture addressing mode
 		const UVWAddressingMode& uvw = stateProps.getTextureAddressingMode();
-		setTextureAddressingMode(unit, uvw);
+		setTextureAddressingMode(texUnit, uvw);
 
 		// Set border color
-		setTextureBorderColor(unit, stateProps.getBorderColor());
-
-		SPtr<GLSLGpuProgramCore> activeProgram = getActiveProgram(gptype);
-		GLuint glProgram = activeProgram->getGLHandle();
-
-		glProgramUniform1i(glProgram, unit, getGLTextureUnit(gptype, unit));
+		setTextureBorderColor(texUnit, stateProps.getBorderColor());
 
 		BS_INC_RENDER_STAT(NumSamplerBinds);
 	}
 
-	void GLRenderAPI::setLoadStoreTexture(GpuProgramType gptype, UINT16 unit, bool enabled, const SPtr<TextureCore>& texPtr,
+	void GLRenderAPI::setLoadStoreTexture(GpuProgramType gptype, UINT16 unit, const SPtr<TextureCore>& texPtr,
 		const TextureSurface& surface)
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
 		// TODO - OpenGL can't bind a certain subset of faces like DX11, only zero, one or all, so I'm ignoring numSlices parameter
+
+		UINT32 imageUnit = getGLImageUnit(gptype, unit);
 		if (texPtr != nullptr)
 		{
 			SPtr<GLTextureCore> tex = std::static_pointer_cast<GLTextureCore>(texPtr);
-			glBindImageTexture(unit, tex->getGLID(), surface.mipLevel, surface.numArraySlices > 1, 
-				surface.arraySlice, tex->getGLFormat(), GL_READ_WRITE);
+			glBindImageTexture(imageUnit, tex->getGLID(), surface.mipLevel, surface.numArraySlices > 1, 
+				surface.arraySlice, GL_READ_WRITE, tex->getGLFormat());
+
+			mImageInfos[imageUnit].uniformIdx = unit;
+			mMaxBoundImageUnits[gptype] = std::max(mMaxBoundImageUnits[gptype], imageUnit + 1);
+
+			SPtr<GLSLGpuProgramCore> activeProgram = getActiveProgram(gptype);
+			if (activeProgram != nullptr)
+			{
+				GLuint glProgram = activeProgram->getGLHandle();
+
+				glProgramUniform1i(glProgram, unit, imageUnit);
+			}
 		}
 		else
-			glBindImageTexture(unit, 0, 0, false, 0, 0, GL_READ_WRITE);
+		{
+			mImageInfos[imageUnit].uniformIdx = (UINT32)-1;
+			glBindImageTexture(imageUnit, 0, 0, false, 0, GL_READ_WRITE, GL_R32F);
+		}
 
 		BS_INC_RENDER_STAT(NumTextureBinds);
 	}
@@ -455,32 +466,62 @@ namespace BansheeEngine
 		SPtr<GLGpuBufferCore> glBuffer = std::static_pointer_cast<GLGpuBufferCore>(buffer);
 		if (!loadStore)
 		{
-			unit = getGLTextureUnit(gptype, unit);
-			if (!activateGLTextureUnit(unit))
+			UINT32 texUnit = getGLTextureUnit(gptype, unit);
+			if (!activateGLTextureUnit(texUnit))
 				return;
 
 			if (glBuffer != nullptr)
 			{
-				mTextureTypes[unit] = GL_TEXTURE_BUFFER;
-				glBindTexture(mTextureTypes[unit], glBuffer->getGLTextureId());
+				if (mTextureInfos[texUnit].type != GL_TEXTURE_BUFFER)
+					glBindTexture(mTextureInfos[texUnit].type, 0);
+
+				mTextureInfos[texUnit].type = GL_TEXTURE_BUFFER;
+				mTextureInfos[texUnit].samplerIdx = unit;
+
+				mMaxBoundTexUnits[gptype] = std::max(mMaxBoundTexUnits[gptype], texUnit + 1);
+
+				glBindTexture(GL_TEXTURE_BUFFER, glBuffer->getGLTextureId());
+
+				SPtr<GLSLGpuProgramCore> activeProgram = getActiveProgram(gptype);
+				if (activeProgram != nullptr)
+				{
+					GLuint glProgram = activeProgram->getGLHandle();
+
+					glProgramUniform1i(glProgram, unit, texUnit);
+				}
 			}
 			else
 			{
-				// Note: This should probably clear all targets instead of just 2D
-				glBindTexture(GL_TEXTURE_BUFFER, 0);
+				mTextureInfos[texUnit].samplerIdx = (UINT32)-1;
+				glBindTexture(mTextureInfos[texUnit].type, 0);
 			}
 
 			activateGLTextureUnit(0);
 		}
 		else
 		{
+			UINT32 imageUnit = getGLImageUnit(gptype, unit);
 			if (glBuffer != nullptr)
 			{
-				glBindImageTexture(unit, glBuffer->getGLTextureId(), 0, false,
-					0, glBuffer->getGLFormat(), GL_READ_WRITE);
+				glBindImageTexture(imageUnit, glBuffer->getGLTextureId(), 0, false,
+					0, GL_READ_WRITE, glBuffer->getGLFormat());
+
+				mImageInfos[imageUnit].uniformIdx = unit;
+				mMaxBoundImageUnits[gptype] = std::max(mMaxBoundImageUnits[gptype], imageUnit + 1);
+
+				SPtr<GLSLGpuProgramCore> activeProgram = getActiveProgram(gptype);
+				if (activeProgram != nullptr)
+				{
+					GLuint glProgram = activeProgram->getGLHandle();
+
+					glProgramUniform1i(glProgram, unit, imageUnit);
+				}
 			}
 			else
-				glBindImageTexture(unit, 0, 0, false, 0, 0, GL_READ_WRITE);
+			{
+				mImageInfos[imageUnit].uniformIdx = (UINT32)-1;
+				glBindImageTexture(imageUnit, 0, 0, false, 0, GL_READ_WRITE, GL_R32F);
+			}
 		}
 
 		BS_INC_RENDER_STAT(NumTextureBinds);
@@ -600,22 +641,11 @@ namespace BansheeEngine
 		{
 			fbo->bind();
 
-			if (GLEW_EXT_framebuffer_sRGB)
-			{
-				// Enable / disable sRGB states
-				if (target->getProperties().isHwGammaEnabled())
-				{
-					glEnable(GL_FRAMEBUFFER_SRGB_EXT);
-
-					// Note: could test GL_FRAMEBUFFER_SRGB_CAPABLE_EXT here before
-					// enabling, but GL spec says incapable surfaces ignore the setting
-					// anyway. We test the capability to enable isHardwareGammaEnabled.
-				}
-				else
-				{
-					glDisable(GL_FRAMEBUFFER_SRGB_EXT);
-				}
-			}
+			// Enable / disable sRGB states
+			if (target->getProperties().isHwGammaEnabled())
+				glEnable(GL_FRAMEBUFFER_SRGB);
+			else
+				glDisable(GL_FRAMEBUFFER_SRGB);
 		}
 		else
 			glBindFramebuffer(GL_FRAMEBUFFER, 0);
@@ -927,37 +957,37 @@ namespace BansheeEngine
 	/* 								PRIVATE		                     		*/
 	/************************************************************************/
 
-	void GLRenderAPI::setTextureAddressingMode(UINT16 stage, const UVWAddressingMode& uvw)
+	void GLRenderAPI::setTextureAddressingMode(UINT16 unit, const UVWAddressingMode& uvw)
 	{
-		if (!activateGLTextureUnit(stage))
+		if (!activateGLTextureUnit(unit))
 			return;
 
-		glTexParameteri(mTextureTypes[stage], GL_TEXTURE_WRAP_S, 
+		glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_WRAP_S,
 			getTextureAddressingMode(uvw.u));
-		glTexParameteri(mTextureTypes[stage], GL_TEXTURE_WRAP_T, 
+		glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_WRAP_T,
 			getTextureAddressingMode(uvw.v));
-		glTexParameteri(mTextureTypes[stage], GL_TEXTURE_WRAP_R, 
+		glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_WRAP_R,
 			getTextureAddressingMode(uvw.w));
 		activateGLTextureUnit(0);
 	}
 
-	void GLRenderAPI::setTextureBorderColor(UINT16 stage, const Color& colour)
+	void GLRenderAPI::setTextureBorderColor(UINT16 unit, const Color& colour)
 	{
 		GLfloat border[4] = { colour.r, colour.g, colour.b, colour.a };
-		if (activateGLTextureUnit(stage))
+		if (activateGLTextureUnit(unit))
 		{
-			glTexParameterfv(mTextureTypes[stage], GL_TEXTURE_BORDER_COLOR, border);
+			glTexParameterfv(mTextureInfos[unit].type, GL_TEXTURE_BORDER_COLOR, border);
 			activateGLTextureUnit(0);
 		}
 	}
 
-	void GLRenderAPI::setTextureMipmapBias(UINT16 stage, float bias)
+	void GLRenderAPI::setTextureMipmapBias(UINT16 unit, float bias)
 	{
 		if (mCurrentCapabilities->hasCapability(RSC_MIPMAP_LOD_BIAS))
 		{
-			if (activateGLTextureUnit(stage))
+			if (activateGLTextureUnit(unit))
 			{
-				glTexParameterf(mTextureTypes[stage], GL_TEXTURE_LOD_BIAS, bias);
+				glTexParameterf(mTextureInfos[unit].type, GL_TEXTURE_LOD_BIAS, bias);
 				activateGLTextureUnit(0);
 			}
 		}
@@ -1333,18 +1363,18 @@ namespace BansheeEngine
 		case FT_MIN:
 			mMinFilter = fo;
 			// Combine with existing mip filter
-			glTexParameteri(mTextureTypes[unit], GL_TEXTURE_MIN_FILTER, getCombinedMinMipFilter());
+			glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_MIN_FILTER, getCombinedMinMipFilter());
 			break;
 		case FT_MAG:
 			switch (fo)
 			{
 			case FO_ANISOTROPIC: // GL treats linear and aniso the same
 			case FO_LINEAR:
-				glTexParameteri(mTextureTypes[unit], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+				glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
 				break;
 			case FO_POINT:
 			case FO_NONE:
-				glTexParameteri(mTextureTypes[unit], GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+				glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
 				break;
 			default:
 				break;
@@ -1353,7 +1383,7 @@ namespace BansheeEngine
 		case FT_MIP:
 			mMipFilter = fo;
 			// Combine with existing min filter
-			glTexParameteri(mTextureTypes[unit], GL_TEXTURE_MIN_FILTER, getCombinedMinMipFilter());
+			glTexParameteri(mTextureInfos[unit].type, GL_TEXTURE_MIN_FILTER, getCombinedMinMipFilter());
 			break;
 		}
 
@@ -1378,7 +1408,7 @@ namespace BansheeEngine
 			maxAnisotropy = 1;
 
 		if (getCurrentAnisotropy(unit) != maxAnisotropy)
-			glTexParameterf(mTextureTypes[unit], GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)maxAnisotropy);
+			glTexParameterf(mTextureInfos[unit].type, GL_TEXTURE_MAX_ANISOTROPY_EXT, (float)maxAnisotropy);
 
 		activateGLTextureUnit(0);
 	}
@@ -1466,6 +1496,7 @@ namespace BansheeEngine
 		const GLSLProgramPipeline* pipeline = mProgramPipelineManager->getPipeline(mCurrentVertexProgram.get(), 
 			mCurrentFragmentProgram.get(), mCurrentGeometryProgram.get(), mCurrentHullProgram.get(), mCurrentDomainProgram.get());
 
+		glUseProgram(0);
 		if(mActivePipeline != pipeline)
 		{
 			glBindProgramPipeline(pipeline->glHandle);
@@ -1490,7 +1521,7 @@ namespace BansheeEngine
 	GLfloat GLRenderAPI::getCurrentAnisotropy(UINT16 unit)
 	{
 		GLfloat curAniso = 0;
-		glGetTexParameterfv(mTextureTypes[unit], GL_TEXTURE_MAX_ANISOTROPY_EXT, &curAniso);
+		glGetTexParameterfv(mTextureInfos[unit].type, GL_TEXTURE_MAX_ANISOTROPY_EXT, &curAniso);
 
 		return curAniso ? curAniso : 1;
 	}
@@ -1670,32 +1701,55 @@ namespace BansheeEngine
 		return primType;
 	}
 
-	UINT32 GLRenderAPI::getGLTextureUnit(GpuProgramType gptype, UINT32 unit)
+	UINT32 GLRenderAPI::getGLTextureUnit(GpuProgramType gptype, UINT32 samplerIdx)
 	{
-		if (gptype != GPT_VERTEX_PROGRAM && gptype != GPT_FRAGMENT_PROGRAM && gptype != GPT_GEOMETRY_PROGRAM)
+		if (gptype == GPT_DOMAIN_PROGRAM || gptype == GPT_HULL_PROGRAM)
 		{
-			BS_EXCEPT(InvalidParametersException, "OpenGL cannot assign textures to this gpu program type: " + toString(gptype));
+			LOGERR("OpenGL cannot assign textures to this gpu program type: " + toString(gptype));
+			return 0;
 		}
 
-		UINT32 numSupportedUnits = mCurrentCapabilities->getNumTextureUnits(gptype);
-		if (unit >= numSupportedUnits)
+		UINT32 offset = mTextureUnitOffsets[gptype];
+		for(UINT32 i = offset; i < mMaxBoundTexUnits[gptype]; i++)
 		{
-			BS_EXCEPT(InvalidParametersException, "Invalid texture unit index for the provided stage. Unit index: " + toString(unit) + ". Stage: " +
-				toString(gptype) + ". Supported range is 0 .. " + toString(numSupportedUnits - 1));
+			UINT32 texUnit = i;
+
+			if (mTextureInfos[texUnit].samplerIdx == samplerIdx || mTextureInfos[texUnit].samplerIdx == (UINT32)-1)
+				return texUnit;
 		}
 
-		switch (gptype)
+		INT32 numSupportedUnits = (INT32)mCurrentCapabilities->getNumTextureUnits(gptype);
+		INT32 numBoundTexUnits = (INT32)mMaxBoundTexUnits[gptype] - (INT32)offset;
+		if (numBoundTexUnits < numSupportedUnits)
+			return mMaxBoundTexUnits[gptype];
+
+		LOGERR("Cannot find an empty slot to bind a texture to.");
+		return 0;
+	}
+
+	UINT32 GLRenderAPI::getGLImageUnit(GpuProgramType gptype, UINT32 uniformIdx)
+	{
+		if (gptype != GPT_FRAGMENT_PROGRAM && gptype != GPT_COMPUTE_PROGRAM)
 		{
-		case GPT_FRAGMENT_PROGRAM:
-			return mFragmentTexOffset + unit;
-		case GPT_VERTEX_PROGRAM:
-			return mVertexTexOffset + unit;
-		case GPT_GEOMETRY_PROGRAM:
-			return mGeometryTexOffset + unit;
-		default:
-			BS_EXCEPT(InternalErrorException, "Invalid program type: " + toString(gptype));
+			LOGERR("OpenGL cannot assign load-store textures to this gpu program type: " + toString(gptype));
+			return 0;
+		}
+
+		UINT32 offset = mImageUnitOffsets[gptype];
+		for (UINT32 i = offset; i < mMaxBoundImageUnits[gptype]; i++)
+		{
+			UINT32 imageUnit = i;
+
+			if (mImageInfos[imageUnit].uniformIdx == uniformIdx || mImageInfos[imageUnit].uniformIdx == (UINT32)-1)
+				return imageUnit;
 		}
 
+		INT32 numSupportedUnits = (INT32)mCurrentCapabilities->getNumLoadStoreTextureUnits(gptype);
+		INT32 numBoundImageUnits = (INT32)mMaxBoundImageUnits[gptype] - (INT32)offset;
+		if (numBoundImageUnits < numSupportedUnits)
+			return mMaxBoundImageUnits[gptype];
+
+		LOGERR("Cannot find an empty slot to bind a load-store texture to.");
 		return 0;
 	}
 
@@ -1704,29 +1758,12 @@ namespace BansheeEngine
 		UINT32 maxNumBindings = mCurrentCapabilities->getNumGpuParamBlockBuffers(gptype);
 		if (binding >= maxNumBindings)
 		{
-			BS_EXCEPT(InvalidParametersException, "Invalid buffer binding for the provided stage. Buffer binding: " + toString(binding) + ". Stage: " +
+			LOGERR("Invalid buffer binding for the provided stage. Buffer binding: " + toString(binding) + ". Stage: " +
 				toString(gptype) + ". Supported range is 0 .. " + toString(maxNumBindings - 1));
+			return 0;
 		}
 
-		switch (gptype)
-		{
-		case GPT_FRAGMENT_PROGRAM:
-			return mFragmentUBOffset + binding;
-		case GPT_VERTEX_PROGRAM:
-			return mVertexUBOffset + binding;
-		case GPT_GEOMETRY_PROGRAM:
-			return mGeometryUBOffset + binding;
-		case GPT_HULL_PROGRAM:
-			return mHullUBOffset + binding;
-		case GPT_DOMAIN_PROGRAM:
-			return mDomainUBOffset + binding;
-		case GPT_COMPUTE_PROGRAM:
-			return mComputeUBOffset + binding;
-		default:
-			BS_EXCEPT(InternalErrorException, "Invalid program type: " + toString(gptype));
-		}
-
-		return 0;
+		return mUBOffsets[gptype] + binding;
 	}
 
 	void GLRenderAPI::setActiveProgram(GpuProgramType gptype, const SPtr<GLSLGpuProgramCore>& program)
@@ -1752,6 +1789,9 @@ namespace BansheeEngine
 			mCurrentComputeProgram = program;
 			break;
 		}
+
+		mMaxBoundTexUnits[gptype] = 0;
+		mMaxBoundImageUnits[gptype] = 0;
 	}
 
 	SPtr<GLSLGpuProgramCore> GLRenderAPI::getActiveProgram(GpuProgramType gptype) const
@@ -1817,45 +1857,51 @@ namespace BansheeEngine
 			BS_EXCEPT(RenderingAPIException, "GPU doesn't support frame buffer objects. OpenGL versions lower than 3.0 are not supported.");
 		}
 
-		mFragmentTexOffset = 0;
-		mVertexTexOffset = caps->getNumTextureUnits(GPT_FRAGMENT_PROGRAM);
-		mGeometryTexOffset = mVertexTexOffset + caps->getNumTextureUnits(GPT_VERTEX_PROGRAM);
+		UINT32 curTexUnitOffset = 0;
+		UINT32 curImageUnitOffset = 0;
+		UINT32 curUBOffset = 0;
+		for (UINT32 i = 0; i < 6; i++)
+		{
+			mTextureUnitOffsets[i] = curTexUnitOffset;
+			mImageUnitOffsets[i] = curImageUnitOffset;
+			mUBOffsets[i] = curUBOffset;
 
-		UINT16 numCombinedTexUnits = caps->getNumCombinedTextureUnits();
+			curTexUnitOffset += caps->getNumTextureUnits((GpuProgramType)i);
+			curImageUnitOffset += caps->getNumLoadStoreTextureUnits((GpuProgramType)i);
+			curUBOffset += caps->getNumGpuParamBlockBuffers((GpuProgramType)i);
+		}
 
-		UINT32 totalNumTexUnits = caps->getNumTextureUnits(GPT_VERTEX_PROGRAM);
-		totalNumTexUnits += caps->getNumTextureUnits(GPT_FRAGMENT_PROGRAM);
-		totalNumTexUnits += caps->getNumTextureUnits(GPT_GEOMETRY_PROGRAM);
-		totalNumTexUnits += caps->getNumTextureUnits(GPT_HULL_PROGRAM);
-		totalNumTexUnits += caps->getNumTextureUnits(GPT_DOMAIN_PROGRAM);
-		totalNumTexUnits += caps->getNumTextureUnits(GPT_COMPUTE_PROGRAM);
+		UINT32 totalNumTexUnits = curTexUnitOffset;
+		UINT16 numCombinedTexUnits = caps->getNumCombinedTextureUnits();
 
-		if(totalNumTexUnits > numCombinedTexUnits)
+		if (totalNumTexUnits > numCombinedTexUnits)
 			BS_EXCEPT(InternalErrorException, "Number of combined texture units less than the number of individual units!?");
 
-		mNumTextureTypes = numCombinedTexUnits;
-		mTextureTypes = bs_newN<GLenum>(mNumTextureTypes);
-		for(UINT16 i = 0; i < numCombinedTexUnits; i++)
-			mTextureTypes[i] = GL_TEXTURE_2D;
-
-		mVertexUBOffset = 0;
-		UINT32 totalNumUniformBlocks = caps->getNumGpuParamBlockBuffers(GPT_VERTEX_PROGRAM);
-		mFragmentUBOffset = totalNumUniformBlocks;
-		totalNumUniformBlocks += caps->getNumGpuParamBlockBuffers(GPT_FRAGMENT_PROGRAM);
-		mGeometryUBOffset = totalNumUniformBlocks;
-		totalNumUniformBlocks += caps->getNumGpuParamBlockBuffers(GPT_GEOMETRY_PROGRAM);
-		mHullUBOffset = totalNumUniformBlocks;
-		totalNumUniformBlocks += caps->getNumGpuParamBlockBuffers(GPT_HULL_PROGRAM);
-		mDomainUBOffset = totalNumUniformBlocks;
-		totalNumUniformBlocks += caps->getNumGpuParamBlockBuffers(GPT_DOMAIN_PROGRAM);
-		mComputeUBOffset = totalNumUniformBlocks;
-		totalNumUniformBlocks += caps->getNumGpuParamBlockBuffers(GPT_COMPUTE_PROGRAM);
+		mNumTextureUnits = numCombinedTexUnits;
+		mTextureInfos = bs_newN<TextureInfo>(mNumTextureUnits);
+		for (UINT16 i = 0; i < mNumTextureUnits; i++)
+		{
+			mTextureInfos[i].samplerIdx = (UINT32)-1;
+			mTextureInfos[i].type = GL_TEXTURE_2D;
+		}
 
+		UINT32 totalNumUniformBlocks = curUBOffset;
 		UINT16 numCombinedUniformBlocks = caps->getNumCombinedGpuParamBlockBuffers();
 
 		if(totalNumUniformBlocks > numCombinedUniformBlocks)
 			BS_EXCEPT(InternalErrorException, "Number of combined uniform block buffers less than the number of individual per-stage buffers!?");
 
+		UINT32 totalNumImageUnits = curImageUnitOffset;
+		UINT16 numCombinedImageUnits = caps->getNumCombinedLoadStoreTextureUnits();
+
+		if (totalNumImageUnits > numCombinedImageUnits)
+			BS_EXCEPT(InternalErrorException, "Number of combined load-store texture units less than the number of individual per-stage units!?");
+
+		mNumImageUnits = numCombinedImageUnits;
+		mImageInfos = bs_newN<ImageInfo>(mNumImageUnits);
+		for (UINT16 i = 0; i < mNumImageUnits; i++)
+			mImageInfos[i].uniformIdx = (UINT32)-1;
+
 		TextureManager::startUp<GLTextureManager>(std::ref(*mGLSupport));
 		TextureCoreManager::startUp<GLTextureCoreManager>(std::ref(*mGLSupport));
 	}
@@ -2115,6 +2161,19 @@ namespace BansheeEngine
 
 			glGetIntegerv(GL_MAX_COMPUTE_UNIFORM_BLOCKS, &numUniformBlocks);
 			rsc->setNumGpuParamBlockBuffers(GPT_COMPUTE_PROGRAM, numUniformBlocks);
+
+			// Max number of load-store textures
+			GLint lsfUnits;
+			glGetIntegerv(GL_MAX_FRAGMENT_IMAGE_UNIFORMS, &lsfUnits);
+			rsc->setNumLoadStoreTextureUnits(GPT_FRAGMENT_PROGRAM, static_cast<UINT16>(lsfUnits));
+
+			GLint lscUnits;
+			glGetIntegerv(GL_MAX_COMPUTE_IMAGE_UNIFORMS, &lscUnits);
+			rsc->setNumLoadStoreTextureUnits(GPT_COMPUTE_PROGRAM, static_cast<UINT16>(lscUnits));
+
+			GLint combinedLoadStoreTextureUnits;
+			glGetIntegerv(GL_MAX_COMBINED_IMAGE_UNIFORMS, &combinedLoadStoreTextureUnits);
+			rsc->setNumCombinedLoadStoreTextureUnits(static_cast<UINT16>(combinedLoadStoreTextureUnits));
 		}
 
 		GLint combinedTexUnits;

+ 1 - 7
Source/BansheeGLRenderAPI/Source/BsGLTexture.cpp

@@ -46,12 +46,6 @@ namespace BansheeEngine
 		if (numMips > maxMips)
 			BS_EXCEPT(InvalidParametersException, "Invalid number of mipmaps. Maximum allowed is: " + toString(maxMips));
 
-		if ((usage & TU_RENDERTARGET) != 0)
-		{
-			if (texType != TEX_TYPE_2D)
-				BS_EXCEPT(NotImplementedException, "Only 2D render targets are supported at the moment");
-		}
-
 		if ((usage & TU_DEPTHSTENCIL) != 0)
 		{
 			if (texType != TEX_TYPE_2D)
@@ -105,7 +99,7 @@ namespace BansheeEngine
 					GL_DEPTH_STENCIL, depthStencilFormat, nullptr);
 			}
 		}
-		else // Non-render textures
+		else
 		{
 			GLenum baseFormat = GLPixelUtil::getGLOriginFormat(pixFormat);
 			GLenum baseDataType = GLPixelUtil::getGLOriginDataType(pixFormat);

+ 1 - 1
Source/RenderBeast/Source/BsPostProcessing.cpp

@@ -112,7 +112,7 @@ namespace BansheeEngine
 
 		// Note: This is ugly, add a better way to clear load/store textures?
 		TextureSurface blankSurface;
-		rapi.setLoadStoreTexture(GPT_COMPUTE_PROGRAM, 0, false, nullptr, blankSurface);
+		rapi.setLoadStoreTexture(GPT_COMPUTE_PROGRAM, 0, nullptr, blankSurface);
 
 		mOutput = ppInfo.histogramTex->renderTexture;
 	}

+ 2 - 2
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -939,9 +939,9 @@ namespace BansheeEngine
 				const TextureSurface& surface = params->getLoadStoreSurface(iter->second.slot);
 
 				if (texture == nullptr)
-					rapi.setLoadStoreTexture(stages[i], iter->second.slot, false, nullptr, surface);
+					rapi.setLoadStoreTexture(stages[i], iter->second.slot, nullptr, surface);
 				else
-					rapi.setLoadStoreTexture(stages[i], iter->second.slot, true, texture, surface);
+					rapi.setLoadStoreTexture(stages[i], iter->second.slot, texture, surface);
 			}
 
 			for (auto iter = paramDesc.buffers.begin(); iter != paramDesc.buffers.end(); ++iter)