Преглед на файлове

Separated constant GpuParams data into a GpuPipelineParamInfo object. Aside from being cleaner, it also gives Vulkan descriptor layouts a place to reside.

BearishSun преди 9 години
родител
ревизия
c8b4aaf0fb
променени са 26 файла, в които са добавени 614 реда и са изтрити 287 реда
  1. 45 20
      Documentation/Manuals/Native/gpuPrograms.md
  2. 1 1
      Documentation/Manuals/Native/materials.md
  3. 6 8
      Documentation/Manuals/Native/renderAPI.md
  4. 25 3
      Source/BansheeCore/Include/BsGpuParams.h
  5. 11 3
      Source/BansheeCore/Include/BsGpuPipelineParamInfo.h
  6. 3 11
      Source/BansheeCore/Include/BsHardwareBufferManager.h
  7. 5 3
      Source/BansheeCore/Include/BsRenderStateManager.h
  8. 21 0
      Source/BansheeCore/Source/BsGpuParams.cpp
  9. 2 2
      Source/BansheeCore/Source/BsGpuParamsSet.cpp
  10. 7 4
      Source/BansheeCore/Source/BsGpuPipelineParamInfo.cpp
  11. 66 38
      Source/BansheeCore/Source/BsGpuPipelineState.cpp
  12. 3 3
      Source/BansheeCore/Source/BsHardwareBufferManager.cpp
  13. 4 4
      Source/BansheeCore/Source/BsRenderStateManager.cpp
  14. 2 0
      Source/BansheeVulkanRenderAPI/CMakeSources.cmake
  15. 3 3
      Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuParams.h
  16. 29 1
      Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineParamInfo.h
  17. 23 5
      Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineState.h
  18. 7 3
      Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuProgram.h
  19. 1 1
      Source/BansheeVulkanRenderAPI/Include/BsVulkanHardwareBufferManager.h
  20. 2 1
      Source/BansheeVulkanRenderAPI/Include/BsVulkanRenderStateManager.h
  21. 52 155
      Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuParams.cpp
  22. 153 5
      Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineParamInfo.cpp
  23. 132 8
      Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp
  24. 1 1
      Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuProgram.cpp
  25. 2 2
      Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderStateManager.cpp
  26. 8 2
      Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

+ 45 - 20
Documentation/Manuals/Native/gpuPrograms.md

@@ -36,21 +36,57 @@ SPtr<GpuProgram> myProgram = GpuProgram::create(desc);
  
  
 Once the GPU program has been created it is not guaranteed to be usable. The compilation of the provided source code could have failed, which you can check by calling @ref BansheeEngine::GpuProgram::isCompiled() "GpuProgram::isCompiled", and retrieve the error message by calling @ref BansheeEngine::GpuProgram::getCompileErrorMessage() "GpuProgram::getCompileErrorMessage". Be aware that both of these methods are only valid after the core thread has initialized the object. You can ensure this by calling @ref BansheeEngine::CoreObject::blockUntilCoreInitialized "GpuProgram::blockUntilCoreInitialized" but be aware this will block the calling thread which can result in a significant performance impact.
 Once the GPU program has been created it is not guaranteed to be usable. The compilation of the provided source code could have failed, which you can check by calling @ref BansheeEngine::GpuProgram::isCompiled() "GpuProgram::isCompiled", and retrieve the error message by calling @ref BansheeEngine::GpuProgram::getCompileErrorMessage() "GpuProgram::getCompileErrorMessage". Be aware that both of these methods are only valid after the core thread has initialized the object. You can ensure this by calling @ref BansheeEngine::CoreObject::blockUntilCoreInitialized "GpuProgram::blockUntilCoreInitialized" but be aware this will block the calling thread which can result in a significant performance impact.
 
 
-# GPU program parameters {#gpuPrograms_b}
-Once the GPU program has been compiled you can access information about its parameters (constant variables, or uniforms in DirectX/OpenGL lingo). Use @ref BansheeEngine::GpuProgram::getParamDesc "GpuProgram::getParamDesc" to retrieve a structure containing all GPU parameters, including primitive parameters, textures, samplers, buffers and parameter buffers (constant/uniform buffers in DirectX/OpenGL lingo). 
+# Using GPU programs for rendering {#gpuPrograms_b}
+To use a GPU program in a draw or dispatch call, you must first create a GPU pipeline object using the relevant GPU programs.
 
 
-You generally don't need to use this information directly, but you provide it when creating a @ref BansheeEngine::GpuParams "GpuParams" object.
+There are two types of pipeline objects: @ref BansheeEngine::GraphicsPipelineState "GraphicsPipelineState" and @ref BansheeEngine::ComputePipelineState "ComputePipelineState".
+
+Example to create a graphics pipeline:
+~~~~~~~~~~~~~{.cpp}
+PIPELINE_STATE_CORE_DESC desc;
+desc.vertexProgram = ...
+desc.fragmentProgram = ...;
+desc.geometryProgram = ...;
+desc.hullProgram = ...;
+desc.domainProgram = ...;
+
+SPtr<GraphicsPipelineStateCore> graphicsPipeline = GraphicsPipelineStateCore::create(desc);
+~~~~~~~~~~~~~
+
+Example to create a compute pipeline:
+~~~~~~~~~~~~~{.cpp}
+SPtr<GpuProgramCore> computeProgram = ...;
+SPtr<ComputePipelineStateCore> computePipeline = ComputePipelineStateCore::create(computeProgram);
+~~~~~~~~~~~~~
+
+Once created the pipeline can be bound for rendering by calling @ref BansheeEngine::RenderAPICore::setGraphicsPipeline "RenderAPICore::setGraphicsPipeline" or @ref BansheeEngine::RenderAPI::setComputePipeline "RenderAPI::setComputePipeline".
+
+~~~~~~~~~~~~~{.cpp}
+// Bind pipeline for use (continued from above)
+
+RenderAPICore& rapi = RenderAPICore::instance();
+rapi.setGraphicsPipeline(graphicsPipeline);
+// Or: rapi.setComputePipeline(computePipeline);
+~~~~~~~~~~~~~
+
+Once pipeline state is bound, any subsequent draw/dispatch calls will then use the GPU programs attached to that state. 
+
+Much more detailed information about pipelines and rendering is provided in the [render API manual](@ref renderAPI).
+
+# GPU program parameters {#gpuPrograms_c}
+Although you can use a GPU program without any parameters, most will require some additional data in order to perform their operations. Program parameters represent data that is static throughout a single GPU program execution (e.g. a draw call). For example, when drawing a 3D object you will usually want to provide a projection matrix that transforms the object from 3D to 2D, according to the camera the user is viewing the object through.
+
+You can access information about GPU program parameters by calling @ref BansheeEngine::GpuProgram::getParamDesc "GpuProgram::getParamDesc". This will return a structure containing information about all GPU parameters used by that GPU program. This includes primitives (int, float, etc.), textures, samplers, buffers and parameter buffers (constant/uniform buffers in DirectX/OpenGL lingo). 
+
+You generally don't need to use this information directly. It is instead automatically parsed when you create a GPU pipeline. Once you have a pipeline you can use it to create a *GpuParams* object that allows you to assign values to all parameters of a specific pipeline.
 
 
 ## GpuParams {#gpuPrograms_b_a}
 ## GpuParams {#gpuPrograms_b_a}
-@ref BansheeEngine::GpuParams "GpuParams" is a container for all parameters required by a set of GPU programs (one per stage). It allows you to set primitive/texture/sampler/buffer parameters used by the GPU programs, which it stores in an internal buffer. You can then bind it to the pipeline and it will be used by the bound GPU programs.
+@ref BansheeEngine::GpuParams "GpuParams" is a container for all parameters required by a single GPU pipeline (graphics or compute). It allows you to set primitive/texture/sampler/buffer parameters used by the GPU programs, which it stores in an internal buffer. You can then bind it before executing at draw/dispatch call, and the assigned parameters will be used by GPU programs in the current pipeline.
 
 
 For example to assign a texture and a 2D vector as input to the program we created earlier:
 For example to assign a texture and a 2D vector as input to the program we created earlier:
 ~~~~~~~~~~~~~{.cpp}
 ~~~~~~~~~~~~~{.cpp}
-GPU_PARAMS_DESC desc;
-desc.fragmentParams = myProgram->getParamDesc();
-... usually you want to set other stages as well ...
-
-SPtr<GpuParams> params = GpuParams::create(desc);
+SPtr<GraphicsPipelineState> graphicsPipeline = ...;
+SPtr<GpuParams> params = GpuParams::create(graphicsPipeline);
 
 
 // Retrieve GPU param handles we can then read/write to
 // Retrieve GPU param handles we can then read/write to
 GpuParamVec2 myVectorParam;
 GpuParamVec2 myVectorParam;
@@ -67,17 +103,6 @@ As you can see we must first retrieve a handle to the parameter, and then we can
 
 
 See the [render API manual](@ref renderAPI) for more information about how to set and bind GPU program parameters.
 See the [render API manual](@ref renderAPI) for more information about how to set and bind GPU program parameters.
 
 
-# Using GPU programs for rendering {#gpuPrograms_c}
-You can bind a GPU program to the pipeline by assigning it to a @ref BansheeEngine::GraphicsPipelineState "GraphicsPipelineState" and calling @ref BansheeEngine::RenderAPI::setGraphicsPipeline "RenderAPI::setGraphicsPipeline". 
-
-Or alternatively if using a compute program you can bind it by assigning it to a @ref BansheeEngine::ComputePipelineState "ComputePipelineState" and calling @ref BansheeEngine::RenderAPI::setComputePipeline "RenderAPI::setComputePipeline". 
-
-Once pipeline state is bound, any subsequent draw/dispatch calls will then use the GPU programs attached to that state. 
-
-You can bind parameters for use in the GPU program by calling @ref BansheeEngine::RenderAPICore::setGpuParams "RenderAPICore::setGpuParams" which accepts the @ref BansheeEngine::GpuParamsCore "GpuParamsCore" object we described above.
-
-Much more detailed information about rendering is provided in the [render API manual](@ref renderAPI).
-
 # Core thread GPU programs {#gpuPrograms_e}
 # Core thread GPU programs {#gpuPrograms_e}
 So far we have only talked about the simulation thread @ref BansheeEngine::GpuProgram "GpuProgram" but have ignored the core thread @ref BansheeEngine::GpuProgramCore "GpuProgramCore". The functionality between the two is mostly the same, with the major difference being that operations performed on the core thread version are immediate. So calls to @ref BansheeEngine::GpuProgramCore::isCompiled() "GpuProgramCore::isCompiled" and @ref BansheeEngine::GpuProgramCore::getCompileErrorMessage() "GpuProgramCore::getCompileErrorMessage" don't require any waiting.
 So far we have only talked about the simulation thread @ref BansheeEngine::GpuProgram "GpuProgram" but have ignored the core thread @ref BansheeEngine::GpuProgramCore "GpuProgramCore". The functionality between the two is mostly the same, with the major difference being that operations performed on the core thread version are immediate. So calls to @ref BansheeEngine::GpuProgramCore::isCompiled() "GpuProgramCore::isCompiled" and @ref BansheeEngine::GpuProgramCore::getCompileErrorMessage() "GpuProgramCore::getCompileErrorMessage" don't require any waiting.
 
 

+ 1 - 1
Documentation/Manuals/Native/materials.md

