Kaynağa Gözat

Refactoring renderer so camera parameter buffers are populated on camera change, instead of every frame

BearishSun 9 yıl önce
ebeveyn
işleme
b00680e742

+ 45 - 3
Source/BansheeCore/Include/BsGpuParamsSet.h

@@ -35,18 +35,36 @@ namespace bs
 		typedef typename TPassTypes<Core>::GraphicsPipelineStateType GraphicsPipelineStateType;
 		typedef typename TPassTypes<Core>::ComputePipelineStateType ComputePipelineStateType;
 
+		/** Binding location for a single GPU param block buffer. */
+		struct BlockBinding
+		{
+			UINT32 set;
+			UINT32 slot;
+		};
+
+		/** All bindings for GPU param block buffers, for a single pass. */
+		struct PassBlockBindings
+		{
+			BlockBinding bindings[GPT_COUNT];
+		};
+
 		/** Information about a parameter block buffer. */
 		struct BlockInfo
 		{
-			BlockInfo(const String& name, const ParamBlockPtrType& buffer, bool shareable)
-				:name(name), buffer(buffer), shareable(shareable), allowUpdate(true), isUsed(true)
+			BlockInfo(const String& name, UINT32 set, UINT32 slot, const ParamBlockPtrType& buffer, bool shareable)
+				: name(name), set(set), slot(slot), buffer(buffer), shareable(shareable), allowUpdate(true), isUsed(true)
+				, passData(nullptr)
 			{ }
 
 			String name;
+			UINT32 set;
+			UINT32 slot;
 			ParamBlockPtrType buffer;
 			bool shareable;
 			bool allowUpdate;
 			bool isUsed;
+
+			PassBlockBindings* passData;
 		};
 
 		/** Information about how a data parameter maps from a material parameter into a parameter block buffer. */
@@ -81,7 +99,7 @@ namespace bs
 		/** Information about all object parameters for a specific pass. */
 		struct PassParamInfo
 		{
-			StageParamInfo stages[6];
+			StageParamInfo stages[GPT_COUNT];
 		};
 
 	public:
@@ -99,6 +117,28 @@ namespace bs
 		 */
 		SPtr<GpuParamsType> getGpuParams(UINT32 passIdx = 0);
 
+		/** 
+		 * Searches for a parameter block buffer with the specified name, and returns an index you can use for accessing it.
+		 * Returns -1 if buffer was not found.
+		 */
+		UINT32 getParamBlockBufferIndex(const String& name) const;
+
+		/**
+		 * Assign a parameter block buffer with the specified index to all the relevant child GpuParams.
+		 *
+		 * @param[in]	index			Index of the buffer, as retrieved from getParamBlockBufferIndex().
+		 * @param[in]	paramBlock		Parameter block to assign.
+		 * @param[in]	ignoreInUpdate	If true the buffer will not be updated during the update() call. This is useful
+		 *								if the caller wishes to manually update the buffer contents externally, to prevent
+		 *								overwriting manually written data during update.
+		 *
+		 * @note	
+		 * Parameter block buffers can be used as quick way of setting multiple parameters on a material at once, or
+		 * potentially sharing parameters between multiple materials. This reduces driver overhead as the parameters
+		 * in the buffers need only be set once and then reused multiple times.
+		 */
+		void setParamBlockBuffer(UINT32 index, const ParamBlockPtrType& paramBlock, bool ignoreInUpdate = false);
+
 		/**
 		 * Assign a parameter block buffer with the specified name to all the relevant child GpuParams.
 		 *
@@ -139,6 +179,8 @@ namespace bs
 		Vector<BlockInfo> mBlocks;
 		Vector<DataParamInfo> mDataParamInfos;
 		PassParamInfo* mPassParamInfos;
+
+		UINT8* mData;
 	};
 
 	/** Sim thread version of TGpuParamsSet<Core>. */

+ 2 - 3
Source/BansheeCore/Include/BsMaterial.h

