diff --git a/Components/Terrain/src/OgreTerrainMaterialGeneratorA.cpp b/Components/Terrain/src/OgreTerrainMaterialGeneratorA.cpp index 3bbec6ae748..d07809837d6 100644 --- a/Components/Terrain/src/OgreTerrainMaterialGeneratorA.cpp +++ b/Components/Terrain/src/OgreTerrainMaterialGeneratorA.cpp @@ -253,17 +253,7 @@ namespace Ogre } // clear everything mat->removeAllTechniques(); - - // Automatically disable normal & parallax mapping if card cannot handle it - // We do this rather than having a specific technique for it since it's simpler - auto rsc = Root::getSingletonPtr()->getRenderSystem()->getCapabilities(); - if (getRequiredLayers(terrain, mPSSM) > rsc->getNumTextureUnits()) - { - setLayerNormalMappingEnabled(false); - setLayerParallaxMappingEnabled(false); - LogManager::getSingleton().logWarning( - "TerrainMaterialGeneratorA: Normal mapping disabled due to lack of texture units"); - } + Pass* pass; pass = mat->createTechnique()->createPass(); @@ -271,6 +261,28 @@ namespace Ogre pass->setSpecular(ColourValue::White); pass->setShininess(32); // user param + // Automatically disable normal & parallax mapping if card cannot handle it + // We do this rather than having a specific technique for it since it's simpler + auto rsc = Root::getSingletonPtr()->getRenderSystem()->getCapabilities(); + const uint8 requiredLayers = getRequiredLayers(terrain, mPSSM); + + if( requiredLayers > rsc->getNumTextureUnits() ) + { + if( requiredLayers <= rsc->getNumTextureUnitsWide() ) + { + // fits in the wider profile — no need to disable features + pass->setDescriptorProfileHint( DescriptorProfileHint::AllUnits ); + } + else + { + // still doesn't fit even with the widest available profile — fall back as before + setLayerNormalMappingEnabled(false); + setLayerParallaxMappingEnabled(false); + LogManager::getSingleton().logWarning( + "TerrainMaterialGeneratorA: Normal mapping disabled due to lack of texture units"); + } + } + if(mLayerSpecularMappingEnabled) { // we use this to inject our specular map diff --git a/OgreMain/include/OgrePass.h b/OgreMain/include/OgrePass.h index 29a33170dce..209babcce43 100644 --- a/OgreMain/include/OgrePass.h +++ b/OgreMain/include/OgrePass.h @@ -57,6 +57,11 @@ namespace Ogre { IS_UNKNOWN }; + enum class DescriptorProfileHint + { + Default, AllUnits, Compute + }; + /** Class defining a single pass of a Technique (of a Material): a single rendering call. If a pass does not explicitly use a vertex or fragment shader, %Ogre will calculate @@ -238,6 +243,8 @@ namespace Ogre { PolygonMode mPolygonMode; /// Illumination stage? IlluminationStage mIlluminationStage; + /// Descriptor set profile hint + DescriptorProfileHint mDescriptorProfileHint; Light::LightTypes mOnlyLightType; FogMode mFogMode; @@ -1474,6 +1481,15 @@ namespace Ogre { void setIlluminationStage(IlluminationStage is) { mIlluminationStage = is; } /// Get the manually assigned illumination stage, if any IlluminationStage getIlluminationStage() const { return mIlluminationStage; } + + /// Manually set the descriptor set profile hint for Vulkan + void setDescriptorProfileHint(DescriptorProfileHint hint) + { + mDescriptorProfileHint = hint; + } + /// Get the manually assigned descriptor set profile hint + DescriptorProfileHint getDescriptorProfileHint() const { return mDescriptorProfileHint; } + /** There are some default hash functions used to order passes so that render state changes are minimised, this enumerates them. */ diff --git a/OgreMain/include/OgreRenderSystem.h b/OgreMain/include/OgreRenderSystem.h index 8d3000cb8cf..2f25143ecfb 100644 --- a/OgreMain/include/OgreRenderSystem.h +++ b/OgreMain/include/OgreRenderSystem.h @@ -777,6 +777,14 @@ namespace Ogre /** Sets how to rasterise triangles, as points, wireframe or solid polys. */ virtual void _setPolygonMode(PolygonMode level) = 0; + /** Sets profile hints for layout */ + virtual void _setPassHints( const Pass* pass ) {} + + virtual ushort _getCurrentPassNumTextureUnits() const + { + return getCapabilities()->getNumTextureUnits(); + } + /** This method allows you to set all the stencil buffer parameters in one call. Unlike other render states, stencilling is left for the application to turn diff --git a/OgreMain/include/OgreRenderSystemCapabilities.h b/OgreMain/include/OgreRenderSystemCapabilities.h index 9a62d5659f3..eb88b42ef62 100644 --- a/OgreMain/include/OgreRenderSystemCapabilities.h +++ b/OgreMain/include/OgreRenderSystemCapabilities.h @@ -266,7 +266,7 @@ namespace Ogre typedef std::set ShaderProfiles; private: /// This is used to build a database of RSC's - /// if a RSC with same name, but newer version is introduced, the older one + /// if a RSC with same name, but newer version is introduced, the older one /// will be removed DriverVersion mDriverVersion; /// GPU Vendor @@ -277,6 +277,8 @@ namespace Ogre /// The number of texture units available ushort mNumTextureUnits; + /// The number of texture units available under an alternate profile, with fallback to the regular number + ushort mNumTextureUnitsWide; /// The stencil buffer bit depth ushort mStencilBufferBitDepth; /// Stores the capabilities flags. @@ -308,7 +310,7 @@ namespace Ogre /// The number of vertex attributes available ushort mNumVertexAttributes; - public: + public: RenderSystemCapabilities (); /** Set the driver version. */ @@ -355,17 +357,17 @@ namespace Ogre { if (mDriverVersion.major < v.major) return true; - else if (mDriverVersion.major == v.major && - mDriverVersion.minor < v.minor) + else if (mDriverVersion.major == v.major && + mDriverVersion.minor < v.minor) return true; - else if (mDriverVersion.major == v.major && - mDriverVersion.minor == v.minor && - mDriverVersion.release < v.release) + else if (mDriverVersion.major == v.major && + mDriverVersion.minor == v.minor && + mDriverVersion.release < v.release) return true; - else if (mDriverVersion.major == v.major && - mDriverVersion.minor == v.minor && - mDriverVersion.release == v.release && - mDriverVersion.build < v.build) + else if (mDriverVersion.major == v.major && + mDriverVersion.minor == v.minor && + mDriverVersion.release == v.release && + mDriverVersion.build < v.build) return true; return false; } @@ -373,6 +375,13 @@ namespace Ogre void setNumTextureUnits(ushort num) { mNumTextureUnits = num; + if (mNumTextureUnitsWide == 0) + mNumTextureUnitsWide = num; + } + + void setNumTextureUnitsWide(ushort num) + { + mNumTextureUnitsWide = num; } /// @deprecated do not use @@ -401,12 +410,12 @@ namespace Ogre supports. For use in rendering, this determines how many texture units the - are available for multitexturing (i.e. rendering multiple - textures in a single pass). Where a Material has multiple - texture layers, it will try to use multitexturing where + are available for multitexturing (i.e. rendering multiple + textures in a single pass). Where a Material has multiple + texture layers, it will try to use multitexturing where available, and where it is not available, will perform multipass rendering to achieve the same effect. This property only applies - to the fixed-function pipeline, the number available to the + to the fixed-function pipeline, the number available to the programmable pipeline depends on the shader model in use. */ ushort getNumTextureUnits(void) const @@ -414,6 +423,17 @@ namespace Ogre return mNumTextureUnits; } + /** Returns the greater than or equal number of texture units an + alternate profile provides. + + Presently specific to Vulkan. For use with the AllUnits descriptor + set profile. + */ + ushort getNumTextureUnitsWide(void) const + { + return mNumTextureUnitsWide; + } + /// @deprecated assume 8-bit stencil buffer ushort getStencilBufferBitDepth(void) const { @@ -438,8 +458,8 @@ namespace Ogre /** Adds a capability flag */ - void setCapability(const Capabilities c) - { + void setCapability(const Capabilities c) + { int index = (CAPS_CATEGORY_MASK & c) >> OGRE_CAPS_BITSHIFT; // zero out the index from the stored capability mCapabilities[index] |= (c & ~CAPS_CATEGORY_MASK); @@ -447,8 +467,8 @@ namespace Ogre /** Remove a capability flag */ - void unsetCapability(const Capabilities c) - { + void unsetCapability(const Capabilities c) + { int index = (CAPS_CATEGORY_MASK & c) >> OGRE_CAPS_BITSHIFT; // zero out the index from the stored capability mCapabilities[index] &= (~c | CAPS_CATEGORY_MASK); @@ -630,8 +650,8 @@ namespace Ogre }; - inline String to_string(GPUVendor v) { return RenderSystemCapabilities::vendorToString(v); } inline String to_string(const DriverVersion& v) { return v.toString(); } + inline String to_string(GPUVendor v) { return RenderSystemCapabilities::vendorToString(v); } /** @} */ /** @} */ diff --git a/OgreMain/src/OgrePass.cpp b/OgreMain/src/OgrePass.cpp index 6be5dccdd34..5d7f10e7377 100644 --- a/OgreMain/src/OgrePass.cpp +++ b/OgreMain/src/OgrePass.cpp @@ -247,6 +247,7 @@ namespace Ogre { mLightScissoring = oth.mLightScissoring; mLightClipPlanes = oth.mLightClipPlanes; mIlluminationStage = oth.mIlluminationStage; + mDescriptorProfileHint = oth.mDescriptorProfileHint; mLightMask = oth.mLightMask; for(int i = 0; i < GPT_PIPELINE_COUNT; i++) diff --git a/OgreMain/src/OgreRenderSystem.cpp b/OgreMain/src/OgreRenderSystem.cpp index 0adee764256..44e2ee67b4f 100644 --- a/OgreMain/src/OgreRenderSystem.cpp +++ b/OgreMain/src/OgreRenderSystem.cpp @@ -422,7 +422,7 @@ namespace Ogre { //----------------------------------------------------------------------- void RenderSystem::_setTextureUnitSettings(size_t texUnit, TextureUnitState& tl) { - if(texUnit >= getCapabilities()->getNumTextureUnits()) + if(texUnit >= _getCurrentPassNumTextureUnits()) return; // This method is only ever called to set a texture unit to valid details diff --git a/OgreMain/src/OgreRenderSystemCapabilitiesSerializer.cpp b/OgreMain/src/OgreRenderSystemCapabilitiesSerializer.cpp index f8315f1acb7..4239c6a65a0 100644 --- a/OgreMain/src/OgreRenderSystemCapabilitiesSerializer.cpp +++ b/OgreMain/src/OgreRenderSystemCapabilitiesSerializer.cpp @@ -82,6 +82,7 @@ namespace Ogre file << endl; file << "\t" << "num_texture_units " << StringConverter::toString(caps->getNumTextureUnits()) << endl; + file << "\t" << "num_texture_units_wide " << StringConverter::toString(caps->getNumTextureUnitsWide()) << endl; file << "\t" << "num_multi_render_targets " << StringConverter::toString(caps->getNumMultiRenderTargets()) << endl; file << "\t" << "vertex_program_constant_float_count " << StringConverter::toString(caps->getConstantFloatCount(GPT_VERTEX_PROGRAM)) << endl; file << "\t" << "fragment_program_constant_float_count " << StringConverter::toString(caps->getConstantFloatCount(GPT_FRAGMENT_PROGRAM)) << endl; diff --git a/OgreMain/src/OgreSceneManager.cpp b/OgreMain/src/OgreSceneManager.cpp index 3f9a7ecd7d4..a21d6f559fe 100644 --- a/OgreMain/src/OgreSceneManager.cpp +++ b/OgreMain/src/OgreSceneManager.cpp @@ -787,6 +787,7 @@ const Pass* SceneManager::_setPass(const Pass* pass, bool shadowDerivation) size_t startLightIndex = pass->getStartLight(); size_t shadowTexUnitIndex = 0; size_t shadowTexIndex = mTextureShadowRenderer.getShadowTexIndex(startLightIndex); + mDestRenderSystem->_setPassHints( pass ); for(auto *pTex : pass->getTextureUnitStates()) { if (!pass->getIteratePerLight() && isShadowTechniqueTextureBased() && diff --git a/PlugIns/GLSLang/include/OgreGLSLangProgramManager.h b/PlugIns/GLSLang/include/OgreGLSLangProgramManager.h index f344c3de432..3ea55d76c7f 100644 --- a/PlugIns/GLSLang/include/OgreGLSLangProgramManager.h +++ b/PlugIns/GLSLang/include/OgreGLSLangProgramManager.h @@ -35,7 +35,11 @@ class GLSLangProgram : public HighLevelGpuProgram void prepareImpl() override; std::vector mAssembly; + String mDescriptorSetProfile; public: + void setDescriptorSetProfile(const String& profile) { mDescriptorSetProfile = profile; } + const String& getDescriptorSetProfile() const { return mDescriptorSetProfile; } + GLSLangProgram(ResourceManager* creator, const String& name, ResourceHandle handle, const String& group, bool isManual, ManualResourceLoader* loader); ~GLSLangProgram(); diff --git a/PlugIns/GLSLang/src/OgreGLSLang.cpp b/PlugIns/GLSLang/src/OgreGLSLang.cpp index 6d4e32b6065..370b413957e 100644 --- a/PlugIns/GLSLang/src/OgreGLSLang.cpp +++ b/PlugIns/GLSLang/src/OgreGLSLang.cpp @@ -327,6 +327,20 @@ static EShLanguage getShLanguage(GpuProgramType type) return EShLangFragment; } +class CmdDescriptorSetProfile : public ParamCommand +{ +public: + String doGet(const void* target) const override + { + return static_cast(target)->getDescriptorSetProfile(); + } + void doSet(void* target, const String& val) override + { + static_cast(target)->setDescriptorSetProfile(val); + } +}; +static CmdDescriptorSetProfile msCmdDescriptorSetProfile; + GLSLangProgram::GLSLangProgram(ResourceManager* creator, const String& name, ResourceHandle handle, const String& group, bool isManual, ManualResourceLoader* loader) : HighLevelGpuProgram(creator, name, handle, group, isManual, loader) @@ -334,6 +348,10 @@ GLSLangProgram::GLSLangProgram(ResourceManager* creator, const String& name, Res if (createParamDictionary("glslangProgram")) { setupBaseParamDictionary(); + ParamDictionary* dict = getParamDictionary(); + dict->addParameter(ParameterDef("descriptor_set_profile", "Descriptor set profile override. Values: Auto, " + "Graphics, Compute, AllUnits", PT_STRING), &msCmdDescriptorSetProfile); + memset(&DefaultTBuiltInResource.limits, 1, sizeof(TLimits)); if(sizeof(TBuiltInResource) == 420) // replace with build_info.h, when it is universally available @@ -369,6 +387,8 @@ void GLSLangProgram::createLowLevelImpl() mAssemblerProgram = GpuProgramManager::getSingleton().createProgram(mName + "/Delegate", mGroup, mSyntaxCode, mType); + if (!mDescriptorSetProfile.empty()) + mAssemblerProgram->setParameter("descriptor_set_profile", mDescriptorSetProfile); String assemblyStr((char*)mAssembly.data(), mAssembly.size() * sizeof(uint32)); mAssemblerProgram->setSource(assemblyStr); mAssembly.clear(); // delegate stores the data now diff --git a/RenderSystems/Vulkan/include/OgreVulkanDevice.h b/RenderSystems/Vulkan/include/OgreVulkanDevice.h index 6449c6bc556..adf2e739dc4 100644 --- a/RenderSystems/Vulkan/include/OgreVulkanDevice.h +++ b/RenderSystems/Vulkan/include/OgreVulkanDevice.h @@ -90,7 +90,7 @@ namespace Ogre static VkInstance createInstance( FastArray &extensions, FastArray &layers, - PFN_vkDebugReportCallbackEXT debugCallback ); + void* pNext = nullptr); void createPhysicalDevice( uint32 deviceIdx ); diff --git a/RenderSystems/Vulkan/include/OgreVulkanProgram.h b/RenderSystems/Vulkan/include/OgreVulkanProgram.h index e298b55f997..bebd889ea07 100644 --- a/RenderSystems/Vulkan/include/OgreVulkanProgram.h +++ b/RenderSystems/Vulkan/include/OgreVulkanProgram.h @@ -42,6 +42,14 @@ namespace Ogre class VulkanProgram : public GpuProgram { public: + enum DescriptorSetProfileHint + { + DescriptorSetProfileAuto, + DescriptorSetProfileGraphics, + DescriptorSetProfileCompute, + DescriptorSetProfileAllUnits + }; + VulkanProgram( ResourceManager *creator, const String &name, ResourceHandle handle, const String &group, bool isManual, ManualResourceLoader *loader, VulkanDevice *device ); @@ -53,6 +61,11 @@ namespace Ogre VkPipelineShaderStageCreateInfo getPipelineShaderStageCi() const; uint32 getDrawIdLocation() const { return mDrawIdLocation; } + DescriptorSetProfileHint getDescriptorSetProfileHint() const { return mDescriptorSetProfileHint; } + void setDescriptorSetProfileHint( DescriptorSetProfileHint hint ) + { + mDescriptorSetProfileHint = hint; + } private: void loadFromSource() override; @@ -61,6 +74,7 @@ namespace Ogre VulkanDevice *mDevice; VkShaderModule mShaderModule; uint32 mDrawIdLocation; + DescriptorSetProfileHint mDescriptorSetProfileHint; }; /** Factory class for Vulkan programs. */ diff --git a/RenderSystems/Vulkan/include/OgreVulkanRenderPassDescriptor.h b/RenderSystems/Vulkan/include/OgreVulkanRenderPassDescriptor.h index bccf8fb77ec..cfa85ce6d4c 100644 --- a/RenderSystems/Vulkan/include/OgreVulkanRenderPassDescriptor.h +++ b/RenderSystems/Vulkan/include/OgreVulkanRenderPassDescriptor.h @@ -67,6 +67,7 @@ namespace Ogre /// /// Thus we generate VkRenderPass and FBOs together VkRenderPass mRenderPass; + VkRenderPass mRenderPassLoad; VulkanFrameBufferDescValue(); }; @@ -80,6 +81,7 @@ namespace Ogre VulkanTextureGpu* mDepth; uint8 mNumColourEntries = 0; uint8 mSlice = 0; + bool mResuming = false; private: // 1 per MRT // 1 per MRT MSAA resolve diff --git a/RenderSystems/Vulkan/include/OgreVulkanRenderSystem.h b/RenderSystems/Vulkan/include/OgreVulkanRenderSystem.h index 88321b130fa..28c3d56f67c 100644 --- a/RenderSystems/Vulkan/include/OgreVulkanRenderSystem.h +++ b/RenderSystems/Vulkan/include/OgreVulkanRenderSystem.h @@ -32,7 +32,6 @@ THE SOFTWARE. #include "OgreVulkanPrerequisites.h" #include "OgreRenderSystem.h" -#include "OgreVulkanProgram.h" #include "OgreVulkanRenderPassDescriptor.h" @@ -53,6 +52,27 @@ namespace Ogre class _OgreVulkanExport VulkanRenderSystem : public RenderSystem { friend class VulkanSampler; + friend class VulkanTextureGpu; + public: + enum DescriptorSetProfileId { + Graphics, + Compute, + AllUnits, + NUM_DESCRIPTOR_SET_PROFILES + }; + + struct DescriptorSetProfile { + VkDescriptorSetLayout layout; + VkPipelineLayout pipelineLayout; + std::shared_ptr pool; + std::vector bindings; + std::vector poolSizes; + std::vector writes; + std::unordered_map cache; + uint32 numTextureUnits = 0u; + }; + + private: bool mInitialized; VulkanHardwareBufferManager *mHardwareBufferManager; @@ -81,17 +101,18 @@ namespace Ogre bool mHasValidationLayers; - PFN_vkCreateDebugReportCallbackEXT CreateDebugReportCallback; - PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallback; - VkDebugReportCallbackEXT mDebugReportCallback; + static const uint32 FRAMES_IN_FLIGHT = 3; + std::vector mDeferredViewDeletions[FRAMES_IN_FLIGHT]; + + PFN_vkCreateDebugUtilsMessengerEXT CreateDebugUtilsMessenger; + PFN_vkDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessenger; + VkDebugUtilsMessengerEXT mDebugMessenger; /// Declared here to avoid constant reallocations FastArray mImageBarriers; void addInstanceDebugCallback( void ); - void flushRootLayout( void ); - // Pipeline State Infos VkGraphicsPipelineCreateInfo pipelineCi; VkPipelineLayoutCreateInfo pipelineLayoutCi; @@ -101,18 +122,20 @@ namespace Ogre VkPipelineRasterizationStateCreateInfo rasterState; VkPipelineColorBlendStateCreateInfo blendStateCi; std::array shaderStages; + VkPipelineShaderStageCreateInfo mComputeShaderStage; std::array mBoundGpuPrograms; - // descriptor set layout - std::vector mDescriptorSetBindings; - std::vector mDescriptorPoolSizes; - std::vector mDescriptorWrites; + std::array mProfiles; + std::array mUBOInfo; std::array mUBODynOffsets; std::array mImageInfos; - VkDescriptorSetLayout mDescriptorSetLayout; - - VkPipelineLayout mLayout; + VkDescriptorImageInfo mComputeImageInfo; + std::array mStorageImageInfos; + std::array mStorageTextures; + std::array mStorageImageViews; + DescriptorSetProfileId mBoundComputeProfile; + DescriptorSetProfileId mBoundGraphicsProfile; std::array blendStates; @@ -121,21 +144,21 @@ namespace Ogre VkRect2D mScissorRect; VkPipelineViewportStateCreateInfo viewportStateCi; - std::unordered_map mDescriptorSetCache; std::unordered_map mRenderPassCache; std::unordered_map mPipelineCache; - - std::shared_ptr mDescriptorPool; + std::unordered_map mComputePipelineCache; // clears the pipeline cache void clearPipelineCache(); - + void initializeVkInstance( void ); void enumerateDevices(); uint32 getSelectedDeviceIdx() const; + void setStorageTexture( size_t texUnit, VulkanTextureGpu *texture, int mipmapLevel ); + void clearStorageTextureBindings(); - VkDescriptorSet getDescriptorSet(); - VkPipeline getPipeline(); + VkDescriptorSet getDescriptorSet( DescriptorSetProfileId profile ); + VkPipeline getPipeline( DescriptorSetProfileId profile ); public: VulkanRenderSystem(); ~VulkanRenderSystem(); @@ -168,6 +191,7 @@ namespace Ogre void _endFrame( void ) override; void _render( const RenderOperation &op ) override; + void _dispatchCompute( const Vector3i &workgroupDim ) override; void bindGpuProgram(GpuProgram* prg) override; void bindGpuProgramParameters( GpuProgramType gptype, @@ -179,6 +203,8 @@ namespace Ogre Real getMinimumDepthInputValue( void ) override; Real getMaximumDepthInputValue( void ) override; + ushort _getCurrentPassNumTextureUnits() const override; + void beginProfileEvent( const String &eventName ) override; void endProfileEvent( void ) override; void markProfileEvent( const String &event ) override; @@ -208,6 +234,7 @@ namespace Ogre void setScissorTest(bool enabled, const Rect& rect = Rect()) override; void setStencilState(const StencilState& state) override; void _setPolygonMode(PolygonMode level) override; + void _setPassHints(const Pass* pass) override; void _convertProjectionMatrix(const Matrix4& matrix, Matrix4& dest, bool) override; void _setDepthBias(float constantBias, float slopeScaleBias = 0.0f) override; void _setDepthBufferParams(bool depthTest, bool depthWrite, CompareFunction depthFunction) override; diff --git a/RenderSystems/Vulkan/include/OgreVulkanTextureGpu.h b/RenderSystems/Vulkan/include/OgreVulkanTextureGpu.h index 121de6ff7f6..2f0b234be71 100644 --- a/RenderSystems/Vulkan/include/OgreVulkanTextureGpu.h +++ b/RenderSystems/Vulkan/include/OgreVulkanTextureGpu.h @@ -134,7 +134,7 @@ namespace Ogre virtual void destroyMsaaSurface( void ); public: bool hasMsaaExplicitResolves() const { return false; } - bool isUav() const { return false; } + bool isUav() const { return (getUsage() & TU_UNORDERED_ACCESS) != 0; } bool isMultisample() const { return mFSAA > 1; } virtual bool isRenderWindowSpecific() const { return false; } @@ -160,6 +160,9 @@ namespace Ogre VkImageView createView( void ) const; VkImageView getDefaultDisplaySrv( void ) const { return mDefaultDisplaySrv; } + void createShaderAccessPoint( uint bindPoint, TextureAccess access = TA_READ_WRITE, + int mipmapLevel = 0, int textureArrayIndex = 0, + PixelFormat format = PF_UNKNOWN ) override; void destroyView( VkImageView imageView ); diff --git a/RenderSystems/Vulkan/src/OgreVulkanDevice.cpp b/RenderSystems/Vulkan/src/OgreVulkanDevice.cpp index a9a32de7dab..70f20a103ba 100644 --- a/RenderSystems/Vulkan/src/OgreVulkanDevice.cpp +++ b/RenderSystems/Vulkan/src/OgreVulkanDevice.cpp @@ -80,7 +80,7 @@ namespace Ogre //------------------------------------------------------------------------- VkInstance VulkanDevice::createInstance( FastArray &extensions, FastArray &layers, - PFN_vkDebugReportCallbackEXT debugCallback) + void* pNext) { VkInstanceCreateInfo createInfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO}; VkApplicationInfo appInfo = {VK_STRUCTURE_TYPE_APPLICATION_INFO}; @@ -95,6 +95,7 @@ namespace Ogre createInfo.enabledExtensionCount = extensions.size(); createInfo.ppEnabledExtensionNames = extensions.data(); + createInfo.pNext = pNext; #ifdef VK_KHR_portability_enumeration for( const char *extension : extensions ) @@ -104,13 +105,6 @@ namespace Ogre } #endif -#if 1 //OGRE_DEBUG_MODE - VkDebugReportCallbackCreateInfoEXT debugCb = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT}; - debugCb.pfnCallback = debugCallback; - debugCb.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT; - createInfo.pNext = &debugCb; -#endif - VkInstance instance; OGRE_VK_CHECK(vkCreateInstance(&createInfo, 0, &instance)); return instance; diff --git a/RenderSystems/Vulkan/src/OgreVulkanProgram.cpp b/RenderSystems/Vulkan/src/OgreVulkanProgram.cpp index 80b79a1ac52..e6079753cf6 100644 --- a/RenderSystems/Vulkan/src/OgreVulkanProgram.cpp +++ b/RenderSystems/Vulkan/src/OgreVulkanProgram.cpp @@ -42,17 +42,82 @@ THE SOFTWARE. namespace Ogre { + namespace + { + class CmdDescriptorSetProfile : public ParamCommand + { + public: + String doGet( const void *target ) const override + { + const auto *program = static_cast( target ); + switch( program->getDescriptorSetProfileHint() ) + { + case VulkanProgram::DescriptorSetProfileGraphics: + return "Graphics"; + case VulkanProgram::DescriptorSetProfileCompute: + return "Compute"; + case VulkanProgram::DescriptorSetProfileAllUnits: + return "AllUnits"; + default: + return "Auto"; + } + } + + void doSet( void *target, const String &val ) override + { + auto *program = static_cast( target ); + String lowered = val; + StringUtil::toLowerCase( lowered ); + + if( lowered == "auto" ) + { + program->setDescriptorSetProfileHint( VulkanProgram::DescriptorSetProfileAuto ); + } + else if( lowered == "graphics" ) + { + program->setDescriptorSetProfileHint( + VulkanProgram::DescriptorSetProfileGraphics ); + } + else if( lowered == "compute" ) + { + program->setDescriptorSetProfileHint( + VulkanProgram::DescriptorSetProfileCompute ); + } + else if( lowered == "allunits" ) + { + program->setDescriptorSetProfileHint( + VulkanProgram::DescriptorSetProfileAllUnits ); + } + { + LogManager::getSingleton().logWarning( + "[Vulkan] Unknown descriptor_set_profile '" + val + + "'. Falling back to Auto" ); + program->setDescriptorSetProfileHint( VulkanProgram::DescriptorSetProfileAuto ); + } + } + }; + + static CmdDescriptorSetProfile msDescriptorSetProfileCmd; + } + //----------------------------------------------------------------------- VulkanProgram::VulkanProgram( ResourceManager *creator, const String &name, ResourceHandle handle, const String &group, bool isManual, ManualResourceLoader *loader, VulkanDevice *device ) : GpuProgram( creator, name, handle, group, isManual, loader ), mDevice( device ), - mShaderModule( VK_NULL_HANDLE ) + mShaderModule( VK_NULL_HANDLE ), + mDescriptorSetProfileHint( DescriptorSetProfileAuto ) { if( createParamDictionary( "VulkanProgram" ) ) { setupBaseParamDictionary(); + ParamDictionary *dict = getParamDictionary(); + dict->addParameter( ParameterDef( "descriptor_set_profile", + "Descriptor set profile override. Values: Auto, " + "Graphics, Compute, AllUnits", + PT_STRING ), + &msDescriptorSetProfileCmd ); } mDrawIdLocation = /*( mShaderSyntax == GLSL ) */ true ? 15 : 0; diff --git a/RenderSystems/Vulkan/src/OgreVulkanRenderPassDescriptor.cpp b/RenderSystems/Vulkan/src/OgreVulkanRenderPassDescriptor.cpp index 0433a39dbbe..9d8714ef1f1 100644 --- a/RenderSystems/Vulkan/src/OgreVulkanRenderPassDescriptor.cpp +++ b/RenderSystems/Vulkan/src/OgreVulkanRenderPassDescriptor.cpp @@ -380,6 +380,22 @@ namespace Ogre fbCreateInfo.height = mTargetHeight; fbCreateInfo.layers = layers; + // Create a second render pass with LOAD_OP_LOAD for resuming after compute + // Modify attachments in-place (they're local) + for( uint32 i = 0; i < attachmentIdx; ++i ) + { + if( attachments[i].loadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ) + attachments[i].loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + if( attachments[i].stencilLoadOp == VK_ATTACHMENT_LOAD_OP_CLEAR ) + attachments[i].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD; + + // Must match the layout the previous pass left the attachment in + if( attachments[i].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED ) + attachments[i].initialLayout = attachments[i].finalLayout; + } + + OGRE_VK_CHECK(vkCreateRenderPass( mQueue->mDevice, &renderPassCreateInfo, 0, &fboDesc.mRenderPassLoad )); + const size_t numFramebuffers = std::max( fboDesc.mWindowImageViews.size(), 1u ); fboDesc.mFramebuffers.resize( numFramebuffers ); for( size_t i = 0u; i < numFramebuffers; ++i ) @@ -432,6 +448,12 @@ namespace Ogre vkDestroyRenderPass( queue->mDevice, fboDesc.mRenderPass, 0 ); fboDesc.mRenderPass = 0; + + if( fboDesc.mRenderPassLoad ) + { + vkDestroyRenderPass( queue->mDevice, fboDesc.mRenderPassLoad, 0 ); + fboDesc.mRenderPassLoad = 0; + } } //----------------------------------------------------------------------------------- void VulkanRenderPassDescriptor::entriesModified( bool createFbo ) @@ -515,7 +537,7 @@ namespace Ogre return mSharedFboItor->second.mRenderPass; } //----------------------------------------------------------------------------------- - void VulkanRenderPassDescriptor::performLoadActions() +void VulkanRenderPassDescriptor::performLoadActions() { VkCommandBuffer cmdBuffer = mQueue->mCurrentCmdBuffer; @@ -530,13 +552,12 @@ namespace Ogre VkSemaphore semaphore = textureVulkan->getImageAcquiredSemaphore(); if( semaphore ) { - // We cannot start executing color attachment commands until the semaphore says so mQueue->addWindowToWaitFor( semaphore ); } } VkRenderPassBeginInfo passBeginInfo = {VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO}; - passBeginInfo.renderPass = fboDesc.mRenderPass; + passBeginInfo.renderPass = mResuming ? fboDesc.mRenderPassLoad : fboDesc.mRenderPass; passBeginInfo.framebuffer = fboDesc.mFramebuffers[fboIdx]; passBeginInfo.renderArea.offset.x = 0; passBeginInfo.renderArea.offset.y = 0; @@ -559,6 +580,7 @@ namespace Ogre // Another encoder will have to be created, and don't let ours linger // since mCurrentRenderPassDescriptor probably doesn't even point to 'this' mQueue->endAllEncoders( false ); + mResuming = true; } //----------------------------------------------------------------------------------- //----------------------------------------------------------------------------------- diff --git a/RenderSystems/Vulkan/src/OgreVulkanRenderSystem.cpp b/RenderSystems/Vulkan/src/OgreVulkanRenderSystem.cpp index b4ce3d825a0..4e5fc9e9689 100644 --- a/RenderSystems/Vulkan/src/OgreVulkanRenderSystem.cpp +++ b/RenderSystems/Vulkan/src/OgreVulkanRenderSystem.cpp @@ -28,6 +28,7 @@ THE SOFTWARE. #include "OgreVulkanRenderSystem.h" +#include #include #include "OgreGpuProgramManager.h" @@ -37,7 +38,6 @@ THE SOFTWARE. #include "OgreVulkanDevice.h" #include "OgreVulkanMappings.h" #include "OgreVulkanProgram.h" -#include "OgreVulkanRenderPassDescriptor.h" #include "OgreVulkanTextureGpuManager.h" #include "OgreVulkanUtils.h" #include "OgreVulkanWindow.h" @@ -49,7 +49,6 @@ THE SOFTWARE. #include "OgreDepthBuffer.h" #include "OgreRoot.h" -#include "OgreVulkanWindow.h" #include "OgrePixelFormat.h" namespace Ogre @@ -70,33 +69,37 @@ namespace Ogre 14, // VES_TANGENT - 1 }; - static VKAPI_ATTR VkBool32 dbgFunc( VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, - uint64_t srcObject, size_t location, int32_t msgCode, - const char *pLayerPrefix, const char *pMsg, void *pUserData ) - { - const char* messageType = "INFORMATION"; - - if (msgFlags & VK_DEBUG_REPORT_WARNING_BIT_EXT) - messageType = "WARNING"; - else if (msgFlags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) - messageType = "PERFORMANCE WARNING"; - else if (msgFlags & VK_DEBUG_REPORT_ERROR_BIT_EXT) - messageType = "ERROR"; - else if (msgFlags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) - messageType = "DEBUG"; - - LogManager::getSingleton().logMessage( - StringUtil::format("%s: [%s] Code %d : %s", messageType, pLayerPrefix, msgCode, pMsg)); - - /* - * false indicates that layer should not bail-out of an - * API call that had validation failures. This may mean that the - * app dies inside the driver due to invalid parameter(s). - * That's what would happen without validation layers, so we'll - * keep that behavior here. - */ - return false; - } +static VKAPI_ATTR VkBool32 VKAPI_CALL dbgUtilsCallback( + VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, + VkDebugUtilsMessageTypeFlagsEXT messageType, + const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, + void *pUserData ) +{ + const char *severity = "INFO"; + if( messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT ) + severity = "ERROR"; + else if( messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT ) + severity = "WARNING"; + else if( messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT ) + severity = "VERBOSE"; + + const char *type = ""; + if( messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT ) + type = "VALIDATION"; + else if( messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT ) + type = "PERFORMANCE"; + else if( messageType & VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT ) + type = "GENERAL"; + + LogManager::getSingleton().logMessage( + StringUtil::format( "[Vulkan][%s][%s] %s (ID: %s #%d)", + severity, type, + pCallbackData->pMessage, + pCallbackData->pMessageIdName ? pCallbackData->pMessageIdName : "", + pCallbackData->messageIdNumber ) ); + + return VK_FALSE; +} //------------------------------------------------------------------------- VulkanRenderSystem::VulkanRenderSystem() : @@ -110,9 +113,9 @@ namespace Ogre mDevice( 0 ), mCurrentRenderPassDescriptor( 0 ), mHasValidationLayers( false ), - CreateDebugReportCallback( 0 ), - DestroyDebugReportCallback( 0 ), - mDebugReportCallback( 0 ), + CreateDebugUtilsMessenger( 0 ), + DestroyDebugUtilsMessenger( 0 ), + mDebugMessenger( 0 ), pipelineCi{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO}, pipelineLayoutCi{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO}, vertexFormatCi{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO}, @@ -123,6 +126,12 @@ namespace Ogre mUBOInfo{}, mUBODynOffsets{}, mImageInfos{}, + mComputeImageInfo{}, + mStorageImageInfos{}, + mStorageTextures{}, + mStorageImageViews{}, + mBoundComputeProfile( Compute ), + mBoundGraphicsProfile( Graphics ), depthStencilStateCi{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO}, mVkViewport{}, mScissorRect{}, @@ -140,6 +149,12 @@ namespace Ogre pipelineCi.pStages = shaderStages.data(); pipelineCi.stageCount = 2; // vertex+fragment + for(auto& p : mProfiles) + { + p.layout = VK_NULL_HANDLE; + p.pipelineLayout = VK_NULL_HANDLE; + } + inputAssemblyCi.primitiveRestartEnable = false; mssCi.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; @@ -159,37 +174,86 @@ namespace Ogre viewportStateCi.viewportCount = 1u; viewportStateCi.scissorCount = 1u; - // use a single descriptor set for all shaders - mDescriptorSetBindings.push_back({0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); - mDescriptorSetBindings.push_back({1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + // single descriptor set for most shaders + auto& graphics = mProfiles[Graphics]; + graphics.bindings.push_back({0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + graphics.bindings.push_back({1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); for(uint32 i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS; ++i) - mDescriptorSetBindings.push_back({2 + i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + graphics.bindings.push_back({2 + i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + graphics.numTextureUnits = OGRE_MAX_TEXTURE_COORD_SETS; // one descriptor will have at most OGRE_MAX_TEXTURE_LAYERS and one UBO per shader type (for now) - mDescriptorPoolSizes.push_back({VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, GPT_COUNT}); - mDescriptorPoolSizes.push_back({VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, OGRE_MAX_TEXTURE_COORD_SETS}); + graphics.poolSizes.push_back({VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, GPT_COUNT}); + graphics.poolSizes.push_back({VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, OGRE_MAX_TEXTURE_COORD_SETS}); // silence validation layer, when unused mUBOInfo[0].range = 1; mUBOInfo[1].range = 1; - mDescriptorWrites.resize(OGRE_MAX_TEXTURE_COORD_SETS + 2, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}); - mDescriptorWrites[0].dstBinding = 0; - mDescriptorWrites[0].descriptorCount = 1; - mDescriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - mDescriptorWrites[0].pBufferInfo = mUBOInfo.data(); + graphics.writes.resize(OGRE_MAX_TEXTURE_COORD_SETS + 2, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}); + graphics.writes[0].dstBinding = 0; + graphics.writes[0].descriptorCount = 1; + graphics.writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + graphics.writes[0].pBufferInfo = mUBOInfo.data(); - mDescriptorWrites[1].dstBinding = 1; - mDescriptorWrites[1].descriptorCount = 1; - mDescriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; - mDescriptorWrites[1].pBufferInfo = mUBOInfo.data() + 1; + graphics.writes[1].dstBinding = 1; + graphics.writes[1].descriptorCount = 1; + graphics.writes[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + graphics.writes[1].pBufferInfo = mUBOInfo.data() + 1; for(int i = 0; i < OGRE_MAX_TEXTURE_COORD_SETS; i++) { - mDescriptorWrites[i + 2].dstBinding = 2 + i; - mDescriptorWrites[i + 2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; - mDescriptorWrites[i + 2].pImageInfo = mImageInfos.data() + i; - mDescriptorWrites[i + 2].descriptorCount = 1; + graphics.writes[i + 2].dstBinding = 2 + i; + graphics.writes[i + 2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + graphics.writes[i + 2].pImageInfo = mImageInfos.data() + i; + graphics.writes[i + 2].descriptorCount = 1; + } + + // Minimal compute profile for image write + dynamic UBO. + auto &compute = mProfiles[Compute]; + compute.bindings.push_back( {0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT} ); + compute.bindings.push_back( + {1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_COMPUTE_BIT} ); + compute.poolSizes.push_back( {VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1u} ); + compute.poolSizes.push_back( {VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1u} ); + compute.writes.resize( 2u, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET} ); + compute.writes[0].dstBinding = 0; + compute.writes[0].descriptorCount = 1; + compute.writes[0].descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE; + compute.writes[0].pImageInfo = &mComputeImageInfo; + compute.writes[1].dstBinding = 1; + compute.writes[1].descriptorCount = 1; + compute.writes[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + compute.writes[1].pBufferInfo = mUBOInfo.data() + 1; + + auto &allUnits = mProfiles[AllUnits]; + allUnits.bindings.push_back({0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + allUnits.bindings.push_back({1, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + for(uint32 i = 0; i < OGRE_MAX_TEXTURE_LAYERS; ++i) + allUnits.bindings.push_back({2 + i, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_ALL_GRAPHICS}); + allUnits.numTextureUnits = OGRE_MAX_TEXTURE_LAYERS; + + allUnits.poolSizes.push_back({VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, GPT_COUNT}); + allUnits.poolSizes.push_back({VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, OGRE_MAX_TEXTURE_LAYERS}); + + allUnits.writes.resize(OGRE_MAX_TEXTURE_LAYERS + 2, {VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET}); + + allUnits.writes[0].dstBinding = 0; + allUnits.writes[0].descriptorCount = 1; + allUnits.writes[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + allUnits.writes[0].pBufferInfo = mUBOInfo.data(); + + allUnits.writes[1].dstBinding = 1; + allUnits.writes[1].descriptorCount = 1; + allUnits.writes[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC; + allUnits.writes[1].pBufferInfo = mUBOInfo.data() + 1; + + for(int i = 0; i < OGRE_MAX_TEXTURE_LAYERS; i++) + { + allUnits.writes[i + 2].dstBinding = 2 + i; + allUnits.writes[i + 2].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; + allUnits.writes[i + 2].pImageInfo = mImageInfos.data() + i; + allUnits.writes[i + 2].descriptorCount = 1; } if(volkInitialize() != VK_SUCCESS) @@ -252,10 +316,10 @@ namespace Ogre { shutdown(); - if( mDebugReportCallback ) + if( mDebugMessenger ) { - DestroyDebugReportCallback( mVkInstance, mDebugReportCallback, 0 ); - mDebugReportCallback = 0; + DestroyDebugUtilsMessenger( mVkInstance, mDebugMessenger, nullptr ); + mDebugMessenger = 0; } if( mVkInstance ) @@ -289,16 +353,30 @@ namespace Ogre OGRE_DELETE mSPIRVProgramFactory; mSPIRVProgramFactory = 0; - vkDestroyPipelineLayout(mDevice->mDevice, mLayout, 0); - vkDestroyDescriptorSetLayout(mDevice->mDevice, mDescriptorSetLayout, 0); + // Flush deferred image view deletions + for( uint32 i = 0; i < FRAMES_IN_FLIGHT; ++i ) + { + for( VkImageView view : mDeferredViewDeletions[i] ) + vkDestroyImageView( mDevice->mDevice, view, nullptr ); + mDeferredViewDeletions[i].clear(); + } + + clearStorageTextureBindings(); + + for(auto& p : mProfiles) + { + if(p.pipelineLayout) + vkDestroyPipelineLayout(mDevice->mDevice, p.pipelineLayout, 0); + if(p.layout) + vkDestroyDescriptorSetLayout(mDevice->mDevice, p.layout, 0); + p.pool.reset(); + } for(auto it : mRenderPassCache) { vkDestroyRenderPass(mDevice->mDevice, it.second, 0); } - mDescriptorPool.reset(); - clearPipelineCache(); delete mDevice; @@ -312,6 +390,13 @@ namespace Ogre } mPipelineCache.clear(); + + for( auto it : mComputePipelineCache ) + { + vkDestroyPipeline( mDevice->mDevice, it.second, 0 ); + } + + mComputePipelineCache.clear(); } //------------------------------------------------------------------------- const String &VulkanRenderSystem::getName( void ) const @@ -370,45 +455,33 @@ namespace Ogre mIsReverseDepthBufferEnabled = StringConverter::parseBool(value); } //------------------------------------------------------------------------- - void VulkanRenderSystem::addInstanceDebugCallback( void ) +void VulkanRenderSystem::addInstanceDebugCallback( void ) +{ + CreateDebugUtilsMessenger = (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + mVkInstance, "vkCreateDebugUtilsMessengerEXT" ); + DestroyDebugUtilsMessenger = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr( + mVkInstance, "vkDestroyDebugUtilsMessengerEXT" ); + + if( !CreateDebugUtilsMessenger || !DestroyDebugUtilsMessenger ) { - CreateDebugReportCallback = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr( - mVkInstance, "vkCreateDebugReportCallbackEXT" ); - DestroyDebugReportCallback = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr( - mVkInstance, "vkDestroyDebugReportCallbackEXT" ); - if( !CreateDebugReportCallback ) - { - LogManager::getSingleton().logMessage( - "[Vulkan] GetProcAddr: Unable to find vkCreateDebugReportCallbackEXT. " - "Debug reporting won't be available" ); - return; - } - if( !DestroyDebugReportCallback ) - { - LogManager::getSingleton().logMessage( - "[Vulkan] GetProcAddr: Unable to find vkDestroyDebugReportCallbackEXT. " - "Debug reporting won't be available" ); - return; - } - // DebugReportMessage = - // (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr( mVkInstance, "vkDebugReportMessageEXT" - // ); - // if( !DebugReportMessage ) - //{ - // LogManager::getSingleton().logMessage( - // "[Vulkan] GetProcAddr: Unable to find DebugReportMessage. " - // "Debug reporting won't be available" ); - //} - - VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT}; - PFN_vkDebugReportCallbackEXT callback; - callback = dbgFunc; - dbgCreateInfo.pfnCallback = callback; - dbgCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | - VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT; - OGRE_VK_CHECK( - CreateDebugReportCallback( mVkInstance, &dbgCreateInfo, 0, &mDebugReportCallback )); + LogManager::getSingleton().logMessage( + "[Vulkan] Unable to find vkCreateDebugUtilsMessengerEXT. " + "Debug reporting won't be available" ); + return; } + + VkDebugUtilsMessengerCreateInfoEXT messengerCi = {VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + messengerCi.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; + messengerCi.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + messengerCi.pfnUserCallback = dbgUtilsCallback; + messengerCi.pUserData = nullptr; + + OGRE_VK_CHECK( CreateDebugUtilsMessenger( mVkInstance, &messengerCi, nullptr, &mDebugMessenger ) ); +} //------------------------------------------------------------------------- HardwareOcclusionQuery *VulkanRenderSystem::createHardwareOcclusionQuery( void ) { @@ -520,6 +593,7 @@ namespace Ogre if( props.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT ) rsc->setCapability( RSC_TEXTURE_COMPRESSION_ASTC ); } + rsc->setCapability( RSC_COMPUTE_PROGRAM ); const VkPhysicalDeviceLimits &deviceLimits = mDevice->mDeviceProperties.limits; //rsc->setMaximumResolutions( deviceLimits.maxImageDimension2D, deviceLimits.maxImageDimension3D, @@ -544,6 +618,7 @@ namespace Ogre //rsc->setCapability( RSC_HWSTENCIL ); rsc->setNumTextureUnits( OGRE_MAX_TEXTURE_COORD_SETS ); + rsc->setNumTextureUnitsWide( OGRE_MAX_TEXTURE_LAYERS ); rsc->setCapability( RSC_TEXTURE_COMPRESSION ); rsc->setCapability( RSC_32BIT_INDEX ); rsc->setCapability( RSC_TWO_SIDED_STENCIL ); @@ -629,7 +704,80 @@ namespace Ogre //------------------------------------------------------------------------- void VulkanRenderSystem::resetAllBindings( void ) { + for( auto &imgInfo : mImageInfos ) + imgInfo = {}; + + for( auto &imgInfo : mStorageImageInfos ) + imgInfo = {}; + + mStorageTextures.fill( nullptr ); + mStorageImageViews.fill( VK_NULL_HANDLE ); + mComputeImageInfo = {}; + mBoundComputeProfile = Compute; + mBoundGraphicsProfile = Graphics; + mUBODynOffsets = {0u, 0u}; + } + void VulkanRenderSystem::setStorageTexture( size_t texUnit, VulkanTextureGpu *texture, int mipmapLevel ) + { + if( texUnit >= OGRE_MAX_TEXTURE_LAYERS ) + return; + + if( texture == mStorageTextures[texUnit] && mStorageImageViews[texUnit] != VK_NULL_HANDLE ) + return; + + // NEVER destroy here — just orphan the old view for later cleanup + if( mStorageImageViews[texUnit] != VK_NULL_HANDLE ) + mDeferredViewDeletions[mDevice->mGraphicsQueue.mCurrentFrameIdx].push_back( mStorageImageViews[texUnit] ); + + mStorageImageViews[texUnit] = VK_NULL_HANDLE; + mStorageTextures[texUnit] = texture; + mStorageImageInfos[texUnit] = {}; + + if( !texture ) + return; + + const int maxMipLevel = static_cast( texture->getNumMipmaps() ); + const int clampedMip = std::max( 0, std::min( mipmapLevel, maxMipLevel ) ); + + VkImageViewUsageCreateInfo viewUsageCi = {VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO}; + viewUsageCi.usage = VK_IMAGE_USAGE_STORAGE_BIT; + + VkImageViewCreateInfo viewCi = {VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO}; + viewCi.pNext = &viewUsageCi; + viewCi.image = texture->getFinalTextureName(); + viewCi.viewType = VK_IMAGE_VIEW_TYPE_2D; + viewCi.format = VulkanMappings::get( texture->getFormat() ); + viewCi.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; + viewCi.subresourceRange.baseMipLevel = static_cast( clampedMip ); + viewCi.subresourceRange.levelCount = 1; + viewCi.subresourceRange.baseArrayLayer = 0; + viewCi.subresourceRange.layerCount = 1; + + VkImageView imageView; + OGRE_VK_CHECK( vkCreateImageView( mDevice->mDevice, &viewCi, nullptr, &imageView ) ); + + mStorageImageViews[texUnit] = imageView; + + mStorageImageInfos[texUnit].sampler = VK_NULL_HANDLE; + mStorageImageInfos[texUnit].imageView = imageView; + mStorageImageInfos[texUnit].imageLayout = VK_IMAGE_LAYOUT_GENERAL; + + // After creating the new view, clear stale cache entries + mProfiles[Compute].cache.clear(); + } + //------------------------------------------------------------------------- + void VulkanRenderSystem::clearStorageTextureBindings() + { + for( size_t i = 0u; i < mStorageImageViews.size(); ++i ) + { + if( mStorageImageViews[i] != VK_NULL_HANDLE ) + mDeferredViewDeletions[mDevice->mGraphicsQueue.mCurrentFrameIdx].push_back( mStorageImageViews[i] ); + + mStorageImageViews[i] = VK_NULL_HANDLE; + mStorageTextures[i] = nullptr; + mStorageImageInfos[i] = {}; + } } //------------------------------------------------------------------------- void VulkanRenderSystem::initializeVkInstance( void ) @@ -660,8 +808,8 @@ namespace Ogre reqInstanceExtensions.push_back(VulkanWindow::getRequiredExtensionName()); } - if (debugEnabled && extensionName == "VK_EXT_debug_report") - reqInstanceExtensions.push_back("VK_EXT_debug_report"); + if (debugEnabled && extensionName == "VK_EXT_debug_utils") + reqInstanceExtensions.push_back("VK_EXT_debug_utils"); #ifdef VK_KHR_portability_enumeration // portability (non fully conformant) implementations like MoltenVK are @@ -669,6 +817,7 @@ namespace Ogre if (extensionName == VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) reqInstanceExtensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); #endif + } reqInstanceExtensions.push_back("VK_KHR_surface"); // required for window surface @@ -698,11 +847,33 @@ namespace Ogre "Debug Layer requested, but VK_LAYER_KHRONOS_validation layer not present"); } - mVkInstance = VulkanDevice::createInstance(reqInstanceExtensions, instanceLayers, dbgFunc); + VkDebugUtilsMessengerCreateInfoEXT debugCi = {VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT}; + void *pNext = nullptr; + + if( mHasValidationLayers ) + { + debugCi.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; + debugCi.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | + VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debugCi.pfnUserCallback = dbgUtilsCallback; + debugCi.pUserData = nullptr; + pNext = &debugCi; + } + + mVkInstance = VulkanDevice::createInstance(reqInstanceExtensions, instanceLayers, pNext); volkLoadInstanceOnly(mVkInstance); if(mHasValidationLayers) + { + LogManager::getSingleton().logMessage("[Vulkan] Setting up debug utils messenger..."); addInstanceDebugCallback(); + } + else + { + LogManager::getSingleton().logMessage("[Vulkan] No validation layers active"); + } } //------------------------------------------------------------------------- RenderWindow *VulkanRenderSystem::_createRenderWindow( const String &name, uint32 width, uint32 height, @@ -767,7 +938,8 @@ namespace Ogre deviceExtensions.push_back( VK_KHR_SPIRV_1_4_EXTENSION_NAME ); mRealCapabilities->setCapability(RSC_MESH_PROGRAM); - mDescriptorSetBindings[0].stageFlags |= VK_SHADER_STAGE_MESH_BIT_NV; + for( auto &binding : mProfiles[Graphics].bindings ) + binding.stageFlags |= VK_SHADER_STAGE_MESH_BIT_NV; } #endif } @@ -794,15 +966,24 @@ namespace Ogre mTextureManager = new VulkanTextureGpuManager(this, mDevice, bCanRestrictImageViewUsage); mTextureManager->_getWarningTexture(); // preload warning texture, so does not interrupt render pass - VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; - descriptorSetLayoutCi.bindingCount = mDescriptorSetBindings.size(); - descriptorSetLayoutCi.pBindings = mDescriptorSetBindings.data(); - OGRE_VK_CHECK(vkCreateDescriptorSetLayout(mActiveDevice->mDevice, &descriptorSetLayoutCi, nullptr, - &mDescriptorSetLayout)); - - pipelineLayoutCi.pSetLayouts = &mDescriptorSetLayout; - pipelineLayoutCi.setLayoutCount = 1; - OGRE_VK_CHECK(vkCreatePipelineLayout(mActiveDevice->mDevice, &pipelineLayoutCi, 0, &mLayout)); + for( auto &profile : mProfiles ) + { + if( profile.bindings.empty() ) + continue; + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCi = + {VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO}; + descriptorSetLayoutCi.bindingCount = profile.bindings.size(); + descriptorSetLayoutCi.pBindings = profile.bindings.data(); + OGRE_VK_CHECK( vkCreateDescriptorSetLayout( mActiveDevice->mDevice, &descriptorSetLayoutCi, + nullptr, &profile.layout ) ); + + pipelineLayoutCi.pSetLayouts = &profile.layout; + pipelineLayoutCi.setLayoutCount = 1; + OGRE_VK_CHECK( + vkCreatePipelineLayout( mActiveDevice->mDevice, &pipelineLayoutCi, 0, + &profile.pipelineLayout ) ); + } // allocate 1.5MB buffer. Holds e.g. 1024 batches of 512 bytes for 3 frames-in-flight resizeAutoParamsBuffer(1024 * 512 * 3); @@ -837,9 +1018,15 @@ namespace Ogre mUBOInfo[1].buffer = mUBOInfo[0].buffer; // descriptors referring to old buffer are invalidated - mDescriptorSetCache.clear(); - mActiveDevice->mGraphicsQueue.queueForDeletion(mDescriptorPool); - mDescriptorPool.reset( new VulkanDescriptorPool(mDescriptorPoolSizes, mDescriptorSetLayout, mDevice)); + for(auto& p : mProfiles) + { + if(!p.layout) + continue; + p.cache.clear(); + if(p.pool) + mActiveDevice->mGraphicsQueue.queueForDeletion(p.pool); + p.pool.reset( new VulkanDescriptorPool(p.poolSizes, p.layout, mDevice)); + } } //------------------------------------------------------------------------- @@ -853,6 +1040,8 @@ namespace Ogre //------------------------------------------------------------------------- void VulkanRenderSystem::_setTexture( size_t unit, bool enabled, const TexturePtr& texPtr ) { + setStorageTexture( unit, nullptr, 0 ); + if( texPtr && enabled) { VulkanTextureGpu *tex = static_cast( texPtr.get() ); @@ -865,7 +1054,16 @@ namespace Ogre } } //------------------------------------------------------------------------- - void VulkanRenderSystem::_beginFrame( void ) {} + void VulkanRenderSystem::_beginFrame( void ) + { + if( mCurrentRenderPassDescriptor ) + mCurrentRenderPassDescriptor->mResuming = false; + + uint32 frameIdx = mDevice->mGraphicsQueue.mCurrentFrameIdx; + for( VkImageView view : mDeferredViewDeletions[frameIdx] ) + vkDestroyImageView( mDevice->mDevice, view, nullptr ); + mDeferredViewDeletions[frameIdx].clear(); + } //------------------------------------------------------------------------- void VulkanRenderSystem::_notifyActiveEncoderEnded() { @@ -881,90 +1079,141 @@ namespace Ogre endRenderPassDescriptor(); //mActiveDevice->commitAndNextCommandBuffer( SubmissionType::EndFrameAndSwap ); } - //------------------------------------------------------------------------- - void VulkanRenderSystem::flushRootLayout( void ) - { - //VulkanRootLayout *rootLayout = reinterpret_cast( mPso->rsData )->rootLayout; - //rootLayout->bind( mDevice, vaoManager, mGlobalTable ); - } - VkDescriptorSet VulkanRenderSystem::getDescriptorSet() +VkDescriptorSet VulkanRenderSystem::getDescriptorSet( DescriptorSetProfileId profile ) +{ + auto &prf = mProfiles[profile]; + if( !prf.pool ) + return VK_NULL_HANDLE; + + if( profile == Graphics || profile == AllUnits ) { - uint32 hash = HashCombine(0, mUBOInfo); + const uint32 maxTextures = prf.numTextureUnits; // see note below + uint32 hash = HashCombine( 0, mUBOInfo ); int numTextures = 0; - for (; numTextures < OGRE_MAX_TEXTURE_COORD_SETS; numTextures++) + for( ; numTextures < static_cast( maxTextures ); numTextures++ ) { - if (!mImageInfos[numTextures].imageView) + if( !mImageInfos[numTextures].imageView ) break; - hash = HashCombine(hash, mImageInfos[numTextures]); + hash = HashCombine( hash, mImageInfos[numTextures] ); } - VkDescriptorSet retVal = mDescriptorSetCache[hash]; - - if(retVal != VK_NULL_HANDLE) + VkDescriptorSet retVal = prf.cache[hash]; + if( retVal != VK_NULL_HANDLE ) return retVal; - retVal = mDescriptorPool->allocate(); + retVal = prf.pool->allocate(); - mDescriptorWrites[0].dstSet = retVal; - mDescriptorWrites[1].dstSet = retVal; - for(int i = 0; i < numTextures; i++) - { - mDescriptorWrites[i + 2].dstSet = retVal; - } + prf.writes[0].dstSet = retVal; + prf.writes[1].dstSet = retVal; + for( int i = 0; i < numTextures; i++ ) + prf.writes[i + 2].dstSet = retVal; int bindCount = numTextures + 2; - vkUpdateDescriptorSets(mActiveDevice->mDevice, bindCount, mDescriptorWrites.data(), 0, nullptr); - - mDescriptorSetCache[hash] = retVal; + vkUpdateDescriptorSets( mActiveDevice->mDevice, bindCount, prf.writes.data(), 0, nullptr ); + prf.cache[hash] = retVal; return retVal; } - VkPipeline VulkanRenderSystem::getPipeline() + if( profile != Compute ) + return VK_NULL_HANDLE; + + mComputeImageInfo = mStorageImageInfos[0]; + + if( !mComputeImageInfo.imageView ) + return VK_NULL_HANDLE; + + uint32 hash = HashCombine( 0u, mUBOInfo[1] ); + hash = HashCombine( hash, mComputeImageInfo.imageView ); + + VkDescriptorSet retVal = prf.cache[hash]; + if( retVal != VK_NULL_HANDLE ) + return retVal; + + retVal = prf.pool->allocate(); + prf.writes[0].dstSet = retVal; + prf.writes[0].pImageInfo = &mComputeImageInfo; + prf.writes[1].dstSet = retVal; + vkUpdateDescriptorSets( mActiveDevice->mDevice, 2u, prf.writes.data(), 0, nullptr ); + prf.cache[hash] = retVal; + return retVal; +} + +VkPipeline VulkanRenderSystem::getPipeline( DescriptorSetProfileId profile ) +{ + if( profile == Graphics || profile == AllUnits ) { + auto &prf = mProfiles[profile]; pipelineCi.renderPass = mCurrentRenderPassDescriptor->getRenderPass(); - pipelineCi.layout = mLayout; - mssCi.rasterizationSamples = VkSampleCountFlagBits(std::max(mActiveRenderTarget->getFSAA(), 1u)); + pipelineCi.layout = prf.pipelineLayout; + mssCi.rasterizationSamples = + VkSampleCountFlagBits( std::max( mActiveRenderTarget->getFSAA(), 1u ) ); - auto hash = HashCombine(0, pipelineCi.renderPass); - hash = HashCombine(hash, blendStates[0]); - hash = HashCombine(hash, rasterState); - hash = HashCombine(hash, inputAssemblyCi); - hash = HashCombine(hash, mssCi); + auto hash = HashCombine( 0, uint32( profile ) ); // ← the fix from two messages ago, folded in here + hash = HashCombine( hash, pipelineCi.renderPass ); + hash = HashCombine( hash, blendStates[0] ); + hash = HashCombine( hash, rasterState ); + hash = HashCombine( hash, inputAssemblyCi ); + hash = HashCombine( hash, mssCi ); + hash = HashCombine( hash, depthStencilStateCi ); - for(uint32 i = 0; i mDevice, 0, 1, &pipelineCi, 0, &retVal)); + OGRE_VK_CHECK( + vkCreateGraphicsPipelines( mActiveDevice->mDevice, 0, 1, &pipelineCi, 0, &retVal ) ); mPipelineCache[hash] = retVal; + return retVal; + } + + auto &prf = mProfiles[profile]; + if( prf.pipelineLayout == VK_NULL_HANDLE ) + return VK_NULL_HANDLE; + + auto hash = HashCombine( 0u, uint32( profile ) ); + hash = HashCombine( hash, mBoundGpuPrograms[GPT_COMPUTE_PROGRAM] ); + + VkPipeline retVal = mComputePipelineCache[hash]; + if( retVal != VK_NULL_HANDLE ) + return retVal; + + if( !mProgramBound[GPT_COMPUTE_PROGRAM] ) + return VK_NULL_HANDLE; + + const auto &computeStage = mComputeShaderStage; + if( computeStage.stage != VK_SHADER_STAGE_COMPUTE_BIT || + computeStage.module == VK_NULL_HANDLE ) + { + return VK_NULL_HANDLE; + } + + VkComputePipelineCreateInfo computeCi = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; + computeCi.layout = prf.pipelineLayout; + computeCi.stage = computeStage; + OGRE_VK_CHECK( vkCreateComputePipelines( mActiveDevice->mDevice, VK_NULL_HANDLE, 1u, &computeCi, + nullptr, &retVal ) ); + mComputePipelineCache[hash] = retVal; return retVal; } @@ -1034,9 +1283,10 @@ namespace Ogre vkCmdBindIndexBuffer(cmdBuffer, b, 0, itype); } - auto pipeline = getPipeline(); - auto descriptorSet = getDescriptorSet(); - vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineCi.layout, 0, 1, &descriptorSet, 2, + auto pipeline = getPipeline( mBoundGraphicsProfile ); + auto descriptorSet = getDescriptorSet( mBoundGraphicsProfile ); + auto& prf = mProfiles[mBoundGraphicsProfile]; + vkCmdBindDescriptorSets(cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, prf.pipelineLayout, 0, 1, &descriptorSet, 2, mUBODynOffsets.data()); vkCmdBindPipeline( cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline ); @@ -1088,21 +1338,127 @@ namespace Ogre } } //------------------------------------------------------------------------- + void VulkanRenderSystem::_dispatchCompute( const Vector3i &workgroupDim ) + { + if( !mActiveDevice ) + return; + + if( workgroupDim[0] <= 0 || workgroupDim[1] <= 0 || workgroupDim[2] <= 0 ) + return; + + if( !mProgramBound[GPT_COMPUTE_PROGRAM] ) + { + LogManager::getSingleton().logWarning( + "[Vulkan] _dispatchCompute called without a bound compute program" ); + return; + } + + mActiveDevice->mGraphicsQueue.getComputeEncoder(); + VkCommandBuffer cmdBuffer = mActiveDevice->mGraphicsQueue.mCurrentCmdBuffer; + + // DIAGNOSTIC: verify the command buffer is actually recording + if( cmdBuffer == VK_NULL_HANDLE ) + { + LogManager::getSingleton().logError("[Vulkan][Compute] cmdBuffer is NULL after getComputeEncoder"); + return; + } + + auto pipeline = getPipeline( mBoundComputeProfile ); + auto descriptorSet = getDescriptorSet( mBoundComputeProfile ); + if( pipeline == VK_NULL_HANDLE || descriptorSet == VK_NULL_HANDLE ) + { + LogManager::getSingleton().logWarning( + StringUtil::format("[Vulkan] Compute dispatch skipped: pipeline=%p descriptorSet=%p", + (void*)pipeline, (void*)descriptorSet)); + return; + } + + auto &prf = mProfiles[mBoundComputeProfile]; + const uint32 dynOffset = mUBODynOffsets[1]; + + VulkanTextureGpu *computeTexture = mStorageTextures[0]; + if( computeTexture && computeTexture->mCurrLayout != VK_IMAGE_LAYOUT_GENERAL ) + { + VkImageMemoryBarrier barrier = computeTexture->getImageMemoryBarrier(); + barrier.srcAccessMask = VulkanMappings::get( computeTexture ) & static_cast( ~VK_ACCESS_SHADER_READ_BIT ); + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_SHADER_WRITE_BIT; + barrier.oldLayout = computeTexture->mCurrLayout; + barrier.newLayout = VK_IMAGE_LAYOUT_GENERAL; + if( barrier.srcAccessMask == 0u ) + barrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; + + vkCmdPipelineBarrier( cmdBuffer, + VK_PIPELINE_STAGE_ALL_COMMANDS_BIT & mActiveDevice->mSupportedStages, + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, 0u, nullptr, 0u, nullptr, + 1u, &barrier ); + computeTexture->mCurrLayout = VK_IMAGE_LAYOUT_GENERAL; + computeTexture->mNextLayout = VK_IMAGE_LAYOUT_GENERAL; + } + + vkCmdBindPipeline( cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline ); + vkCmdBindDescriptorSets( cmdBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, prf.pipelineLayout, 0, 1, + &descriptorSet, 1, &dynOffset ); + vkCmdDispatch( cmdBuffer, static_cast( workgroupDim[0] ), + static_cast( workgroupDim[1] ), + static_cast( workgroupDim[2] ) ); + + if( computeTexture ) + { + VkImageMemoryBarrier barrier = computeTexture->getImageMemoryBarrier(); + barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT; + barrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; + barrier.oldLayout = VK_IMAGE_LAYOUT_GENERAL; + barrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + + vkCmdPipelineBarrier( cmdBuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT | + VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, + 0, 0u, nullptr, 0u, nullptr, 1u, &barrier ); + computeTexture->mCurrLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + computeTexture->mNextLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + } + } + //------------------------------------------------------------------------- void VulkanRenderSystem::bindGpuProgram(GpuProgram* prg) { - auto shader = static_cast(prg); - shaderStages[prg->getType() % GPT_PIPELINE_COUNT] = shader->getPipelineShaderStageCi(); + if(!prg) + { + LogManager::getSingleton().logError("[Vulkan] bindGpuProgram got null program"); + return; + } + + auto shader = dynamic_cast(prg); + if(!shader) + { + LogManager::getSingleton().logError( + "[Vulkan] bindGpuProgram got non-Vulkan program: name='" + prg->getName() + + "' syntax='" + prg->getSyntaxCode() + "'"); + return; + } + mBoundGpuPrograms[prg->getType()] = prg->_getHash(); + if (prg->getType() == GPT_COMPUTE_PROGRAM) + mComputeShaderStage = shader->getPipelineShaderStageCi(); + else + shaderStages[prg->getType() % GPT_PIPELINE_COUNT] = shader->getPipelineShaderStageCi(); + RenderSystem::bindGpuProgram(prg); } + //------------------------------------------------------------------------- void VulkanRenderSystem::bindGpuProgramParameters( GpuProgramType gptype, - const GpuProgramParametersPtr& params, - uint16 variabilityMask ) + const GpuProgramParametersPtr& params, + uint16 variabilityMask ) { mActiveParameters[gptype] = params; - int dstUBO = gptype % GPT_PIPELINE_COUNT; + // Graphics: vertex → slot 0, fragment → slot 1 + // Compute: always → slot 1 (matches Compute descriptor layout) + int dstUBO; + if( gptype == GPT_COMPUTE_PROGRAM ) + dstUBO = 1; + else + dstUBO = gptype % GPT_PIPELINE_COUNT; auto sizeBytes = params->getConstantList().size(); if(sizeBytes && dstUBO < 2) @@ -1114,7 +1470,6 @@ namespace Ogre if (std::accumulate(mAutoParamsBufferUsage.begin(), mAutoParamsBufferUsage.end(), 0) + step >= mAutoParamsBuffer->getSizeInBytes()) { - // ran out of UBO memory, allocate a bigger buffer resizeAutoParamsBuffer(mAutoParamsBuffer->getSizeInBytes() * 2); } @@ -1219,6 +1574,19 @@ namespace Ogre rasterState.polygonMode = VulkanMappings::get(level); } + void VulkanRenderSystem::_setPassHints( const Pass* pass ) + { + mBoundGraphicsProfile = + ( pass->getDescriptorProfileHint() == DescriptorProfileHint::AllUnits ) ? AllUnits : Graphics; + } + + ushort VulkanRenderSystem::_getCurrentPassNumTextureUnits() const + { + return ( mBoundGraphicsProfile == AllUnits ) + ? mRealCapabilities->getNumTextureUnitsWide() + : mRealCapabilities->getNumTextureUnits(); + } + void VulkanRenderSystem::_convertProjectionMatrix(const Matrix4& matrix, Matrix4& dest, bool) { dest = matrix; diff --git a/RenderSystems/Vulkan/src/OgreVulkanTextureGpu.cpp b/RenderSystems/Vulkan/src/OgreVulkanTextureGpu.cpp index 54643fec362..fc2b345273c 100644 --- a/RenderSystems/Vulkan/src/OgreVulkanTextureGpu.cpp +++ b/RenderSystems/Vulkan/src/OgreVulkanTextureGpu.cpp @@ -39,6 +39,7 @@ THE SOFTWARE. #include "OgreBitwise.h" #include "OgreRoot.h" #include "OgreVulkanTextureGpuWindow.h" +#include "OgreVulkanRenderSystem.h" #define TODO_add_resource_transitions @@ -642,6 +643,21 @@ namespace Ogre return mDefaultDisplaySrv; } //----------------------------------------------------------------------------------- + void VulkanTextureGpu::createShaderAccessPoint( uint bindPoint, TextureAccess access, + int mipmapLevel, int textureArrayIndex, + PixelFormat format ) + { + (void)access; + (void)textureArrayIndex; + (void)format; + + auto *rs = dynamic_cast( Root::getSingleton().getRenderSystem() ); + if( !rs ) + return; + + rs->setStorageTexture( bindPoint, this, mipmapLevel ); + } + //----------------------------------------------------------------------------------- VkImageMemoryBarrier VulkanTextureGpu::getImageMemoryBarrier( void ) const { VkImageMemoryBarrier imageMemBarrier = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER}; diff --git a/Samples/Media/materials/programs/GLSL400/ComputeCS.glsl b/Samples/Media/materials/programs/GLSL400/ComputeCS.glsl index b145d161473..0c5b6556bad 100644 --- a/Samples/Media/materials/programs/GLSL400/ComputeCS.glsl +++ b/Samples/Media/materials/programs/GLSL400/ComputeCS.glsl @@ -2,8 +2,9 @@ layout(binding = 0, rgba8) writeonly uniform image2D image_data; -uniform float roll; - +layout(binding = 1, std140) uniform OgreUniforms { + float roll; +}; layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in; // source/ details: http://wili.cc/blog/opengl-cs.html @@ -14,4 +15,4 @@ void main() float localCoef = length(vec2(ivec2(gl_LocalInvocationID.xy)-8)/8.0); float globalCoef = sin(float(gl_WorkGroupID.x+gl_WorkGroupID.y)*0.1 + roll)*0.5; imageStore(image_data, storePos, vec4(vec2(1.0-globalCoef*localCoef),1.0,1.0)); -} +} \ No newline at end of file diff --git a/Samples/Media/materials/scripts/Compute.material b/Samples/Media/materials/scripts/Compute.material index dacea703136..b552b385f3f 100644 --- a/Samples/Media/materials/scripts/Compute.material +++ b/Samples/Media/materials/scripts/Compute.material @@ -1,11 +1,10 @@ ////////// // GLSL // ////////// -compute_program Compute/CS_GLSL glsl +compute_program Compute/CS_GLSL glslang { source ComputeCS.glsl - syntax glsl430 - has_sampler_binding true + descriptor_set_profile Compute //TODO how to handle glMemoryBarrier? // do other programs need to support this? diff --git a/Samples/Media/materials/scripts/Examples.compositor b/Samples/Media/materials/scripts/Examples.compositor index e2bb7dd3ddf..294ce9454ac 100644 --- a/Samples/Media/materials/scripts/Examples.compositor +++ b/Samples/Media/materials/scripts/Examples.compositor @@ -738,9 +738,6 @@ compositor Compute { target_output { - // just do normal rendering - input previous - // execute compute shaders post-render pass compute { material Compute/Compositor @@ -748,6 +745,10 @@ compositor Compute // so we have to launch 16x16x1 groups thread_groups 16 16 1 } + pass render_scene + { + // just do normal rendering + } } } }