@@ -187,7 +187,7 @@ From the simulation thread you cannot use material to render manually (you must
 
 
 Core thread gives you more flexibility and you can use @ref BansheeEngine::RendererUtility::setPass "RendererUtility::setPass" to bind a specific pass from a material to the pipeline, and @ref BansheeEngine::RendererUtility::setPassParams "RendererUtility::setPassParams" to bind material parameters for a specific pass. 
 Core thread gives you more flexibility and you can use @ref BansheeEngine::RendererUtility::setPass "RendererUtility::setPass" to bind a specific pass from a material to the pipeline, and @ref BansheeEngine::RendererUtility::setPassParams "RendererUtility::setPassParams" to bind material parameters for a specific pass. 
 
 
-In order to retrieve a set of per-program @ref BansheeEngine::GpuParams "GpuParams" that can be used for binding directly to the pipeline, call @ref BansheeEngine::Material::createParamsSet "Material::createParamsSet", followed by @ref BansheeEngine::Material::updateParamsSet "Material::updateParamsSet". You are required to call @ref BansheeEngine::Material::updateParamsSet "Material::updateParamsSet" whenever material parameters change, in order to transfer the new data to @ref BansheeEngine::GpuParams "GpuParams".
+In order to retrieve a set of per-pass @ref BansheeEngine::GpuParams "GpuParams" that can be used for binding directly to the pipeline, call @ref BansheeEngine::Material::createParamsSet "Material::createParamsSet", followed by @ref BansheeEngine::Material::updateParamsSet "Material::updateParamsSet". You are required to call @ref BansheeEngine::Material::updateParamsSet "Material::updateParamsSet" whenever material parameters change, in order to transfer the new data to @ref BansheeEngine::GpuParams "GpuParams".
 
 
 After pass and pass parameters are bound you can follow them with draw calls as described in the [render API](@ref renderAPI) manual to render objects manually. 
 After pass and pass parameters are bound you can follow them with draw calls as described in the [render API](@ref renderAPI) manual to render objects manually. 
 
 

+ 6 - 8
Documentation/Manuals/Native/renderAPI.md

@@ -98,13 +98,9 @@ The pipeline state also requires you to bind at least one GPU program (programma
 Most GPU programs also accept a number of parameters, whether textures, buffers, sampler states or primitive values like floats or integers. These parameters are accessed through @ref BansheeEngine::GpuParamsCore "GpuParamsCore" object. You can use this object to assign individual parameters and then bind the object to the render API using @ref BansheeEngine::RenderAPICore::setGpuParams "RenderAPICore::setGpuParams". See below for an example.
 Most GPU programs also accept a number of parameters, whether textures, buffers, sampler states or primitive values like floats or integers. These parameters are accessed through @ref BansheeEngine::GpuParamsCore "GpuParamsCore" object. You can use this object to assign individual parameters and then bind the object to the render API using @ref BansheeEngine::RenderAPICore::setGpuParams "RenderAPICore::setGpuParams". See below for an example.
 
 
 ~~~~~~~~~~~~~{.cpp}
 ~~~~~~~~~~~~~{.cpp}
-... assuming vertex/fragment programs are created ...
-
-GPU_PARAMS_DESC desc;
-desc.vertexParams = vertProgram->getParamDesc();
-desc.fragmentParams = fragProgram->getParamDesc();
-
-SPtr<GpuParamsCore> params = GpuParamsCore::create(desc);
+... assuming graphics pipeline state and relevant GPU programs are created ...
+SPtr<GraphicsPipelineStateCore> state = ...;
+SPtr<GpuParamsCore> params = GpuParamsCore::create(state);
 
 
 // Retrieve GPU param handles we can then read/write to
 // Retrieve GPU param handles we can then read/write to
 GpuParamVec2Core myVectorParam;
 GpuParamVec2Core myVectorParam;
@@ -247,9 +243,11 @@ After creation use @ref BansheeEngine::RenderAPICore::setComputePipeline "Render
 Since compute pipeline doesn't support render targets, you will want to use load-store textures for output. An example of a simple compute pipeline:
 Since compute pipeline doesn't support render targets, you will want to use load-store textures for output. An example of a simple compute pipeline:
 ~~~~~~~~~~~~~{.cpp}
 ~~~~~~~~~~~~~{.cpp}
 SPtr<GpuProgramCore> computeProgram = ...;
 SPtr<GpuProgramCore> computeProgram = ...;
-SPtr<GpuParamsCore> computeGpuParams = ...;
 
 
 SPtr<ComputePipelineStateCore> state = ComputePipelineStateCore::create(computeProgram);
 SPtr<ComputePipelineStateCore> state = ComputePipelineStateCore::create(computeProgram);
+SPtr<GpuParamsCore> computeGpuParams = GpuParamsCore::create(state);
+
+... optionally set some parameters ...
 
 
 RenderAPICore& rapi = RenderAPICore::instance();
 RenderAPICore& rapi = RenderAPICore::instance();
 rapi.setComputePipeline(state);
 rapi.setComputePipeline(state);

+ 25 - 3
Source/BansheeCore/Include/BsGpuParams.h

@@ -238,7 +238,18 @@ namespace BansheeEngine
 		virtual ~GpuParamsCore() { }
 		virtual ~GpuParamsCore() { }
 
 
 		/** 
 		/** 
-		 * @copydoc GpuParams::create 
+		 * @copydoc GpuParams::create(const SPtr<GraphicsPipelineState>&)
+		 * @param[in]	deviceMask		Mask that determines on which GPU devices should the buffer be created on.
+		 */
+		static SPtr<GpuParamsCore> create(const SPtr<GraphicsPipelineStateCore>& pipelineState,
+										  GpuDeviceFlags deviceMask = GDF_DEFAULT);
+
+		/** @copydoc GpuParams::create(const SPtr<ComputePipelineStateCore>&) */
+		static SPtr<GpuParamsCore> create(const SPtr<ComputePipelineStateCore>& pipelineState,
+										  GpuDeviceFlags deviceMask = GDF_DEFAULT);
+
+		/** 
+		 * @copydoc GpuParams::create(const SPtr<ComputePipelineStateCore>&)
 		 * @param[in]	deviceMask		Mask that determines on which GPU devices should the buffer be created on.
 		 * @param[in]	deviceMask		Mask that determines on which GPU devices should the buffer be created on.
 		 */
 		 */
 		static SPtr<GpuParamsCore> create(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 		static SPtr<GpuParamsCore> create(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
@@ -278,9 +289,20 @@ namespace BansheeEngine
 		SPtr<GpuParamsCore> getCore() const;
 		SPtr<GpuParamsCore> getCore() const;
 
 
 		/**
 		/**
-		 * Creates new GpuParams object using the specified parameter description.
+		 * Creates new GpuParams object that can serve for changing the GPU program parameters on the specified pipeline.
 		 *
 		 *
-		 * @param[in]	paramInfo	Object containing parameter descriptions for all relevant GPU program stages.
+		 * @param[in]	pipelineState	Pipeline state for which this object can set parameters for.
+		 * @return						New GpuParams object.
+		 */
+		static SPtr<GpuParams> create(const SPtr<GraphicsPipelineState>& pipelineState);
+
+		/** @copydoc GpuParams::create(const SPtr<GraphicsPipelineState>&) */
+		static SPtr<GpuParams> create(const SPtr<ComputePipelineState>& pipelineState);
+
+		/** 
+		 * Creates a new set of GPU parameters using an object describing the parameters for a pipeline.
+		 * 
+		 * @param[in]	paramInfo	Description of GPU parameters for a specific GPU pipeline state.
 		 */
 		 */
 		static SPtr<GpuParams> create(const SPtr<GpuPipelineParamInfo>& paramInfo);
 		static SPtr<GpuParams> create(const SPtr<GpuPipelineParamInfo>& paramInfo);
 
 

+ 11 - 3
Source/BansheeCore/Include/BsGpuPipelineParamInfo.h

@@ -42,6 +42,9 @@ namespace BansheeEngine
 		/** Returns the number of sets for the specified parameter type. */
 		/** Returns the number of sets for the specified parameter type. */
 		UINT32 getNumSets(ParamType type) { return mNumSets[(int)type]; }
 		UINT32 getNumSets(ParamType type) { return mNumSets[(int)type]; }
 
 
+		/** Returns the total number of elements across all sets. */
+		UINT32 getNumElements() const { return mTotalNumElements; }
+
 		/** Returns the number of elements in all sets for the specified parameter type. */
 		/** Returns the number of elements in all sets for the specified parameter type. */
 		UINT32 getNumElements(ParamType type) { return mNumElements[(int)type]; }
 		UINT32 getNumElements(ParamType type) { return mNumElements[(int)type]; }
 
 
@@ -67,6 +70,7 @@ namespace BansheeEngine
 		std::array<SPtr<GpuParamDesc>, 6> mParamDescs;
 		std::array<SPtr<GpuParamDesc>, 6> mParamDescs;
 
 
 		UINT32 mTotalNumSets;
 		UINT32 mTotalNumSets;
+		UINT32 mTotalNumElements;
 		UINT32 mNumSets[(int)ParamType::Count];
 		UINT32 mNumSets[(int)ParamType::Count];
 		UINT32 mNumElements[(int)ParamType::Count];
 		UINT32 mNumElements[(int)ParamType::Count];
 		UINT32* mOffsets[(int)ParamType::Count];
 		UINT32* mOffsets[(int)ParamType::Count];
@@ -80,13 +84,17 @@ namespace BansheeEngine
 	public:
 	public:
 		virtual ~GpuPipelineParamInfoCore() { }
 		virtual ~GpuPipelineParamInfoCore() { }
 
 
-		/** @copydoc GpuPipelineParamInfo::create */
-		static SPtr<GpuPipelineParamInfoCore> create(const GPU_PIPELINE_PARAMS_DESC& desc);
+		/** 
+		 * @copydoc GpuPipelineParamInfo::create 
+		 * @param[in]	deviceMask		Mask that determines on which GPU devices should the buffer be created on.
+		 */
+		static SPtr<GpuPipelineParamInfoCore> create(const GPU_PIPELINE_PARAMS_DESC& desc,
+													 GpuDeviceFlags deviceMask = GDF_DEFAULT);
 
 
 	protected:
 	protected:
 		friend class RenderStateCoreManager;
 		friend class RenderStateCoreManager;
 
 
-		GpuPipelineParamInfoCore(const GPU_PIPELINE_PARAMS_DESC& desc);
+		GpuPipelineParamInfoCore(const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask);
 	};
 	};
 
 
 	/** Holds meta-data about a set of GPU parameters used by a single pipeline state. */
 	/** Holds meta-data about a set of GPU parameters used by a single pipeline state. */

+ 3 - 11
Source/BansheeCore/Include/BsHardwareBufferManager.h

@@ -69,11 +69,7 @@ namespace BansheeEngine
 		 */
 		 */
 		SPtr<VertexDeclaration> createVertexDeclaration(const SPtr<VertexDataDesc>& desc);
 		SPtr<VertexDeclaration> createVertexDeclaration(const SPtr<VertexDataDesc>& desc);
 
 
-		/** 
-		 * Creates a new GpuParams object.
-		 *  
-		 * @param[in]	paramInfo	Object containing parameter descriptions for all relevant GPU program stages.
-		 */
+		/** @copydoc GpuParams::create(const SPtr<GpuPipelineParamInfo>&) */
 		SPtr<GpuParams> createGpuParams(const SPtr<GpuPipelineParamInfo>& paramInfo);
 		SPtr<GpuParams> createGpuParams(const SPtr<GpuPipelineParamInfo>& paramInfo);
 	};
 	};
 
 
@@ -128,13 +124,9 @@ namespace BansheeEngine
 		 */
 		 */
 		SPtr<GpuBufferCore> createGpuBuffer(const GPU_BUFFER_DESC& desc, GpuDeviceFlags deviceMask = GDF_DEFAULT);
 		SPtr<GpuBufferCore> createGpuBuffer(const GPU_BUFFER_DESC& desc, GpuDeviceFlags deviceMask = GDF_DEFAULT);
 
 
-		/** 
-		 * @copydoc HardwareBufferManager::createGpuParams 
-		 * @param[in]	deviceMask		Mask that determines on which GPU devices should the object be created on.
-		 */
+		/** @copydoc GpuParamsCore::create(const SPtr<GpuPipelineParamInfoCore>&) */
 		SPtr<GpuParamsCore> createGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 		SPtr<GpuParamsCore> createGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 											GpuDeviceFlags deviceMask = GDF_DEFAULT);
 											GpuDeviceFlags deviceMask = GDF_DEFAULT);
-
 	protected:
 	protected:
 		friend class IndexBuffer;
 		friend class IndexBuffer;
 		friend class IndexBufferCore;
 		friend class IndexBufferCore;
@@ -167,7 +159,7 @@ namespace BansheeEngine
 
 
 		/** @copydoc createGpuParams */
 		/** @copydoc createGpuParams */
 		virtual SPtr<GpuParamsCore> createGpuParamsInternal(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 		virtual SPtr<GpuParamsCore> createGpuParamsInternal(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
-			GpuDeviceFlags deviceMask = GDF_DEFAULT);
+															GpuDeviceFlags deviceMask = GDF_DEFAULT);
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 5 - 3
Source/BansheeCore/Include/BsRenderStateManager.h