@@ -150,9 +150,8 @@ namespace bs
 		SPtr<GpuParamsSetType> createParamsSet(UINT32 techniqueIdx = 0);
 
 		/**
-		 * Updates the provided parameter set by recording in it any changes that were made since the last call. It is
-		 * assumed only a single parameter set exists per-material per-technique. If there are multiple the material
-		 * will not be able to track which parameters were updated since the last call.
+		 * Updates the provided parameter set by recording in it any changes that were made in the material parameters 
+		 * since the last call. 
 		 *
 		 * @param[in]	paramsSet		Parameter set to update.
 		 * @param[in]	dirtyBitIdx		Index to use when checking if parameters are dirty. Must be in range [0, 30]. Allows

+ 118 - 50
Source/BansheeCore/Source/BsGpuParamsSet.cpp

@@ -65,6 +65,8 @@ namespace bs
 		int size;
 		bool external;
 		UINT32 sequentialIdx;
+		UINT32 set;
+		UINT32 slot;
 	};
 
 	Vector<SPtr<GpuParamDesc>> getAllParamDescs(const SPtr<Technique>& technique)
@@ -176,13 +178,15 @@ namespace bs
 		struct BlockInfo
 		{
 			BlockInfo() { }
-			BlockInfo(const String& name, const SPtr<GpuParamDesc>& paramDesc, bool isValid = true)
-				:name(name), paramDesc(paramDesc), isValid(isValid)
+			BlockInfo(const GpuParamBlockDesc* blockDesc, const SPtr<GpuParamDesc>& paramDesc, bool isValid = true)
+				:blockDesc(blockDesc), paramDesc(paramDesc), isValid(isValid)
 			{ }
 
-			String name;
+			const GpuParamBlockDesc* blockDesc;
 			SPtr<GpuParamDesc> paramDesc;
 			bool isValid;
+			UINT32 set;
+			UINT32 slot;
 		};
 
 		// Make sure param blocks with the same name actually contain the same fields
@@ -202,15 +206,17 @@ namespace bs
 				auto iterFind = uniqueParamBlocks.find(blockIter->first);
 				if (iterFind == uniqueParamBlocks.end())
 				{
-					uniqueParamBlocks[blockIter->first] = BlockInfo(blockIter->first, *iter);
+					uniqueParamBlocks[blockIter->first] = BlockInfo(&curBlock, *iter);
 					continue;
 				}
 
+				const GpuParamBlockDesc& otherBlock = *iterFind->second.blockDesc;
+
 				// The block was already determined as invalid, no need to check further
 				if (!iterFind->second.isValid)
 					continue;
 
-				String otherBlockName = iterFind->second.name;
+				String otherBlockName = otherBlock.name;
 				SPtr<GpuParamDesc> otherDesc = iterFind->second.paramDesc;
 
 				for (auto myParamIter = curDesc.params.begin(); myParamIter != curDesc.params.end(); ++myParamIter)
@@ -241,7 +247,9 @@ namespace bs
 				if (!isBlockValid)
 				{
 					LOGWRN("Found two param blocks with the same name but different contents: " + blockIter->first);
-					uniqueParamBlocks[blockIter->first] = BlockInfo(blockIter->first, nullptr, false);
+					uniqueParamBlocks[blockIter->first] = BlockInfo(&curBlock, nullptr, false);
+
+					continue;
 				}
 			}
 		}
@@ -252,11 +260,15 @@ namespace bs
 			if (!entry.second.isValid)
 				continue;
 
+			const GpuParamBlockDesc& curBlock = *entry.second.blockDesc;
+
 			ShaderBlockDesc shaderBlockDesc;
 			shaderBlockDesc.external = false;
 			shaderBlockDesc.usage = GPBU_STATIC;
-			shaderBlockDesc.size = 0;
+			shaderBlockDesc.size = curBlock.blockSize * sizeof(UINT32);
 			shaderBlockDesc.name = entry.first;
+			shaderBlockDesc.set = curBlock.set;
+			shaderBlockDesc.slot = curBlock.slot;
 
 			auto iterFind = shaderDesc.find(entry.first);
 			if (iterFind != shaderDesc.end())
@@ -265,17 +277,6 @@ namespace bs
 				shaderBlockDesc.usage = iterFind->second.usage;
 			}
 
-			for (auto iter2 = paramDescs.begin(); iter2 != paramDescs.end(); ++iter2)
-			{
-				auto findParamBlockDesc = (*iter2)->paramBlocks.find(entry.first);
-
-				if (findParamBlockDesc != (*iter2)->paramBlocks.end())
-				{
-					shaderBlockDesc.size = findParamBlockDesc->second.blockSize * sizeof(UINT32);
-					break;
-				}
-			}
-
 			output.push_back(shaderBlockDesc);
 		}
 
@@ -509,10 +510,11 @@ namespace bs
 			paramBlock.sequentialIdx = (UINT32)mBlocks.size();
 
 			paramBlockBuffers[paramBlock.name] = newParamBlockBuffer;
-			mBlocks.push_back(BlockInfo(paramBlock.name, newParamBlockBuffer, true));
+			mBlocks.push_back(BlockInfo(paramBlock.name, paramBlock.set, paramBlock.slot, newParamBlockBuffer, true));
 		}
 
 		//// Assign param block buffers and generate information about data parameters
+		assert(numPasses < 64); // BlockInfo flags uses UINT64 for tracking usage
 		for (UINT32 i = 0; i < numPasses; i++)
 		{
 			SPtr<GpuParamsType> paramPtr = mPassParams[i];
@@ -549,7 +551,8 @@ namespace bs
 						globalBlockIdx = (UINT32)mBlocks.size();
 
 						paramPtr->setParamBlockBuffer(progType, iterBlockDesc->first, newParamBlockBuffer);
-						mBlocks.push_back(BlockInfo(iterBlockDesc->first, newParamBlockBuffer, false));
+						mBlocks.emplace_back(iterBlockDesc->first, iterBlockDesc->second.set, 
+							iterBlockDesc->second.slot, newParamBlockBuffer, false);
 					}
 					else
 					{
@@ -605,7 +608,7 @@ namespace bs
 
 			if(iterFind == mBlocks.end())
 			{
-				mBlocks.push_back(BlockInfo(entry.first, nullptr, true));
+				mBlocks.push_back(BlockInfo(entry.first, 0, 0, nullptr, true));
 				mBlocks.back().isUsed = false;
 			}
 		}
@@ -674,9 +677,15 @@ namespace bs
 			}
 
 			// Transfer all objects into their permanent storage
+			UINT32 numBlocks = (UINT32)mBlocks.size();
+			UINT32 blockBindingsSize = numBlocks * numPasses * sizeof(PassBlockBindings);
 			UINT32 objectParamInfosSize = totalNumObjects * sizeof(ObjectParamInfo) + numPasses * sizeof(PassParamInfo);
-			mPassParamInfos = (PassParamInfo*)bs_alloc(objectParamInfosSize);
+			mData = (UINT8*)bs_alloc(objectParamInfosSize + blockBindingsSize);
+			UINT8* dataIter = mData;
+
+			mPassParamInfos = (PassParamInfo*)dataIter;
 			memset(mPassParamInfos, 0, objectParamInfosSize);
+			dataIter += objectParamInfosSize;
 
 			StageParamInfo* stageInfos = (StageParamInfo*)mPassParamInfos;
 
@@ -736,6 +745,46 @@ namespace bs
 				}
 			}
 
+			// Determine on which passes & stages are buffers used on
+			for (auto& block : mBlocks)
+			{
+				block.passData = (PassBlockBindings*)dataIter;
+				dataIter += sizeof(PassBlockBindings) * numPasses;
+			}
+
+			for (auto& block : mBlocks)
+			{
+				for (UINT32 i = 0; i < numPasses; i++)
+				{
+					SPtr<GpuParamsType> paramPtr = mPassParams[i];
+					for (UINT32 j = 0; j < NUM_STAGES; j++)
+					{
+						GpuProgramType progType = (GpuProgramType)j;
+
+						SPtr<GpuParamDesc> curDesc = paramPtr->getParamDesc(progType);
+						if (curDesc == nullptr)
+						{
+							block.passData[i].bindings[j].set = -1;
+							block.passData[i].bindings[j].slot = -1;
+
+							continue;
+						}
+
+						auto iterFind = curDesc->paramBlocks.find(block.name);
+						if (iterFind == curDesc->paramBlocks.end())
+						{
+							block.passData[i].bindings[j].set = -1;
+							block.passData[i].bindings[j].slot = -1;
+
+							continue;
+						}
+
+						block.passData[i].bindings[j].set = iterFind->second.set;
+						block.passData[i].bindings[j].slot = iterFind->second.slot;
+					}
+				}
+			}
+
 			bs_frame_free(offsets);
 		}
 		bs_frame_clear();
@@ -745,7 +794,7 @@ namespace bs
 	TGpuParamsSet<Core>::~TGpuParamsSet()
 	{
 		// All allocations share the same memory, so we just clear it all at once
-		bs_free(mPassParamInfos);
+		bs_free(mData);
 	}
 
 	template<bool Core>
@@ -758,51 +807,70 @@ namespace bs
 	}
 
 	template<bool Core>
-	void TGpuParamsSet<Core>::setParamBlockBuffer(const String& name, const ParamBlockPtrType& paramBlock, 
-		bool ignoreInUpdate)
+	UINT32 TGpuParamsSet<Core>::getParamBlockBufferIndex(const String& name) const
 	{
-		UINT32 foundIdx = (UINT32)-1;
-		for(UINT32 i = 0; i < (UINT32)mBlocks.size(); i++)
+		for (UINT32 i = 0; i < (UINT32)mBlocks.size(); i++)
 		{
-			BlockInfo& block = mBlocks[i];
-			if(block.name == name)
-			{
-				if (!block.shareable)
-				{
-					LOGERR("Cannot set parameter block buffer with the name \"" + name + "\". Buffer is not assignable. ");
-					return;
-				}
-
-				foundIdx = i;
-			}
+			const BlockInfo& block = mBlocks[i];
+			if (block.name == name)
+				return i;
 		}
 
-		if(foundIdx == (UINT32)-1)
+		return -1;
+	}
+
+	template<bool Core>
+	void TGpuParamsSet<Core>::setParamBlockBuffer(UINT32 index, const ParamBlockPtrType& paramBlock,
+												  bool ignoreInUpdate)
+	{
+		BlockInfo& blockInfo = mBlocks[index];
+		if (!blockInfo.shareable)
 		{
-			LOGERR("Cannot set parameter block buffer with the name \"" + name + "\". Buffer name not found. ");
+			LOGERR("Cannot set parameter block buffer with the name \"" + blockInfo.name + 
+				"\". Buffer is not assignable. ");
 			return;
 		}
 
-		if (!mBlocks[foundIdx].isUsed)
+		if (!blockInfo.isUsed)
 			return;
 
-		mBlocks[foundIdx].buffer = paramBlock;
-		mBlocks[foundIdx].allowUpdate = !ignoreInUpdate;
+		blockInfo.allowUpdate = !ignoreInUpdate;
 
-		UINT32 numPasses = (UINT32)mPassParams.size();
-		for (UINT32 j = 0; j < numPasses; j++)
+		if (blockInfo.buffer != paramBlock)
 		{
-			SPtr<GpuParamsType> paramPtr = mPassParams[j];
-			for (UINT32 i = 0; i < NUM_STAGES; i++)
+			blockInfo.buffer = paramBlock;
+
+			UINT32 numPasses = (UINT32)mPassParams.size();
+			for (UINT32 j = 0; j < numPasses; j++)
 			{
-				GpuProgramType progType = (GpuProgramType)i;
+				SPtr<GpuParamsType> paramPtr = mPassParams[j];
+				for (UINT32 i = 0; i < NUM_STAGES; i++)
+				{
+					GpuProgramType progType = (GpuProgramType)i;
 
-				if (paramPtr->hasParamBlock(progType, name))
-					paramPtr->setParamBlockBuffer(progType, name, paramBlock);
+					const BlockBinding& binding = blockInfo.passData[j].bindings[progType];
+
+					if (binding.slot != -1)
+						paramPtr->setParamBlockBuffer(binding.set, binding.slot, paramBlock);
+				}
 			}
 		}
 	}
 
+	template<bool Core>
+	void TGpuParamsSet<Core>::setParamBlockBuffer(const String& name, const ParamBlockPtrType& paramBlock, 
+		bool ignoreInUpdate)
+	{
+		UINT32 bufferIdx = getParamBlockBufferIndex(name);
+		if(bufferIdx == (UINT32)-1)
+		{
+			LOGERR("Cannot set parameter block buffer with the name \"" + name + "\". Buffer name not found. ");
+			return;
+		}
+
+		setParamBlockBuffer(bufferIdx, paramBlock, ignoreInUpdate);
+	}
+
 	template<bool Core>
 	void TGpuParamsSet<Core>::update(const SPtr<MaterialParamsType>& params, UINT32 dirtyBitIdx, bool updateAll)
 	{

+ 0 - 38
Source/RenderBeast/Include/BsObjectRendering.h

@@ -22,19 +22,6 @@ namespace bs
 		BS_PARAM_BLOCK_ENTRY(float, gTime)
 	BS_PARAM_BLOCK_END
 
-	BS_PARAM_BLOCK_BEGIN(PerCameraParamBuffer)
-		BS_PARAM_BLOCK_ENTRY(Vector3, gViewDir)
-		BS_PARAM_BLOCK_ENTRY(Vector3, gViewOrigin)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatViewProj)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatView)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatProj)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvProj)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvViewProj)
-		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatScreenToWorld)
-		BS_PARAM_BLOCK_ENTRY(Vector2, gDeviceZToWorldZ)
-		BS_PARAM_BLOCK_ENTRY(Vector4, gClipToUVScaleOffset)
-	BS_PARAM_BLOCK_END
-
 	BS_PARAM_BLOCK_BEGIN(PerObjectParamBuffer)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatWorldViewProj)
 		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatWorld)
@@ -54,21 +41,6 @@ namespace bs
 		float worldDeterminantSign;
 	};
 
-	/**	Data bound to the shader when rendering a with a specific camera. */
-	struct CameraShaderData
-	{
-		Vector3 viewDir;
-		Vector3 viewOrigin;
-		Matrix4 view;
-		Matrix4 proj;
-		Matrix4 viewProj;
-		Matrix4 invProj;
-		Matrix4 invViewProj;
-		Matrix4 screenToWorld;
-		Vector2 deviceZToWorldZ;
-		Vector4 clipToUVScaleOffset;
-	};
-
 	/** Manages initialization and rendering of individual renderable object, represented as RenderableElement%s. */
 	class BS_BSRND_EXPORT ObjectRenderer
 	{
@@ -81,24 +53,14 @@ namespace bs
 		/** Updates global per frame parameter buffers with new values. To be called at the start of every frame. */
 		void setParamFrameParams(float time);
 
-		/**
-		 * Updates global per frame parameter buffers with new values. To be called at the start of rendering for every 
-		 * camera.
-		 */
-		void setPerCameraParams(const CameraShaderData& cameraData);
-
 		/**
 		 * Updates object specific parameter buffers with new values. To be called whenever object specific values change.
 		 */
 		void setPerObjectParams(const BeastRenderableElement& element, const RenderableShaderData& data,
 			const Matrix4& wvpMatrix, const SPtr<GpuBufferCore>& boneMatrices = nullptr);
 
-		/** Returns a buffer that stores per-camera parameters. */
-		const PerCameraParamBuffer& getPerCameraParams() const { return mPerCameraParams; }
-
 	protected:
 		PerFrameParamBuffer mPerFrameParams;
-		PerCameraParamBuffer mPerCameraParams;
 		PerObjectParamBuffer mPerObjectParams;
 	};
 