@@ -163,8 +163,9 @@ namespace BansheeEngine
 		SPtr<ComputePipelineStateCore> createComputePipelineState(const SPtr<GpuProgramCore>& program,
 		SPtr<ComputePipelineStateCore> createComputePipelineState(const SPtr<GpuProgramCore>& program,
 			GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 			GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 
 
-		/**	Creates and initializes a new GpuPipelineParamInfoCore. */
-		SPtr<GpuPipelineParamInfoCore> createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc) const;
+		/** @copydoc GpuPipelineParamInfoCore::create */
+		SPtr<GpuPipelineParamInfoCore> createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc,
+															   GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 
 
 		/** Creates an uninitialized sampler state. Requires manual initialization after creation. */
 		/** Creates an uninitialized sampler state. Requires manual initialization after creation. */
 		SPtr<SamplerStateCore> _createSamplerState(const SAMPLER_STATE_DESC& desc, 
 		SPtr<SamplerStateCore> _createSamplerState(const SAMPLER_STATE_DESC& desc, 
@@ -188,7 +189,8 @@ namespace BansheeEngine
 																		   GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 																		   GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 
 
 		/**	Creates an uninitialized GpuPipelineParamInfoCore. Requires manual initialization after creation. */
 		/**	Creates an uninitialized GpuPipelineParamInfoCore. Requires manual initialization after creation. */
-		virtual SPtr<GpuPipelineParamInfoCore> _createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc) const;
+		virtual SPtr<GpuPipelineParamInfoCore> _createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc,
+																		GpuDeviceFlags deviceMask = GDF_DEFAULT) const;
 
 
 		/** Gets a sampler state initialized with default options. */
 		/** Gets a sampler state initialized with default options. */
 		const SPtr<SamplerStateCore>& getDefaultSamplerState() const;
 		const SPtr<SamplerStateCore>& getDefaultSamplerState() const;

+ 21 - 0
Source/BansheeCore/Source/BsGpuParams.cpp

@@ -4,6 +4,7 @@
 #include "BsGpuParamDesc.h"
 #include "BsGpuParamDesc.h"
 #include "BsGpuParamBlockBuffer.h"
 #include "BsGpuParamBlockBuffer.h"
 #include "BsGpuPipelineParamInfo.h"
 #include "BsGpuPipelineParamInfo.h"
+#include "BsGpuPipelineState.h"
 #include "BsVector2.h"
 #include "BsVector2.h"
 #include "BsTexture.h"
 #include "BsTexture.h"
 #include "BsGpuBuffer.h"
 #include "BsGpuBuffer.h"
@@ -625,6 +626,16 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	SPtr<GpuParamsCore> GpuParamsCore::create(const SPtr<GraphicsPipelineStateCore>& pipelineState, GpuDeviceFlags deviceMask)
+	{
+		return HardwareBufferCoreManager::instance().createGpuParams(pipelineState->getParamInfo(), deviceMask);
+	}
+
+	SPtr<GpuParamsCore> GpuParamsCore::create(const SPtr<ComputePipelineStateCore>& pipelineState, GpuDeviceFlags deviceMask)
+	{
+		return HardwareBufferCoreManager::instance().createGpuParams(pipelineState->getParamInfo(), deviceMask);
+	}
+
 	SPtr<GpuParamsCore> GpuParamsCore::create(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
 	SPtr<GpuParamsCore> GpuParamsCore::create(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
 	{
 	{
 		return HardwareBufferCoreManager::instance().createGpuParams(paramInfo, deviceMask);
 		return HardwareBufferCoreManager::instance().createGpuParams(paramInfo, deviceMask);
@@ -665,6 +676,16 @@ namespace BansheeEngine
 		markListenerResourcesDirty();
 		markListenerResourcesDirty();
 	}
 	}
 
 
+	SPtr<GpuParams> GpuParams::create(const SPtr<GraphicsPipelineState>& pipelineState)
+	{
+		return HardwareBufferManager::instance().createGpuParams(pipelineState->getParamInfo());
+	}
+
+	SPtr<GpuParams> GpuParams::create(const SPtr<ComputePipelineState>& pipelineState)
+	{
+		return HardwareBufferManager::instance().createGpuParams(pipelineState->getParamInfo());
+	}
+
 	SPtr<GpuParams> GpuParams::create(const SPtr<GpuPipelineParamInfo>& paramInfo)
 	SPtr<GpuParams> GpuParams::create(const SPtr<GpuPipelineParamInfo>& paramInfo)
 	{
 	{
 		return HardwareBufferManager::instance().createGpuParams(paramInfo);
 		return HardwareBufferManager::instance().createGpuParams(paramInfo);

+ 2 - 2
Source/BansheeCore/Source/BsGpuParamsSet.cpp

@@ -477,11 +477,11 @@ namespace BansheeEngine
 
 
 			GraphicsPipelineStateType gfxPipeline = curPass->getGraphicsPipelineState();
 			GraphicsPipelineStateType gfxPipeline = curPass->getGraphicsPipelineState();
 			if(gfxPipeline != nullptr)
 			if(gfxPipeline != nullptr)
-				mPassParams[i] = GpuParamsType::create(gfxPipeline->getParamInfo());
+				mPassParams[i] = GpuParamsType::create(gfxPipeline);
 			else
 			else
 			{
 			{
 				ComputePipelineStateType computePipeline = curPass->getComputePipelineState();
 				ComputePipelineStateType computePipeline = curPass->getComputePipelineState();
-				mPassParams[i] = GpuParamsType::create(computePipeline->getParamInfo());
+				mPassParams[i] = GpuParamsType::create(computePipeline);
 			}
 			}
 		}
 		}
 
 

+ 7 - 4
Source/BansheeCore/Source/BsGpuPipelineParamInfo.cpp

@@ -7,7 +7,7 @@
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	GpuPipelineParamInfoBase::GpuPipelineParamInfoBase(const GPU_PIPELINE_PARAMS_DESC& desc)
 	GpuPipelineParamInfoBase::GpuPipelineParamInfoBase(const GPU_PIPELINE_PARAMS_DESC& desc)
-		:mTotalNumSets(0)
+		:mTotalNumSets(0), mTotalNumElements(0)
 	{
 	{
 		mParamDescs[GPT_FRAGMENT_PROGRAM] = desc.fragmentParams;
 		mParamDescs[GPT_FRAGMENT_PROGRAM] = desc.fragmentParams;
 		mParamDescs[GPT_VERTEX_PROGRAM] = desc.vertexParams;
 		mParamDescs[GPT_VERTEX_PROGRAM] = desc.vertexParams;
@@ -121,6 +121,8 @@ namespace BansheeEngine
 		{
 		{
 			for (UINT32 j = 0; j < mNumSets[i]; j++)
 			for (UINT32 j = 0; j < mNumSets[i]; j++)
 				mNumElements[i] += slotsPerSet[i][j];
 				mNumElements[i] += slotsPerSet[i][j];
+
+			mTotalNumElements += mNumElements[i];
 		}
 		}
 
 
 		UINT32 setOffsetsSize = sizeof(UINT32) * totalNumSets;
 		UINT32 setOffsetsSize = sizeof(UINT32) * totalNumSets;
@@ -182,13 +184,14 @@ namespace BansheeEngine
 		return globalSlot;
 		return globalSlot;
 	}
 	}
 
 
-	GpuPipelineParamInfoCore::GpuPipelineParamInfoCore(const GPU_PIPELINE_PARAMS_DESC& desc)
+	GpuPipelineParamInfoCore::GpuPipelineParamInfoCore(const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask)
 		:GpuPipelineParamInfoBase(desc)
 		:GpuPipelineParamInfoBase(desc)
 	{ }
 	{ }
 
 
-	SPtr<GpuPipelineParamInfoCore> GpuPipelineParamInfoCore::create(const GPU_PIPELINE_PARAMS_DESC& desc)
+	SPtr<GpuPipelineParamInfoCore> GpuPipelineParamInfoCore::create(const GPU_PIPELINE_PARAMS_DESC& desc, 
+		GpuDeviceFlags deviceMask)
 	{
 	{
-		return RenderStateCoreManager::instance().createPipelineParamInfo(desc);
+		return RenderStateCoreManager::instance().createPipelineParamInfo(desc, deviceMask);
 	}
 	}
 
 
 	GpuPipelineParamInfo::GpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc)
 	GpuPipelineParamInfo::GpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc)

+ 66 - 38
Source/BansheeCore/Source/BsGpuPipelineState.cpp

@@ -24,17 +24,6 @@ namespace BansheeEngine
 		output.domainProgram = input.domainProgram != nullptr ? input.domainProgram->getCore() : nullptr;
 		output.domainProgram = input.domainProgram != nullptr ? input.domainProgram->getCore() : nullptr;
 	}
 	}
 
 
-	SPtr<GpuParamDesc> getParamDesc(const SPtr<GpuProgram>& program)
-	{
-		program->blockUntilCoreInitialized();
-		return program->getParamDesc();
-	}
-
-	SPtr<GpuParamDesc> getParamDesc(const SPtr<GpuProgramCore>& program)
-	{
-		return program->getParamDesc();
-	}
-
 	template<bool Core>
 	template<bool Core>
 	TGraphicsPipelineState<Core>::TGraphicsPipelineState()
 	TGraphicsPipelineState<Core>::TGraphicsPipelineState()
 	{ }
 	{ }
@@ -42,33 +31,33 @@ namespace BansheeEngine
 	template<bool Core>
 	template<bool Core>
 	TGraphicsPipelineState<Core>::TGraphicsPipelineState(const StateDescType& data)
 	TGraphicsPipelineState<Core>::TGraphicsPipelineState(const StateDescType& data)
 		:mData(data)
 		:mData(data)
+	{ }
+
+	template class TGraphicsPipelineState < false > ;
+	template class TGraphicsPipelineState < true >;
+
+	GraphicsPipelineStateCore::GraphicsPipelineStateCore(const PIPELINE_STATE_CORE_DESC& desc, GpuDeviceFlags deviceMask)
+		:TGraphicsPipelineState(desc)
 	{
 	{
 		GPU_PIPELINE_PARAMS_DESC paramsDesc;
 		GPU_PIPELINE_PARAMS_DESC paramsDesc;
-		if (data.vertexProgram != nullptr)
-			paramsDesc.vertexParams = getParamDesc(data.vertexProgram);
+		if (desc.vertexProgram != nullptr)
+			paramsDesc.vertexParams = desc.vertexProgram->getParamDesc();
 
 
-		if (data.fragmentProgram != nullptr)
-			paramsDesc.fragmentParams = getParamDesc(data.fragmentProgram);
+		if (desc.fragmentProgram != nullptr)
+			paramsDesc.fragmentParams = desc.fragmentProgram->getParamDesc();
 
 
-		if (data.geometryProgram != nullptr)
-			paramsDesc.geometryParams = getParamDesc(data.geometryProgram);
+		if (desc.geometryProgram != nullptr)
+			paramsDesc.geometryParams = desc.geometryProgram->getParamDesc();
 
 
-		if (data.hullProgram != nullptr)
-			paramsDesc.hullParams = getParamDesc(data.hullProgram);
+		if (desc.hullProgram != nullptr)
+			paramsDesc.hullParams = desc.hullProgram->getParamDesc();
 
 
-		if (data.domainProgram != nullptr)
-			paramsDesc.domainParams = getParamDesc(data.domainProgram);
+		if (desc.domainProgram != nullptr)
+			paramsDesc.domainParams = desc.domainProgram->getParamDesc();
 
 
-		mParamInfo = GpuPipelineParamInfoType::create(paramsDesc);
+		mParamInfo = GpuPipelineParamInfoCore::create(paramsDesc, deviceMask);
 	}
 	}
 
 
-	template class TGraphicsPipelineState < false > ;
-	template class TGraphicsPipelineState < true >;
-
-	GraphicsPipelineStateCore::GraphicsPipelineStateCore(const PIPELINE_STATE_CORE_DESC& desc, GpuDeviceFlags deviceMask)
-		:TGraphicsPipelineState(desc)
-	{ }
-
 	SPtr<GraphicsPipelineStateCore> GraphicsPipelineStateCore::create(const PIPELINE_STATE_CORE_DESC& desc, GpuDeviceFlags deviceMask)
 	SPtr<GraphicsPipelineStateCore> GraphicsPipelineStateCore::create(const PIPELINE_STATE_CORE_DESC& desc, GpuDeviceFlags deviceMask)
 	{
 	{
 		return RenderStateCoreManager::instance().createGraphicsPipelineState(desc, deviceMask);
 		return RenderStateCoreManager::instance().createGraphicsPipelineState(desc, deviceMask);
@@ -76,7 +65,40 @@ namespace BansheeEngine
 
 
 	GraphicsPipelineState::GraphicsPipelineState(const PIPELINE_STATE_DESC& desc)
 	GraphicsPipelineState::GraphicsPipelineState(const PIPELINE_STATE_DESC& desc)
 		:TGraphicsPipelineState(desc)
 		:TGraphicsPipelineState(desc)
-	{ }
+	{
+		GPU_PIPELINE_PARAMS_DESC paramsDesc;
+		if (desc.vertexProgram != nullptr)
+		{
+			desc.vertexProgram->blockUntilCoreInitialized();
+			paramsDesc.vertexParams = desc.vertexProgram->getParamDesc();
+		}
+
+		if (desc.fragmentProgram != nullptr)
+		{
+			desc.fragmentProgram->blockUntilCoreInitialized();
+			paramsDesc.fragmentParams = desc.fragmentProgram->getParamDesc();
+		}
+
+		if (desc.geometryProgram != nullptr)
+		{
+			desc.geometryProgram->blockUntilCoreInitialized();
+			paramsDesc.geometryParams = desc.geometryProgram->getParamDesc();
+		}
+
+		if (desc.hullProgram != nullptr)
+		{
+			desc.hullProgram->blockUntilCoreInitialized();
+			paramsDesc.hullParams = desc.hullProgram->getParamDesc();
+		}
+		
+		if (desc.domainProgram != nullptr)
+		{
+			desc.domainProgram->blockUntilCoreInitialized();
+			paramsDesc.domainParams = desc.domainProgram->getParamDesc();
+		}
+
+		mParamInfo = GpuPipelineParamInfo::create(paramsDesc);
+	}
 
 
 	SPtr<GraphicsPipelineStateCore> GraphicsPipelineState::getCore() const
 	SPtr<GraphicsPipelineStateCore> GraphicsPipelineState::getCore() const
 	{
 	{
@@ -103,19 +125,19 @@ namespace BansheeEngine
 	template<bool Core>
 	template<bool Core>
 	TComputePipelineState<Core>::TComputePipelineState(const GpuProgramType& program)
 	TComputePipelineState<Core>::TComputePipelineState(const GpuProgramType& program)
 		:mProgram(program)
 		:mProgram(program)
-	{
-		GPU_PIPELINE_PARAMS_DESC paramsDesc;
-		paramsDesc.computeParams = getParamDesc(program);
-
-		mParamInfo = GpuPipelineParamInfoType::create(paramsDesc);
-	}
+	{ }
 
 
 	template class TComputePipelineState < false >;
 	template class TComputePipelineState < false >;
 	template class TComputePipelineState < true >;
 	template class TComputePipelineState < true >;
 
 
 	ComputePipelineStateCore::ComputePipelineStateCore(const SPtr<GpuProgramCore>& program, GpuDeviceFlags deviceMask)
 	ComputePipelineStateCore::ComputePipelineStateCore(const SPtr<GpuProgramCore>& program, GpuDeviceFlags deviceMask)
 		:TComputePipelineState(program)
 		:TComputePipelineState(program)
-	{ }
+	{
+		GPU_PIPELINE_PARAMS_DESC paramsDesc;
+		paramsDesc.computeParams = program->getParamDesc();
+
+		mParamInfo = GpuPipelineParamInfoCore::create(paramsDesc, deviceMask);
+	}
 
 
 	SPtr<ComputePipelineStateCore> ComputePipelineStateCore::create(const SPtr<GpuProgramCore>& program, 
 	SPtr<ComputePipelineStateCore> ComputePipelineStateCore::create(const SPtr<GpuProgramCore>& program, 
 		GpuDeviceFlags deviceMask)
 		GpuDeviceFlags deviceMask)
@@ -125,7 +147,13 @@ namespace BansheeEngine
 
 
 	ComputePipelineState::ComputePipelineState(const SPtr<GpuProgram>& program)
 	ComputePipelineState::ComputePipelineState(const SPtr<GpuProgram>& program)
 		:TComputePipelineState(program)
 		:TComputePipelineState(program)
-	{ }
+	{
+		GPU_PIPELINE_PARAMS_DESC paramsDesc;
+		program->blockUntilCoreInitialized();
+		paramsDesc.computeParams = program->getParamDesc();
+
+		mParamInfo = GpuPipelineParamInfo::create(paramsDesc);
+	}
 
 
 	SPtr<ComputePipelineStateCore> ComputePipelineState::getCore() const
 	SPtr<ComputePipelineStateCore> ComputePipelineState::getCore() const
 	{
 	{

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

@@ -100,7 +100,7 @@ namespace BansheeEngine
 	}
 	}
 
 
 	SPtr<GpuParamsCore> HardwareBufferCoreManager::createGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 	SPtr<GpuParamsCore> HardwareBufferCoreManager::createGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
-		GpuDeviceFlags deviceMask)
+																   GpuDeviceFlags deviceMask)
     {
     {
 		SPtr<GpuParamsCore> params = createGpuParamsInternal(paramInfo, deviceMask);
 		SPtr<GpuParamsCore> params = createGpuParamsInternal(paramInfo, deviceMask);
 		params->initialize();
 		params->initialize();
@@ -146,8 +146,8 @@ namespace BansheeEngine
 		return ret;
 		return ret;
 	}
 	}
 
 
-	SPtr<GpuParamsCore> HardwareBufferCoreManager::createGpuParamsInternal(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
-		GpuDeviceFlags deviceMask)
+	SPtr<GpuParamsCore> HardwareBufferCoreManager::createGpuParamsInternal(
+		const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
     {
     {
 		GpuParamsCore* params = new (bs_alloc<GpuParamsCore>()) GpuParamsCore(paramInfo, deviceMask);
 		GpuParamsCore* params = new (bs_alloc<GpuParamsCore>()) GpuParamsCore(paramInfo, deviceMask);
 		SPtr<GpuParamsCore> paramsPtr = bs_shared_ptr<GpuParamsCore>(params);
 		SPtr<GpuParamsCore> paramsPtr = bs_shared_ptr<GpuParamsCore>(params);

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

@@ -232,9 +232,9 @@ namespace BansheeEngine
 	}
 	}
 
 
 	SPtr<GpuPipelineParamInfoCore> RenderStateCoreManager::createPipelineParamInfo(
 	SPtr<GpuPipelineParamInfoCore> RenderStateCoreManager::createPipelineParamInfo(
-		const GPU_PIPELINE_PARAMS_DESC& desc) const
+		const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask) const
 	{
 	{
-		SPtr<GpuPipelineParamInfoCore> paramInfo = _createPipelineParamInfo(desc);
+		SPtr<GpuPipelineParamInfoCore> paramInfo = _createPipelineParamInfo(desc, deviceMask);
 		paramInfo->initialize();
 		paramInfo->initialize();
 
 
 		return paramInfo;
 		return paramInfo;
@@ -330,11 +330,11 @@ namespace BansheeEngine
 	}
 	}
 
 
 	SPtr<GpuPipelineParamInfoCore> RenderStateCoreManager::_createPipelineParamInfo(
 	SPtr<GpuPipelineParamInfoCore> RenderStateCoreManager::_createPipelineParamInfo(
-		const GPU_PIPELINE_PARAMS_DESC& desc) const
+		const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask) const
 	{
 	{
 		SPtr<GpuPipelineParamInfoCore> paramInfo =
 		SPtr<GpuPipelineParamInfoCore> paramInfo =
 			bs_shared_ptr<GpuPipelineParamInfoCore>(new (bs_alloc<GpuPipelineParamInfoCore>())
 			bs_shared_ptr<GpuPipelineParamInfoCore>(new (bs_alloc<GpuPipelineParamInfoCore>())
-													GpuPipelineParamInfoCore(desc));
+													GpuPipelineParamInfoCore(desc, deviceMask));
 
 
 		paramInfo->_setThisPtr(paramInfo);
 		paramInfo->_setThisPtr(paramInfo);
 
 

+ 2 - 0
Source/BansheeVulkanRenderAPI/CMakeSources.cmake

@@ -28,6 +28,7 @@ set(BS_BANSHEEVULKANRENDERAPI_INC_NOFILTER
 	"Include/BsVulkanQueue.h"
 	"Include/BsVulkanQueue.h"
 	"Include/BsVulkanDescriptorSet.h"
 	"Include/BsVulkanDescriptorSet.h"
 	"Include/BsVulkanSamplerState.h"
 	"Include/BsVulkanSamplerState.h"
+	"Include/BsVulkanGpuPipelineParamInfo.h"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_INC_MANAGERS
@@ -73,6 +74,7 @@ set(BS_BANSHEEVULKANRENDERAPI_SRC_NOFILTER
 	"Source/BsVulkanQueue.cpp"
 	"Source/BsVulkanQueue.cpp"
 	"Source/BsVulkanDescriptorSet.cpp"
 	"Source/BsVulkanDescriptorSet.cpp"
 	"Source/BsVulkanSamplerState.cpp"
 	"Source/BsVulkanSamplerState.cpp"
+	"Source/BsVulkanGpuPipelineParamInfo.cpp"
 )
 )
 
 
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS
 set(BS_BANSHEEVULKANRENDERAPI_SRC_MANAGERS

+ 3 - 3
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuParams.h

@@ -57,7 +57,6 @@ namespace BansheeEngine
 		/** All GPU param data related to a single descriptor set. */
 		/** All GPU param data related to a single descriptor set. */
 		struct PerSetData
 		struct PerSetData
 		{
 		{
-			VulkanDescriptorLayout* layout;
 			VulkanDescriptorSet* latestSet;
 			VulkanDescriptorSet* latestSet;
 			Vector<VulkanDescriptorSet*> sets;
 			Vector<VulkanDescriptorSet*> sets;
 
 
@@ -70,14 +69,15 @@ namespace BansheeEngine
 		struct PerDeviceData
 		struct PerDeviceData
 		{
 		{
 			PerSetData* perSetData;
 			PerSetData* perSetData;
-			UINT32 numSets;
-			VkPipelineLayout pipelineLayout;
 		};
 		};
 
 
 		friend class VulkanHardwareBufferCoreManager;
 		friend class VulkanHardwareBufferCoreManager;
 
 
 		VulkanGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask);
 		VulkanGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask);
 
 
+		/** @copydoc GpuParamsCore::initialize */
+		void initialize() override;
+
 		PerDeviceData mPerDeviceData[BS_MAX_DEVICES];
 		PerDeviceData mPerDeviceData[BS_MAX_DEVICES];
 		GpuDeviceFlags mDeviceMask;
 		GpuDeviceFlags mDeviceMask;
 		UINT8* mData;
 		UINT8* mData;

+ 29 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineParamInfo.h

@@ -15,10 +15,38 @@ namespace BansheeEngine
 	class VulkanGpuPipelineParamInfo : public GpuPipelineParamInfoCore
 	class VulkanGpuPipelineParamInfo : public GpuPipelineParamInfoCore
 	{
 	{
 	public:
 	public:
-		VulkanGpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc);
+		VulkanGpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask);
 		~VulkanGpuPipelineParamInfo();
 		~VulkanGpuPipelineParamInfo();
 
 
+		/** Returns the number of bindings present at the layout at the specified index. */
+		UINT32 getNumBindings(UINT32 layoutIdx) const { return mLayoutInfos[layoutIdx].numBindings; }
+
+		/** Returns a pointer to an array of bindings for the layout at the specified index. */
+		VkDescriptorSetLayoutBinding* getBindings(UINT32 layoutIdx) const { return mLayoutInfos[layoutIdx].bindings; }
+
+		/** 
+		 * Returns a layout for the specified device, at the specified index. Returns null if no layout for the specified 
+		 * device index. 
+		 */
+		VulkanDescriptorLayout* getLayout(UINT32 deviceIdx, UINT32 layoutIdx) const;
+
 	private:
 	private:
+		/**	@copydoc GpuPipelineParamInfoCore::initialize */
+		void initialize() override;
+
+		/** Data related to a single descriptor set layout. */
+		struct LayoutInfo
+		{
+			VkDescriptorSetLayoutBinding* bindings;
+			UINT32 numBindings;
+		};
+
+		GpuDeviceFlags mDeviceMask;
+
+		VulkanDescriptorLayout** mLayouts[BS_MAX_DEVICES];
+		LayoutInfo* mLayoutInfos;
+
+		UINT8* mData;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 23 - 5
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuPipelineState.h

@@ -43,17 +43,22 @@ namespace BansheeEngine
 		/** 
 		/** 
 		 * Create a new Vulkan graphics pipeline. 
 		 * Create a new Vulkan graphics pipeline. 
 		 * 
 		 * 
-		 * @param[in]	device				Device to create the pipeline for.
+		 * @param[in]	deviceIdx			Index of the device to create the pipeline for.
 		 * @param[in]	framebuffer			Framebuffer object that defines the surfaces this pipeline will render to.
 		 * @param[in]	framebuffer			Framebuffer object that defines the surfaces this pipeline will render to.
 		 * @param[in]	readOnlyDepth		True if the pipeline is only allowed to read the depth buffer, without writes.
 		 * @param[in]	readOnlyDepth		True if the pipeline is only allowed to read the depth buffer, without writes.
 		 * @param[in]	drawOp				Type of geometry that will be drawn using the pipeline.
 		 * @param[in]	drawOp				Type of geometry that will be drawn using the pipeline.
-		 * @param[in]	pipelineLayout		Layout describing the resources used by the pipeline.
 		 * @param[in]	vertexInputState	State describing inputs to the vertex program.
 		 * @param[in]	vertexInputState	State describing inputs to the vertex program.
 		 * @return							Vulkan graphics pipeline object.
 		 * @return							Vulkan graphics pipeline object.
 		 */
 		 */
-		VkPipeline createPipeline(VkDevice device, VulkanFramebuffer* framebuffer, bool readOnlyDepth,
-								  DrawOperationType drawOp, VkPipelineLayout pipelineLayout,
-								  VkPipelineVertexInputStateCreateInfo* vertexInputState);
+		VkPipeline createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer, bool readOnlyDepth,
+								  DrawOperationType drawOp, VkPipelineVertexInputStateCreateInfo* vertexInputState);
+
+		/** Contains pipeline data specific to a single Vulkan device. */
+		struct PerDeviceData
+		{
+			VulkanDevice* device;
+			VkPipelineLayout pipelineLayout;
+		};
 
 
 		VkPipelineShaderStageCreateInfo mShaderStageInfos[5];
 		VkPipelineShaderStageCreateInfo mShaderStageInfos[5];
 		VkPipelineInputAssemblyStateCreateInfo mInputAssemblyInfo;
 		VkPipelineInputAssemblyStateCreateInfo mInputAssemblyInfo;
@@ -67,6 +72,9 @@ namespace BansheeEngine
 		VkPipelineDynamicStateCreateInfo mDynamicStateInfo;
 		VkPipelineDynamicStateCreateInfo mDynamicStateInfo;
 		VkDynamicState mDynamicStates[3];
 		VkDynamicState mDynamicStates[3];
 		VkGraphicsPipelineCreateInfo mPipelineInfo;
 		VkGraphicsPipelineCreateInfo mPipelineInfo;
+
+		GpuDeviceFlags mDeviceMask;
+		PerDeviceData mPerDeviceData[BS_MAX_DEVICES];
 	};
 	};
 
 
 	/**	Vulkan implementation of a compute pipeline state. */
 	/**	Vulkan implementation of a compute pipeline state. */
@@ -82,6 +90,16 @@ namespace BansheeEngine
 
 
 		/**	@copydoc ComputePipelineStateCore::initialize */
 		/**	@copydoc ComputePipelineStateCore::initialize */
 		void initialize() override;
 		void initialize() override;
+
+		/** Contains pipeline data specific to a single Vulkan device. */
+		struct PerDeviceData
+		{
+			VulkanDevice* device;
+			VulkanPipeline* pipeline;
+		};
+
+		GpuDeviceFlags mDeviceMask;
+		PerDeviceData mPerDeviceData[BS_MAX_DEVICES];
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 7 - 3
Source/BansheeVulkanRenderAPI/Include/BsVulkanGpuProgram.h

@@ -12,13 +12,17 @@ namespace BansheeEngine
 	 */
 	 */
 
 
 	/**	Abstraction of a Vulkan shader object. */
 	/**	Abstraction of a Vulkan shader object. */
-	class VulkanGpuProgramCore : public GpuProgramCore
+	class 
+	VulkanGpuProgramCore : public GpuProgramCore
 	{
 	{
 	public:
 	public:
 		virtual ~VulkanGpuProgramCore();
 		virtual ~VulkanGpuProgramCore();
 
 
-		/** Returns a handle to the Vulkan shader module. */
-		VkShaderModule getHandle() const;
+		/** 
+		 * Returns a handle to the Vulkan shader module, on the specified device. If program device mask doesn't 
+		 * include the provided device, null is returned.  
+		 */
+		VkShaderModule getHandle(UINT32 deviceIdx) const;
 
 
 	protected:
 	protected:
 		friend class VulkanGLSLProgramFactory;
 		friend class VulkanGLSLProgramFactory;

+ 1 - 1
Source/BansheeVulkanRenderAPI/Include/BsVulkanHardwareBufferManager.h

@@ -36,7 +36,7 @@ namespace BansheeEngine
 
 
 		/** @copydoc HardwareBufferCoreManager::createGpuParamsInternal */
 		/** @copydoc HardwareBufferCoreManager::createGpuParamsInternal */
 		SPtr<GpuParamsCore> createGpuParamsInternal(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
 		SPtr<GpuParamsCore> createGpuParamsInternal(const SPtr<GpuPipelineParamInfoCore>& paramInfo,
-			GpuDeviceFlags deviceMask = GDF_DEFAULT) override;
+													GpuDeviceFlags deviceMask = GDF_DEFAULT) override;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

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

@@ -28,7 +28,8 @@ namespace BansheeEngine
 			GpuDeviceFlags deviceMask = GDF_DEFAULT) const override;
 			GpuDeviceFlags deviceMask = GDF_DEFAULT) const override;
 
 
 		/** @copydoc RenderStateCoreManager::_createPipelineParamInfo */
 		/** @copydoc RenderStateCoreManager::_createPipelineParamInfo */
-		SPtr<GpuPipelineParamInfoCore> _createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc) const override;
+		SPtr<GpuPipelineParamInfoCore> _createPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc,
+			 GpuDeviceFlags deviceMask = GDF_DEFAULT) const override;
 	};
 	};
 
 
 	/** @} */
 	/** @} */