+ 3 - 2
Source/RenderBeast/Include/BsRenderBeast.h

@@ -123,8 +123,9 @@ namespace bs
 		 *
 		 * @param[in]	camera		Camera whose data to update.
 		 * @param[in]	forceRemove	If true, the camera data will be removed instead of updated.
+		 * @return					Renderer camera object that represents the camera. Null if camera was removed.
 		 */
-		void updateCameraData(const CameraCore* camera, bool forceRemove = false);
+		RendererCamera* updateCameraData(const CameraCore* camera, bool forceRemove = false);
 
 		/**
 		 * Updates the render options on the core thread.
@@ -194,7 +195,7 @@ namespace bs
 
 		// Core thread only fields
 		Vector<RendererRenderTarget> mRenderTargets;
-		UnorderedMap<const CameraCore*, RendererCamera> mCameras;
+		UnorderedMap<const CameraCore*, RendererCamera*> mCameras;
 		UnorderedMap<SamplerOverrideKey, MaterialSamplerOverrides*> mSamplerOverrides;
 
 		Vector<RendererObject> mRenderables;

+ 27 - 2
Source/RenderBeast/Include/BsRendererCamera.h

@@ -15,6 +15,19 @@ namespace bs
 	 *  @{
 	 */
 
+	BS_PARAM_BLOCK_BEGIN(PerCameraParamBuffer)
+		BS_PARAM_BLOCK_ENTRY(Vector3, gViewDir)
+		BS_PARAM_BLOCK_ENTRY(Vector3, gViewOrigin)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatViewProj)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatView)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatProj)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvProj)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatInvViewProj)
+		BS_PARAM_BLOCK_ENTRY(Matrix4, gMatScreenToWorld)
+		BS_PARAM_BLOCK_ENTRY(Vector2, gDeviceZToWorldZ)
+		BS_PARAM_BLOCK_ENTRY(Vector4, gClipToUVScaleOffset)
+	BS_PARAM_BLOCK_END
+
 	/** Contains information about a Camera, used by the Renderer. */
 	class RendererCamera
 	{
@@ -64,18 +77,27 @@ namespace bs
 		 *									object. If the bit for an object is already set to true, the method will never
 		 *									change it to false which allows the same bitfield to be provided to multiple
 		 *									renderer cameras. Must be the same size as the @p renderables array.
+		 *									
+		 *									As a side-effect, per-camera visibility data is also calculated and can be
+		 *									retrieved by calling getVisibilityMask().
 		 */
 		void determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds, 
 			Vector<bool>& visibility);
 