+ 52 - 155
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuParams.cpp

@@ -11,7 +11,7 @@
 #include "BsVulkanDescriptorSet.h"
 #include "BsVulkanDescriptorSet.h"
 #include "BsVulkanDescriptorLayout.h"
 #include "BsVulkanDescriptorLayout.h"
 #include "BsVulkanSamplerState.h"
 #include "BsVulkanSamplerState.h"
-#include "BsVulkanGpuBuffer.h"
+#include "BsVulkanGpuPipelineParamInfo.h"
 #include "BsVulkanCommandBuffer.h"
 #include "BsVulkanCommandBuffer.h"
 #include "BsGpuParamDesc.h"
 #include "BsGpuParamDesc.h"
 
 
@@ -20,126 +20,47 @@ namespace BansheeEngine
 	VulkanGpuParams::VulkanGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
 	VulkanGpuParams::VulkanGpuParams(const SPtr<GpuPipelineParamInfoCore>& paramInfo, GpuDeviceFlags deviceMask)
 		: GpuParamsCore(paramInfo, deviceMask), mPerDeviceData(), mDeviceMask(deviceMask), mData(nullptr), mSetsDirty(nullptr)
 		: GpuParamsCore(paramInfo, deviceMask), mPerDeviceData(), mDeviceMask(deviceMask), mData(nullptr), mSetsDirty(nullptr)
 	{
 	{
-		// Generate all required bindings
-		UINT32 numBindings = 0;
-		UINT32 numSets = 0;
+		
+	}
 
 
-		UINT32 numElementTypes = (UINT32)ElementType::Count;
-		for (UINT32 i = 0; i < numElementTypes; i++)
+	VulkanGpuParams::~VulkanGpuParams()
+	{
 		{
 		{
-			numBindings += mNumElements[i];
-			numSets = std::max(numSets, mNumSets[i]);
-		}
-
-		UINT32 bindingsPerSetBytes = sizeof(UINT32) * numSets;
-		UINT32* bindingsPerSet = (UINT32*)bs_stack_alloc(bindingsPerSetBytes);
-
-		UINT32 bindingsSize = sizeof(VkDescriptorSetLayoutBinding) * numBindings;
-		VkDescriptorSetLayoutBinding* bindings = (VkDescriptorSetLayoutBinding*)bs_stack_alloc(bindingsSize);
-		memset(bindings, 0, bindingsSize);
+			Lock lock(mMutex);
 
 
-		UINT32 globalBindingIdx = 0;
-		for (UINT32 i = 0; i < numSets; i++)
-		{
-			bindingsPerSet[i] = 0;
-			for (UINT32 j = 0; j < numElementTypes; j++)
+			UINT32 numSets = mParamInfo->getNumSets();
+			for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
 			{
 			{
-				if (i >= mNumSets[j])
-					continue;
-
-				UINT32 start = mOffsets[j][i];
-
-				UINT32 end;
-				if (i < (mNumSets[j] - 1))
-					end = mOffsets[j][i + 1];
-				else
-					end = mNumElements[j];
-
-				UINT32 elementsInSet = end - start;
-				for (UINT32 k = 0; k < elementsInSet; k++)
+				for (UINT32 j = 0; j < numSets; j++)
 				{
 				{
-					VkDescriptorSetLayoutBinding& binding = bindings[globalBindingIdx + k];
-					binding.binding = bindingsPerSet[i] + k;
+					for (auto& entry : mPerDeviceData[i].perSetData[j].sets)
+						entry->destroy();
 				}
 				}
-
-				globalBindingIdx += elementsInSet;
-				bindingsPerSet[i] += elementsInSet;
 			}
 			}
 		}
 		}
 
 
-		UINT32* bindingOffsets = (UINT32*)bs_stack_alloc(sizeof(UINT32) * numSets);
-		if (numSets > 0)
-		{
-			bindingOffsets[0] = 0;
-
-			for (UINT32 i = 1; i < numSets; i++)
-				bindingOffsets[i] = bindingsPerSet[i - 1];
-		}
-
-		VkShaderStageFlags stageFlagsLookup[6];
-		stageFlagsLookup[GPT_VERTEX_PROGRAM] = VK_SHADER_STAGE_VERTEX_BIT;
-		stageFlagsLookup[GPT_HULL_PROGRAM] = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
-		stageFlagsLookup[GPT_DOMAIN_PROGRAM] = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
-		stageFlagsLookup[GPT_GEOMETRY_PROGRAM] = VK_SHADER_STAGE_GEOMETRY_BIT;
-		stageFlagsLookup[GPT_FRAGMENT_PROGRAM] = VK_SHADER_STAGE_FRAGMENT_BIT;
-		stageFlagsLookup[GPT_COMPUTE_PROGRAM] = VK_SHADER_STAGE_COMPUTE_BIT;
-
-		UINT32 numParamDescs = sizeof(mParamDescs) / sizeof(mParamDescs[0]);
-		for (UINT32 i = 0; i < numParamDescs; i++)
-		{
-			const SPtr<GpuParamDesc>& paramDesc = mParamDescs[i];
-			if (paramDesc == nullptr)
-				continue;
-
-			auto setUpBindings = [&](auto& params, VkDescriptorType descType)
-			{
-				for (auto& entry : params)
-				{
-					UINT32 bindingIdx = bindingOffsets[entry.second.set] + entry.second.slot;
-
-					VkDescriptorSetLayoutBinding& binding = bindings[bindingIdx];
-					binding.descriptorCount = 1;
-					binding.stageFlags |= stageFlagsLookup[i];
-					binding.descriptorType = descType;
-				}
-			};
-
-			// Note: Assuming all textures and samplers use the same set/slot combination, and that they're combined
-			setUpBindings(paramDesc->paramBlocks, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
-			setUpBindings(paramDesc->textures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
-			setUpBindings(paramDesc->loadStoreTextures, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
-			setUpBindings(paramDesc->samplers, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
-
-			// Set up buffer bindings
-			for (auto& entry : paramDesc->buffers)
-			{
-				bool isLoadStore = entry.second.type != GPOT_BYTE_BUFFER &&
-					entry.second.type != GPOT_STRUCTURED_BUFFER;
-
-				UINT32 bindingIdx = bindingOffsets[entry.second.set] + entry.second.slot;
+		bs_free(mData); // Everything allocated under a single buffer to a single free is enough
+	}
 
 
-				VkDescriptorSetLayoutBinding& binding = bindings[bindingIdx];
-				binding.descriptorCount = 1;
-				binding.stageFlags |= stageFlagsLookup[i];
-				binding.descriptorType = isLoadStore ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
-			}
-		}
+	void VulkanGpuParams::initialize()
+	{
+		VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
 
 
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
 		VulkanDevice* devices[BS_MAX_DEVICES];
 		VulkanDevice* devices[BS_MAX_DEVICES];
 
 
-		// Allocate layouts per-device
+		VulkanUtility::getDevices(rapi, mDeviceMask, devices);
+
 		UINT32 numDevices = 0;
 		UINT32 numDevices = 0;
 		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
 		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
 		{
 		{
-			if (VulkanUtility::isDeviceIdxSet(rapi, i, deviceMask))
-				devices[i] = rapi._getDevice(i).get();
-			else
-				devices[i] = nullptr;
-
-			numDevices++;
+			if (devices != nullptr)
+				numDevices++;
 		}
 		}
 
 
+		UINT32 numSets = vkParamInfo.getNumSets();
+		UINT32 numBindings = vkParamInfo.getNumElements();
+
 		// Note: I'm assuming a single WriteInfo per binding, but if arrays sizes larger than 1 are eventually supported
 		// Note: I'm assuming a single WriteInfo per binding, but if arrays sizes larger than 1 are eventually supported
 		// I'll need to adjust the code.
 		// I'll need to adjust the code.
 		UINT32 setsDirtyBytes = sizeof(bool) * numSets;
 		UINT32 setsDirtyBytes = sizeof(bool) * numSets;
@@ -155,27 +76,22 @@ namespace BansheeEngine
 		memset(mSetsDirty, 1, setsDirtyBytes);
 		memset(mSetsDirty, 1, setsDirtyBytes);
 		dataIter += setsDirtyBytes;
 		dataIter += setsDirtyBytes;
 
 
-		for(UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
 		{
 		{
-			if(devices[i] == nullptr)
+			if (devices[i] == nullptr)
 			{
 			{
-				mPerDeviceData[i].numSets = 0;
 				mPerDeviceData[i].perSetData = nullptr;
 				mPerDeviceData[i].perSetData = nullptr;
 
 
 				continue;
 				continue;
 			}
 			}
 
 
-			mPerDeviceData[i].numSets = numSets;
 			mPerDeviceData[i].perSetData = (PerSetData*)dataIter;
 			mPerDeviceData[i].perSetData = (PerSetData*)dataIter;
 			dataIter += sizeof(perSetBytes);
 			dataIter += sizeof(perSetBytes);
 
 
 			VulkanDescriptorManager& descManager = devices[i]->getDescriptorManager();
 			VulkanDescriptorManager& descManager = devices[i]->getDescriptorManager();
-			VulkanDescriptorLayout** layouts = (VulkanDescriptorLayout**)bs_stack_alloc(numSets * sizeof(VulkanDescriptorLayout*));
-
-			UINT32 bindingOffset = 0;
 			for (UINT32 j = 0; j < numSets; j++)
 			for (UINT32 j = 0; j < numSets; j++)
 			{
 			{
-				UINT32 numBindingsPerSet = bindingsPerSet[j];
+				UINT32 numBindingsPerSet = vkParamInfo.getNumBindings(j);
 
 
 				PerSetData& perSetData = mPerDeviceData[i].perSetData[j];
 				PerSetData& perSetData = mPerDeviceData[i].perSetData[j];
 				perSetData.writeSetInfos = (VkWriteDescriptorSet*)dataIter;
 				perSetData.writeSetInfos = (VkWriteDescriptorSet*)dataIter;
@@ -184,22 +100,20 @@ namespace BansheeEngine
 				perSetData.writeInfos = (WriteInfo*)dataIter;
 				perSetData.writeInfos = (WriteInfo*)dataIter;
 				dataIter += sizeof(WriteInfo) * numBindingsPerSet;
 				dataIter += sizeof(WriteInfo) * numBindingsPerSet;
 
 
-				VkDescriptorSetLayoutBinding* perSetBindings = &bindings[bindingOffset];
-				perSetData.layout = descManager.getLayout(perSetBindings, numBindingsPerSet);
+				VulkanDescriptorLayout* layout = vkParamInfo.getLayout(i, j);
 				perSetData.numElements = numBindingsPerSet;
 				perSetData.numElements = numBindingsPerSet;
-				perSetData.latestSet = descManager.createSet(perSetData.layout);
+				perSetData.latestSet = descManager.createSet(layout);
 				perSetData.sets.push_back(perSetData.latestSet);
 				perSetData.sets.push_back(perSetData.latestSet);
 
 
-				layouts[j] = perSetData.layout;
-
-				for(UINT32 k = 0; k < numBindingsPerSet; k++)
+				VkDescriptorSetLayoutBinding* perSetBindings = vkParamInfo.getBindings(j);
+				for (UINT32 k = 0; k < numBindingsPerSet; k++)
 				{
 				{
 					// Note: Instead of using one structure per binding, it's possible to update multiple at once
 					// Note: Instead of using one structure per binding, it's possible to update multiple at once
 					// by specifying larger descriptorCount, if they all share type and shader stages.
 					// by specifying larger descriptorCount, if they all share type and shader stages.
 					VkWriteDescriptorSet& writeSetInfo = perSetData.writeSetInfos[k];
 					VkWriteDescriptorSet& writeSetInfo = perSetData.writeSetInfos[k];
 					writeSetInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 					writeSetInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
 					writeSetInfo.pNext = nullptr;
 					writeSetInfo.pNext = nullptr;
-					writeSetInfo.dstSet = VK_NULL_HANDLE; // TODO
+					writeSetInfo.dstSet = VK_NULL_HANDLE;
 					writeSetInfo.dstBinding = perSetBindings[k].binding;
 					writeSetInfo.dstBinding = perSetBindings[k].binding;
 					writeSetInfo.dstArrayElement = 0;
 					writeSetInfo.dstArrayElement = 0;
 					writeSetInfo.descriptorCount = perSetBindings[k].descriptorCount;
 					writeSetInfo.descriptorCount = perSetBindings[k].descriptorCount;
@@ -209,7 +123,7 @@ namespace BansheeEngine
 						writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
 						writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE ||
 						writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
 						writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
 
 
-					if(isImage)
+					if (isImage)
 					{
 					{
 						bool isLoadStore = writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
 						bool isLoadStore = writeSetInfo.descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
 
 
@@ -242,36 +156,10 @@ namespace BansheeEngine
 						writeSetInfo.pImageInfo = nullptr;
 						writeSetInfo.pImageInfo = nullptr;
 					}
 					}
 				}
 				}
-
-				bindingOffset += numBindingsPerSet;
-			}
-
-			mPerDeviceData[i].pipelineLayout = descManager.getPipelineLayout(layouts, numSets);
-
-			bs_stack_free(layouts);
-		}
-
-		bs_stack_free(bindingOffsets);
-		bs_stack_free(bindings);
-		bs_stack_free(bindingsPerSet);
-	}
-
-	VulkanGpuParams::~VulkanGpuParams()
-	{
-		{
-			Lock lock(mMutex);
-
-			for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
-			{
-				for (UINT32 j = 0; j < mPerDeviceData[i].numSets; j++)
-				{
-					for (auto& entry : mPerDeviceData[i].perSetData[j].sets)
-						entry->destroy();
-				}
 			}
 			}
 		}
 		}
 
 
-		bs_free(mData); // Everything allocated under a single buffer to a single free is enough
+		GpuParamsCore::initialize();
 	}
 	}
 
 
 	void VulkanGpuParams::setParamBlockBuffer(UINT32 set, UINT32 slot, const SPtr<GpuParamBlockBufferCore>& paramBlockBuffer)
 	void VulkanGpuParams::setParamBlockBuffer(UINT32 set, UINT32 slot, const SPtr<GpuParamBlockBufferCore>& paramBlockBuffer)
@@ -409,11 +297,18 @@ namespace BansheeEngine
 		if (perDeviceData.perSetData == nullptr)
 		if (perDeviceData.perSetData == nullptr)
 			return;
 			return;
 
 
+		UINT32 numParamBlocks = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::ParamBlock);
+		UINT32 numTextures = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::Texture);
+		UINT32 numStorageTextures = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::LoadStoreTexture);
+		UINT32 numBuffers = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::Buffer);
+		UINT32 numSamplers = mParamInfo->getNumElements(GpuPipelineParamInfo::ParamType::SamplerState);
+		UINT32 numSets = mParamInfo->getNumSets();
+
 		// Registers resources with the command buffer
 		// Registers resources with the command buffer
 		// Note: Makes the assumption that this object (and all of the resources it holds) are externally locked, and will
 		// Note: Makes the assumption that this object (and all of the resources it holds) are externally locked, and will
 		// not be modified on another thread while being bound.
 		// not be modified on another thread while being bound.
 		VulkanCmdBuffer* internalCB = buffer.getInternal();
 		VulkanCmdBuffer* internalCB = buffer.getInternal();
-		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::ParamBlock]; i++)
+		for (UINT32 i = 0; i < numParamBlocks; i++)
 		{
 		{
 			if (mParamBlockBuffers[i] == nullptr)
 			if (mParamBlockBuffers[i] == nullptr)
 				continue;
 				continue;
@@ -424,7 +319,7 @@ namespace BansheeEngine
 			internalCB->registerResource(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
 			internalCB->registerResource(resource, VK_ACCESS_UNIFORM_READ_BIT, VulkanUseFlag::Read);
 		}
 		}
 
 
-		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::Buffer]; i++)
+		for (UINT32 i = 0; i < numBuffers; i++)
 		{
 		{
 			if (mBuffers[i] == nullptr)
 			if (mBuffers[i] == nullptr)
 				continue;
 				continue;
@@ -444,7 +339,7 @@ namespace BansheeEngine
 			internalCB->registerResource(resource, accessFlags, useFlags);
 			internalCB->registerResource(resource, accessFlags, useFlags);
 		}
 		}
 
 
-		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::SamplerState]; i++)
+		for (UINT32 i = 0; i < numSamplers; i++)
 		{
 		{
 			if (mSamplerStates[i] == nullptr)
 			if (mSamplerStates[i] == nullptr)
 				continue;
 				continue;
@@ -458,7 +353,7 @@ namespace BansheeEngine
 			internalCB->registerResource(resource, VulkanUseFlag::Read);
 			internalCB->registerResource(resource, VulkanUseFlag::Read);
 		}
 		}
 
 
-		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::LoadStoreTexture]; i++)
+		for (UINT32 i = 0; i < numStorageTextures; i++)
 		{
 		{
 			if (mLoadStoreTextures[i] == nullptr)
 			if (mLoadStoreTextures[i] == nullptr)
 				continue;
 				continue;
@@ -483,7 +378,7 @@ namespace BansheeEngine
 			internalCB->registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, range, useFlags);
 			internalCB->registerResource(resource, accessFlags, VK_IMAGE_LAYOUT_GENERAL, range, useFlags);
 		}
 		}
 
 
-		for (UINT32 i = 0; i < mNumElements[(UINT32)ElementType::Texture]; i++)
+		for (UINT32 i = 0; i < numTextures; i++)
 		{
 		{
 			if (mTextures[i] == nullptr)
 			if (mTextures[i] == nullptr)
 				continue;
 				continue;
@@ -515,9 +410,10 @@ namespace BansheeEngine
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
 		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
 		VulkanDevice& device = *rapi._getDevice(deviceIdx);
 		VulkanDevice& device = *rapi._getDevice(deviceIdx);
 		VulkanDescriptorManager& descManager = device.getDescriptorManager();
 		VulkanDescriptorManager& descManager = device.getDescriptorManager();
+		VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
 
 
 		Lock(mMutex);
 		Lock(mMutex);
-		for (UINT32 i = 0; i < perDeviceData.numSets; i++)
+		for (UINT32 i = 0; i < numSets; i++)
 		{
 		{
 			PerSetData& perSetData = perDeviceData.perSetData[i];
 			PerSetData& perSetData = perDeviceData.perSetData[i];
 
 
@@ -540,7 +436,8 @@ namespace BansheeEngine
 				}
 				}
 
 
 				// Cannot find an empty set, allocate a new one
 				// Cannot find an empty set, allocate a new one
-				perSetData.latestSet = descManager.createSet(perSetData.layout);
+				VulkanDescriptorLayout* layout = vkParamInfo.getLayout(deviceIdx, i);
+				perSetData.latestSet = descManager.createSet(layout);
 				perSetData.sets.push_back(perSetData.latestSet);
 				perSetData.sets.push_back(perSetData.latestSet);
 			}
 			}
 
 