+		/** Returns the visibility mask calculated with the last call to determineVisible(). */
+		const Vector<bool> getVisibilityMask() const { return mVisibility; }
+
 		/** 
 		 * Returns a structure containing information about post-processing effects. This structure will be modified and
 		 * maintained by the post-processing system.
 		 */
 		PostProcessInfo& getPPInfo() { return mPostProcessInfo; }
 
-		/** Returns an object with camera's information, used for populating per-camera parameter buffers. */
-		CameraShaderData getShaderData();
+		/** Updates the GPU buffer containing per-camera information, with the latest data. */
+		void updatePerCameraBuffer();
+
+		/** Returns a buffer that stores per-camera parameters. */
+		PerCameraParamBuffer& getPerCameraBuffer() { return mParams; }
 
 	private:
 		/**
@@ -95,6 +117,9 @@ namespace bs
 		SPtr<RenderTargets> mRenderTargets;
 		PostProcessInfo mPostProcessInfo;
 		bool mUsingRenderTargets;
+
+		PerCameraParamBuffer mParams;
+		Vector<bool> mVisibility;
 	};
 
 	/** @} */

+ 3 - 0
Source/RenderBeast/Include/BsRendererObject.h

@@ -43,6 +43,9 @@ namespace bs
 		/** Index of the technique in the material to render the element with. */
 		UINT32 techniqueIdx;
 