@@ -570,8 +467,8 @@ namespace BansheeEngine
 			return;
 			return;
 		}
 		}
 
 
-		VkDescriptorSet* sets = bs_stack_alloc<VkDescriptorSet>(perDeviceData.numSets);
-		for (UINT32 i = 0; i < perDeviceData.numSets; i++)
+		VkDescriptorSet* sets = bs_stack_alloc<VkDescriptorSet>(numSets);
+		for (UINT32 i = 0; i < numSets; i++)
 		{
 		{
 			VulkanDescriptorSet* set = perDeviceData.perSetData[i].latestSet;
 			VulkanDescriptorSet* set = perDeviceData.perSetData[i].latestSet;
 
 
@@ -580,7 +477,7 @@ namespace BansheeEngine
 		}
 		}
 
 
 		vkCmdBindDescriptorSets(vkCB, bindPoint, perDeviceData.pipelineLayout, 0, 
 		vkCmdBindDescriptorSets(vkCB, bindPoint, perDeviceData.pipelineLayout, 0, 
-			perDeviceData.numSets, sets, 0, nullptr);
+			numSets, sets, 0, nullptr);
 
 
 		bs_stack_free(sets);
 		bs_stack_free(sets);
 	}
 	}

+ 153 - 5
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineParamInfo.cpp

@@ -1,17 +1,165 @@
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //********************************** Banshee Engine (www.banshee3d.com) **************************************************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 //**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
 #include "BsVulkanGpuPipelineParamInfo.h"
 #include "BsVulkanGpuPipelineParamInfo.h"
+#include "BsVulkanUtility.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanDevice.h"
+#include "BsGpuParamDesc.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
-	VulkanGpuPipelineParamInfo::VulkanGpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc)
-		:GpuPipelineParamInfoCore(desc)
+	VulkanGpuPipelineParamInfo::VulkanGpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask)
+		:GpuPipelineParamInfoCore(desc, deviceMask), mDeviceMask(deviceMask), mLayouts(), mLayoutInfos(), mData(nullptr)
+	{ }
+
+	VulkanGpuPipelineParamInfo::~VulkanGpuPipelineParamInfo()
 	{
 	{
-		// TODO - Will get called from main thread!
+		bs_free(mData);
 	}
 	}
 
 
-	VulkanGpuPipelineParamInfo::~VulkanGpuPipelineParamInfo()
+	void VulkanGpuPipelineParamInfo::initialize()
 	{
 	{
-		
+		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
+
+		VulkanDevice* devices[BS_MAX_DEVICES];
+		VulkanUtility::getDevices(rapi, mDeviceMask, devices);
+
+		UINT32 numDevices = 0;
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (devices != nullptr)
+				numDevices++;
+		}
+
+		UINT32 numElementTypes = (UINT32)ParamType::Count;
+		UINT32 bindingsSize = sizeof(VkDescriptorSetLayoutBinding) * mTotalNumElements;
+		UINT32 layoutInfoSize = sizeof(LayoutInfo) * mTotalNumSets;
+		UINT32 layoutsSize = sizeof(VulkanDescriptorLayout*) * mTotalNumSets;
+
+		mData = (UINT8*)bs_alloc(bindingsSize + layoutInfoSize + layoutsSize * numDevices);
+		UINT8* dataPtr = mData;
+
+		mLayoutInfos = (LayoutInfo*)dataPtr;
+		dataPtr += layoutInfoSize;
+
+		VkDescriptorSetLayoutBinding* bindings = (VkDescriptorSetLayoutBinding*)dataPtr;
+		dataPtr += bindingsSize;
+
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (devices[i] == nullptr)
+			{
+				mLayouts[i] = nullptr;
+				continue;
+			}
+
+			mLayouts[i] = (VulkanDescriptorLayout**)dataPtr;
+			dataPtr += layoutsSize;
+		}
+
+		memset(bindings, 0, bindingsSize);
+
+		UINT32 globalBindingIdx = 0;
+		for (UINT32 i = 0; i < mTotalNumSets; i++)
+		{
+			mLayoutInfos[i].numBindings = 0;
+			mLayoutInfos[i].bindings = nullptr;
+
+			for (UINT32 j = 0; j < numElementTypes; j++)
+			{
+				if (i >= mNumSets[j])
+					continue;
+				
+				UINT32 start = mOffsets[j][i];
+
+				UINT32 end;
+				if (i < (mNumSets[j] - 1))
+					end = mOffsets[j][i + 1];
+				else
+					end = mNumElements[j];
+
+				UINT32 elementsInSet = end - start;
+				for (UINT32 k = 0; k < elementsInSet; k++)
+				{
+					VkDescriptorSetLayoutBinding& binding = bindings[globalBindingIdx + k];
+					binding.binding = mLayoutInfos[i].numBindings + k;
+				}
+
+				globalBindingIdx += elementsInSet;
+
+				mLayoutInfos[i].numBindings += elementsInSet;
+			}
+		}
+
+		UINT32 offset = 0;
+		for (UINT32 i = 0; i < mTotalNumSets; i++)
+		{
+			mLayoutInfos[i].bindings = &bindings[offset];
+			offset += mLayoutInfos[i].numBindings;
+		}
+
+		VkShaderStageFlags stageFlagsLookup[6];
+		stageFlagsLookup[GPT_VERTEX_PROGRAM] = VK_SHADER_STAGE_VERTEX_BIT;
+		stageFlagsLookup[GPT_HULL_PROGRAM] = VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT;
+		stageFlagsLookup[GPT_DOMAIN_PROGRAM] = VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT;
+		stageFlagsLookup[GPT_GEOMETRY_PROGRAM] = VK_SHADER_STAGE_GEOMETRY_BIT;
+		stageFlagsLookup[GPT_FRAGMENT_PROGRAM] = VK_SHADER_STAGE_FRAGMENT_BIT;
+		stageFlagsLookup[GPT_COMPUTE_PROGRAM] = VK_SHADER_STAGE_COMPUTE_BIT;
+
+		UINT32 numParamDescs = sizeof(mParamDescs) / sizeof(mParamDescs[0]);
+		for (UINT32 i = 0; i < numParamDescs; i++)
+		{
+			const SPtr<GpuParamDesc>& paramDesc = mParamDescs[i];
+			if (paramDesc == nullptr)
+				continue;
+
+			auto setUpBindings = [&](auto& params, VkDescriptorType descType)
+			{
+				for (auto& entry : params)
+				{
+					VkDescriptorSetLayoutBinding& binding = mLayoutInfos[entry.second.set].bindings[entry.second.slot];
+					binding.descriptorCount = 1;
+					binding.stageFlags |= stageFlagsLookup[i];
+					binding.descriptorType = descType;
+				}
+			};
+
+			// Note: Assuming all textures and samplers use the same set/slot combination, and that they're combined
+			setUpBindings(paramDesc->paramBlocks, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
+			setUpBindings(paramDesc->textures, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+			setUpBindings(paramDesc->loadStoreTextures, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE);
+			setUpBindings(paramDesc->samplers, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
+
+			// Set up buffer bindings
+			for (auto& entry : paramDesc->buffers)
+			{
+				bool isLoadStore = entry.second.type != GPOT_BYTE_BUFFER &&
+					entry.second.type != GPOT_STRUCTURED_BUFFER;
+
+				VkDescriptorSetLayoutBinding& binding = mLayoutInfos[entry.second.set].bindings[entry.second.slot];
+				binding.descriptorCount = 1;
+				binding.stageFlags |= stageFlagsLookup[i];
+				binding.descriptorType = isLoadStore ? VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER : VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER;
+			}
+		}
+
+		// Allocate layouts per-device
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (mLayouts[i] == nullptr)
+				continue;
+
+			VulkanDescriptorManager& descManager = devices[i]->getDescriptorManager();
+			for (UINT32 j = 0; j < mTotalNumSets; j++)
+				mLayouts[i][j] = descManager.getLayout(mLayoutInfos[j].bindings, mLayoutInfos[j].numBindings);
+		}
+	}
+
+	VulkanDescriptorLayout* VulkanGpuPipelineParamInfo::getLayout(UINT32 deviceIdx, UINT32 layoutIdx) const
+	{
+		if (deviceIdx >= BS_MAX_DEVICES || mLayouts[deviceIdx] == nullptr)
+			return nullptr;
+
+		return mLayouts[deviceIdx][layoutIdx];
 	}
 	}
 }
 }

+ 132 - 8
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuPipelineState.cpp

@@ -5,6 +5,8 @@
 #include "BsVulkanGpuProgram.h"
 #include "BsVulkanGpuProgram.h"
 #include "BsVulkanFramebuffer.h"
 #include "BsVulkanFramebuffer.h"
 #include "BsVulkanUtility.h"
 #include "BsVulkanUtility.h"
+#include "BsVulkanRenderAPI.h"
+#include "BsVulkanGpuPipelineParamInfo.h"
 #include "BsRasterizerState.h"
 #include "BsRasterizerState.h"
 #include "BsDepthStencilState.h"
 #include "BsDepthStencilState.h"
 #include "BsBlendState.h"
 #include "BsBlendState.h"
@@ -21,8 +23,9 @@ namespace BansheeEngine
 		vkDestroyPipeline(mOwner->getDevice().getLogical(), mPipeline, gVulkanAllocator);
 		vkDestroyPipeline(mOwner->getDevice().getLogical(), mPipeline, gVulkanAllocator);
 	}
 	}
 
 
-	VulkanGraphicsPipelineStateCore::VulkanGraphicsPipelineStateCore(const PIPELINE_STATE_CORE_DESC& desc, GpuDeviceFlags deviceMask)
-		:GraphicsPipelineStateCore(desc, deviceMask)
+	VulkanGraphicsPipelineStateCore::VulkanGraphicsPipelineStateCore(const PIPELINE_STATE_CORE_DESC& desc,
+																	 GpuDeviceFlags deviceMask)
+		:GraphicsPipelineStateCore(desc, deviceMask), mDeviceMask(deviceMask)
 	{
 	{
 		
 		
 	}
 	}
@@ -30,6 +33,8 @@ namespace BansheeEngine
 	VulkanGraphicsPipelineStateCore::~VulkanGraphicsPipelineStateCore()
 	VulkanGraphicsPipelineStateCore::~VulkanGraphicsPipelineStateCore()
 	{
 	{
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_PipelineState);
 		BS_INC_RENDER_STAT_CAT(ResDestroyed, RenderStatObject_PipelineState);
+
+		// TODO - Destroy pipeline
 	}
 	}
 
 
 	void VulkanGraphicsPipelineStateCore::initialize()
 	void VulkanGraphicsPipelineStateCore::initialize()
@@ -56,7 +61,7 @@ namespace BansheeEngine
 			stageCI.pNext = nullptr;
 			stageCI.pNext = nullptr;
 			stageCI.flags = 0;
 			stageCI.flags = 0;
 			stageCI.stage = stages[i].first;
 			stageCI.stage = stages[i].first;