+		/** Index to which should the per-camera param block buffer be bound to. */
+		UINT32 perCameraBindingIdx;
+
 		/** 
 		 * Parameter for setting global bone pose transforms used for an element with skeletal animation, null otherwise. 
 		 */

+ 0 - 18
Source/RenderBeast/Source/BsObjectRendering.cpp

@@ -34,8 +34,6 @@ namespace bs
 		{
 			if (paramBlockDesc.second.rendererSemantic == RBS_PerFrame)
 				element.params->setParamBlockBuffer(paramBlockDesc.second.name, mPerFrameParams.getBuffer(), true);
-			else if (paramBlockDesc.second.rendererSemantic == RBS_PerCamera)
-				element.params->setParamBlockBuffer(paramBlockDesc.second.name, mPerCameraParams.getBuffer(), true);
 			else if (paramBlockDesc.second.rendererSemantic == RBS_PerObject)
 				element.params->setParamBlockBuffer(paramBlockDesc.second.name, mPerObjectParams.getBuffer(), true);
 		}
@@ -62,22 +60,6 @@ namespace bs
 		mPerFrameParams.gTime.set(time);
 	}
 
-	void ObjectRenderer::setPerCameraParams(const CameraShaderData& cameraData)
-	{
-		mPerCameraParams.gViewDir.set(cameraData.viewDir);
-		mPerCameraParams.gViewOrigin.set(cameraData.viewOrigin);
-		mPerCameraParams.gMatView.set(cameraData.view);
-		mPerCameraParams.gMatProj.set(cameraData.proj);
-		mPerCameraParams.gMatViewProj.set(cameraData.viewProj);
-		mPerCameraParams.gMatInvProj.set(cameraData.invProj);
-		mPerCameraParams.gMatInvViewProj.set(cameraData.invViewProj);
-		mPerCameraParams.gMatScreenToWorld.set(cameraData.screenToWorld);
-		mPerCameraParams.gDeviceZToWorldZ.set(cameraData.deviceZToWorldZ);
-		mPerCameraParams.gClipToUVScaleOffset.set(cameraData.clipToUVScaleOffset);
-
-		mPerCameraParams.flushToGPU();
-	}
-
 	void ObjectRenderer::setPerObjectParams(const BeastRenderableElement& element, const RenderableShaderData& data,
 		const Matrix4& wvpMatrix, const SPtr<GpuBufferCore>& boneMatrices)
 	{

+ 89 - 28
Source/RenderBeast/Source/BsRenderBeast.cpp

@@ -86,6 +86,9 @@ namespace bs
 		if (mObjectRenderer != nullptr)
 			bs_delete(mObjectRenderer);
 
+		for (auto& entry : mCameras)
+			bs_delete(entry.second);
+
 		mRenderTargets.clear();
 		mCameras.clear();
 		mRenderables.clear();
@@ -262,6 +265,25 @@ namespace bs
 				}
 
 				mObjectRenderer->initElement(renElement);
+
+				// Set up or find bind spots for relevant GPU param buffers
+				SPtr<ShaderCore> shader = renElement.material->getShader();
+				if (shader != nullptr)
+				{
+					const Map<String, SHADER_PARAM_BLOCK_DESC>& paramBlockDescs = shader->getParamBlocks();
+
+					for (auto& paramBlockDesc : paramBlockDescs)
+					{
+						if (paramBlockDesc.second.rendererSemantic == RBS_PerCamera)
+						{
+							renElement.perCameraBindingIdx = renElement.params->getParamBlockBufferIndex(paramBlockDesc.second.name);
+
+							// TODO - Verify size
+						}
+					}
+				}
+				else
+					renElement.perCameraBindingIdx = -1;
 			}
 		}
 	}
@@ -399,20 +421,26 @@ namespace bs
 
 	void RenderBeast::notifyCameraAdded(const CameraCore* camera)
 	{
-		updateCameraData(camera);
+		RendererCamera* renCamera = updateCameraData(camera);
+		renCamera->updatePerCameraBuffer();
 	}
 
 	void RenderBeast::notifyCameraUpdated(const CameraCore* camera, UINT32 updateFlag)
 	{
+		RendererCamera* rendererCam;
 		if((updateFlag & (UINT32)CameraDirtyFlag::Everything) != 0)
 		{
-			updateCameraData(camera);
+			rendererCam = updateCameraData(camera);
 		}
 		else if((updateFlag & (UINT32)CameraDirtyFlag::PostProcess) != 0)
 		{
-			RendererCamera& rendererCam = mCameras[camera];
-			rendererCam.updatePP();
-		} 
+			rendererCam = mCameras[camera];
+			rendererCam->updatePP();
+		}
+		else
+			rendererCam = mCameras[camera];
+
+		rendererCam->updatePerCameraBuffer();
 	}
 
 	void RenderBeast::notifyCameraRemoved(const CameraCore* camera)
@@ -425,17 +453,27 @@ namespace bs
 		return bs_shared_ptr_new<StandardPostProcessSettings>();
 	}
 
-	void RenderBeast::updateCameraData(const CameraCore* camera, bool forceRemove)
+	RendererCamera* RenderBeast::updateCameraData(const CameraCore* camera, bool forceRemove)
 	{
+		RendererCamera* output;
+
 		SPtr<RenderTargetCore> renderTarget = camera->getViewport()->getTarget();
 		if(forceRemove)
 		{
-			mCameras.erase(camera);
+			auto iterFind = mCameras.find(camera);
+			if(iterFind != mCameras.end())
+			{
+				bs_delete(iterFind->second);
+				mCameras.erase(iterFind);
+			}
+
 			renderTarget = nullptr;
+			output = nullptr;
 		}
 		else
 		{
-			mCameras[camera] = RendererCamera(camera, mCoreOptions->stateReductionMode);
+			output = bs_new<RendererCamera>(camera, mCoreOptions->stateReductionMode);
+			mCameras[camera] = output;
 		}
 
 		// Remove from render target list
@@ -499,6 +537,8 @@ namespace bs
 				std::sort(begin(cameras), end(cameras), cameraComparer);
 			}
 		}
+
+		return output;
 	}
 
 	void RenderBeast::setOptions(const SPtr<CoreRendererOptions>& options)
@@ -525,8 +565,8 @@ namespace bs
 
 		for (auto& entry : mCameras)
 		{
-			RendererCamera& rendererCam = entry.second;
-			rendererCam.update(mCoreOptions->stateReductionMode);
+			RendererCamera* rendererCam = entry.second;
+			rendererCam->update(mCoreOptions->stateReductionMode);
 		}
 	}
 
@@ -562,8 +602,9 @@ namespace bs
 		mVisibility.assign(mVisibility.size(), false);
 
 		for (auto& entry : mCameras)
-			entry.second.determineVisible(mRenderables, mWorldBounds, mVisibility);
+			entry.second->determineVisible(mRenderables, mWorldBounds, mVisibility);
 
+		// Retrieve animation data
 		AnimationManager::instance().waitUntilComplete();
 		const RendererAnimationData& animData = AnimationManager::instance().getRendererData();
 		RendererFrame frameInfo(delta, animData);
@@ -614,15 +655,34 @@ namespace bs
 		gProfilerCPU().beginSample("Render");
 
 		const CameraCore* camera = rtInfo.cameras[camIdx];
-		RendererCamera& rendererCam = mCameras[camera];
-		CameraShaderData cameraShaderData = rendererCam.getShaderData();
+		RendererCamera* rendererCam = mCameras[camera];
+		PerCameraParamBuffer& parCameraBuffer = rendererCam->getPerCameraBuffer();
+		parCameraBuffer.flushToGPU();
 
 		assert(!camera->getFlags().isSet(CameraFlag::Overlay));
 