-			stageCI.module = program->getHandle();
+			stageCI.module = VK_NULL_HANDLE;
 			stageCI.pName = program->getProperties().getEntryPoint().c_str();
 			stageCI.pName = program->getProperties().getEntryPoint().c_str();
 			stageCI.pSpecializationInfo = nullptr;
 			stageCI.pSpecializationInfo = nullptr;
 
 
@@ -205,13 +210,42 @@ namespace BansheeEngine
 		mPipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 		mPipelineInfo.basePipelineHandle = VK_NULL_HANDLE;
 		mPipelineInfo.basePipelineIndex = -1;
 		mPipelineInfo.basePipelineIndex = -1;
 
 
+		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
+
+		VulkanDevice* devices[BS_MAX_DEVICES];
+		VulkanUtility::getDevices(rapi, mDeviceMask, devices);
+
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			mPerDeviceData[i].device = devices[i];
+
+			if (devices[i] != nullptr)
+			{
+				VulkanDescriptorManager& descManager = mPerDeviceData[i].device->getDescriptorManager();
+				VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
+
+				UINT32 numLayouts = vkParamInfo.getNumSets();
+				VulkanDescriptorLayout** layouts = (VulkanDescriptorLayout**)bs_stack_alloc(sizeof(VulkanDescriptorLayout*) * numLayouts);
+
+				for (UINT32 j = 0; j < numLayouts; j++)
+					layouts[j] = vkParamInfo.getLayout(i, j);
+
+				mPerDeviceData[i].pipelineLayout = descManager.getPipelineLayout(layouts, numLayouts);
+
+				bs_stack_free(layouts);
+			}
+			else
+			{
+				mPerDeviceData[i].pipelineLayout = VK_NULL_HANDLE;
+			}
+		}
+
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_PipelineState);
 		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_PipelineState);
 		GraphicsPipelineStateCore::initialize();
 		GraphicsPipelineStateCore::initialize();
 	}
 	}
 
 
-	VkPipeline VulkanGraphicsPipelineStateCore::createPipeline(VkDevice device, VulkanFramebuffer* framebuffer,
+	VkPipeline VulkanGraphicsPipelineStateCore::createPipeline(UINT32 deviceIdx, VulkanFramebuffer* framebuffer,
 														  bool readOnlyDepth, DrawOperationType drawOp,
 														  bool readOnlyDepth, DrawOperationType drawOp,
-														  VkPipelineLayout pipelineLayout,
 														  VkPipelineVertexInputStateCreateInfo* vertexInputState)
 														  VkPipelineVertexInputStateCreateInfo* vertexInputState)
 	{
 	{
 		mInputAssemblyInfo.topology = VulkanUtility::getDrawOp(drawOp);
 		mInputAssemblyInfo.topology = VulkanUtility::getDrawOp(drawOp);
@@ -246,7 +280,7 @@ namespace BansheeEngine
 		}
 		}
 
 
 		mPipelineInfo.renderPass = framebuffer->getRenderPass();
 		mPipelineInfo.renderPass = framebuffer->getRenderPass();
-		mPipelineInfo.layout = pipelineLayout;
+		mPipelineInfo.layout = mPerDeviceData[deviceIdx].pipelineLayout;
 		mPipelineInfo.pVertexInputState = vertexInputState;
 		mPipelineInfo.pVertexInputState = vertexInputState;
 
 
 		if (framebuffer->hasDepthAttachment())
 		if (framebuffer->hasDepthAttachment())
@@ -259,6 +293,31 @@ namespace BansheeEngine
 		else
 		else
 			mPipelineInfo.pColorBlendState = nullptr;
 			mPipelineInfo.pColorBlendState = nullptr;
 
 
+		std::pair<VkShaderStageFlagBits, GpuProgramCore*> stages[] =
+		{
+			{ VK_SHADER_STAGE_VERTEX_BIT, mData.vertexProgram.get() },
+			{ VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT, mData.hullProgram.get() },
+			{ VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, mData.domainProgram.get() },
+			{ VK_SHADER_STAGE_GEOMETRY_BIT, mData.geometryProgram.get() },
+			{ VK_SHADER_STAGE_FRAGMENT_BIT, mData.fragmentProgram.get() }
+		};
+
+		UINT32 stageOutputIdx = 0;
+		UINT32 numStages = sizeof(stages) / sizeof(stages[0]);
+		for (UINT32 i = 0; i < numStages; i++)
+		{
+			VulkanGpuProgramCore* program = static_cast<VulkanGpuProgramCore*>(stages[i].second);
+			if (program == nullptr)
+				continue;
+
+			VkPipelineShaderStageCreateInfo& stageCI = mShaderStageInfos[stageOutputIdx];
+			stageCI.module = program->getHandle(deviceIdx);
+
+			stageOutputIdx++;
+		}
+
+		VkDevice device = mPerDeviceData[deviceIdx].device->getLogical();
+
 		VkPipeline pipeline;
 		VkPipeline pipeline;
 		VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &mPipelineInfo, gVulkanAllocator, &pipeline);
 		VkResult result = vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, &mPipelineInfo, gVulkanAllocator, &pipeline);
 		assert(result != VK_SUCCESS);
 		assert(result != VK_SUCCESS);
@@ -277,16 +336,81 @@ namespace BansheeEngine
 
 
 	VulkanComputePipelineStateCore::VulkanComputePipelineStateCore(const SPtr<GpuProgramCore>& program, 
 	VulkanComputePipelineStateCore::VulkanComputePipelineStateCore(const SPtr<GpuProgramCore>& program, 
 		GpuDeviceFlags deviceMask)
 		GpuDeviceFlags deviceMask)
-		:ComputePipelineStateCore(program, deviceMask)
+		:ComputePipelineStateCore(program, deviceMask), mDeviceMask(deviceMask)
 	{ }
 	{ }
 
 
 	VulkanComputePipelineStateCore::~VulkanComputePipelineStateCore()
 	VulkanComputePipelineStateCore::~VulkanComputePipelineStateCore()
 	{
 	{
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			if (mPerDeviceData[i].device == nullptr)
+				continue;
 
 
+			mPerDeviceData[i].pipeline->destroy();
+		}
 	}
 	}
 
 
 	void VulkanComputePipelineStateCore::initialize()
 	void VulkanComputePipelineStateCore::initialize()
 	{
 	{
-		// TODO
+		VulkanGpuProgramCore* vkProgram = static_cast<VulkanGpuProgramCore*>(mProgram.get());
+
+		VkPipelineShaderStageCreateInfo stageCI;
+		stageCI.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
+		stageCI.pNext = nullptr;
+		stageCI.flags = 0;
+		stageCI.stage = VK_SHADER_STAGE_COMPUTE_BIT;
+		stageCI.module = VK_NULL_HANDLE;
+		stageCI.pName = vkProgram->getProperties().getEntryPoint().c_str();
+		stageCI.pSpecializationInfo = nullptr;
+
+		VkComputePipelineCreateInfo pipelineCI;
+		pipelineCI.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
+		pipelineCI.pNext = nullptr;
+		pipelineCI.flags = 0;
+		pipelineCI.stage = stageCI;
+		pipelineCI.basePipelineHandle = VK_NULL_HANDLE;
+		pipelineCI.basePipelineIndex = -1;
+
+		VulkanRenderAPI& rapi = static_cast<VulkanRenderAPI&>(RenderAPICore::instance());
+
+		VulkanDevice* devices[BS_MAX_DEVICES];
+		VulkanUtility::getDevices(rapi, mDeviceMask, devices);
+
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
+		{
+			mPerDeviceData[i].device = devices[i];
+
+			if (devices[i] != nullptr)
+			{
+				VulkanDescriptorManager& descManager = devices[i]->getDescriptorManager();
+				VulkanResourceManager& rescManager = devices[i]->getResourceManager();
+				VulkanGpuPipelineParamInfo& vkParamInfo = static_cast<VulkanGpuPipelineParamInfo&>(*mParamInfo);
+
+				UINT32 numLayouts = vkParamInfo.getNumSets();
+				VulkanDescriptorLayout** layouts = (VulkanDescriptorLayout**)bs_stack_alloc(sizeof(VulkanDescriptorLayout*) * numLayouts);
+
+				for (UINT32 j = 0; j < numLayouts; j++)
+					layouts[j] = vkParamInfo.getLayout(i, j);
+
+				stageCI.module = vkProgram->getHandle(i);
+				pipelineCI.layout = descManager.getPipelineLayout(layouts, numLayouts);
+
+				VkPipeline pipeline;
+				VkResult result = vkCreateComputePipelines(devices[i]->getLogical(), VK_NULL_HANDLE, 1, &pipelineCI,
+														   gVulkanAllocator, &pipeline);
+				assert(result != VK_SUCCESS);
+
+
+				mPerDeviceData[i].pipeline = rescManager.create<VulkanPipeline>(pipeline);
+				bs_stack_free(layouts);
+			}
+			else
+			{
+				mPerDeviceData[i].pipeline = nullptr;
+			}
+		}
+
+		BS_INC_RENDER_STAT_CAT(ResCreated, RenderStatObject_PipelineState);
+		ComputePipelineStateCore::initialize();
 	}
 	}
 }
 }

+ 1 - 1
Source/BansheeVulkanRenderAPI/Source/BsVulkanGpuProgram.cpp

@@ -35,7 +35,7 @@ namespace BansheeEngine
 		GpuProgramCore::initialize();
 		GpuProgramCore::initialize();
 	}
 	}
 
 
-	VkShaderModule VulkanGpuProgramCore::getHandle() const
+	VkShaderModule VulkanGpuProgramCore::getHandle(UINT32 deviceIdx) const
 	{
 	{
 		// TODO
 		// TODO
 		return VK_NULL_HANDLE;
 		return VK_NULL_HANDLE;

+ 2 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanRenderStateManager.cpp

@@ -40,11 +40,11 @@ namespace BansheeEngine
 	}
 	}
 
 
 	SPtr<GpuPipelineParamInfoCore> VulkanRenderStateCoreManager::_createPipelineParamInfo(
 	SPtr<GpuPipelineParamInfoCore> VulkanRenderStateCoreManager::_createPipelineParamInfo(
-		const GPU_PIPELINE_PARAMS_DESC& desc) const
+		const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask) const
 	{
 	{
 		SPtr<VulkanGpuPipelineParamInfo> paramInfo =
 		SPtr<VulkanGpuPipelineParamInfo> paramInfo =
 			bs_shared_ptr<VulkanGpuPipelineParamInfo>(new (bs_alloc<VulkanGpuPipelineParamInfo>())
 			bs_shared_ptr<VulkanGpuPipelineParamInfo>(new (bs_alloc<VulkanGpuPipelineParamInfo>())
-													  VulkanGpuPipelineParamInfo(desc));
+													  VulkanGpuPipelineParamInfo(desc, deviceMask));
 		paramInfo->_setThisPtr(paramInfo);
 		paramInfo->_setThisPtr(paramInfo);
 
 
 		return paramInfo;
 		return paramInfo;

+ 8 - 2
Source/BansheeVulkanRenderAPI/Source/BsVulkanUtility.cpp

@@ -438,10 +438,16 @@ namespace BansheeEngine
 
 
 	void VulkanUtility::getDevices(const VulkanRenderAPI& rapi, GpuDeviceFlags flags, VulkanDevice*(&devices)[BS_MAX_DEVICES])
 	void VulkanUtility::getDevices(const VulkanRenderAPI& rapi, GpuDeviceFlags flags, VulkanDevice*(&devices)[BS_MAX_DEVICES])
 	{
 	{
-		UINT32 numDevices = std::min(BS_MAX_DEVICES, rapi._getNumDevices());
+		UINT32 numDevices = rapi._getNumDevices();
 
 
-		for (UINT32 i = 0; i < numDevices; i++)
+		for (UINT32 i = 0; i < BS_MAX_DEVICES; i++)
 		{
 		{
+			if(i >= numDevices)
+			{
+				devices[i] = nullptr;
+				continue;
+			}
+
 			VulkanDevice* device = rapi._getDevice(i).get();
 			VulkanDevice* device = rapi._getDevice(i).get();
 
 
 			if (isDeviceIdxSet(rapi, i, flags))
 			if (isDeviceIdxSet(rapi, i, flags))