-		mObjectRenderer->setPerCameraParams(cameraShaderData);
-		rendererCam.beginRendering(true);
+		// Assign camera data to all relevant renderables
+		const Vector<bool>& visibility = rendererCam->getVisibilityMask();
+		UINT32 numRenderables = (UINT32)mRenderables.size();
+		for (UINT32 i = 0; i < numRenderables; i++)
+		{
+			if (!visibility[i])
+				continue;
+
+			for (auto& element : mRenderables[i].elements)
+			{
+				if (element.perCameraBindingIdx != -1)
+					element.params->setParamBlockBuffer(element.perCameraBindingIdx, parCameraBuffer.getBuffer(), true);
+			}
+		}
+
+		Matrix4 proj = camera->getProjectionMatrixRS();
+		Matrix4 view = camera->getViewMatrix();
+		Matrix4 viewProj = proj * view;
+
+		rendererCam->beginRendering(true);
 
-		SPtr<RenderTargets> renderTargets = rendererCam.getRenderTargets();
+		SPtr<RenderTargets> renderTargets = rendererCam->getRenderTargets();
 		renderTargets->bindGBuffer();
 
 		//// Trigger pre-scene callbacks
@@ -642,20 +702,22 @@ namespace bs
 				callbackData.callback();
 			}
 		}
+
+
 		
 		//// Render base pass
-		const Vector<RenderQueueElement>& opaqueElements = rendererCam.getOpaqueQueue()->getSortedElements();
+		const Vector<RenderQueueElement>& opaqueElements = rendererCam->getOpaqueQueue()->getSortedElements();
 		for (auto iter = opaqueElements.begin(); iter != opaqueElements.end(); ++iter)
 		{
 			BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
-			renderElement(*renderElem, iter->passIdx, iter->applyPass, frameInfo, cameraShaderData.viewProj);
+			renderElement(*renderElem, iter->passIdx, iter->applyPass, frameInfo, viewProj);
 		}
 
 		renderTargets->bindSceneColor(true);
 
 		//// Render light pass
 		{
-			SPtr<GpuParamBlockBufferCore> perCameraBuffer = mObjectRenderer->getPerCameraParams().getBuffer();
+			SPtr<GpuParamBlockBufferCore> perCameraBuffer = rendererCam->getPerCameraBuffer().getBuffer();;
 
 			mDirLightMat->bind(renderTargets, perCameraBuffer);
 			for (auto& light : mDirectionalLights)
@@ -715,11 +777,11 @@ namespace bs
 		renderTargets->bindSceneColor(false);
 		
 		// Render transparent objects (TODO - No lighting yet)
-		const Vector<RenderQueueElement>& transparentElements = rendererCam.getTransparentQueue()->getSortedElements();
+		const Vector<RenderQueueElement>& transparentElements = rendererCam->getTransparentQueue()->getSortedElements();
 		for (auto iter = transparentElements.begin(); iter != transparentElements.end(); ++iter)
 		{
 			BeastRenderableElement* renderElem = static_cast<BeastRenderableElement*>(iter->renderElem);
-			renderElement(*renderElem, iter->passIdx, iter->applyPass, frameInfo, cameraShaderData.viewProj);
+			renderElement(*renderElem, iter->passIdx, iter->applyPass, frameInfo, viewProj);
 		}
 
 		// Render non-overlay post-scene callbacks
@@ -738,7 +800,7 @@ namespace bs
 
 		// TODO - If GBuffer has multiple samples, I should resolve them before post-processing
 		PostProcessing::instance().postProcess(renderTargets->getSceneColorRT(),
-			camera, rendererCam.getPPInfo(), frameInfo.delta);
+			camera, rendererCam->getPPInfo(), frameInfo.delta);
 
 		// Render overlay post-scene callbacks
 		if (iterCameraCallbacks != mRenderCallbacks.end())
@@ -754,7 +816,7 @@ namespace bs
 			}
 		}
 
-		rendererCam.endRendering();
+		rendererCam->endRendering();
 
 		gProfilerCPU().endSample("Render");
 	}
@@ -767,11 +829,10 @@ namespace bs
 		assert(camera->getFlags().isSet(CameraFlag::Overlay));
 
 		SPtr<ViewportCore> viewport = camera->getViewport();
-		RendererCamera& rendererCam = mCameras[camera];
-		CameraShaderData cameraShaderData = rendererCam.getShaderData();
+		RendererCamera* rendererCam = mCameras[camera];
+		rendererCam->getPerCameraBuffer().flushToGPU();
 
-		mObjectRenderer->setPerCameraParams(cameraShaderData);
-		rendererCam.beginRendering(false);
+		rendererCam->beginRendering(false);
 
 		SPtr<RenderTargetCore> target = rtData.target;
 
@@ -813,7 +874,7 @@ namespace bs
 			}
 		}
 
-		rendererCam.endRendering();
+		rendererCam->endRendering();
 
 		gProfilerCPU().endSample("RenderOverlay");
 	}

+ 42 - 22
Source/RenderBeast/Source/BsRendererCamera.cpp

@@ -81,6 +81,9 @@ namespace bs
 	void RendererCamera::determineVisible(Vector<RendererObject>& renderables, const Vector<Bounds>& renderableBounds, 
 		Vector<bool>& visibility)
 	{
+		mVisibility.clear();
+		mVisibility.resize(renderables.size(), false);
+
 		bool isOverlayCamera = mCamera->getFlags().isSet(CameraFlag::Overlay);
 		if (isOverlayCamera)
 			return;
@@ -109,6 +112,7 @@ namespace bs
 				if (worldFrustum.intersects(boundingBox))
 				{
 					visibility[i] = true;
+					mVisibility[i] = true;
 
 					float distanceToCamera = (mCamera->getPosition() - boundingBox.getCenter()).length();
 
@@ -168,14 +172,18 @@ namespace bs
 		return output;
 	}
 
-	CameraShaderData RendererCamera::getShaderData()
+	void RendererCamera::updatePerCameraBuffer()
 	{
-		CameraShaderData data;
-		data.proj = mCamera->getProjectionMatrixRS();
-		data.view = mCamera->getViewMatrix();
-		data.viewProj = data.proj * data.view;
-		data.invProj = data.proj.inverse();
-		data.invViewProj = data.viewProj.inverse(); // Note: Calculate inverses separately (better precision possibly)
+		Matrix4 proj = mCamera->getProjectionMatrixRS();
+		Matrix4 view = mCamera->getViewMatrix();
+		Matrix4 viewProj = proj * view;
+		Matrix4 invViewProj = viewProj.inverse();
+
+		mParams.gMatProj.set(proj);
+		mParams.gMatView.set(view);
+		mParams.gMatViewProj.set(viewProj);
+		mParams.gMatInvViewProj.set(invViewProj); // Note: Calculate inverses separately (better precision possibly)
+		mParams.gMatInvProj.set(proj.inverse());
 
 		// Construct a special inverse view-projection matrix that had projection entries that affect z and w eliminated.
 		// Used to transform a vector(clip_x, clip_y, view_z, view_w), where clip_x/clip_y are in clip space, and 
@@ -183,15 +191,15 @@ namespace bs
 
 		// Only projects z/w coordinates
 		Matrix4 projZ = Matrix4::IDENTITY;
-		projZ[2][2] = data.proj[2][2];
-		projZ[2][3] = data.proj[2][3];
-		projZ[3][2] = data.proj[3][2];
+		projZ[2][2] = proj[2][2];
+		projZ[2][3] = proj[2][3];
+		projZ[3][2] = proj[3][2];
 		projZ[3][3] = 0.0f;
 
-		data.screenToWorld = data.invViewProj * projZ;
-		data.viewDir = mCamera->getForward();
-		data.viewOrigin = mCamera->getPosition();
-		data.deviceZToWorldZ = getDeviceZTransform(data.proj);
+		mParams.gMatScreenToWorld.set(invViewProj * projZ);
+		mParams.gViewDir.set(mCamera->getForward());
+		mParams.gViewOrigin.set(mCamera->getPosition());
+		mParams.gDeviceZToWorldZ.set(getDeviceZTransform(proj));
 
 		SPtr<ViewportCore> viewport = mCamera->getViewport();
 		SPtr<RenderTargetCore> rt = viewport->getTarget();
@@ -199,20 +207,32 @@ namespace bs
 		float halfWidth = viewport->getWidth() * 0.5f;
 		float halfHeight = viewport->getHeight() * 0.5f;
 
-		float rtWidth = (float)rt->getProperties().getWidth();
-		float rtHeight = (float)rt->getProperties().getHeight();
+		float rtWidth;
+		float rtHeight;
+
+		if(rt != nullptr)
+		{
+			rtWidth = (float)rt->getProperties().getWidth();
+			rtHeight = (float)rt->getProperties().getHeight();
+		}
+		else
+		{
+			rtWidth = 20.0f;
+			rtHeight = 20.0f;
+		}
 
 		RenderAPICore& rapi = RenderAPICore::instance();
 		const RenderAPIInfo& rapiInfo = rapi.getAPIInfo();
 
-		data.clipToUVScaleOffset.x = halfWidth / rtWidth;
-		data.clipToUVScaleOffset.y = -halfHeight / rtHeight;
-		data.clipToUVScaleOffset.z = viewport->getX() / rtWidth + (halfWidth + rapiInfo.getHorizontalTexelOffset()) / rtWidth;
-		data.clipToUVScaleOffset.w = viewport->getY() / rtHeight + (halfHeight + rapiInfo.getVerticalTexelOffset()) / rtHeight;
+		Vector4 clipToUVScaleOffset;
+		clipToUVScaleOffset.x = halfWidth / rtWidth;
+		clipToUVScaleOffset.y = -halfHeight / rtHeight;
+		clipToUVScaleOffset.z = viewport->getX() / rtWidth + (halfWidth + rapiInfo.getHorizontalTexelOffset()) / rtWidth;
+		clipToUVScaleOffset.w = viewport->getY() / rtHeight + (halfHeight + rapiInfo.getVerticalTexelOffset()) / rtHeight;
 
 		if (!rapiInfo.getNDCYAxisDown())
-			data.clipToUVScaleOffset.y = -data.clipToUVScaleOffset.y;
+			clipToUVScaleOffset.y = -clipToUVScaleOffset.y;
 
-		return data;
+		mParams.gClipToUVScaleOffset.set(clipToUVScaleOffset);
 	}
 }