| //========= Copyright Valve Corporation ============// |
| |
| #if defined( _WIN32 ) |
| #define VK_USE_PLATFORM_WIN32_KHR |
| #else |
| #define SDL_VIDEO_DRIVER_X11 |
| #define VK_USE_PLATFORM_XLIB_KHR |
| #endif |
| #include <vulkan/vulkan.h> |
| #include <SDL.h> |
| #include <SDL_syswm.h> |
| #include <stdio.h> |
| #include <string> |
| #include <cstdlib> |
| #include <inttypes.h> |
| #include <openvr.h> |
| #include <deque> |
| |
| #include "shared/lodepng.h" |
| #include "shared/Matrices.h" |
| #include "shared/pathtools.h" |
| |
| #if defined(POSIX) |
| #include "unistd.h" |
| #endif |
| |
| #ifndef _countof |
| #define _countof(x) (sizeof(x)/sizeof((x)[0])) |
| #endif |
| |
| void ThreadSleep( unsigned long nMilliseconds ) |
| { |
| #if defined(_WIN32) |
| ::Sleep( nMilliseconds ); |
| #elif defined(POSIX) |
| usleep( nMilliseconds * 1000 ); |
| #endif |
| } |
| |
| // Pipeline state objects |
| enum PipelineStateObjectEnum_t |
| { |
| PSO_SCENE = 0, |
| PSO_AXES, |
| PSO_RENDERMODEL, |
| PSO_COMPANION, |
| PSO_COUNT |
| }; |
| |
| // Indices of descriptor sets for rendering |
| enum DescriptorSetIndex_t |
| { |
| DESCRIPTOR_SET_LEFT_EYE_SCENE = 0, |
| DESCRIPTOR_SET_RIGHT_EYE_SCENE, |
| DESCRIPTOR_SET_COMPANION_LEFT_TEXTURE, |
| DESCRIPTOR_SET_COMPANION_RIGHT_TEXTURE, |
| DESCRIPTOR_SET_LEFT_EYE_RENDER_MODEL0, |
| DESCRIPTOR_SET_LEFT_EYE_RENDER_MODEL_MAX = DESCRIPTOR_SET_LEFT_EYE_RENDER_MODEL0 + vr::k_unMaxTrackedDeviceCount, |
| DESCRIPTOR_SET_RIGHT_EYE_RENDER_MODEL0, |
| DESCRIPTOR_SET_RIGHT_EYE_RENDER_MODEL_MAX = DESCRIPTOR_SET_RIGHT_EYE_RENDER_MODEL0 + vr::k_unMaxTrackedDeviceCount, |
| NUM_DESCRIPTOR_SETS |
| }; |
| |
| class VulkanRenderModel |
| { |
| public: |
| VulkanRenderModel( const std::string & sRenderModelName ); |
| ~VulkanRenderModel(); |
| |
| bool BInit( VkDevice pDevice, const VkPhysicalDeviceMemoryProperties &memoryProperties, VkCommandBuffer pCommandBuffer, vr::TrackedDeviceIndex_t unTrackedDeviceIndex, VkDescriptorSet pDescriptorSets[ 2 ], const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ); |
| void Cleanup(); |
| void Draw( vr::EVREye nEye, VkCommandBuffer pCommandBuffer, VkPipelineLayout pPipelineLayout, const Matrix4 &matMVP ); |
| const std::string & GetName() const { return m_sModelName; } |
| |
| private: |
| VkDevice m_pDevice; |
| VkPhysicalDeviceMemoryProperties m_physicalDeviceMemoryProperties; |
| VkBuffer m_pVertexBuffer; |
| VkDeviceMemory m_pVertexBufferMemory; |
| VkBuffer m_pIndexBuffer; |
| VkDeviceMemory m_pIndexBufferMemory; |
| VkImage m_pImage; |
| VkDeviceMemory m_pImageMemory; |
| VkImageView m_pImageView; |
| VkBuffer m_pImageStagingBuffer; |
| VkDeviceMemory m_pImageStagingBufferMemory; |
| VkBuffer m_pConstantBuffer[ 2 ]; |
| VkDeviceMemory m_pConstantBufferMemory[ 2 ]; |
| void *m_pConstantBufferData[ 2 ]; |
| VkDescriptorSet m_pDescriptorSets[ 2 ]; |
| VkSampler m_pSampler; |
| |
| size_t m_unVertexCount; |
| vr::TrackedDeviceIndex_t m_unTrackedDeviceIndex; |
| std::string m_sModelName; |
| }; |
| |
| static bool g_bPrintf = true; |
| |
| // Vulkan extension entrypoints |
| static PFN_vkCreateDebugReportCallbackEXT g_pVkCreateDebugReportCallbackEXT = nullptr; |
| static PFN_vkDestroyDebugReportCallbackEXT g_pVkDestroyDebugReportCallbackEXT = nullptr; |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //------------------------------------------------------------------------------ |
| class CMainApplication |
| { |
| public: |
| CMainApplication( int argc, char *argv[] ); |
| virtual ~CMainApplication(); |
| |
| bool BInit(); |
| bool BInitVulkan(); |
| bool BInitVulkanInstance(); |
| bool BInitVulkanDevice(); |
| bool BInitVulkanSwapchain(); |
| bool BInitCompositor(); |
| bool GetVulkanInstanceExtensionsRequired( std::vector< std::string > &outInstanceExtensionList ); |
| bool GetVulkanDeviceExtensionsRequired( VkPhysicalDevice pPhysicalDevice, std::vector< std::string > &outDeviceExtensionList ); |
| |
| void SetupRenderModels(); |
| |
| void Shutdown(); |
| |
| void RunMainLoop(); |
| bool HandleInput(); |
| void ProcessVREvent( const vr::VREvent_t & event ); |
| void RenderFrame(); |
| |
| bool SetupTexturemaps(); |
| static void GenMipMapRGBA( const uint8_t *pSrc, uint8_t *ppDst, int nSrcWidth, int nSrcHeight, int *pDstWidthOut, int *pDstHeightOut ); |
| |
| void SetupScene(); |
| void AddCubeToScene( Matrix4 mat, std::vector<float> &vertdata ); |
| void AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector<float> &vertdata ); |
| |
| void UpdateControllerAxes(); |
| |
| bool SetupStereoRenderTargets(); |
| void SetupCompanionWindow(); |
| void SetupCameras(); |
| |
| void RenderStereoTargets(); |
| void RenderCompanionWindow(); |
| void RenderScene( vr::Hmd_Eye nEye ); |
| |
| Matrix4 GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ); |
| Matrix4 GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ); |
| Matrix4 GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye ); |
| void UpdateHMDMatrixPose(); |
| |
| Matrix4 ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ); |
| |
| bool CreateAllShaders(); |
| void CreateAllDescriptorSets(); |
| |
| void SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex ); |
| VulkanRenderModel *FindOrLoadRenderModel( vr::TrackedDeviceIndex_t unTrackedDeviceIndex, const char *pchRenderModelName ); |
| |
| private: |
| bool m_bDebugVulkan; |
| bool m_bVerbose; |
| bool m_bPerf; |
| bool m_bVblank; |
| int m_nMSAASampleCount; |
| // Optional scaling factor to render with supersampling (defaults off, use -scale) |
| float m_flSuperSampleScale; |
| |
| vr::IVRSystem *m_pHMD; |
| vr::IVRRenderModels *m_pRenderModels; |
| std::string m_strDriver; |
| std::string m_strDisplay; |
| vr::TrackedDevicePose_t m_rTrackedDevicePose[ vr::k_unMaxTrackedDeviceCount ]; |
| Matrix4 m_rmat4DevicePose[ vr::k_unMaxTrackedDeviceCount ]; |
| bool m_rbShowTrackedDevice[ vr::k_unMaxTrackedDeviceCount ]; |
| |
| private: // SDL bookkeeping |
| SDL_Window *m_pCompanionWindow; |
| uint32_t m_nCompanionWindowWidth; |
| uint32_t m_nCompanionWindowHeight; |
| |
| private: |
| int m_iTrackedControllerCount; |
| int m_iTrackedControllerCount_Last; |
| int m_iValidPoseCount; |
| int m_iValidPoseCount_Last; |
| bool m_bShowCubes; |
| |
| std::string m_strPoseClasses; // what classes we saw poses for this frame |
| char m_rDevClassChar[ vr::k_unMaxTrackedDeviceCount ]; // for each device, a character representing its class |
| |
| int m_iSceneVolumeWidth; |
| int m_iSceneVolumeHeight; |
| int m_iSceneVolumeDepth; |
| float m_fScaleSpacing; |
| float m_fScale; |
| |
| int m_iSceneVolumeInit; // if you want something other than the default 20x20x20 |
| |
| float m_fNearClip; |
| float m_fFarClip; |
| |
| unsigned int m_uiVertcount; |
| unsigned int m_uiCompanionWindowIndexSize; |
| |
| VkInstance m_pInstance; |
| VkDevice m_pDevice; |
| VkPhysicalDevice m_pPhysicalDevice; |
| VkQueue m_pQueue; |
| VkSurfaceKHR m_pSurface; |
| VkSwapchainKHR m_pSwapchain; |
| VkPhysicalDeviceProperties m_physicalDeviceProperties; |
| VkPhysicalDeviceMemoryProperties m_physicalDeviceMemoryProperties; |
| VkPhysicalDeviceFeatures m_physicalDeviceFeatures; |
| uint32_t m_nQueueFamilyIndex; |
| VkDebugReportCallbackEXT m_pDebugReportCallback; |
| uint32_t m_nSwapQueueImageCount; |
| uint32_t m_nFrameIndex; |
| uint32_t m_nCurrentSwapchainImage; |
| std::vector< VkImage > m_swapchainImages; |
| std::vector< VkImageView > m_pSwapchainImageViews; |
| std::vector< VkFramebuffer > m_pSwapchainFramebuffers; |
| std::vector< VkSemaphore > m_pSwapchainSemaphores; |
| VkRenderPass m_pSwapchainRenderPass; |
| |
| |
| VkCommandPool m_pCommandPool; |
| VkDescriptorPool m_pDescriptorPool; |
| VkDescriptorSet m_pDescriptorSets[ NUM_DESCRIPTOR_SETS ]; |
| |
| struct VulkanCommandBuffer_t |
| { |
| VkCommandBuffer m_pCommandBuffer; |
| VkFence m_pFence; |
| }; |
| std::deque< VulkanCommandBuffer_t > m_commandBuffers; |
| VulkanCommandBuffer_t m_currentCommandBuffer; |
| |
| VulkanCommandBuffer_t GetCommandBuffer(); |
| |
| // Scene resources |
| VkBuffer m_pSceneVertexBuffer; |
| VkDeviceMemory m_pSceneVertexBufferMemory; |
| VkBufferView m_pSceneVertexBufferView; |
| VkBuffer m_pSceneConstantBuffer[ 2 ]; |
| VkDeviceMemory m_pSceneConstantBufferMemory[ 2 ]; |
| void *m_pSceneConstantBufferData[ 2 ]; |
| VkImage m_pSceneImage; |
| VkDeviceMemory m_pSceneImageMemory; |
| VkImageView m_pSceneImageView; |
| VkBuffer m_pSceneStagingBuffer; |
| VkDeviceMemory m_pSceneStagingBufferMemory; |
| VkSampler m_pSceneSampler; |
| |
| // Storage for VS and PS for each PSO |
| VkShaderModule m_pShaderModules[ PSO_COUNT * 2 ]; |
| VkPipeline m_pPipelines[ PSO_COUNT ]; |
| VkDescriptorSetLayout m_pDescriptorSetLayout; |
| VkPipelineLayout m_pPipelineLayout; |
| VkPipelineCache m_pPipelineCache; |
| |
| // Companion window resources |
| VkBuffer m_pCompanionWindowVertexBuffer; |
| VkDeviceMemory m_pCompanionWindowVertexBufferMemory; |
| VkBuffer m_pCompanionWindowIndexBuffer; |
| VkDeviceMemory m_pCompanionWindowIndexBufferMemory; |
| |
| // Controller axes resources |
| VkBuffer m_pControllerAxesVertexBuffer; |
| VkDeviceMemory m_pControllerAxesVertexBufferMemory; |
| |
| unsigned int m_uiControllerVertcount; |
| |
| Matrix4 m_mat4HMDPose; |
| Matrix4 m_mat4eyePosLeft; |
| Matrix4 m_mat4eyePosRight; |
| |
| Matrix4 m_mat4ProjectionCenter; |
| Matrix4 m_mat4ProjectionLeft; |
| Matrix4 m_mat4ProjectionRight; |
| |
| struct VertexDataScene |
| { |
| Vector3 position; |
| Vector2 texCoord; |
| }; |
| |
| struct VertexDataWindow |
| { |
| Vector2 position; |
| Vector2 texCoord; |
| |
| VertexDataWindow( const Vector2 & pos, const Vector2 tex ) : position(pos), texCoord(tex) { } |
| }; |
| |
| struct FramebufferDesc |
| { |
| VkImage m_pImage; |
| VkImageLayout m_nImageLayout; |
| VkDeviceMemory m_pDeviceMemory; |
| VkImageView m_pImageView; |
| VkImage m_pDepthStencilImage; |
| VkImageLayout m_nDepthStencilImageLayout; |
| VkDeviceMemory m_pDepthStencilDeviceMemory; |
| VkImageView m_pDepthStencilImageView; |
| VkRenderPass m_pRenderPass; |
| VkFramebuffer m_pFramebuffer; |
| }; |
| FramebufferDesc m_leftEyeDesc; |
| FramebufferDesc m_rightEyeDesc; |
| |
| bool CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc ); |
| |
| uint32_t m_nRenderWidth; |
| uint32_t m_nRenderHeight; |
| |
| std::vector< VulkanRenderModel * > m_vecRenderModels; |
| VulkanRenderModel *m_rTrackedDeviceToRenderModel[ vr::k_unMaxTrackedDeviceCount ]; |
| }; |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Outputs a set of optional arguments to debugging output, using |
| // the printf format setting specified in fmt*. |
| //----------------------------------------------------------------------------- |
| void dprintf( const char *fmt, ... ) |
| { |
| va_list args; |
| char buffer[ 2048 ]; |
| |
| va_start( args, fmt ); |
| vsnprintf( buffer, sizeof( buffer ), fmt, args ); |
| va_end( args ); |
| |
| if ( g_bPrintf ) |
| printf( "%s", buffer ); |
| |
| OutputDebugStringA( buffer ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: VK_EXT_debug_report callback |
| //----------------------------------------------------------------------------- |
| static VkBool32 VKAPI_PTR VKDebugMessageCallback( VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType, uint64_t object, |
| size_t location, int32_t messageCode, const char* pLayerPrefix, const char *pMessage, void *pUserData ) |
| { |
| char buf[4096] = { 0 }; |
| switch ( flags ) |
| { |
| case VK_DEBUG_REPORT_ERROR_BIT_EXT: |
| sprintf( buf, "VK ERROR %s %" PRIu64 ":%d: %s\n", pLayerPrefix, uint64_t( location ), messageCode, pMessage ); |
| break; |
| case VK_DEBUG_REPORT_WARNING_BIT_EXT: |
| sprintf( buf, "VK WARNING %s %" PRIu64 ":%d: %s\n", pLayerPrefix, uint64_t( location ), messageCode, pMessage ); |
| break; |
| case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: |
| sprintf( buf, "VK PERF %s %" PRIu64 ":%d: %s\n", pLayerPrefix, uint64_t( location ), messageCode, pMessage ); |
| break; |
| case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: |
| sprintf( buf, "VK INFO %s %" PRIu64 ":%d: %s\n", pLayerPrefix, uint64_t( location ), messageCode, pMessage ); |
| break; |
| case VK_DEBUG_REPORT_DEBUG_BIT_EXT: |
| sprintf( buf, "VK DEBUG %s %" PRIu64 ":%d: %s\n", pLayerPrefix, uint64_t( location ), messageCode, pMessage ); |
| break; |
| default: |
| break; |
| } |
| |
| dprintf( "%s\n", buf ); |
| |
| return VK_FALSE; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Determine the memory type index from the memory requirements |
| // and type bits |
| //----------------------------------------------------------------------------- |
| static bool MemoryTypeFromProperties( const VkPhysicalDeviceMemoryProperties &memoryProperties, uint32_t nMemoryTypeBits, VkMemoryPropertyFlags nMemoryProperties, uint32_t *pTypeIndexOut ) |
| { |
| for ( uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++ ) |
| { |
| if ( ( nMemoryTypeBits & 1 ) == 1) |
| { |
| // Type is available, does it match user properties? |
| if ( ( memoryProperties.memoryTypes[i].propertyFlags & nMemoryProperties ) == nMemoryProperties ) |
| { |
| *pTypeIndexOut = i; |
| return true; |
| } |
| } |
| nMemoryTypeBits >>= 1; |
| } |
| |
| // No memory types matched, return failure |
| return false; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Helper function to create Vulkan static VB/IBs |
| //----------------------------------------------------------------------------- |
| static bool CreateVulkanBuffer( VkDevice pDevice, const VkPhysicalDeviceMemoryProperties &memoryProperties, const void *pBufferData, VkDeviceSize nSize, VkBufferUsageFlags nUsage, VkBuffer *ppBufferOut, VkDeviceMemory *ppDeviceMemoryOut ) |
| { |
| // Create the vertex buffer and fill with data |
| VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
| bufferCreateInfo.size = nSize; |
| bufferCreateInfo.usage = nUsage; |
| VkResult nResult = vkCreateBuffer( pDevice, &bufferCreateInfo, nullptr, ppBufferOut ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "%s - vkCreateBuffer failed with error %d\n", __FUNCTION__, nResult ); |
| return false; |
| } |
| |
| VkMemoryRequirements memoryRequirements = {}; |
| vkGetBufferMemoryRequirements( pDevice, *ppBufferOut, &memoryRequirements ); |
| |
| VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| if ( !MemoryTypeFromProperties( memoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &allocInfo.memoryTypeIndex ) ) |
| { |
| dprintf( "%s - failed to find matching memoryTypeIndex for buffer\n", __FUNCTION__ ); |
| return false; |
| } |
| allocInfo.allocationSize = memoryRequirements.size; |
| |
| nResult = vkAllocateMemory( pDevice, &allocInfo, nullptr, ppDeviceMemoryOut ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "%s - vkCreateBuffer failed with error %d\n", __FUNCTION__, nResult ); |
| return false; |
| } |
| |
| nResult = vkBindBufferMemory( pDevice, *ppBufferOut, *ppDeviceMemoryOut, 0 ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "%s vkBindBufferMemory failed with error %d\n", __FUNCTION__, nResult ); |
| return false; |
| } |
| |
| if ( pBufferData != nullptr ) |
| { |
| void *pData; |
| nResult = vkMapMemory( pDevice, *ppDeviceMemoryOut, 0, VK_WHOLE_SIZE, 0, &pData ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "%s - vkMapMemory returned error %d\n", __FUNCTION__, nResult ); |
| return false; |
| } |
| memcpy( pData, pBufferData, nSize ); |
| vkUnmapMemory( pDevice, *ppDeviceMemoryOut ); |
| |
| VkMappedMemoryRange memoryRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; |
| memoryRange.memory = *ppDeviceMemoryOut; |
| memoryRange.size = VK_WHOLE_SIZE; |
| vkFlushMappedMemoryRanges( pDevice, 1, &memoryRange ); |
| |
| } |
| return true; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Constructor |
| //----------------------------------------------------------------------------- |
| CMainApplication::CMainApplication( int argc, char *argv[] ) |
| : m_pCompanionWindow(NULL) |
| , m_nCompanionWindowWidth( 640 ) |
| , m_nCompanionWindowHeight( 320 ) |
| , m_pHMD( NULL ) |
| , m_pRenderModels( NULL ) |
| , m_bDebugVulkan( false ) |
| , m_bVerbose( false ) |
| , m_bPerf( false ) |
| , m_bVblank( false ) |
| , m_nMSAASampleCount( 4 ) |
| , m_flSuperSampleScale( 1.0f ) |
| , m_iTrackedControllerCount( 0 ) |
| , m_iTrackedControllerCount_Last( -1 ) |
| , m_iValidPoseCount( 0 ) |
| , m_iValidPoseCount_Last( -1 ) |
| , m_iSceneVolumeInit( 20 ) |
| , m_strPoseClasses("") |
| , m_bShowCubes( true ) |
| , m_pInstance( VK_NULL_HANDLE ) |
| , m_pDevice( VK_NULL_HANDLE ) |
| , m_pPhysicalDevice( VK_NULL_HANDLE ) |
| , m_pQueue( VK_NULL_HANDLE ) |
| , m_pSurface( VK_NULL_HANDLE ) |
| , m_pSwapchain( VK_NULL_HANDLE ) |
| , m_pDebugReportCallback( VK_NULL_HANDLE ) |
| , m_pCommandPool( VK_NULL_HANDLE ) |
| , m_pDescriptorPool( VK_NULL_HANDLE ) |
| , m_nSwapQueueImageCount( 0 ) |
| , m_nFrameIndex( 0 ) |
| , m_nCurrentSwapchainImage( 0 ) |
| , m_pSceneVertexBuffer( VK_NULL_HANDLE ) |
| , m_pSceneVertexBufferMemory( VK_NULL_HANDLE ) |
| , m_pSceneVertexBufferView( VK_NULL_HANDLE ) |
| , m_pSceneImage( VK_NULL_HANDLE ) |
| , m_pSceneImageMemory( VK_NULL_HANDLE ) |
| , m_pSceneImageView( VK_NULL_HANDLE ) |
| , m_pSceneStagingBuffer( VK_NULL_HANDLE ) |
| , m_pSceneStagingBufferMemory( VK_NULL_HANDLE ) |
| , m_pSceneSampler( VK_NULL_HANDLE ) |
| , m_pDescriptorSetLayout( VK_NULL_HANDLE ) |
| , m_pPipelineLayout( VK_NULL_HANDLE ) |
| , m_pPipelineCache( VK_NULL_HANDLE ) |
| , m_pCompanionWindowVertexBuffer( VK_NULL_HANDLE ) |
| , m_pCompanionWindowVertexBufferMemory( VK_NULL_HANDLE ) |
| , m_pCompanionWindowIndexBuffer( VK_NULL_HANDLE ) |
| , m_pCompanionWindowIndexBufferMemory( VK_NULL_HANDLE ) |
| , m_pControllerAxesVertexBuffer( VK_NULL_HANDLE ) |
| , m_pControllerAxesVertexBufferMemory( VK_NULL_HANDLE ) |
| { |
| memset( &m_leftEyeDesc, 0, sizeof( m_leftEyeDesc ) ); |
| memset( &m_rightEyeDesc, 0, sizeof( m_rightEyeDesc ) ); |
| memset( &m_pShaderModules[ 0 ], 0, sizeof( m_pShaderModules ) ); |
| memset( &m_pPipelines[ 0 ], 0, sizeof( m_pPipelines ) ); |
| memset( m_pSceneConstantBufferData, 0, sizeof( m_pSceneConstantBufferData ) ); |
| memset( m_pDescriptorSets, 0, sizeof( m_pDescriptorSets ) ); |
| |
| for( int i = 1; i < argc; i++ ) |
| { |
| if( !stricmp( argv[i], "-vulkandebug" ) ) |
| { |
| m_bDebugVulkan = true; |
| } |
| else if( !stricmp( argv[i], "-verbose" ) ) |
| { |
| m_bVerbose = true; |
| } |
| else if( !stricmp( argv[i], "-novblank" ) ) |
| { |
| m_bVblank = false; |
| } |
| else if ( !stricmp( argv[i], "-msaa" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) ) |
| { |
| m_nMSAASampleCount = atoi( argv[ i + 1 ] ); |
| i++; |
| } |
| else if ( !stricmp( argv[i], "-supersample" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) ) |
| { |
| m_flSuperSampleScale = ( float )atof( argv[ i + 1 ] ); |
| i++; |
| } |
| else if( !stricmp( argv[i], "-noprintf" ) ) |
| { |
| g_bPrintf = false; |
| } |
| else if ( !stricmp( argv[i], "-cubevolume" ) && ( argc > i + 1 ) && ( *argv[ i + 1 ] != '-' ) ) |
| { |
| m_iSceneVolumeInit = atoi( argv[ i + 1 ] ); |
| i++; |
| } |
| } |
| // other initialization tasks are done in BInit |
| memset( m_rDevClassChar, 0, sizeof( m_rDevClassChar ) ); |
| }; |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Destructor |
| //----------------------------------------------------------------------------- |
| CMainApplication::~CMainApplication() |
| { |
| // work is done in Shutdown |
| dprintf( "Shutdown" ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Helper to get a string from a tracked device property and turn it |
| // into a std::string |
| //----------------------------------------------------------------------------- |
| std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL ) |
| { |
| uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError ); |
| if( unRequiredBufferLen == 0 ) |
| return ""; |
| |
| char *pchBuffer = new char[ unRequiredBufferLen ]; |
| unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError ); |
| std::string sResult = pchBuffer; |
| delete [] pchBuffer; |
| return sResult; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInit() |
| { |
| if ( SDL_Init( SDL_INIT_VIDEO | SDL_INIT_TIMER ) < 0 ) |
| { |
| dprintf("%s - SDL could not initialize! SDL Error: %s\n", __FUNCTION__, SDL_GetError()); |
| return false; |
| } |
| |
| // Loading the SteamVR Runtime |
| vr::EVRInitError eError = vr::VRInitError_None; |
| m_pHMD = vr::VR_Init( &eError, vr::VRApplication_Scene ); |
| |
| if ( eError != vr::VRInitError_None ) |
| { |
| m_pHMD = NULL; |
| char buf[1024]; |
| sprintf_s( buf, sizeof( buf ), "Unable to init VR runtime: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) ); |
| SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "VR_Init Failed", buf, NULL ); |
| return false; |
| } |
| |
| m_pRenderModels = (vr::IVRRenderModels *)vr::VR_GetGenericInterface( vr::IVRRenderModels_Version, &eError ); |
| if( !m_pRenderModels ) |
| { |
| m_pHMD = NULL; |
| vr::VR_Shutdown(); |
| |
| char buf[1024]; |
| sprintf_s( buf, sizeof( buf ), "Unable to get render model interface: %s", vr::VR_GetVRInitErrorAsEnglishDescription( eError ) ); |
| SDL_ShowSimpleMessageBox( SDL_MESSAGEBOX_ERROR, "VR_Init Failed", buf, NULL ); |
| return false; |
| } |
| |
| int nWindowPosX = 700; |
| int nWindowPosY = 100; |
| Uint32 unWindowFlags = SDL_WINDOW_SHOWN; |
| |
| m_pCompanionWindow = SDL_CreateWindow( "hellovr [Vulkan]", nWindowPosX, nWindowPosY, m_nCompanionWindowWidth, m_nCompanionWindowHeight, unWindowFlags ); |
| if (m_pCompanionWindow == NULL) |
| { |
| dprintf( "%s - Window could not be created! SDL Error: %s\n", __FUNCTION__, SDL_GetError() ); |
| return false; |
| } |
| |
| m_strDriver = "No Driver"; |
| m_strDisplay = "No Display"; |
| |
| m_strDriver = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_TrackingSystemName_String ); |
| m_strDisplay = GetTrackedDeviceString( m_pHMD, vr::k_unTrackedDeviceIndex_Hmd, vr::Prop_SerialNumber_String ); |
| |
| std::string strWindowTitle = "hellovr [Vulkan] - " + m_strDriver + " " + m_strDisplay; |
| SDL_SetWindowTitle( m_pCompanionWindow, strWindowTitle.c_str() ); |
| |
| // cube array |
| m_iSceneVolumeWidth = m_iSceneVolumeInit; |
| m_iSceneVolumeHeight = m_iSceneVolumeInit; |
| m_iSceneVolumeDepth = m_iSceneVolumeInit; |
| |
| m_fScale = 0.3f; |
| m_fScaleSpacing = 4.0f; |
| |
| m_fNearClip = 0.1f; |
| m_fFarClip = 30.0f; |
| |
| m_uiVertcount = 0; |
| m_uiCompanionWindowIndexSize = 0; |
| |
| if ( !BInitVulkan() ) |
| { |
| dprintf( "%s - Unable to initialize Vulkan!\n", __FUNCTION__ ); |
| return false; |
| } |
| |
| if ( !BInitCompositor() ) |
| { |
| dprintf( "%s - Failed to initialize VR Compositor!\n", __FUNCTION__ ); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------------------- |
| // Ask OpenVR for the list of instance extensions required |
| //-------------------------------------------------------------------------------------- |
| bool CMainApplication::GetVulkanInstanceExtensionsRequired( std::vector< std::string > &outInstanceExtensionList ) |
| { |
| if ( !vr::VRCompositor() ) |
| { |
| return false; |
| } |
| |
| outInstanceExtensionList.clear(); |
| uint32_t nBufferSize = vr::VRCompositor()->GetVulkanInstanceExtensionsRequired( nullptr, 0 ); |
| if ( nBufferSize > 0 ) |
| { |
| // Allocate memory for the space separated list and query for it |
| char *pExtensionStr = new char[ nBufferSize ]; |
| pExtensionStr[0] = 0; |
| vr::VRCompositor()->GetVulkanInstanceExtensionsRequired( pExtensionStr, nBufferSize ); |
| |
| // Break up the space separated list into entries on the CUtlStringList |
| std::string curExtStr; |
| uint32_t nIndex = 0; |
| while ( pExtensionStr[ nIndex ] != 0 && ( nIndex < nBufferSize ) ) |
| { |
| if ( pExtensionStr[ nIndex ] == ' ' ) |
| { |
| outInstanceExtensionList.push_back( curExtStr ); |
| curExtStr.clear(); |
| } |
| else |
| { |
| curExtStr += pExtensionStr[ nIndex ]; |
| } |
| nIndex++; |
| } |
| if ( curExtStr.size() > 0 ) |
| { |
| outInstanceExtensionList.push_back( curExtStr ); |
| } |
| |
| delete [] pExtensionStr; |
| } |
| |
| return true; |
| } |
| |
| //-------------------------------------------------------------------------------------- |
| // Ask OpenVR for the list of device extensions required |
| //-------------------------------------------------------------------------------------- |
| bool CMainApplication::GetVulkanDeviceExtensionsRequired( VkPhysicalDevice pPhysicalDevice, std::vector< std::string > &outDeviceExtensionList ) |
| { |
| if ( !vr::VRCompositor() ) |
| { |
| return false; |
| } |
| |
| outDeviceExtensionList.clear(); |
| uint32_t nBufferSize = vr::VRCompositor()->GetVulkanDeviceExtensionsRequired( ( VkPhysicalDevice_T * ) pPhysicalDevice, nullptr, 0 ); |
| if ( nBufferSize > 0 ) |
| { |
| // Allocate memory for the space separated list and query for it |
| char *pExtensionStr = new char[ nBufferSize ]; |
| pExtensionStr[0] = 0; |
| vr::VRCompositor()->GetVulkanDeviceExtensionsRequired( ( VkPhysicalDevice_T * ) pPhysicalDevice, pExtensionStr, nBufferSize ); |
| |
| // Break up the space separated list into entries on the CUtlStringList |
| std::string curExtStr; |
| uint32_t nIndex = 0; |
| while ( pExtensionStr[ nIndex ] != 0 && ( nIndex < nBufferSize ) ) |
| { |
| if ( pExtensionStr[ nIndex ] == ' ' ) |
| { |
| outDeviceExtensionList.push_back( curExtStr ); |
| curExtStr.clear(); |
| } |
| else |
| { |
| curExtStr += pExtensionStr[ nIndex ]; |
| } |
| nIndex++; |
| } |
| if ( curExtStr.size() > 0 ) |
| { |
| outDeviceExtensionList.push_back( curExtStr ); |
| } |
| |
| delete [] pExtensionStr; |
| } |
| |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize Vulkan VkInstance |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInitVulkanInstance() |
| { |
| VkResult nResult; |
| |
| //----------------------// |
| // VkInstance creation // |
| //----------------------// |
| // Query OpenVR to determine which instance extensions need to be enabled before creating the instance |
| std::vector< std::string > requiredInstanceExtensions; |
| if ( !GetVulkanInstanceExtensionsRequired( requiredInstanceExtensions ) ) |
| { |
| dprintf( "Could not determine OpenVR Vulkan instance extensions.\n" ); |
| return false; |
| } |
| |
| // Additional required instance extensions |
| requiredInstanceExtensions.push_back( VK_KHR_SURFACE_EXTENSION_NAME ); |
| #if defined ( _WIN32 ) |
| requiredInstanceExtensions.push_back( VK_KHR_WIN32_SURFACE_EXTENSION_NAME ); |
| #else |
| requiredInstanceExtensions.push_back( VK_KHR_XLIB_SURFACE_EXTENSION_NAME ); |
| #endif |
| |
| uint32_t nEnabledLayerCount = 0; |
| VkLayerProperties *pLayerProperties = nullptr; |
| char **ppEnabledLayerNames = nullptr; |
| |
| // Enable validation layers |
| if ( m_bDebugVulkan ) |
| { |
| // OpenVR: no unique_objects when using validation with SteamVR |
| char const *pInstanceValidationLayers[] = |
| { |
| "VK_LAYER_GOOGLE_threading", |
| "VK_LAYER_LUNARG_parameter_validation", |
| "VK_LAYER_LUNARG_object_tracker", |
| "VK_LAYER_LUNARG_image", |
| "VK_LAYER_LUNARG_core_validation", |
| "VK_LAYER_LUNARG_swapchain" |
| }; |
| |
| uint32_t nInstanceLayerCount = 0; |
| VkResult nResult = vkEnumerateInstanceLayerProperties( &nInstanceLayerCount, nullptr ); |
| if ( nResult == VK_SUCCESS && nInstanceLayerCount > 0 ) |
| { |
| pLayerProperties = new VkLayerProperties[ nInstanceLayerCount ]; |
| ppEnabledLayerNames = new char*[ nInstanceLayerCount ]; |
| nResult = vkEnumerateInstanceLayerProperties( &nInstanceLayerCount, pLayerProperties ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Error vkEnumerateInstanceLayerProperties in %d\n", nResult ); |
| return false; |
| } |
| |
| uint32_t nLayerIndex = 0; |
| for ( nLayerIndex = 0; nLayerIndex < nInstanceLayerCount; nLayerIndex++ ) |
| { |
| for ( uint32_t nLayer = 0; nLayer < _countof( pInstanceValidationLayers ); nLayer++ ) |
| { |
| if ( strstr( pLayerProperties[ nLayerIndex ].layerName, pInstanceValidationLayers[ nLayer ] ) != NULL ) |
| { |
| ppEnabledLayerNames[ nEnabledLayerCount++ ] = pLayerProperties[ nLayerIndex ].layerName; |
| } |
| } |
| } |
| requiredInstanceExtensions.push_back( VK_EXT_DEBUG_REPORT_EXTENSION_NAME ); |
| } |
| } |
| |
| uint32_t nInstanceExtensionCount = 0; |
| nResult = vkEnumerateInstanceExtensionProperties( NULL, &nInstanceExtensionCount, NULL ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkEnumerateInstanceExtensionProperties failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Allocate enough ExtensionProperties to support all extensions being enabled |
| char** ppEnableInstanceExtensionNames = new char*[ requiredInstanceExtensions.size() ]; |
| int32_t nEnableInstanceExtensionNamesCount = 0; |
| VkExtensionProperties *pExtensionProperties = new VkExtensionProperties[ nInstanceExtensionCount ]; |
| if ( nInstanceExtensionCount > 0 ) |
| { |
| nResult = vkEnumerateInstanceExtensionProperties( NULL, &nInstanceExtensionCount, pExtensionProperties ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkEnumerateInstanceExtensionProperties failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| for ( size_t nExt = 0; nExt < requiredInstanceExtensions.size(); nExt++ ) |
| { |
| bool bFound = false; |
| uint32_t nExtIndex = 0; |
| for ( nExtIndex = 0; nExtIndex < nInstanceExtensionCount; nExtIndex++ ) |
| { |
| if ( strcmp( requiredInstanceExtensions[ nExt ].c_str(), pExtensionProperties[ nExtIndex ].extensionName ) == 0 ) |
| { |
| bFound = true; |
| ppEnableInstanceExtensionNames[ nEnableInstanceExtensionNamesCount++ ] = pExtensionProperties[ nExtIndex ].extensionName; |
| break; |
| } |
| } |
| |
| if ( !bFound ) |
| { |
| dprintf( "Vulkan missing requested extension '%s'.\n", requiredInstanceExtensions[ nExt ] ); |
| } |
| } |
| |
| if ( nEnableInstanceExtensionNamesCount != requiredInstanceExtensions.size() ) |
| { |
| return false; |
| } |
| } |
| |
| VkApplicationInfo appInfo = { VK_STRUCTURE_TYPE_APPLICATION_INFO }; |
| appInfo.pApplicationName = "hellovr_vulkan"; |
| appInfo.applicationVersion = 1; |
| appInfo.pEngineName = nullptr; |
| appInfo.engineVersion = 1; |
| appInfo.apiVersion = VK_MAKE_VERSION( 1, 0, 0 ); |
| |
| // Create the instance |
| VkInstanceCreateInfo instanceCreateInfo = {}; |
| instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
| instanceCreateInfo.pNext = NULL; |
| instanceCreateInfo.pApplicationInfo = &appInfo; |
| instanceCreateInfo.enabledExtensionCount = nEnableInstanceExtensionNamesCount; |
| instanceCreateInfo.ppEnabledExtensionNames = ppEnableInstanceExtensionNames; |
| instanceCreateInfo.enabledLayerCount = nEnabledLayerCount; |
| instanceCreateInfo.ppEnabledLayerNames = ppEnabledLayerNames; |
| |
| nResult = vkCreateInstance( &instanceCreateInfo, nullptr, &m_pInstance ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateInstance failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Enable debug report extension |
| if ( m_bDebugVulkan ) |
| { |
| g_pVkCreateDebugReportCallbackEXT = ( PFN_vkCreateDebugReportCallbackEXT ) vkGetInstanceProcAddr( m_pInstance, "vkCreateDebugReportCallbackEXT" ); |
| g_pVkDestroyDebugReportCallbackEXT = ( PFN_vkDestroyDebugReportCallbackEXT ) vkGetInstanceProcAddr( m_pInstance, "vkDestroyDebugReportCallbackEXT" ); |
| |
| VkDebugReportCallbackCreateInfoEXT debugReportCreateInfo = {}; |
| debugReportCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT; |
| debugReportCreateInfo.pNext = NULL; |
| debugReportCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT; |
| debugReportCreateInfo.pfnCallback = VKDebugMessageCallback; |
| debugReportCreateInfo.pUserData = NULL; |
| g_pVkCreateDebugReportCallbackEXT( m_pInstance, &debugReportCreateInfo, NULL, &m_pDebugReportCallback ); |
| } |
| |
| delete [] ppEnableInstanceExtensionNames; |
| delete [] ppEnabledLayerNames; |
| delete [] pLayerProperties; |
| delete [] pExtensionProperties; |
| |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize Vulkan VkDevice |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInitVulkanDevice() |
| { |
| uint32_t nDeviceCount = 0; |
| VkResult nResult = vkEnumeratePhysicalDevices( m_pInstance, &nDeviceCount, NULL ); |
| if ( nResult != VK_SUCCESS || nDeviceCount == 0 ) |
| { |
| dprintf( "vkEnumeratePhysicalDevices failed, unable to init and enumerate GPUs with Vulkan.\n" ); |
| return false; |
| } |
| |
| VkPhysicalDevice *pPhysicalDevices = new VkPhysicalDevice[ nDeviceCount ]; |
| nResult = vkEnumeratePhysicalDevices( m_pInstance, &nDeviceCount, pPhysicalDevices ); |
| if ( nResult != VK_SUCCESS || nDeviceCount == 0 ) |
| { |
| dprintf( "vkEnumeratePhysicalDevices failed, unable to init and enumerate GPUs with Vulkan.\n" ); |
| return false; |
| } |
| |
| // Query OpenVR for the physical device to use |
| uint64_t pHMDPhysicalDevice = 0; |
| m_pHMD->GetOutputDevice( &pHMDPhysicalDevice, vr::TextureType_Vulkan, ( VkInstance_T * ) m_pInstance ); |
| |
| // Select the HMD physical device |
| m_pPhysicalDevice = VK_NULL_HANDLE; |
| for ( uint32_t nPhysicalDevice = 0; nPhysicalDevice < nDeviceCount; nPhysicalDevice++ ) |
| { |
| if ( ( ( VkPhysicalDevice ) pHMDPhysicalDevice ) == pPhysicalDevices[ nPhysicalDevice ] ) |
| { |
| m_pPhysicalDevice = ( VkPhysicalDevice ) pHMDPhysicalDevice; |
| break; |
| } |
| } |
| if ( m_pPhysicalDevice == VK_NULL_HANDLE ) |
| { |
| // Fallback: Grab the first physical device |
| dprintf( "Failed to find GetOutputDevice VkPhysicalDevice, falling back to choosing first device.\n" ); |
| m_pPhysicalDevice = pPhysicalDevices[ 0 ]; |
| } |
| delete [] pPhysicalDevices; |
| |
| vkGetPhysicalDeviceProperties( m_pPhysicalDevice, &m_physicalDeviceProperties ); |
| vkGetPhysicalDeviceMemoryProperties( m_pPhysicalDevice, &m_physicalDeviceMemoryProperties ); |
| vkGetPhysicalDeviceFeatures( m_pPhysicalDevice, &m_physicalDeviceFeatures ); |
| |
| //--------------------// |
| // VkDevice creation // |
| //--------------------// |
| // Query OpenVR for the required device extensions for this physical device |
| std::vector< std::string > requiredDeviceExtensions; |
| GetVulkanDeviceExtensionsRequired( m_pPhysicalDevice, requiredDeviceExtensions ); |
| // Add additional required extensions |
| requiredDeviceExtensions.push_back( VK_KHR_SWAPCHAIN_EXTENSION_NAME ); |
| |
| // Find the first graphics queue |
| uint32_t nQueueCount = 0; |
| vkGetPhysicalDeviceQueueFamilyProperties( m_pPhysicalDevice, &nQueueCount, 0 ); |
| VkQueueFamilyProperties *pQueueFamilyProperties = new VkQueueFamilyProperties[ nQueueCount ]; |
| vkGetPhysicalDeviceQueueFamilyProperties( m_pPhysicalDevice, &nQueueCount, pQueueFamilyProperties ); |
| if ( nQueueCount == 0 ) |
| { |
| dprintf( "Failed to get queue properties.\n" ); |
| return false; |
| } |
| uint32_t nGraphicsQueueIndex = 0; |
| for ( nGraphicsQueueIndex = 0; nGraphicsQueueIndex < nQueueCount; nGraphicsQueueIndex++ ) |
| { |
| if ( pQueueFamilyProperties[ nGraphicsQueueIndex].queueFlags & VK_QUEUE_GRAPHICS_BIT ) |
| { |
| break; |
| } |
| } |
| if ( nGraphicsQueueIndex >= nQueueCount ) |
| { |
| dprintf( "No graphics queue found\n" ); |
| return false; |
| } |
| m_nQueueFamilyIndex = nGraphicsQueueIndex; |
| delete [] pQueueFamilyProperties; |
| |
| uint32_t nDeviceExtensionCount = 0; |
| nResult = vkEnumerateDeviceExtensionProperties( m_pPhysicalDevice, NULL, &nDeviceExtensionCount, NULL ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkEnumerateDeviceExtensionProperties failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Allocate enough ExtensionProperties to support all extensions being enabled |
| const char** ppDeviceExtensionNames = new const char* [ nDeviceExtensionCount ]; |
| uint32_t nEnabledDeviceExtensionCount = 0; |
| |
| // Enable required device extensions |
| VkExtensionProperties *pDeviceExtProperties = new VkExtensionProperties[ nDeviceExtensionCount ]; |
| memset( pDeviceExtProperties, 0, sizeof( VkExtensionProperties ) * nDeviceExtensionCount ); |
| if ( nDeviceExtensionCount > 0 ) |
| { |
| nResult = vkEnumerateDeviceExtensionProperties( m_pPhysicalDevice, NULL, &nDeviceExtensionCount, pDeviceExtProperties ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkEnumerateDeviceExtensionProperties failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| for ( size_t nRequiredDeviceExt = 0; nRequiredDeviceExt < requiredDeviceExtensions.size(); nRequiredDeviceExt++ ) |
| { |
| bool bExtFound = false; |
| for ( uint32_t nDeviceExt = 0; nDeviceExt < nDeviceExtensionCount; nDeviceExt++ ) |
| { |
| if ( stricmp( requiredDeviceExtensions[ nRequiredDeviceExt ].c_str(), pDeviceExtProperties[ nDeviceExt ].extensionName ) == 0 ) |
| { |
| bExtFound = true; |
| break; |
| } |
| } |
| |
| if ( bExtFound ) |
| { |
| ppDeviceExtensionNames[ nEnabledDeviceExtensionCount ] = requiredDeviceExtensions[ nRequiredDeviceExt ].c_str(); |
| nEnabledDeviceExtensionCount++; |
| } |
| } |
| } |
| |
| // Create the device |
| VkDeviceQueueCreateInfo deviceQueueCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO }; |
| deviceQueueCreateInfo.queueFamilyIndex = m_nQueueFamilyIndex; |
| deviceQueueCreateInfo.queueCount = 1; |
| float fQueuePriority = 1.0f; |
| deviceQueueCreateInfo.pQueuePriorities = &fQueuePriority; |
| |
| VkDeviceCreateInfo deviceCreateInfo = { VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO }; |
| deviceCreateInfo.queueCreateInfoCount = 1; |
| deviceCreateInfo.pQueueCreateInfos = &deviceQueueCreateInfo; |
| deviceCreateInfo.enabledExtensionCount = nEnabledDeviceExtensionCount; |
| deviceCreateInfo.ppEnabledExtensionNames = ppDeviceExtensionNames; |
| deviceCreateInfo.pEnabledFeatures = &m_physicalDeviceFeatures; |
| |
| nResult = vkCreateDevice( m_pPhysicalDevice, &deviceCreateInfo, nullptr, &m_pDevice ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateDevice failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Get the device queue |
| vkGetDeviceQueue( m_pDevice, m_nQueueFamilyIndex, 0, &m_pQueue ); |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize Vulkan swapchain and associated images |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInitVulkanSwapchain() |
| { |
| //----------------------// |
| // Swapchain creation // |
| //----------------------// |
| SDL_SysWMinfo wmInfo; |
| SDL_VERSION( &wmInfo.version ); |
| SDL_GetWindowWMInfo( m_pCompanionWindow, &wmInfo ); |
| VkResult nResult; |
| #ifdef VK_USE_PLATFORM_WIN32_KHR |
| VkWin32SurfaceCreateInfoKHR win32SurfaceCreateInfo = {}; |
| win32SurfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; |
| win32SurfaceCreateInfo.pNext = NULL; |
| win32SurfaceCreateInfo.flags = 0; |
| win32SurfaceCreateInfo.hinstance = GetModuleHandle( NULL ); |
| win32SurfaceCreateInfo.hwnd = ( HWND ) wmInfo.info.win.window; |
| nResult = vkCreateWin32SurfaceKHR( m_pInstance, &win32SurfaceCreateInfo, nullptr, &m_pSurface ); |
| #else |
| VkXlibSurfaceCreateInfoKHR xlibSurfaceCreateInfo = { VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR }; |
| xlibSurfaceCreateInfo.flags = 0; |
| xlibSurfaceCreateInfo.dpy = wmInfo.info.x11.display; |
| xlibSurfaceCreateInfo.window = wmInfo.info.x11.window; |
| nResult = vkCreateXlibSurfaceKHR( m_pInstance, &xlibSurfaceCreateInfo, nullptr, &m_pSurface ); |
| #endif |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Failed to create VkSurfaceKHR error %d.\n", nResult ); |
| return false; |
| } |
| |
| VkBool32 bSupportsPresent = VK_FALSE; |
| nResult = vkGetPhysicalDeviceSurfaceSupportKHR( m_pPhysicalDevice, m_nQueueFamilyIndex, m_pSurface, &bSupportsPresent ); |
| if ( nResult != VK_SUCCESS || bSupportsPresent == VK_FALSE ) |
| { |
| dprintf( "vkGetPhysicalDeviceSurfaceSupportKHR present not supported.\n" ); |
| return false; |
| } |
| |
| // Query supported swapchain formats |
| VkFormat nSwapChainFormat; |
| uint32_t nFormatIndex = 0; |
| uint32_t nNumSupportedSwapChainFormats = 0; |
| VkColorSpaceKHR nColorSpace; |
| if ( vkGetPhysicalDeviceSurfaceFormatsKHR( m_pPhysicalDevice, m_pSurface, &nNumSupportedSwapChainFormats, NULL ) != VK_SUCCESS ) |
| { |
| dprintf( "Unable to query size of supported swapchain formats.\n" ); |
| return false; |
| } |
| VkSurfaceFormatKHR *pSupportedSurfaceFormats = new VkSurfaceFormatKHR[ nNumSupportedSwapChainFormats ]; |
| if ( vkGetPhysicalDeviceSurfaceFormatsKHR( m_pPhysicalDevice, m_pSurface, &nNumSupportedSwapChainFormats, pSupportedSurfaceFormats ) != VK_SUCCESS ) |
| { |
| dprintf( "Unable to query supported swapchain formats.\n" ); |
| return false; |
| } |
| if ( nNumSupportedSwapChainFormats == 1 && pSupportedSurfaceFormats[0].format == VK_FORMAT_UNDEFINED ) |
| { |
| nSwapChainFormat = VK_FORMAT_B8G8R8A8_UNORM; |
| } |
| else |
| { |
| // Favor sRGB if it's available |
| for ( nFormatIndex = 0; nFormatIndex < nNumSupportedSwapChainFormats; nFormatIndex++ ) |
| { |
| if ( pSupportedSurfaceFormats[ nFormatIndex ].format == VK_FORMAT_B8G8R8A8_SRGB || |
| pSupportedSurfaceFormats[ nFormatIndex ].format == VK_FORMAT_R8G8B8A8_SRGB ) |
| { |
| break; |
| } |
| } |
| if ( nFormatIndex == nNumSupportedSwapChainFormats ) |
| { |
| // Default to the first one if no sRGB |
| nFormatIndex = 0; |
| } |
| nSwapChainFormat = pSupportedSurfaceFormats[ nFormatIndex ].format; |
| } |
| nColorSpace = pSupportedSurfaceFormats[ nFormatIndex ].colorSpace; |
| delete [] pSupportedSurfaceFormats; |
| |
| // Check the surface properties and formats |
| VkSurfaceCapabilitiesKHR surfaceCaps = {}; |
| nResult = vkGetPhysicalDeviceSurfaceCapabilitiesKHR( m_pPhysicalDevice, m_pSurface, &surfaceCaps ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkGetPhysicalDeviceSurfaceCapabilitiesKHR failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| uint32_t nPresentModeCount = 0; |
| nResult = vkGetPhysicalDeviceSurfacePresentModesKHR( m_pPhysicalDevice, m_pSurface, &nPresentModeCount, NULL ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkGetPhysicalDeviceSurfacePresentModesKHR failed with error %d\n", nResult ); |
| return false; |
| } |
| VkPresentModeKHR *pPresentModes = new VkPresentModeKHR[ nPresentModeCount ]; |
| nResult = vkGetPhysicalDeviceSurfacePresentModesKHR( m_pPhysicalDevice, m_pSurface, &nPresentModeCount, pPresentModes ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkGetPhysicalDeviceSurfacePresentModesKHR failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // width and height are either both -1, or both not -1. |
| VkExtent2D swapChainExtent; |
| if ( surfaceCaps.currentExtent.width == -1 ) |
| { |
| // If the surface size is undefined, the size is set to the size of the images requested. |
| swapChainExtent.width = m_nCompanionWindowWidth; |
| swapChainExtent.height = m_nCompanionWindowHeight; |
| } |
| else |
| { |
| // If the surface size is defined, the swap chain size must match |
| swapChainExtent = surfaceCaps.currentExtent; |
| } |
| |
| // VK_PRESENT_MODE_FIFO_KHR - equivalent of eglSwapInterval(1). The presentation engine waits for the next vertical blanking period to update |
| // the current image. Tearing cannot be observed. This mode must be supported by all implementations. |
| VkPresentModeKHR swapChainPresentMode = VK_PRESENT_MODE_FIFO_KHR; |
| for ( uint32_t i = 0; i < nPresentModeCount; i++ ) |
| { |
| // Order of preference for no vsync: |
| // 1. VK_PRESENT_MODE_IMMEDIATE_KHR - The presentation engine does not wait for a vertical blanking period to update the current image, |
| // meaning this mode may result in visible tearing |
| // 2. VK_PRESENT_MODE_MAILBOX_KHR - The presentation engine waits for the next vertical blanking period to update the current image. Tearing cannot be observed. |
| // An internal single-entry queue is used to hold pending presentation requests. |
| // 3. VK_PRESENT_MODE_FIFO_RELAXED_KHR - equivalent of eglSwapInterval(-1). |
| if ( pPresentModes[i] == VK_PRESENT_MODE_IMMEDIATE_KHR ) |
| { |
| // The presentation engine does not wait for a vertical blanking period to update the |
| // current image, meaning this mode may result in visible tearing |
| swapChainPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR; |
| break; |
| } |
| else if ( pPresentModes[i] == VK_PRESENT_MODE_MAILBOX_KHR ) |
| { |
| swapChainPresentMode = VK_PRESENT_MODE_MAILBOX_KHR; |
| } |
| else if ( ( swapChainPresentMode != VK_PRESENT_MODE_MAILBOX_KHR ) && |
| ( pPresentModes[i] == VK_PRESENT_MODE_FIFO_RELAXED_KHR ) ) |
| { |
| // VK_PRESENT_MODE_FIFO_RELAXED_KHR - equivalent of eglSwapInterval(-1) |
| swapChainPresentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR; |
| } |
| } |
| |
| // Have a swap queue depth of at least three frames |
| m_nSwapQueueImageCount = surfaceCaps.minImageCount; |
| if ( m_nSwapQueueImageCount < 2 ) |
| { |
| m_nSwapQueueImageCount = 2; |
| } |
| if ( ( surfaceCaps.maxImageCount > 0 ) && ( m_nSwapQueueImageCount > surfaceCaps.maxImageCount ) ) |
| { |
| // Application must settle for fewer images than desired: |
| m_nSwapQueueImageCount = surfaceCaps.maxImageCount; |
| } |
| |
| VkSurfaceTransformFlagsKHR preTransform; |
| if ( surfaceCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR ) |
| { |
| preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; |
| } |
| else |
| { |
| preTransform = surfaceCaps.currentTransform; |
| } |
| |
| VkImageUsageFlags nImageUsageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
| if ( ( surfaceCaps.supportedUsageFlags & VK_IMAGE_USAGE_TRANSFER_DST_BIT ) ) |
| { |
| nImageUsageFlags |= VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| } |
| else |
| { |
| dprintf( "Vulkan swapchain does not support VK_IMAGE_USAGE_TRANSFER_DST_BIT. Some operations may not be supported.\n" ); |
| } |
| |
| VkSwapchainCreateInfoKHR swapChainCreateInfo = {}; |
| swapChainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; |
| swapChainCreateInfo.pNext = NULL; |
| swapChainCreateInfo.surface = m_pSurface; |
| swapChainCreateInfo.minImageCount = m_nSwapQueueImageCount; |
| swapChainCreateInfo.imageFormat = nSwapChainFormat; |
| swapChainCreateInfo.imageColorSpace = nColorSpace; |
| swapChainCreateInfo.imageExtent = swapChainExtent; |
| swapChainCreateInfo.imageUsage = nImageUsageFlags; |
| swapChainCreateInfo.preTransform = ( VkSurfaceTransformFlagBitsKHR ) preTransform; |
| swapChainCreateInfo.imageArrayLayers = 1; |
| swapChainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; |
| swapChainCreateInfo.queueFamilyIndexCount = 0; |
| swapChainCreateInfo.pQueueFamilyIndices = NULL; |
| swapChainCreateInfo.presentMode = swapChainPresentMode; |
| swapChainCreateInfo.clipped = VK_TRUE; |
| if ( ( surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR ) != 0 ) |
| { |
| swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
| } |
| else if ( ( surfaceCaps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR ) != 0 ) |
| { |
| swapChainCreateInfo.compositeAlpha = VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR; |
| } |
| else |
| { |
| dprintf( "Unexpected value for VkSurfaceCapabilitiesKHR.compositeAlpha: %x\n", surfaceCaps.supportedCompositeAlpha ); |
| } |
| |
| nResult = vkCreateSwapchainKHR( m_pDevice, &swapChainCreateInfo, NULL, &m_pSwapchain ); |
| if( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateSwapchainKHR returned an error %d.\n", nResult ); |
| return false; |
| } |
| |
| nResult = vkGetSwapchainImagesKHR( m_pDevice, m_pSwapchain, &m_nSwapQueueImageCount, NULL ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkGetSwapchainImagesKHR failed with error %d\n", nResult ); |
| return false; |
| } |
| m_swapchainImages.resize( m_nSwapQueueImageCount ); |
| vkGetSwapchainImagesKHR( m_pDevice, m_pSwapchain, &m_nSwapQueueImageCount, &m_swapchainImages[ 0 ] ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkGetSwapchainImagesKHR failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Create a renderpass |
| uint32_t nTotalAttachments = 1; |
| VkAttachmentDescription attachmentDesc; |
| VkAttachmentReference attachmentReference; |
| attachmentReference.attachment = 0; |
| attachmentReference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| |
| attachmentDesc.format = nSwapChainFormat; |
| attachmentDesc.samples = VK_SAMPLE_COUNT_1_BIT; |
| attachmentDesc.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
| attachmentDesc.storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachmentDesc.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attachmentDesc.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachmentDesc.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachmentDesc.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachmentDesc.flags = 0; |
| |
| VkSubpassDescription subPassCreateInfo = { }; |
| subPassCreateInfo.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subPassCreateInfo.flags = 0; |
| subPassCreateInfo.inputAttachmentCount = 0; |
| subPassCreateInfo.pInputAttachments = NULL; |
| subPassCreateInfo.colorAttachmentCount = 1; |
| subPassCreateInfo.pColorAttachments = &attachmentReference; |
| subPassCreateInfo.pResolveAttachments = NULL; |
| subPassCreateInfo.pDepthStencilAttachment = NULL; |
| subPassCreateInfo.preserveAttachmentCount = 0; |
| subPassCreateInfo.pPreserveAttachments = NULL; |
| |
| VkRenderPassCreateInfo renderPassCreateInfo = { }; |
| renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| renderPassCreateInfo.flags = 0; |
| renderPassCreateInfo.attachmentCount = 1; |
| renderPassCreateInfo.pAttachments = &attachmentDesc; |
| renderPassCreateInfo.subpassCount = 1; |
| renderPassCreateInfo.pSubpasses = &subPassCreateInfo; |
| renderPassCreateInfo.dependencyCount = 0; |
| renderPassCreateInfo.pDependencies = NULL; |
| |
| nResult = vkCreateRenderPass( m_pDevice, &renderPassCreateInfo, NULL, &m_pSwapchainRenderPass ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateRenderPass failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Create image views and framebuffers for each swapchain image |
| for ( size_t nImage = 0; nImage < m_swapchainImages.size(); nImage++ ) |
| { |
| VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; |
| imageViewCreateInfo.flags = 0; |
| imageViewCreateInfo.image = m_swapchainImages[ nImage ]; |
| imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
| imageViewCreateInfo.format = nSwapChainFormat; |
| imageViewCreateInfo.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; |
| imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageViewCreateInfo.subresourceRange.baseMipLevel = 0; |
| imageViewCreateInfo.subresourceRange.levelCount = 1; |
| imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; |
| imageViewCreateInfo.subresourceRange.layerCount = 1; |
| VkImageView pImageView = VK_NULL_HANDLE; |
| vkCreateImageView( m_pDevice, &imageViewCreateInfo, nullptr, &pImageView ); |
| m_pSwapchainImageViews.push_back( pImageView ); |
| |
| VkImageView attachments[ 1 ] = { pImageView }; |
| VkFramebufferCreateInfo framebufferCreateInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; |
| framebufferCreateInfo.renderPass = m_pSwapchainRenderPass; |
| framebufferCreateInfo.attachmentCount = 1; |
| framebufferCreateInfo.pAttachments = &attachments[ 0 ]; |
| framebufferCreateInfo.width = m_nCompanionWindowWidth; |
| framebufferCreateInfo.height = m_nCompanionWindowHeight; |
| framebufferCreateInfo.layers = 1; |
| VkFramebuffer pFramebuffer; |
| nResult = vkCreateFramebuffer( m_pDevice, &framebufferCreateInfo, NULL, &pFramebuffer ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateFramebuffer failed with error %d.\n", nResult ); |
| return false; |
| } |
| m_pSwapchainFramebuffers.push_back( pFramebuffer ); |
| |
| VkSemaphoreCreateInfo semaphoreCreateInfo = { VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO }; |
| VkSemaphore pSemaphore = VK_NULL_HANDLE; |
| vkCreateSemaphore( m_pDevice, &semaphoreCreateInfo, nullptr, &pSemaphore ); |
| m_pSwapchainSemaphores.push_back( pSemaphore ); |
| } |
| |
| delete [] pPresentModes; |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize Vulkan. Returns true if Vulkan has been successfully |
| // initialized, false if shaders could not be created. |
| // If failure occurred in a module other than shaders, the function |
| // may return true or throw an error. |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInitVulkan() |
| { |
| if ( !BInitVulkanInstance() ) |
| return false; |
| |
| if ( !BInitVulkanDevice() ) |
| return false; |
| |
| if ( !BInitVulkanSwapchain() ) |
| return false; |
| |
| VkResult nResult; |
| |
| // Create the command pool |
| { |
| VkCommandPoolCreateInfo commandPoolCreateInfo = { VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO }; |
| commandPoolCreateInfo.queueFamilyIndex = m_nQueueFamilyIndex; |
| commandPoolCreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; |
| nResult = vkCreateCommandPool( m_pDevice, &commandPoolCreateInfo, nullptr, &m_pCommandPool ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateCommandPool returned error %d.", nResult ); |
| return false; |
| } |
| } |
| |
| // Command buffer used during resource loading |
| m_currentCommandBuffer = GetCommandBuffer(); |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; |
| commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
| vkBeginCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer, &commandBufferBeginInfo ); |
| |
| SetupTexturemaps(); |
| SetupScene(); |
| SetupCameras(); |
| SetupStereoRenderTargets(); |
| SetupCompanionWindow(); |
| |
| if( !CreateAllShaders() ) |
| return false; |
| |
| CreateAllDescriptorSets(); |
| SetupRenderModels(); |
| |
| // Submit the command buffer used during loading |
| vkEndCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer ); |
| VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; |
| submitInfo.commandBufferCount = 1; |
| submitInfo.pCommandBuffers = &m_currentCommandBuffer.m_pCommandBuffer; |
| vkQueueSubmit( m_pQueue, 1, &submitInfo, m_currentCommandBuffer.m_pFence ); |
| m_commandBuffers.push_front( m_currentCommandBuffer ); |
| |
| m_currentCommandBuffer.m_pCommandBuffer = VK_NULL_HANDLE; |
| m_currentCommandBuffer.m_pFence = VK_NULL_HANDLE; |
| |
| // Wait for the GPU before proceeding |
| vkQueueWaitIdle( m_pQueue ); |
| |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Initialize Compositor. Returns true if the compositor was |
| // successfully initialized, false otherwise. |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::BInitCompositor() |
| { |
| vr::EVRInitError peError = vr::VRInitError_None; |
| |
| if ( !vr::VRCompositor() ) |
| { |
| dprintf( "Compositor initialization failed. See log file for details\n" ); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::Shutdown() |
| { |
| if ( m_pDevice != VK_NULL_HANDLE ) |
| { |
| // Idle the device to make sure no work is outstanding |
| vkDeviceWaitIdle( m_pDevice ); |
| } |
| |
| if( m_pHMD ) |
| { |
| vr::VR_Shutdown(); |
| m_pHMD = NULL; |
| } |
| |
| for( std::vector< VulkanRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ ) |
| { |
| delete (*i); |
| } |
| m_vecRenderModels.clear(); |
| |
| if ( m_pDevice != VK_NULL_HANDLE ) |
| { |
| for( std::deque< VulkanCommandBuffer_t >::iterator i = m_commandBuffers.begin(); i != m_commandBuffers.end(); i++ ) |
| { |
| vkFreeCommandBuffers( m_pDevice, m_pCommandPool, 1, &i->m_pCommandBuffer ); |
| vkDestroyFence( m_pDevice, i->m_pFence, nullptr ); |
| } |
| |
| vkDestroyCommandPool( m_pDevice, m_pCommandPool, nullptr ); |
| vkDestroyDescriptorPool( m_pDevice, m_pDescriptorPool, nullptr ); |
| |
| FramebufferDesc *pFramebufferDescs[2] = { &m_leftEyeDesc, &m_rightEyeDesc }; |
| for ( int32_t nFramebuffer = 0; nFramebuffer < 2; nFramebuffer++ ) |
| { |
| if ( pFramebufferDescs[ nFramebuffer ]->m_pImageView != VK_NULL_HANDLE ) |
| { |
| vkDestroyImageView( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pImageView, nullptr ); |
| vkDestroyImage( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pImage, nullptr ); |
| vkFreeMemory( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pDeviceMemory, nullptr ); |
| vkDestroyImageView( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pDepthStencilImageView, nullptr ); |
| vkDestroyImage( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pDepthStencilImage, nullptr ); |
| vkFreeMemory( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pDepthStencilDeviceMemory, nullptr ); |
| vkDestroyRenderPass( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pRenderPass, nullptr ); |
| vkDestroyFramebuffer( m_pDevice, pFramebufferDescs[ nFramebuffer ]->m_pFramebuffer, nullptr ); |
| } |
| } |
| |
| vkDestroyImageView( m_pDevice, m_pSceneImageView, nullptr ); |
| vkDestroyImage( m_pDevice, m_pSceneImage, nullptr ); |
| vkFreeMemory( m_pDevice, m_pSceneImageMemory, nullptr ); |
| vkDestroyBuffer( m_pDevice, m_pSceneStagingBuffer, nullptr ); |
| vkFreeMemory( m_pDevice, m_pSceneStagingBufferMemory, nullptr ); |
| vkDestroySampler( m_pDevice, m_pSceneSampler, nullptr ); |
| vkDestroyBuffer( m_pDevice, m_pSceneVertexBuffer, nullptr ); |
| vkFreeMemory( m_pDevice, m_pSceneVertexBufferMemory, nullptr ); |
| for ( uint32_t nEye = 0; nEye < _countof( m_pSceneConstantBuffer); nEye++ ) |
| { |
| vkDestroyBuffer( m_pDevice, m_pSceneConstantBuffer[ nEye ], nullptr ); |
| vkFreeMemory( m_pDevice, m_pSceneConstantBufferMemory[ nEye ], nullptr ); |
| } |
| |
| vkDestroyBuffer( m_pDevice, m_pCompanionWindowVertexBuffer, nullptr ); |
| vkFreeMemory( m_pDevice, m_pCompanionWindowVertexBufferMemory, nullptr ); |
| vkDestroyBuffer( m_pDevice, m_pCompanionWindowIndexBuffer, nullptr ); |
| vkFreeMemory( m_pDevice, m_pCompanionWindowIndexBufferMemory, nullptr ); |
| |
| vkDestroyBuffer( m_pDevice, m_pControllerAxesVertexBuffer, nullptr ); |
| vkFreeMemory( m_pDevice, m_pControllerAxesVertexBufferMemory, nullptr ); |
| |
| vkDestroyPipelineLayout( m_pDevice, m_pPipelineLayout, nullptr ); |
| vkDestroyDescriptorSetLayout( m_pDevice, m_pDescriptorSetLayout, nullptr ); |
| for ( uint32_t nPSO = 0; nPSO < PSO_COUNT; nPSO++ ) |
| { |
| vkDestroyPipeline( m_pDevice, m_pPipelines[ nPSO ], nullptr ); |
| } |
| for ( uint32_t nShader = 0; nShader < _countof( m_pShaderModules); nShader++ ) |
| { |
| vkDestroyShaderModule( m_pDevice, m_pShaderModules[ nShader ], nullptr ); |
| } |
| vkDestroyPipelineCache( m_pDevice, m_pPipelineCache, nullptr ); |
| |
| if ( m_pDebugReportCallback != VK_NULL_HANDLE ) |
| { |
| g_pVkDestroyDebugReportCallbackEXT( m_pInstance, m_pDebugReportCallback, nullptr ); |
| } |
| |
| for ( size_t nSwapchainIndex = 0; nSwapchainIndex < m_pSwapchainFramebuffers.size(); nSwapchainIndex++ ) |
| { |
| vkDestroyFramebuffer( m_pDevice, m_pSwapchainFramebuffers[ nSwapchainIndex ], nullptr ); |
| vkDestroyImageView( m_pDevice, m_pSwapchainImageViews[ nSwapchainIndex ], nullptr ); |
| vkDestroySemaphore( m_pDevice, m_pSwapchainSemaphores[ nSwapchainIndex ], nullptr ); |
| } |
| vkDestroyRenderPass( m_pDevice, m_pSwapchainRenderPass, nullptr ); |
| |
| vkDestroySwapchainKHR( m_pDevice, m_pSwapchain, nullptr ); |
| vkDestroySurfaceKHR( m_pInstance, m_pSurface, nullptr ); |
| vkDestroyDevice( m_pDevice, nullptr ); |
| vkDestroyInstance( m_pInstance, nullptr ); |
| } |
| if( m_pCompanionWindow ) |
| { |
| SDL_DestroyWindow(m_pCompanionWindow); |
| } |
| |
| SDL_Quit(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::HandleInput() |
| { |
| SDL_Event sdlEvent; |
| bool bRet = false; |
| |
| while ( SDL_PollEvent( &sdlEvent ) != 0 ) |
| { |
| if ( sdlEvent.type == SDL_QUIT ) |
| { |
| bRet = true; |
| } |
| else if ( sdlEvent.type == SDL_KEYDOWN ) |
| { |
| if ( sdlEvent.key.keysym.sym == SDLK_ESCAPE |
| || sdlEvent.key.keysym.sym == SDLK_q ) |
| { |
| bRet = true; |
| } |
| if( sdlEvent.key.keysym.sym == SDLK_c ) |
| { |
| m_bShowCubes = !m_bShowCubes; |
| } |
| } |
| } |
| |
| // Process SteamVR events |
| vr::VREvent_t event; |
| while( m_pHMD->PollNextEvent( &event, sizeof( event ) ) ) |
| { |
| ProcessVREvent( event ); |
| } |
| |
| // Process SteamVR controller state |
| for( vr::TrackedDeviceIndex_t unDevice = 0; unDevice < vr::k_unMaxTrackedDeviceCount; unDevice++ ) |
| { |
| vr::VRControllerState_t state; |
| if( m_pHMD->GetControllerState( unDevice, &state, sizeof(state) ) ) |
| { |
| m_rbShowTrackedDevice[ unDevice ] = state.ulButtonPressed == 0; |
| } |
| } |
| |
| return bRet; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::RunMainLoop() |
| { |
| bool bQuit = false; |
| |
| SDL_StartTextInput(); |
| SDL_ShowCursor( SDL_DISABLE ); |
| |
| while ( !bQuit ) |
| { |
| bQuit = HandleInput(); |
| |
| RenderFrame(); |
| } |
| |
| SDL_StopTextInput(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Processes a single VR event |
| //----------------------------------------------------------------------------- |
| void CMainApplication::ProcessVREvent( const vr::VREvent_t & event ) |
| { |
| switch( event.eventType ) |
| { |
| case vr::VREvent_TrackedDeviceActivated: |
| { |
| SetupRenderModelForTrackedDevice( event.trackedDeviceIndex ); |
| dprintf( "Device %u attached. Setting up render model.\n", event.trackedDeviceIndex ); |
| } |
| break; |
| case vr::VREvent_TrackedDeviceDeactivated: |
| { |
| dprintf( "Device %u detached.\n", event.trackedDeviceIndex ); |
| } |
| break; |
| case vr::VREvent_TrackedDeviceUpdated: |
| { |
| dprintf( "Device %u updated.\n", event.trackedDeviceIndex ); |
| } |
| break; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::RenderFrame() |
| { |
| if ( m_pHMD ) |
| { |
| m_currentCommandBuffer = GetCommandBuffer(); |
| |
| // Start the command buffer |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; |
| commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
| vkBeginCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer, &commandBufferBeginInfo ); |
| |
| UpdateControllerAxes(); |
| RenderStereoTargets(); |
| RenderCompanionWindow(); |
| |
| // End the command buffer |
| vkEndCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer ); |
| |
| // Submit the command buffer |
| VkPipelineStageFlags nWaitDstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
| VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; |
| submitInfo.commandBufferCount = 1; |
| submitInfo.pCommandBuffers = &m_currentCommandBuffer.m_pCommandBuffer; |
| submitInfo.waitSemaphoreCount = 1; |
| submitInfo.pWaitSemaphores = &m_pSwapchainSemaphores[ m_nFrameIndex ]; |
| submitInfo.pWaitDstStageMask = &nWaitDstStageMask; |
| vkQueueSubmit( m_pQueue, 1, &submitInfo, m_currentCommandBuffer.m_pFence ); |
| |
| // Add the command buffer back for later recycling |
| m_commandBuffers.push_front( m_currentCommandBuffer ); |
| |
| m_currentCommandBuffer.m_pCommandBuffer = VK_NULL_HANDLE; |
| m_currentCommandBuffer.m_pFence = VK_NULL_HANDLE; |
| |
| // Submit to SteamVR |
| vr::VRTextureBounds_t bounds; |
| bounds.uMin = 0.0f; |
| bounds.uMax = 1.0f; |
| bounds.vMin = 0.0f; |
| bounds.vMax = 1.0f; |
| |
| vr::VRVulkanTextureData_t vulkanData; |
| vulkanData.m_nImage = ( uint64_t ) m_leftEyeDesc.m_pImage; |
| vulkanData.m_pDevice = ( VkDevice_T * ) m_pDevice; |
| vulkanData.m_pPhysicalDevice = ( VkPhysicalDevice_T * ) m_pPhysicalDevice; |
| vulkanData.m_pInstance = ( VkInstance_T *) m_pInstance; |
| vulkanData.m_pQueue = ( VkQueue_T * ) m_pQueue; |
| vulkanData.m_nQueueFamilyIndex = m_nQueueFamilyIndex; |
| |
| vulkanData.m_nWidth = m_nRenderWidth; |
| vulkanData.m_nHeight = m_nRenderHeight; |
| vulkanData.m_nFormat = VK_FORMAT_R8G8B8A8_SRGB; |
| vulkanData.m_nSampleCount = m_nMSAASampleCount; |
| |
| vr::Texture_t texture = { &vulkanData, vr::TextureType_Vulkan, vr::ColorSpace_Auto }; |
| vr::VRCompositor()->Submit( vr::Eye_Left, &texture, &bounds ); |
| |
| vulkanData.m_nImage = ( uint64_t ) m_rightEyeDesc.m_pImage; |
| vr::VRCompositor()->Submit( vr::Eye_Right, &texture, &bounds ); |
| } |
| |
| VkPresentInfoKHR presentInfo = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR }; |
| presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; |
| presentInfo.pNext = NULL; |
| presentInfo.swapchainCount = 1; |
| presentInfo.pSwapchains = &m_pSwapchain; |
| presentInfo.pImageIndices = &m_nCurrentSwapchainImage; |
| vkQueuePresentKHR( m_pQueue, &presentInfo ); |
| |
| // Spew out the controller and pose count whenever they change. |
| if ( m_iTrackedControllerCount != m_iTrackedControllerCount_Last || m_iValidPoseCount != m_iValidPoseCount_Last ) |
| { |
| m_iValidPoseCount_Last = m_iValidPoseCount; |
| m_iTrackedControllerCount_Last = m_iTrackedControllerCount; |
| |
| dprintf( "PoseCount:%d(%s) Controllers:%d\n", m_iValidPoseCount, m_strPoseClasses.c_str(), m_iTrackedControllerCount ); |
| } |
| |
| UpdateHMDMatrixPose(); |
| |
| m_nFrameIndex = ( m_nFrameIndex + 1 ) % m_swapchainImages.size(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Creates all the shaders used by HelloVR Vulkan |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::CreateAllShaders() |
| { |
| VkResult nResult; |
| std::string sExecutableDirectory = Path_StripFilename( Path_GetExecutablePath() ); |
| |
| const char *pShaderNames[ PSO_COUNT ] = |
| { |
| "scene", |
| "axes", |
| "rendermodel", |
| "companion" |
| }; |
| const char *pStageNames[ 2 ] = |
| { |
| "vs", |
| "ps" |
| }; |
| |
| // Load the SPIR-V into shader modules |
| for ( int32_t nShader = 0; nShader < PSO_COUNT; nShader++ ) |
| { |
| for ( int32_t nStage = 0; nStage <= 1; nStage++ ) |
| { |
| char shaderFileName[ 1024 ]; |
| sprintf( shaderFileName, "../shaders/%s_%s.spv", pShaderNames[ nShader ], pStageNames[ nStage ] ); |
| std::string shaderPath = Path_MakeAbsolute( shaderFileName, sExecutableDirectory ); |
| |
| FILE *fp = fopen( shaderPath.c_str(), "rb" ); |
| if ( fp == NULL ) |
| { |
| dprintf( "Error opening SPIR-V file: %s\n", shaderPath.c_str() ); |
| return false; |
| } |
| fseek( fp, 0, SEEK_END ); |
| size_t nSize = ftell( fp ); |
| fseek( fp, 0, SEEK_SET ); |
| |
| char *pBuffer = new char[ nSize ]; |
| if ( fread( pBuffer, 1, nSize, fp ) != nSize ) |
| { |
| dprintf( "Error reading SPIR-V file: %s\n", shaderPath.c_str() ); |
| return false; |
| } |
| fclose( fp ); |
| |
| // Create the shader module |
| VkShaderModuleCreateInfo shaderModuleCreateInfo = { VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO }; |
| shaderModuleCreateInfo.codeSize = nSize; |
| shaderModuleCreateInfo.pCode = ( const uint32_t *) pBuffer; |
| nResult = vkCreateShaderModule( m_pDevice, &shaderModuleCreateInfo, nullptr, &m_pShaderModules[ nShader * 2 + nStage ] ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Error creating shader module for %s, error %d\n", shaderPath.c_str(), nResult ); |
| return false; |
| } |
| |
| delete [] pBuffer; |
| } |
| } |
| |
| // Create a descriptor set layout/pipeline layout compatible with all of our shaders. See bin/shaders/build_vulkan_shaders.bat for |
| // how the HLSL is compiled with glslangValidator and binding numbers are generated |
| VkDescriptorSetLayoutBinding layoutBindings[3] = {}; |
| layoutBindings[0].binding = 0; |
| layoutBindings[0].descriptorCount = 1; |
| layoutBindings[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| layoutBindings[0].stageFlags = VK_SHADER_STAGE_VERTEX_BIT; |
| |
| layoutBindings[1].binding = 1; |
| layoutBindings[1].descriptorCount = 1; |
| layoutBindings[1].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; |
| layoutBindings[1].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; |
| |
| layoutBindings[2].binding = 2; |
| layoutBindings[2].descriptorCount = 1; |
| layoutBindings[2].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; |
| layoutBindings[2].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; |
| |
| VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO }; |
| descriptorSetLayoutCreateInfo.bindingCount = 3; |
| descriptorSetLayoutCreateInfo.pBindings = &layoutBindings[ 0 ]; |
| nResult = vkCreateDescriptorSetLayout( m_pDevice, &descriptorSetLayoutCreateInfo, nullptr, &m_pDescriptorSetLayout ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateDescriptorSetLayout failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO }; |
| pipelineLayoutCreateInfo.pNext = NULL; |
| pipelineLayoutCreateInfo.setLayoutCount = 1; |
| pipelineLayoutCreateInfo.pSetLayouts = &m_pDescriptorSetLayout; |
| pipelineLayoutCreateInfo.pushConstantRangeCount = 0; |
| pipelineLayoutCreateInfo.pPushConstantRanges = NULL; |
| nResult = vkCreatePipelineLayout( m_pDevice, &pipelineLayoutCreateInfo, nullptr, &m_pPipelineLayout ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreatePipelineLayout failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Create pipeline cache |
| VkPipelineCacheCreateInfo pipelineCacheCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO }; |
| vkCreatePipelineCache( m_pDevice, &pipelineCacheCreateInfo, NULL, &m_pPipelineCache ); |
| |
| // Renderpass for each PSO that is compatible with what it will render to |
| VkRenderPass pRenderPasses[ PSO_COUNT ] = |
| { |
| m_leftEyeDesc.m_pRenderPass, |
| m_leftEyeDesc.m_pRenderPass, |
| m_leftEyeDesc.m_pRenderPass, |
| m_pSwapchainRenderPass |
| }; |
| |
| size_t nStrides[ PSO_COUNT ] = |
| { |
| sizeof( VertexDataScene ), // PSO_SCENE |
| sizeof( float ) * 6, // PSO_AXES |
| sizeof( vr::RenderModel_Vertex_t ), // PSO_RENDERMODEL |
| sizeof( VertexDataWindow ) // PSO_COMPANION |
| }; |
| |
| VkVertexInputAttributeDescription attributeDescriptions[ PSO_COUNT * 3 ] |
| { |
| // PSO_SCENE |
| { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }, |
| { 1, 0, VK_FORMAT_R32G32_SFLOAT, offsetof( VertexDataScene, texCoord ) }, |
| { 0, 0, VK_FORMAT_UNDEFINED, 0 }, |
| // PSO_AXES |
| { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }, |
| { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, sizeof( float ) * 3 }, |
| { 0, 0, VK_FORMAT_UNDEFINED, 0 }, |
| // PSO_RENDERMODEL |
| { 0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0 }, |
| { 1, 0, VK_FORMAT_R32G32B32_SFLOAT, offsetof( vr::RenderModel_Vertex_t, vNormal ) }, |
| { 2, 0, VK_FORMAT_R32G32_SFLOAT, offsetof( vr::RenderModel_Vertex_t, rfTextureCoord ) }, |
| // PSO_COMPANION |
| { 0, 0, VK_FORMAT_R32G32_SFLOAT, 0 }, |
| { 1, 0, VK_FORMAT_R32G32_SFLOAT, sizeof( float ) * 2 }, |
| { 0, 0, VK_FORMAT_UNDEFINED, 0 }, |
| }; |
| |
| // Create the PSOs |
| for ( uint32_t nPSO = 0; nPSO < PSO_COUNT; nPSO++ ) |
| { |
| VkGraphicsPipelineCreateInfo pipelineCreateInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO }; |
| |
| // VkPipelineVertexInputStateCreateInfo |
| VkVertexInputBindingDescription bindingDescription; |
| bindingDescription.binding = 0; |
| bindingDescription.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; |
| bindingDescription.stride = nStrides[ nPSO ]; |
| |
| VkPipelineVertexInputStateCreateInfo vertexInputCreateInfo = { VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO }; |
| for ( uint32_t nAttr = 0; nAttr < 3; nAttr++ ) |
| { |
| if ( attributeDescriptions[ nPSO * 3 + nAttr ].format != VK_FORMAT_UNDEFINED ) |
| { |
| vertexInputCreateInfo.vertexAttributeDescriptionCount++; |
| } |
| } |
| vertexInputCreateInfo.pVertexAttributeDescriptions = &attributeDescriptions[ nPSO * 3 ]; |
| vertexInputCreateInfo.vertexBindingDescriptionCount = 1; |
| vertexInputCreateInfo.pVertexBindingDescriptions = &bindingDescription; |
| |
| // VkPipelineDepthStencilStateCreateInfo |
| VkPipelineDepthStencilStateCreateInfo dsState = { VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO }; |
| dsState.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO; |
| dsState.depthTestEnable = ( nPSO != PSO_COMPANION ) ? VK_TRUE : VK_FALSE; |
| dsState.depthWriteEnable = ( nPSO != PSO_COMPANION ) ? VK_TRUE : VK_FALSE; |
| dsState.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL; |
| dsState.depthBoundsTestEnable = VK_FALSE; |
| dsState.stencilTestEnable = VK_FALSE; |
| dsState.minDepthBounds = 0.0f; |
| dsState.maxDepthBounds = 0.0f; |
| |
| // VkPipelineColorBlendStateCreateInfo |
| VkPipelineColorBlendStateCreateInfo cbState = { VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO }; |
| cbState.logicOpEnable = VK_FALSE; |
| cbState.logicOp = VK_LOGIC_OP_COPY; |
| VkPipelineColorBlendAttachmentState cbAttachmentState = {}; |
| cbAttachmentState.blendEnable = VK_FALSE; |
| cbAttachmentState.colorWriteMask = 0xf; |
| cbState.attachmentCount = 1; |
| cbState.pAttachments = &cbAttachmentState; |
| |
| // VkPipelineColorBlendStateCreateInfo |
| VkPipelineRasterizationStateCreateInfo rsState = { VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO }; |
| rsState.polygonMode = VK_POLYGON_MODE_FILL; |
| rsState.cullMode = VK_CULL_MODE_BACK_BIT; |
| rsState.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE; |
| rsState.lineWidth = 1.0f; |
| |
| // VkPipelineInputAssemblyStateCreateInfo |
| VkPipelineInputAssemblyStateCreateInfo iaState = { VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO }; |
| iaState.topology = ( nPSO == PSO_AXES ) ? VK_PRIMITIVE_TOPOLOGY_LINE_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; |
| iaState.primitiveRestartEnable = VK_FALSE; |
| |
| // VkPipelineMultisampleStateCreateInfo |
| VkPipelineMultisampleStateCreateInfo msState = { VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO }; |
| msState.rasterizationSamples = ( nPSO == PSO_COMPANION ) ? VK_SAMPLE_COUNT_1_BIT : ( VkSampleCountFlagBits ) m_nMSAASampleCount; |
| msState.minSampleShading = 0.0f; |
| uint32_t nSampleMask = 0xFFFFFFFF; |
| msState.pSampleMask = &nSampleMask; |
| |
| // VkPipelineViewportStateCreateInfo |
| VkPipelineViewportStateCreateInfo vpState = { VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO }; |
| vpState.viewportCount = 1; |
| vpState.scissorCount = 1; |
| |
| VkPipelineShaderStageCreateInfo shaderStages[ 2 ] = { }; |
| shaderStages[ 0 ].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| shaderStages[ 0 ].stage = VK_SHADER_STAGE_VERTEX_BIT; |
| shaderStages[ 0 ].module = m_pShaderModules[ nPSO * 2 + 0 ]; |
| shaderStages[ 0 ].pName = "VSMain"; |
| |
| shaderStages[ 1 ].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; |
| shaderStages[ 1 ].stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
| shaderStages[ 1 ].module = m_pShaderModules[ nPSO * 2 + 1 ]; |
| shaderStages[ 1 ].pName = "PSMain"; |
| |
| pipelineCreateInfo.layout = m_pPipelineLayout; |
| |
| // Set pipeline states |
| pipelineCreateInfo.pVertexInputState = &vertexInputCreateInfo; |
| pipelineCreateInfo.pInputAssemblyState = &iaState; |
| pipelineCreateInfo.pViewportState = &vpState; |
| pipelineCreateInfo.pRasterizationState = &rsState; |
| pipelineCreateInfo.pMultisampleState = &msState; |
| pipelineCreateInfo.pDepthStencilState = &dsState; |
| pipelineCreateInfo.pColorBlendState = &cbState; |
| pipelineCreateInfo.stageCount = 2; |
| pipelineCreateInfo.pStages = &shaderStages[ 0 ]; |
| pipelineCreateInfo.renderPass = pRenderPasses[ nPSO ]; |
| |
| static VkDynamicState dynamicStates[] = |
| { |
| VK_DYNAMIC_STATE_VIEWPORT, |
| VK_DYNAMIC_STATE_SCISSOR, |
| }; |
| |
| static VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo = {}; |
| dynamicStateCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; |
| dynamicStateCreateInfo.pNext = NULL; |
| dynamicStateCreateInfo.dynamicStateCount = _countof( dynamicStates ); |
| dynamicStateCreateInfo.pDynamicStates = &dynamicStates[ 0 ]; |
| pipelineCreateInfo.pDynamicState = &dynamicStateCreateInfo; |
| |
| |
| // Create the pipeline |
| nResult = vkCreateGraphicsPipelines( m_pDevice, m_pPipelineCache, 1, &pipelineCreateInfo, NULL, &m_pPipelines[ nPSO ] ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateGraphicsPipelines failed with error %d\n", nResult ); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Creates all the descriptor sets |
| //----------------------------------------------------------------------------- |
| void CMainApplication::CreateAllDescriptorSets() |
| { |
| VkDescriptorPoolSize poolSizes[ 3 ]; |
| poolSizes[ 0 ].descriptorCount = NUM_DESCRIPTOR_SETS; |
| poolSizes[ 0 ].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| poolSizes[ 1 ].descriptorCount = NUM_DESCRIPTOR_SETS; |
| poolSizes[ 1 ].type = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; |
| poolSizes[ 2 ].descriptorCount = NUM_DESCRIPTOR_SETS; |
| poolSizes[ 2 ].type = VK_DESCRIPTOR_TYPE_SAMPLER; |
| |
| VkDescriptorPoolCreateInfo descriptorPoolCreateInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; |
| descriptorPoolCreateInfo.flags = 0; |
| descriptorPoolCreateInfo.maxSets = NUM_DESCRIPTOR_SETS; |
| descriptorPoolCreateInfo.poolSizeCount = _countof( poolSizes ); |
| descriptorPoolCreateInfo.pPoolSizes = &poolSizes[ 0 ]; |
| |
| vkCreateDescriptorPool( m_pDevice, &descriptorPoolCreateInfo, nullptr, &m_pDescriptorPool ); |
| |
| for ( int nDescriptorSet = 0; nDescriptorSet < NUM_DESCRIPTOR_SETS; nDescriptorSet++ ) |
| { |
| VkDescriptorSetAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO }; |
| allocInfo.descriptorPool = m_pDescriptorPool; |
| allocInfo.descriptorSetCount = 1; |
| allocInfo.pSetLayouts = &m_pDescriptorSetLayout; |
| vkAllocateDescriptorSets( m_pDevice, &allocInfo, &m_pDescriptorSets[ nDescriptorSet ] ); |
| } |
| |
| // Scene descriptor sets |
| for ( uint32_t nEye = 0; nEye < 2; nEye++ ) |
| { |
| VkDescriptorBufferInfo bufferInfo = {}; |
| bufferInfo.buffer = m_pSceneConstantBuffer[ nEye ]; |
| bufferInfo.offset = 0; |
| bufferInfo.range = VK_WHOLE_SIZE; |
| |
| VkDescriptorImageInfo imageInfo = {}; |
| imageInfo.imageView = m_pSceneImageView; |
| imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| |
| VkDescriptorImageInfo samplerInfo = {}; |
| samplerInfo.sampler = m_pSceneSampler; |
| |
| VkWriteDescriptorSet writeDescriptorSets[ 3 ] = { }; |
| writeDescriptorSets[ 0 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 0 ].dstSet = m_pDescriptorSets[ DESCRIPTOR_SET_LEFT_EYE_SCENE + nEye ]; |
| writeDescriptorSets[ 0 ].dstBinding = 0; |
| writeDescriptorSets[ 0 ].descriptorCount = 1; |
| writeDescriptorSets[ 0 ].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| writeDescriptorSets[ 0 ].pBufferInfo = &bufferInfo; |
| writeDescriptorSets[ 1 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 1 ].dstSet = m_pDescriptorSets[ DESCRIPTOR_SET_LEFT_EYE_SCENE + nEye ]; |
| writeDescriptorSets[ 1 ].dstBinding = 1; |
| writeDescriptorSets[ 1 ].descriptorCount = 1; |
| writeDescriptorSets[ 1 ].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; |
| writeDescriptorSets[ 1 ].pImageInfo = &imageInfo; |
| writeDescriptorSets[ 2 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 2 ].dstSet = m_pDescriptorSets[ DESCRIPTOR_SET_LEFT_EYE_SCENE + nEye ]; |
| writeDescriptorSets[ 2 ].dstBinding = 2; |
| writeDescriptorSets[ 2 ].descriptorCount = 1; |
| writeDescriptorSets[ 2 ].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; |
| writeDescriptorSets[ 2 ].pImageInfo = &samplerInfo; |
| |
| vkUpdateDescriptorSets( m_pDevice, _countof( writeDescriptorSets ), writeDescriptorSets, 0, nullptr ); |
| } |
| |
| // Companion window descriptor sets |
| { |
| VkDescriptorImageInfo imageInfo = {}; |
| imageInfo.imageView = m_leftEyeDesc.m_pImageView; |
| imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| |
| VkWriteDescriptorSet writeDescriptorSets[ 1 ] = { }; |
| writeDescriptorSets[ 0 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 0 ].dstSet = m_pDescriptorSets[ DESCRIPTOR_SET_COMPANION_LEFT_TEXTURE ]; |
| writeDescriptorSets[ 0 ].dstBinding = 1; |
| writeDescriptorSets[ 0 ].descriptorCount = 1; |
| writeDescriptorSets[ 0 ].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; |
| writeDescriptorSets[ 0 ].pImageInfo = &imageInfo; |
| vkUpdateDescriptorSets( m_pDevice, _countof( writeDescriptorSets ), writeDescriptorSets, 0, nullptr ); |
| |
| imageInfo.imageView = m_rightEyeDesc.m_pImageView; |
| writeDescriptorSets[ 0 ].dstSet = m_pDescriptorSets[ DESCRIPTOR_SET_COMPANION_RIGHT_TEXTURE ]; |
| vkUpdateDescriptorSets( m_pDevice, _countof( writeDescriptorSets ), writeDescriptorSets, 0, nullptr ); |
| } |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::SetupTexturemaps() |
| { |
| std::string sExecutableDirectory = Path_StripFilename( Path_GetExecutablePath() ); |
| std::string strFullPath = Path_MakeAbsolute( "../cube_texture.png", sExecutableDirectory ); |
| |
| std::vector< unsigned char > imageRGBA; |
| unsigned nImageWidth, nImageHeight; |
| unsigned nError = lodepng::decode( imageRGBA, nImageWidth, nImageHeight, strFullPath.c_str() ); |
| |
| if ( nError != 0 ) |
| return false; |
| |
| // Copy the base level to a buffer, reserve space for mips (overreserve by a bit to avoid having to calc mipchain size ahead of time) |
| VkDeviceSize nBufferSize = 0; |
| uint8_t *pBuffer = new uint8_t[ nImageWidth * nImageHeight * 4 * 2 ]; |
| uint8_t *pPrevBuffer = pBuffer; |
| uint8_t *pCurBuffer = pBuffer; |
| memcpy( pCurBuffer, &imageRGBA[0], sizeof( uint8_t ) * nImageWidth * nImageHeight * 4 ); |
| pCurBuffer += sizeof( uint8_t ) * nImageWidth * nImageHeight * 4; |
| |
| std::vector< VkBufferImageCopy > bufferImageCopies; |
| VkBufferImageCopy bufferImageCopy = {}; |
| bufferImageCopy.bufferOffset = 0; |
| bufferImageCopy.bufferRowLength = 0; |
| bufferImageCopy.bufferImageHeight = 0; |
| bufferImageCopy.imageSubresource.baseArrayLayer = 0; |
| bufferImageCopy.imageSubresource.layerCount = 1; |
| bufferImageCopy.imageSubresource.mipLevel = 0; |
| bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| bufferImageCopy.imageOffset.x = 0; |
| bufferImageCopy.imageOffset.y = 0; |
| bufferImageCopy.imageOffset.z = 0; |
| bufferImageCopy.imageExtent.width = nImageWidth; |
| bufferImageCopy.imageExtent.height = nImageHeight; |
| bufferImageCopy.imageExtent.depth = 1; |
| bufferImageCopies.push_back( bufferImageCopy ); |
| |
| int nMipWidth = nImageWidth; |
| int nMipHeight = nImageHeight; |
| |
| while( nMipWidth > 1 && nMipHeight > 1 ) |
| { |
| GenMipMapRGBA( pPrevBuffer, pCurBuffer, nMipWidth, nMipHeight, &nMipWidth, &nMipHeight ); |
| bufferImageCopy.bufferOffset = pCurBuffer - pBuffer; |
| bufferImageCopy.imageSubresource.mipLevel++; |
| bufferImageCopy.imageExtent.width = nMipWidth; |
| bufferImageCopy.imageExtent.height = nMipHeight; |
| bufferImageCopies.push_back( bufferImageCopy ); |
| pPrevBuffer = pCurBuffer; |
| pCurBuffer += ( nMipWidth * nMipHeight * 4 * sizeof( uint8_t ) ); |
| } |
| nBufferSize = pCurBuffer - pBuffer; |
| |
| // Create the image |
| VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; |
| imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; |
| imageCreateInfo.extent.width = nImageWidth; |
| imageCreateInfo.extent.height = nImageHeight; |
| imageCreateInfo.extent.depth = 1; |
| imageCreateInfo.mipLevels = ( uint32_t ) bufferImageCopies.size(); |
| imageCreateInfo.arrayLayers = 1; |
| imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; |
| imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; |
| imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; |
| imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| imageCreateInfo.flags = 0; |
| vkCreateImage( m_pDevice, &imageCreateInfo, nullptr, &m_pSceneImage ); |
| |
| VkMemoryRequirements memoryRequirements = {}; |
| vkGetImageMemoryRequirements( m_pDevice, m_pSceneImage, &memoryRequirements ); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| memoryAllocateInfo.allocationSize = memoryRequirements.size; |
| MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryAllocateInfo.memoryTypeIndex ); |
| vkAllocateMemory( m_pDevice, &memoryAllocateInfo, nullptr, &m_pSceneImageMemory ); |
| vkBindImageMemory( m_pDevice, m_pSceneImage, m_pSceneImageMemory, 0 ); |
| |
| VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; |
| imageViewCreateInfo.flags = 0; |
| imageViewCreateInfo.image = m_pSceneImage; |
| imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
| imageViewCreateInfo.format = imageCreateInfo.format; |
| imageViewCreateInfo.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; |
| imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageViewCreateInfo.subresourceRange.baseMipLevel = 0; |
| imageViewCreateInfo.subresourceRange.levelCount = imageCreateInfo.mipLevels; |
| imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; |
| imageViewCreateInfo.subresourceRange.layerCount = 1; |
| vkCreateImageView( m_pDevice, &imageViewCreateInfo, nullptr, &m_pSceneImageView ); |
| |
| // Create a staging buffer |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, pBuffer, nBufferSize, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, &m_pSceneStagingBuffer, &m_pSceneStagingBufferMemory ) ) |
| { |
| return false; |
| } |
| |
| // Transition the image to TRANSFER_DST to receive image |
| VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; |
| imageMemoryBarrier.srcAccessMask = 0; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| imageMemoryBarrier.image = m_pSceneImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.subresourceRange.baseMipLevel = 0; |
| imageMemoryBarrier.subresourceRange.levelCount = imageCreateInfo.mipLevels; |
| imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| imageMemoryBarrier.srcQueueFamilyIndex = m_nQueueFamilyIndex; |
| imageMemoryBarrier.dstQueueFamilyIndex = m_nQueueFamilyIndex; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Issue the copy to fill the image data |
| vkCmdCopyBufferToImage( m_currentCommandBuffer.m_pCommandBuffer, m_pSceneStagingBuffer, m_pSceneImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ( uint32_t ) bufferImageCopies.size(), &bufferImageCopies[ 0 ] ); |
| |
| // Transition the image to SHADER_READ_OPTIMAL for reading |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Create the sampler |
| VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; |
| samplerCreateInfo.magFilter = VK_FILTER_LINEAR; |
| samplerCreateInfo.minFilter = VK_FILTER_LINEAR; |
| samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; |
| samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| samplerCreateInfo.anisotropyEnable = VK_TRUE; |
| samplerCreateInfo.maxAnisotropy = 16.0f; |
| samplerCreateInfo.minLod = 0.0f; |
| samplerCreateInfo.maxLod = ( float ) imageCreateInfo.mipLevels; |
| vkCreateSampler( m_pDevice, &samplerCreateInfo, nullptr, &m_pSceneSampler ); |
| |
| delete [] pBuffer; |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: generate next level mipmap for an RGBA image |
| //----------------------------------------------------------------------------- |
| void CMainApplication::GenMipMapRGBA( const uint8_t *pSrc, uint8_t *pDst, int nSrcWidth, int nSrcHeight, int *pDstWidthOut, int *pDstHeightOut ) |
| { |
| *pDstWidthOut = nSrcWidth / 2; |
| if ( *pDstWidthOut <= 0 ) |
| { |
| *pDstWidthOut = 1; |
| } |
| *pDstHeightOut = nSrcHeight / 2; |
| if ( *pDstHeightOut <= 0 ) |
| { |
| *pDstHeightOut = 1; |
| } |
| |
| for ( int y = 0; y < *pDstHeightOut; y++ ) |
| { |
| for ( int x = 0; x < *pDstWidthOut; x++ ) |
| { |
| int nSrcIndex[4]; |
| float r = 0.0f; |
| float g = 0.0f; |
| float b = 0.0f; |
| float a = 0.0f; |
| |
| nSrcIndex[0] = ( ( ( y * 2 ) * nSrcWidth ) + ( x * 2 ) ) * 4; |
| nSrcIndex[1] = ( ( ( y * 2 ) * nSrcWidth ) + ( x * 2 + 1 ) ) * 4; |
| nSrcIndex[2] = ( ( ( ( y * 2 ) + 1 ) * nSrcWidth ) + ( x * 2 ) ) * 4; |
| nSrcIndex[3] = ( ( ( ( y * 2 ) + 1 ) * nSrcWidth ) + ( x * 2 + 1 ) ) * 4; |
| |
| // Sum all pixels |
| for ( int nSample = 0; nSample < 4; nSample++ ) |
| { |
| r += pSrc[ nSrcIndex[ nSample ] ]; |
| g += pSrc[ nSrcIndex[ nSample ] + 1 ]; |
| b += pSrc[ nSrcIndex[ nSample ] + 2 ]; |
| a += pSrc[ nSrcIndex[ nSample ] + 3 ]; |
| } |
| |
| // Average results |
| r /= 4.0; |
| g /= 4.0; |
| b /= 4.0; |
| a /= 4.0; |
| |
| // Store resulting pixels |
| pDst[ ( y * ( *pDstWidthOut ) + x ) * 4 ] = ( uint8_t ) ( r ); |
| pDst[ ( y * ( *pDstWidthOut ) + x ) * 4 + 1] = ( uint8_t ) ( g ); |
| pDst[ ( y * ( *pDstWidthOut ) + x ) * 4 + 2] = ( uint8_t ) ( b ); |
| pDst[ ( y * ( *pDstWidthOut ) + x ) * 4 + 3] = ( uint8_t ) ( a ); |
| } |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: create a sea of cubes |
| //----------------------------------------------------------------------------- |
| void CMainApplication::SetupScene() |
| { |
| if ( !m_pHMD ) |
| return; |
| |
| std::vector<float> vertdataarray; |
| |
| Matrix4 matScale; |
| matScale.scale( m_fScale, m_fScale, m_fScale ); |
| Matrix4 matTransform; |
| matTransform.translate( |
| -( (float)m_iSceneVolumeWidth * m_fScaleSpacing ) / 2.f, |
| -( (float)m_iSceneVolumeHeight * m_fScaleSpacing ) / 2.f, |
| -( (float)m_iSceneVolumeDepth * m_fScaleSpacing ) / 2.f); |
| |
| Matrix4 mat = matScale * matTransform; |
| |
| for( int z = 0; z< m_iSceneVolumeDepth; z++ ) |
| { |
| for( int y = 0; y< m_iSceneVolumeHeight; y++ ) |
| { |
| for( int x = 0; x< m_iSceneVolumeWidth; x++ ) |
| { |
| AddCubeToScene( mat, vertdataarray ); |
| mat = mat * Matrix4().translate( m_fScaleSpacing, 0, 0 ); |
| } |
| mat = mat * Matrix4().translate( -((float)m_iSceneVolumeWidth) * m_fScaleSpacing, m_fScaleSpacing, 0 ); |
| } |
| mat = mat * Matrix4().translate( 0, -((float)m_iSceneVolumeHeight) * m_fScaleSpacing, m_fScaleSpacing ); |
| } |
| m_uiVertcount = vertdataarray.size()/5; |
| |
| // Create the vertex buffer and fill with data |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, &vertdataarray[ 0 ], vertdataarray.size() * sizeof( float ), |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_pSceneVertexBuffer, &m_pSceneVertexBufferMemory ) ) |
| { |
| return; |
| } |
| |
| // Create constant buffer to hold the per-eye CB data |
| for ( uint32_t nEye = 0; nEye < 2; nEye++ ) |
| { |
| VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
| bufferCreateInfo.size = sizeof( Matrix4 ); |
| bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| vkCreateBuffer( m_pDevice, &bufferCreateInfo, nullptr, &m_pSceneConstantBuffer[ nEye ] ); |
| |
| VkMemoryRequirements memoryRequirements = { }; |
| vkGetBufferMemoryRequirements( m_pDevice, m_pSceneConstantBuffer[ nEye ], &memoryRequirements ); |
| |
| VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, &allocInfo.memoryTypeIndex ); |
| allocInfo.allocationSize = memoryRequirements.size; |
| |
| vkAllocateMemory( m_pDevice, &allocInfo, nullptr, &m_pSceneConstantBufferMemory[ nEye ] ); |
| vkBindBufferMemory( m_pDevice, m_pSceneConstantBuffer[ nEye ], m_pSceneConstantBufferMemory[ nEye ], 0 ); |
| |
| // Map and keep mapped persistently |
| vkMapMemory( m_pDevice, m_pSceneConstantBufferMemory[ nEye ], 0, VK_WHOLE_SIZE, 0, &m_pSceneConstantBufferData[ nEye ] ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::AddCubeVertex( float fl0, float fl1, float fl2, float fl3, float fl4, std::vector<float> &vertdata ) |
| { |
| vertdata.push_back( fl0 ); |
| vertdata.push_back( fl1 ); |
| vertdata.push_back( fl2 ); |
| vertdata.push_back( fl3 ); |
| vertdata.push_back( fl4 ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::AddCubeToScene( Matrix4 mat, std::vector<float> &vertdata ) |
| { |
| // Matrix4 mat( outermat.data() ); |
| |
| Vector4 A = mat * Vector4( 0, 0, 0, 1 ); |
| Vector4 B = mat * Vector4( 1, 0, 0, 1 ); |
| Vector4 C = mat * Vector4( 1, 1, 0, 1 ); |
| Vector4 D = mat * Vector4( 0, 1, 0, 1 ); |
| Vector4 E = mat * Vector4( 0, 0, 1, 1 ); |
| Vector4 F = mat * Vector4( 1, 0, 1, 1 ); |
| Vector4 G = mat * Vector4( 1, 1, 1, 1 ); |
| Vector4 H = mat * Vector4( 0, 1, 1, 1 ); |
| |
| // triangles instead of quads |
| AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); //Front |
| AddCubeVertex( F.x, F.y, F.z, 1, 1, vertdata ); |
| AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); |
| AddCubeVertex( G.x, G.y, G.z, 1, 0, vertdata ); |
| AddCubeVertex( H.x, H.y, H.z, 0, 0, vertdata ); |
| AddCubeVertex( E.x, E.y, E.z, 0, 1, vertdata ); |
| |
| AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); //Back |
| AddCubeVertex( A.x, A.y, A.z, 1, 1, vertdata ); |
| AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); |
| AddCubeVertex( D.x, D.y, D.z, 1, 0, vertdata ); |
| AddCubeVertex( C.x, C.y, C.z, 0, 0, vertdata ); |
| AddCubeVertex( B.x, B.y, B.z, 0, 1, vertdata ); |
| |
| AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); //Top |
| AddCubeVertex( G.x, G.y, G.z, 1, 1, vertdata ); |
| AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); |
| AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); |
| AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); |
| AddCubeVertex( H.x, H.y, H.z, 0, 1, vertdata ); |
| |
| AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Bottom |
| AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); |
| AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); |
| AddCubeVertex( F.x, F.y, F.z, 1, 0, vertdata ); |
| AddCubeVertex( E.x, E.y, E.z, 0, 0, vertdata ); |
| AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); |
| |
| AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); //Left |
| AddCubeVertex( E.x, E.y, E.z, 1, 1, vertdata ); |
| AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); |
| AddCubeVertex( H.x, H.y, H.z, 1, 0, vertdata ); |
| AddCubeVertex( D.x, D.y, D.z, 0, 0, vertdata ); |
| AddCubeVertex( A.x, A.y, A.z, 0, 1, vertdata ); |
| |
| AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); //Right |
| AddCubeVertex( B.x, B.y, B.z, 1, 1, vertdata ); |
| AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); |
| AddCubeVertex( C.x, C.y, C.z, 1, 0, vertdata ); |
| AddCubeVertex( G.x, G.y, G.z, 0, 0, vertdata ); |
| AddCubeVertex( F.x, F.y, F.z, 0, 1, vertdata ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Update the vertex data for the controllers as X/Y/Z lines |
| //----------------------------------------------------------------------------- |
| void CMainApplication::UpdateControllerAxes() |
| { |
| // Don't attempt to update controllers if input is not available |
| if( !m_pHMD->IsInputAvailable() ) |
| return; |
| |
| std::vector<float> vertdataarray; |
| |
| m_uiControllerVertcount = 0; |
| m_iTrackedControllerCount = 0; |
| |
| for ( vr::TrackedDeviceIndex_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; ++unTrackedDevice ) |
| { |
| if ( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) ) |
| continue; |
| |
| if( m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) != vr::TrackedDeviceClass_Controller ) |
| continue; |
| |
| m_iTrackedControllerCount += 1; |
| |
| if( !m_rTrackedDevicePose[ unTrackedDevice ].bPoseIsValid ) |
| continue; |
| |
| const Matrix4 & mat = m_rmat4DevicePose[unTrackedDevice]; |
| |
| Vector4 center = mat * Vector4( 0, 0, 0, 1 ); |
| |
| for ( int i = 0; i < 3; ++i ) |
| { |
| Vector3 color( 0, 0, 0 ); |
| Vector4 point( 0, 0, 0, 1 ); |
| point[i] += 0.05f; // offset in X, Y, Z |
| color[i] = 1.0; // R, G, B |
| point = mat * point; |
| vertdataarray.push_back( center.x ); |
| vertdataarray.push_back( center.y ); |
| vertdataarray.push_back( center.z ); |
| |
| vertdataarray.push_back( color.x ); |
| vertdataarray.push_back( color.y ); |
| vertdataarray.push_back( color.z ); |
| |
| vertdataarray.push_back( point.x ); |
| vertdataarray.push_back( point.y ); |
| vertdataarray.push_back( point.z ); |
| |
| vertdataarray.push_back( color.x ); |
| vertdataarray.push_back( color.y ); |
| vertdataarray.push_back( color.z ); |
| |
| m_uiControllerVertcount += 2; |
| } |
| |
| Vector4 start = mat * Vector4( 0, 0, -0.02f, 1 ); |
| Vector4 end = mat * Vector4( 0, 0, -39.f, 1 ); |
| Vector3 color( .92f, .92f, .71f ); |
| |
| vertdataarray.push_back( start.x );vertdataarray.push_back( start.y );vertdataarray.push_back( start.z ); |
| vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); |
| |
| vertdataarray.push_back( end.x );vertdataarray.push_back( end.y );vertdataarray.push_back( end.z ); |
| vertdataarray.push_back( color.x );vertdataarray.push_back( color.y );vertdataarray.push_back( color.z ); |
| m_uiControllerVertcount += 2; |
| } |
| |
| // Setup the VB the first time through. |
| if ( m_pControllerAxesVertexBuffer == VK_NULL_HANDLE && vertdataarray.size() > 0 ) |
| { |
| // Make big enough to hold up to the max number |
| VkDeviceSize nSize = sizeof( float ) * vertdataarray.size(); |
| nSize *= vr::k_unMaxTrackedDeviceCount; |
| |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, nullptr, nSize, |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_pControllerAxesVertexBuffer, &m_pControllerAxesVertexBufferMemory ) ) |
| { |
| return; |
| } |
| } |
| |
| // Update the VB data |
| if ( m_pControllerAxesVertexBuffer != VK_NULL_HANDLE && vertdataarray.size() > 0 ) |
| { |
| void *pData; |
| VkResult nResult = vkMapMemory( m_pDevice, m_pControllerAxesVertexBufferMemory, 0, VK_WHOLE_SIZE, 0, &pData ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkMapMemory returned error %d\n", nResult ); |
| return; |
| } |
| memcpy( pData, &vertdataarray[ 0 ], vertdataarray.size() * sizeof( float ) ); |
| vkUnmapMemory( m_pDevice, m_pControllerAxesVertexBufferMemory ); |
| |
| VkMappedMemoryRange memoryRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; |
| memoryRange.memory = m_pControllerAxesVertexBufferMemory; |
| memoryRange.size = VK_WHOLE_SIZE; |
| vkFlushMappedMemoryRanges( m_pDevice, 1, &memoryRange ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::SetupCameras() |
| { |
| m_mat4ProjectionLeft = GetHMDMatrixProjectionEye( vr::Eye_Left ); |
| m_mat4ProjectionRight = GetHMDMatrixProjectionEye( vr::Eye_Right ); |
| m_mat4eyePosLeft = GetHMDMatrixPoseEye( vr::Eye_Left ); |
| m_mat4eyePosRight = GetHMDMatrixPoseEye( vr::Eye_Right ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Creates a frame buffer. Returns true if the buffer was set up. |
| // Returns false if the setup failed. |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::CreateFrameBuffer( int nWidth, int nHeight, FramebufferDesc &framebufferDesc ) |
| { |
| //---------------------------// |
| // Create color target // |
| //---------------------------// |
| VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; |
| imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; |
| imageCreateInfo.extent.width = nWidth; |
| imageCreateInfo.extent.height = nHeight; |
| imageCreateInfo.extent.depth = 1; |
| imageCreateInfo.mipLevels = 1; |
| imageCreateInfo.arrayLayers = 1; |
| imageCreateInfo.format = VK_FORMAT_R8G8B8A8_SRGB; |
| imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; |
| imageCreateInfo.samples = ( VkSampleCountFlagBits ) m_nMSAASampleCount; |
| imageCreateInfo.usage = ( VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT ); |
| imageCreateInfo.flags = 0; |
| |
| VkResult nResult; |
| nResult = vkCreateImage( m_pDevice, &imageCreateInfo, nullptr, &framebufferDesc.m_pImage ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateImage failed for eye image with error %d\n", nResult ); |
| return false; |
| } |
| VkMemoryRequirements memoryRequirements = {}; |
| vkGetImageMemoryRequirements( m_pDevice, framebufferDesc.m_pImage, &memoryRequirements ); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| memoryAllocateInfo.allocationSize = memoryRequirements.size; |
| if ( !MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryAllocateInfo.memoryTypeIndex ) ) |
| { |
| dprintf( "Failed to find memory type matching requirements.\n" ); |
| return false; |
| } |
| |
| nResult = vkAllocateMemory( m_pDevice, &memoryAllocateInfo, nullptr, &framebufferDesc.m_pDeviceMemory ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Failed to find memory for image.\n" ); |
| return false; |
| } |
| |
| nResult = vkBindImageMemory( m_pDevice, framebufferDesc.m_pImage, framebufferDesc.m_pDeviceMemory, 0 ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Failed to bind memory for image.\n" ); |
| return false; |
| } |
| |
| VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; |
| imageViewCreateInfo.flags = 0; |
| imageViewCreateInfo.image = framebufferDesc.m_pImage; |
| imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
| imageViewCreateInfo.format = imageCreateInfo.format; |
| imageViewCreateInfo.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; |
| imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageViewCreateInfo.subresourceRange.baseMipLevel = 0; |
| imageViewCreateInfo.subresourceRange.levelCount = 1; |
| imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; |
| imageViewCreateInfo.subresourceRange.layerCount = 1; |
| nResult = vkCreateImageView( m_pDevice, &imageViewCreateInfo, nullptr, &framebufferDesc.m_pImageView ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateImageView failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| //-----------------------------------// |
| // Create depth/stencil target // |
| //-----------------------------------// |
| imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; |
| imageCreateInfo.format = VK_FORMAT_D32_SFLOAT; |
| imageCreateInfo.usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT; |
| nResult = vkCreateImage( m_pDevice, &imageCreateInfo, nullptr, &framebufferDesc.m_pDepthStencilImage ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateImage failed for eye depth buffer with error %d\n", nResult ); |
| return false; |
| } |
| vkGetImageMemoryRequirements( m_pDevice, framebufferDesc.m_pDepthStencilImage, &memoryRequirements ); |
| |
| memoryAllocateInfo.allocationSize = memoryRequirements.size; |
| if ( !MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryAllocateInfo.memoryTypeIndex ) ) |
| { |
| dprintf( "Failed to find memory type matching requirements.\n" ); |
| return false; |
| } |
| |
| nResult = vkAllocateMemory( m_pDevice, &memoryAllocateInfo, nullptr, &framebufferDesc.m_pDepthStencilDeviceMemory ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Failed to find memory for image.\n" ); |
| return false; |
| } |
| |
| nResult = vkBindImageMemory( m_pDevice, framebufferDesc.m_pDepthStencilImage, framebufferDesc.m_pDepthStencilDeviceMemory, 0 ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Failed to bind memory for image.\n" ); |
| return false; |
| } |
| |
| imageViewCreateInfo.image = framebufferDesc.m_pDepthStencilImage; |
| imageViewCreateInfo.format = imageCreateInfo.format; |
| imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
| nResult = vkCreateImageView( m_pDevice, &imageViewCreateInfo, nullptr, &framebufferDesc.m_pDepthStencilImageView ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateImageView failed with error %d\n", nResult ); |
| return false; |
| } |
| |
| // Create a renderpass |
| uint32_t nTotalAttachments = 2; |
| VkAttachmentDescription attachmentDescs[ 2 ]; |
| VkAttachmentReference attachmentReferences[ 2 ]; |
| attachmentReferences[ 0 ].attachment = 0; |
| attachmentReferences[ 0 ].layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachmentReferences[ 1 ].attachment = 1; |
| attachmentReferences[ 1 ].layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| |
| attachmentDescs[ 0 ].format = VK_FORMAT_R8G8B8A8_SRGB; |
| attachmentDescs[ 0 ].samples = imageCreateInfo.samples; |
| attachmentDescs[ 0 ].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
| attachmentDescs[ 0 ].storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachmentDescs[ 0 ].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attachmentDescs[ 0 ].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachmentDescs[ 0 ].initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachmentDescs[ 0 ].finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| attachmentDescs[ 0 ].flags = 0; |
| |
| attachmentDescs[ 1 ].format = VK_FORMAT_D32_SFLOAT; |
| attachmentDescs[ 1 ].samples = imageCreateInfo.samples; |
| attachmentDescs[ 1 ].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; |
| attachmentDescs[ 1 ].storeOp = VK_ATTACHMENT_STORE_OP_STORE; |
| attachmentDescs[ 1 ].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; |
| attachmentDescs[ 1 ].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; |
| attachmentDescs[ 1 ].initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| attachmentDescs[ 1 ].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| attachmentDescs[ 1 ].flags = 0; |
| |
| VkSubpassDescription subPassCreateInfo = { }; |
| subPassCreateInfo.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; |
| subPassCreateInfo.flags = 0; |
| subPassCreateInfo.inputAttachmentCount = 0; |
| subPassCreateInfo.pInputAttachments = NULL; |
| subPassCreateInfo.colorAttachmentCount = 1; |
| subPassCreateInfo.pColorAttachments = &attachmentReferences[ 0 ]; |
| subPassCreateInfo.pResolveAttachments = NULL; |
| subPassCreateInfo.pDepthStencilAttachment = &attachmentReferences[ 1 ]; |
| subPassCreateInfo.preserveAttachmentCount = 0; |
| subPassCreateInfo.pPreserveAttachments = NULL; |
| |
| VkRenderPassCreateInfo renderPassCreateInfo = { }; |
| renderPassCreateInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; |
| renderPassCreateInfo.flags = 0; |
| renderPassCreateInfo.attachmentCount = 2; |
| renderPassCreateInfo.pAttachments = &attachmentDescs[ 0 ]; |
| renderPassCreateInfo.subpassCount = 1; |
| renderPassCreateInfo.pSubpasses = &subPassCreateInfo; |
| renderPassCreateInfo.dependencyCount = 0; |
| renderPassCreateInfo.pDependencies = NULL; |
| |
| nResult = vkCreateRenderPass( m_pDevice, &renderPassCreateInfo, NULL, &framebufferDesc.m_pRenderPass ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateRenderPass failed with error %d.\n", nResult ); |
| return false; |
| } |
| |
| // Create the framebuffer |
| VkImageView attachments[ 2 ] = { framebufferDesc.m_pImageView, framebufferDesc.m_pDepthStencilImageView }; |
| VkFramebufferCreateInfo framebufferCreateInfo = { VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO }; |
| framebufferCreateInfo.renderPass = framebufferDesc.m_pRenderPass; |
| framebufferCreateInfo.attachmentCount = 2; |
| framebufferCreateInfo.pAttachments = &attachments[ 0 ]; |
| framebufferCreateInfo.width = nWidth; |
| framebufferCreateInfo.height = nHeight; |
| framebufferCreateInfo.layers = 1; |
| nResult = vkCreateFramebuffer( m_pDevice, &framebufferCreateInfo, NULL, &framebufferDesc.m_pFramebuffer ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkCreateFramebuffer failed with error %d.\n", nResult ); |
| return false; |
| } |
| |
| framebufferDesc.m_nImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| framebufferDesc.m_nDepthStencilImageLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Get an available command buffer or create a new one if |
| // none available. Associate a fence with the command buffer. |
| //----------------------------------------------------------------------------- |
| CMainApplication::VulkanCommandBuffer_t CMainApplication::GetCommandBuffer() |
| { |
| VulkanCommandBuffer_t commandBuffer; |
| if ( m_commandBuffers.size() > 0 ) |
| { |
| // If the fence associated with the command buffer has finished, reset it and return it |
| if ( vkGetFenceStatus( m_pDevice, m_commandBuffers.back().m_pFence ) == VK_SUCCESS ) |
| { |
| VulkanCommandBuffer_t *pCmdBuffer = &m_commandBuffers.back(); |
| commandBuffer.m_pCommandBuffer = pCmdBuffer->m_pCommandBuffer; |
| commandBuffer.m_pFence = pCmdBuffer->m_pFence; |
| |
| vkResetCommandBuffer( commandBuffer.m_pCommandBuffer, VK_COMMAND_BUFFER_RESET_RELEASE_RESOURCES_BIT ); |
| vkResetFences( m_pDevice, 1, &commandBuffer.m_pFence ); |
| m_commandBuffers.pop_back(); |
| return commandBuffer; |
| } |
| } |
| |
| // Create a new command buffer and associated fence |
| VkCommandBufferAllocateInfo commandBufferAllocateInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO }; |
| commandBufferAllocateInfo.commandBufferCount = 1; |
| commandBufferAllocateInfo.commandPool = m_pCommandPool; |
| commandBufferAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; |
| vkAllocateCommandBuffers( m_pDevice, &commandBufferAllocateInfo, &commandBuffer.m_pCommandBuffer ); |
| |
| VkFenceCreateInfo fenceCreateInfo = { VK_STRUCTURE_TYPE_FENCE_CREATE_INFO }; |
| vkCreateFence( m_pDevice, &fenceCreateInfo, nullptr, &commandBuffer.m_pFence ); |
| return commandBuffer; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| bool CMainApplication::SetupStereoRenderTargets() |
| { |
| if ( !m_pHMD ) |
| return false; |
| |
| m_pHMD->GetRecommendedRenderTargetSize( &m_nRenderWidth, &m_nRenderHeight ); |
| m_nRenderWidth = ( uint32_t )( m_flSuperSampleScale * ( float ) m_nRenderWidth ); |
| m_nRenderHeight = ( uint32_t )( m_flSuperSampleScale * ( float ) m_nRenderHeight ); |
| |
| CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, m_leftEyeDesc ); |
| CreateFrameBuffer( m_nRenderWidth, m_nRenderHeight, m_rightEyeDesc ); |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::SetupCompanionWindow() |
| { |
| if ( !m_pHMD ) |
| return; |
| |
| std::vector<VertexDataWindow> vVerts; |
| |
| // left eye verts |
| vVerts.push_back( VertexDataWindow( Vector2(-1, -1), Vector2(0, 1)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(0, -1), Vector2(1, 1)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(-1, 1), Vector2(0, 0)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(0, 1), Vector2(1, 0)) ); |
| |
| // right eye verts |
| vVerts.push_back( VertexDataWindow( Vector2(0, -1), Vector2(0, 1)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(1, -1), Vector2(1, 1)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(0, 1), Vector2(0, 0)) ); |
| vVerts.push_back( VertexDataWindow( Vector2(1, 1), Vector2(1, 0)) ); |
| |
| // Create the vertex buffer and fill with data |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, &vVerts[ 0 ], sizeof( VertexDataWindow ) * vVerts.size(), |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_pCompanionWindowVertexBuffer, &m_pCompanionWindowVertexBufferMemory ) ) |
| { |
| return; |
| } |
| |
| // Create index buffer |
| uint16_t vIndices[] = { 0, 1, 3, 0, 3, 2, 4, 5, 7, 4, 7, 6}; |
| m_uiCompanionWindowIndexSize = _countof( vIndices ); |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, &vIndices[ 0 ], sizeof( vIndices ), |
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT, &m_pCompanionWindowIndexBuffer, &m_pCompanionWindowIndexBufferMemory ) ) |
| { |
| return; |
| } |
| |
| // Transition all of the swapchain images to PRESENT_SRC so they are ready for presentation |
| for ( size_t nSwapchainImage = 0; nSwapchainImage < m_swapchainImages.size(); nSwapchainImage++ ) |
| { |
| VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; |
| imageMemoryBarrier.srcAccessMask = 0; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| imageMemoryBarrier.image = m_swapchainImages[ nSwapchainImage ]; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.subresourceRange.baseMipLevel = 0; |
| imageMemoryBarrier.subresourceRange.levelCount = 1; |
| imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| imageMemoryBarrier.srcQueueFamilyIndex = m_nQueueFamilyIndex; |
| imageMemoryBarrier.dstQueueFamilyIndex = m_nQueueFamilyIndex; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::RenderStereoTargets() |
| { |
| |
| // Set viewport and scissor |
| VkViewport viewport = { 0.0f, 0.0f, (float ) m_nRenderWidth, ( float ) m_nRenderHeight, 0.0f, 1.0f }; |
| vkCmdSetViewport( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &viewport ); |
| VkRect2D scissor = { 0, 0, m_nRenderWidth, m_nRenderHeight }; |
| vkCmdSetScissor( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &scissor ); |
| |
| //----------// |
| // Left Eye // |
| //----------// |
| // Transition eye image to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL |
| VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = m_leftEyeDesc.m_nImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| imageMemoryBarrier.image = m_leftEyeDesc.m_pImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.subresourceRange.baseMipLevel = 0; |
| imageMemoryBarrier.subresourceRange.levelCount = 1; |
| imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| imageMemoryBarrier.srcQueueFamilyIndex = m_nQueueFamilyIndex; |
| imageMemoryBarrier.dstQueueFamilyIndex = m_nQueueFamilyIndex; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_leftEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| |
| // Transition the depth buffer to VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL on first use |
| if ( m_leftEyeDesc.m_nDepthStencilImageLayout == VK_IMAGE_LAYOUT_UNDEFINED ) |
| { |
| imageMemoryBarrier.image = m_leftEyeDesc.m_pDepthStencilImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
| imageMemoryBarrier.srcAccessMask = 0; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = m_leftEyeDesc.m_nDepthStencilImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_leftEyeDesc.m_nDepthStencilImageLayout = imageMemoryBarrier.newLayout; |
| } |
| |
| // Start the renderpass |
| VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; |
| renderPassBeginInfo.renderPass = m_leftEyeDesc.m_pRenderPass; |
| renderPassBeginInfo.framebuffer = m_leftEyeDesc.m_pFramebuffer; |
| renderPassBeginInfo.renderArea.offset.x = 0; |
| renderPassBeginInfo.renderArea.offset.y = 0; |
| renderPassBeginInfo.renderArea.extent.width = m_nRenderWidth; |
| renderPassBeginInfo.renderArea.extent.height = m_nRenderHeight; |
| renderPassBeginInfo.clearValueCount = 2; |
| VkClearValue clearValues[ 2 ]; |
| clearValues[ 0 ].color.float32[ 0 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 1 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 2 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 3 ] = 1.0f; |
| clearValues[ 1 ].depthStencil.depth = 1.0f; |
| clearValues[ 1 ].depthStencil.stencil = 0; |
| renderPassBeginInfo.pClearValues = &clearValues[ 0 ]; |
| vkCmdBeginRenderPass( m_currentCommandBuffer.m_pCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE ); |
| |
| RenderScene( vr::Eye_Left ); |
| |
| vkCmdEndRenderPass( m_currentCommandBuffer.m_pCommandBuffer ); |
| |
| // Transition eye image to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL for display on the companion window |
| imageMemoryBarrier.image = m_leftEyeDesc.m_pImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| imageMemoryBarrier.oldLayout = m_leftEyeDesc.m_nImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_leftEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| |
| //-----------// |
| // Right Eye // |
| //-----------// |
| // Transition to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL |
| imageMemoryBarrier.image = m_rightEyeDesc.m_pImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_TRANSFER_READ_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = m_rightEyeDesc.m_nImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_rightEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| |
| // Transition the depth buffer to VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL on first use |
| if ( m_rightEyeDesc.m_nDepthStencilImageLayout == VK_IMAGE_LAYOUT_UNDEFINED ) |
| { |
| imageMemoryBarrier.image = m_rightEyeDesc.m_pDepthStencilImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT; |
| imageMemoryBarrier.srcAccessMask = 0; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = m_rightEyeDesc.m_nDepthStencilImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_rightEyeDesc.m_nDepthStencilImageLayout = imageMemoryBarrier.newLayout; |
| } |
| |
| // Start the renderpass |
| renderPassBeginInfo.renderPass = m_rightEyeDesc.m_pRenderPass; |
| renderPassBeginInfo.framebuffer = m_rightEyeDesc.m_pFramebuffer; |
| renderPassBeginInfo.pClearValues = &clearValues[ 0 ]; |
| vkCmdBeginRenderPass( m_currentCommandBuffer.m_pCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE ); |
| |
| RenderScene( vr::Eye_Right ); |
| |
| vkCmdEndRenderPass( m_currentCommandBuffer.m_pCommandBuffer ); |
| |
| // Transition eye image to VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL for display on the companion window |
| imageMemoryBarrier.image = m_rightEyeDesc.m_pImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| imageMemoryBarrier.oldLayout = m_rightEyeDesc.m_nImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_rightEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Renders a scene with respect to nEye. |
| //----------------------------------------------------------------------------- |
| void CMainApplication::RenderScene( vr::Hmd_Eye nEye ) |
| { |
| if( m_bShowCubes ) |
| { |
| vkCmdBindPipeline( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelines[ PSO_SCENE ] ); |
| |
| // Update the persistently mapped pointer to the CB data with the latest matrix |
| memcpy( m_pSceneConstantBufferData[ nEye ], GetCurrentViewProjectionMatrix( nEye ).get(), sizeof( Matrix4 ) ); |
| |
| vkCmdBindDescriptorSets( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelineLayout, 0, 1, &m_pDescriptorSets[ DESCRIPTOR_SET_LEFT_EYE_SCENE + nEye ], 0, nullptr ); |
| |
| // Draw |
| VkDeviceSize nOffsets[ 1 ] = { 0 }; |
| vkCmdBindVertexBuffers( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &m_pSceneVertexBuffer, &nOffsets[ 0 ] ); |
| vkCmdDraw( m_currentCommandBuffer.m_pCommandBuffer, m_uiVertcount, 1, 0, 0 ); |
| } |
| |
| bool bIsInputAvailable = m_pHMD->IsInputAvailable(); |
| if( bIsInputAvailable && m_pControllerAxesVertexBuffer != VK_NULL_HANDLE ) |
| { |
| // draw the controller axis lines |
| vkCmdBindPipeline( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelines[ PSO_AXES ] ); |
| |
| VkDeviceSize nOffsets[ 1 ] = { 0 }; |
| vkCmdBindVertexBuffers( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &m_pControllerAxesVertexBuffer, &nOffsets[ 0 ] ); |
| vkCmdDraw( m_currentCommandBuffer.m_pCommandBuffer, m_uiControllerVertcount, 1, 0, 0 ); |
| } |
| |
| // ----- Render Model rendering ----- |
| vkCmdBindPipeline( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelines[ PSO_RENDERMODEL ] ); |
| for( uint32_t unTrackedDevice = 0; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ ) |
| { |
| if( !m_rTrackedDeviceToRenderModel[ unTrackedDevice ] || !m_rbShowTrackedDevice[ unTrackedDevice ] ) |
| continue; |
| |
| const vr::TrackedDevicePose_t & pose = m_rTrackedDevicePose[ unTrackedDevice ]; |
| if( !pose.bPoseIsValid ) |
| continue; |
| |
| if( !bIsInputAvailable && m_pHMD->GetTrackedDeviceClass( unTrackedDevice ) == vr::TrackedDeviceClass_Controller ) |
| continue; |
| |
| const Matrix4 & matDeviceToTracking = m_rmat4DevicePose[ unTrackedDevice ]; |
| Matrix4 matMVP = GetCurrentViewProjectionMatrix( nEye ) * matDeviceToTracking; |
| |
| m_rTrackedDeviceToRenderModel[ unTrackedDevice ]->Draw( nEye, m_currentCommandBuffer.m_pCommandBuffer, m_pPipelineLayout, matMVP ); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::RenderCompanionWindow() |
| { |
| // Get the next swapchain image |
| VkResult nResult = vkAcquireNextImageKHR( m_pDevice, m_pSwapchain, UINT64_MAX, m_pSwapchainSemaphores[ m_nFrameIndex ], VK_NULL_HANDLE, &m_nCurrentSwapchainImage ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "Skipping companion window rendering, vkAcquireNextImageKHR returned %d\n", nResult ); |
| return; |
| } |
| |
| // Transition the swapchain image to COLOR_ATTACHMENT_OPTIMAL for rendering |
| VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| imageMemoryBarrier.image = m_swapchainImages[ m_nCurrentSwapchainImage ]; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.subresourceRange.baseMipLevel = 0; |
| imageMemoryBarrier.subresourceRange.levelCount = 1; |
| imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| imageMemoryBarrier.srcQueueFamilyIndex = m_nQueueFamilyIndex; |
| imageMemoryBarrier.dstQueueFamilyIndex = m_nQueueFamilyIndex; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Start the renderpass |
| VkRenderPassBeginInfo renderPassBeginInfo = { VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO }; |
| renderPassBeginInfo.renderPass = m_pSwapchainRenderPass; |
| renderPassBeginInfo.framebuffer = m_pSwapchainFramebuffers[ m_nCurrentSwapchainImage ]; |
| renderPassBeginInfo.renderArea.offset.x = 0; |
| renderPassBeginInfo.renderArea.offset.y = 0; |
| renderPassBeginInfo.renderArea.extent.width = m_nCompanionWindowWidth; |
| renderPassBeginInfo.renderArea.extent.height = m_nCompanionWindowHeight; |
| VkClearValue clearValues[ 1 ]; |
| clearValues[ 0 ].color.float32[ 0 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 1 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 2 ] = 0.0f; |
| clearValues[ 0 ].color.float32[ 3 ] = 1.0f; |
| renderPassBeginInfo.clearValueCount = _countof( clearValues ); |
| renderPassBeginInfo.pClearValues = &clearValues[ 0 ]; |
| vkCmdBeginRenderPass( m_currentCommandBuffer.m_pCommandBuffer, &renderPassBeginInfo, VK_SUBPASS_CONTENTS_INLINE ); |
| |
| // Set viewport/scissor |
| VkViewport viewport = { 0.0f, 0.0f, (float ) m_nCompanionWindowWidth, ( float ) m_nCompanionWindowHeight, 0.0f, 1.0f }; |
| vkCmdSetViewport( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &viewport ); |
| VkRect2D scissor = { 0, 0, m_nCompanionWindowWidth, m_nCompanionWindowHeight }; |
| vkCmdSetScissor( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &scissor ); |
| |
| // Bind the pipeline and descriptor set |
| vkCmdBindPipeline( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelines[ PSO_COMPANION ] ); |
| vkCmdBindDescriptorSets( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelineLayout, 0, 1, &m_pDescriptorSets[ DESCRIPTOR_SET_COMPANION_LEFT_TEXTURE ], 0, nullptr ); |
| |
| // Draw left eye texture to companion window |
| VkDeviceSize nOffsets[ 1 ] = { 0 }; |
| vkCmdBindVertexBuffers( m_currentCommandBuffer.m_pCommandBuffer, 0, 1, &m_pCompanionWindowVertexBuffer, &nOffsets[ 0 ] ); |
| vkCmdBindIndexBuffer( m_currentCommandBuffer.m_pCommandBuffer, m_pCompanionWindowIndexBuffer, 0, VK_INDEX_TYPE_UINT16 ); |
| vkCmdDrawIndexed( m_currentCommandBuffer.m_pCommandBuffer, m_uiCompanionWindowIndexSize / 2, 1, 0, 0, 0 ); |
| |
| // Draw right eye texture to companion window |
| vkCmdBindDescriptorSets( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, m_pPipelineLayout, 0, 1, &m_pDescriptorSets[ DESCRIPTOR_SET_COMPANION_RIGHT_TEXTURE ], 0, nullptr ); |
| vkCmdDrawIndexed( m_currentCommandBuffer.m_pCommandBuffer, m_uiCompanionWindowIndexSize / 2, 1, ( m_uiCompanionWindowIndexSize / 2 ), 0, 0 ); |
| |
| // End the renderpass |
| vkCmdEndRenderPass( m_currentCommandBuffer.m_pCommandBuffer ); |
| |
| // Transition the swapchain image to PRESENT_SRC for presentation |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Transition both of the eye textures to VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL for SteamVR which requires this layout for submit |
| imageMemoryBarrier.image = m_leftEyeDesc.m_pImage; |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT; |
| imageMemoryBarrier.oldLayout = m_leftEyeDesc.m_nImageLayout; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_leftEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| |
| imageMemoryBarrier.image = m_rightEyeDesc.m_pImage; |
| vkCmdPipelineBarrier( m_currentCommandBuffer.m_pCommandBuffer, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| m_rightEyeDesc.m_nImageLayout = imageMemoryBarrier.newLayout; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Gets a Matrix Projection Eye with respect to nEye. |
| //----------------------------------------------------------------------------- |
| Matrix4 CMainApplication::GetHMDMatrixProjectionEye( vr::Hmd_Eye nEye ) |
| { |
| if ( !m_pHMD ) |
| return Matrix4(); |
| |
| vr::HmdMatrix44_t mat = m_pHMD->GetProjectionMatrix( nEye, m_fNearClip, m_fFarClip ); |
| |
| return Matrix4( |
| mat.m[0][0], mat.m[1][0], mat.m[2][0], mat.m[3][0], |
| mat.m[0][1], mat.m[1][1], mat.m[2][1], mat.m[3][1], |
| mat.m[0][2], mat.m[1][2], mat.m[2][2], mat.m[3][2], |
| mat.m[0][3], mat.m[1][3], mat.m[2][3], mat.m[3][3] |
| ); |
| } |
| |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Gets an HMDMatrixPoseEye with respect to nEye. |
| //----------------------------------------------------------------------------- |
| Matrix4 CMainApplication::GetHMDMatrixPoseEye( vr::Hmd_Eye nEye ) |
| { |
| if ( !m_pHMD ) |
| return Matrix4(); |
| |
| vr::HmdMatrix34_t matEyeRight = m_pHMD->GetEyeToHeadTransform( nEye ); |
| Matrix4 matrixObj( |
| matEyeRight.m[0][0], matEyeRight.m[1][0], matEyeRight.m[2][0], 0.0, |
| matEyeRight.m[0][1], matEyeRight.m[1][1], matEyeRight.m[2][1], 0.0, |
| matEyeRight.m[0][2], matEyeRight.m[1][2], matEyeRight.m[2][2], 0.0, |
| matEyeRight.m[0][3], matEyeRight.m[1][3], matEyeRight.m[2][3], 1.0f |
| ); |
| |
| return matrixObj.invert(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Gets a Current View Projection Matrix with respect to nEye, |
| // which may be an Eye_Left or an Eye_Right. |
| //----------------------------------------------------------------------------- |
| Matrix4 CMainApplication::GetCurrentViewProjectionMatrix( vr::Hmd_Eye nEye ) |
| { |
| Matrix4 matMVP; |
| if( nEye == vr::Eye_Left ) |
| { |
| matMVP = m_mat4ProjectionLeft * m_mat4eyePosLeft * m_mat4HMDPose; |
| } |
| else if( nEye == vr::Eye_Right ) |
| { |
| matMVP = m_mat4ProjectionRight * m_mat4eyePosRight * m_mat4HMDPose; |
| } |
| |
| return matMVP; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| void CMainApplication::UpdateHMDMatrixPose() |
| { |
| if ( !m_pHMD ) |
| return; |
| |
| vr::VRCompositor()->WaitGetPoses(m_rTrackedDevicePose, vr::k_unMaxTrackedDeviceCount, NULL, 0 ); |
| |
| m_iValidPoseCount = 0; |
| m_strPoseClasses = ""; |
| for ( int nDevice = 0; nDevice < vr::k_unMaxTrackedDeviceCount; ++nDevice ) |
| { |
| if ( m_rTrackedDevicePose[nDevice].bPoseIsValid ) |
| { |
| m_iValidPoseCount++; |
| m_rmat4DevicePose[nDevice] = ConvertSteamVRMatrixToMatrix4( m_rTrackedDevicePose[nDevice].mDeviceToAbsoluteTracking ); |
| if (m_rDevClassChar[nDevice]==0) |
| { |
| switch (m_pHMD->GetTrackedDeviceClass(nDevice)) |
| { |
| case vr::TrackedDeviceClass_Controller: m_rDevClassChar[nDevice] = 'C'; break; |
| case vr::TrackedDeviceClass_HMD: m_rDevClassChar[nDevice] = 'H'; break; |
| case vr::TrackedDeviceClass_Invalid: m_rDevClassChar[nDevice] = 'I'; break; |
| case vr::TrackedDeviceClass_GenericTracker: m_rDevClassChar[nDevice] = 'G'; break; |
| case vr::TrackedDeviceClass_TrackingReference: m_rDevClassChar[nDevice] = 'T'; break; |
| default: m_rDevClassChar[nDevice] = '?'; break; |
| } |
| } |
| m_strPoseClasses += m_rDevClassChar[nDevice]; |
| } |
| } |
| |
| if ( m_rTrackedDevicePose[vr::k_unTrackedDeviceIndex_Hmd].bPoseIsValid ) |
| { |
| m_mat4HMDPose = m_rmat4DevicePose[vr::k_unTrackedDeviceIndex_Hmd]; |
| m_mat4HMDPose.invert(); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Finds a render model we've already loaded or loads a new one |
| //----------------------------------------------------------------------------- |
| VulkanRenderModel *CMainApplication::FindOrLoadRenderModel( vr::TrackedDeviceIndex_t unTrackedDeviceIndex, const char *pchRenderModelName ) |
| { |
| VulkanRenderModel *pRenderModel = NULL; |
| // To simplify the Vulkan rendering code, create an instance of the model for each model name. This is less efficient |
| // memory wise, but simplifies the rendering code so we can store the transform in a constant buffer associated with |
| // the model itself. You would not want to do this in a production application. |
| //for( std::vector< VulkanRenderModel * >::iterator i = m_vecRenderModels.begin(); i != m_vecRenderModels.end(); i++ ) |
| //{ |
| //if( !stricmp( (*i)->GetName().c_str(), pchRenderModelName ) ) |
| //{ |
| //pRenderModel = *i; |
| //break; |
| //} |
| //} |
| |
| // load the model if we didn't find one |
| if( !pRenderModel ) |
| { |
| vr::RenderModel_t *pModel; |
| vr::EVRRenderModelError error; |
| while ( 1 ) |
| { |
| error = vr::VRRenderModels()->LoadRenderModel_Async( pchRenderModelName, &pModel ); |
| if ( error != vr::VRRenderModelError_Loading ) |
| break; |
| |
| ThreadSleep( 1 ); |
| } |
| |
| if ( error != vr::VRRenderModelError_None ) |
| { |
| dprintf( "Unable to load render model %s - %s\n", pchRenderModelName, vr::VRRenderModels()->GetRenderModelErrorNameFromEnum( error ) ); |
| return NULL; // move on to the next tracked device |
| } |
| |
| vr::RenderModel_TextureMap_t *pTexture; |
| while ( 1 ) |
| { |
| error = vr::VRRenderModels()->LoadTexture_Async( pModel->diffuseTextureId, &pTexture ); |
| if ( error != vr::VRRenderModelError_Loading ) |
| break; |
| |
| ThreadSleep( 1 ); |
| } |
| |
| if ( error != vr::VRRenderModelError_None ) |
| { |
| dprintf( "Unable to load render texture id:%d for render model %s\n", pModel->diffuseTextureId, pchRenderModelName ); |
| vr::VRRenderModels()->FreeRenderModel( pModel ); |
| return NULL; // move on to the next tracked device |
| } |
| |
| pRenderModel = new VulkanRenderModel( pchRenderModelName ); |
| VkDescriptorSet pDescriptorSets[ 2 ] = |
| { |
| m_pDescriptorSets[ DESCRIPTOR_SET_LEFT_EYE_RENDER_MODEL0 + unTrackedDeviceIndex ], |
| m_pDescriptorSets[ DESCRIPTOR_SET_RIGHT_EYE_RENDER_MODEL0 + unTrackedDeviceIndex ], |
| }; |
| |
| // If this gets called during HandleInput() there will be no command buffer current, so create one |
| // and submit it immediately. |
| bool bNewCommandBuffer = false; |
| if ( m_currentCommandBuffer.m_pCommandBuffer == VK_NULL_HANDLE ) |
| { |
| m_currentCommandBuffer = GetCommandBuffer(); |
| |
| // Start the command buffer |
| VkCommandBufferBeginInfo commandBufferBeginInfo = { VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO }; |
| commandBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; |
| vkBeginCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer, &commandBufferBeginInfo ); |
| bNewCommandBuffer = true; |
| } |
| if ( !pRenderModel->BInit( m_pDevice, m_physicalDeviceMemoryProperties, m_currentCommandBuffer.m_pCommandBuffer, unTrackedDeviceIndex, pDescriptorSets, *pModel, *pTexture ) ) |
| { |
| dprintf( "Unable to create Vulkan model from render model %s\n", pchRenderModelName ); |
| delete pRenderModel; |
| pRenderModel = NULL; |
| } |
| else |
| { |
| m_vecRenderModels.push_back( pRenderModel ); |
| |
| // If this is during HandleInput() there is was no command buffer current, so submit it now. |
| if ( bNewCommandBuffer ) |
| { |
| vkEndCommandBuffer( m_currentCommandBuffer.m_pCommandBuffer ); |
| |
| // Submit now |
| VkSubmitInfo submitInfo = { VK_STRUCTURE_TYPE_SUBMIT_INFO }; |
| submitInfo.commandBufferCount = 1; |
| submitInfo.pCommandBuffers = &m_currentCommandBuffer.m_pCommandBuffer; |
| vkQueueSubmit( m_pQueue, 1, &submitInfo, m_currentCommandBuffer.m_pFence ); |
| m_commandBuffers.push_front( m_currentCommandBuffer ); |
| |
| // Reset current command buffer |
| m_currentCommandBuffer.m_pCommandBuffer = VK_NULL_HANDLE; |
| m_currentCommandBuffer.m_pFence = VK_NULL_HANDLE; |
| } |
| } |
| vr::VRRenderModels()->FreeRenderModel( pModel ); |
| vr::VRRenderModels()->FreeTexture( pTexture ); |
| } |
| |
| return pRenderModel; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Create/destroy Vulkan a Render Model for a single tracked device |
| //----------------------------------------------------------------------------- |
| void CMainApplication::SetupRenderModelForTrackedDevice( vr::TrackedDeviceIndex_t unTrackedDeviceIndex ) |
| { |
| if( unTrackedDeviceIndex >= vr::k_unMaxTrackedDeviceCount ) |
| return; |
| |
| // try to find a model we've already set up |
| std::string sRenderModelName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_RenderModelName_String ); |
| VulkanRenderModel *pRenderModel = FindOrLoadRenderModel( unTrackedDeviceIndex, sRenderModelName.c_str() ); |
| if( !pRenderModel ) |
| { |
| std::string sTrackingSystemName = GetTrackedDeviceString( m_pHMD, unTrackedDeviceIndex, vr::Prop_TrackingSystemName_String ); |
| dprintf( "Unable to load render model for tracked device %d (%s.%s)", unTrackedDeviceIndex, sTrackingSystemName.c_str(), sRenderModelName.c_str() ); |
| } |
| else |
| { |
| m_rTrackedDeviceToRenderModel[ unTrackedDeviceIndex ] = pRenderModel; |
| m_rbShowTrackedDevice[ unTrackedDeviceIndex ] = true; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Create/destroy Vulkan Render Models |
| //----------------------------------------------------------------------------- |
| void CMainApplication::SetupRenderModels() |
| { |
| memset( m_rTrackedDeviceToRenderModel, 0, sizeof( m_rTrackedDeviceToRenderModel ) ); |
| |
| if( !m_pHMD ) |
| return; |
| |
| for( uint32_t unTrackedDevice = vr::k_unTrackedDeviceIndex_Hmd + 1; unTrackedDevice < vr::k_unMaxTrackedDeviceCount; unTrackedDevice++ ) |
| { |
| if( !m_pHMD->IsTrackedDeviceConnected( unTrackedDevice ) ) |
| continue; |
| |
| SetupRenderModelForTrackedDevice( unTrackedDevice ); |
| } |
| |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Converts a SteamVR matrix to our local matrix class |
| //----------------------------------------------------------------------------- |
| Matrix4 CMainApplication::ConvertSteamVRMatrixToMatrix4( const vr::HmdMatrix34_t &matPose ) |
| { |
| Matrix4 matrixObj( |
| matPose.m[0][0], matPose.m[1][0], matPose.m[2][0], 0.0, |
| matPose.m[0][1], matPose.m[1][1], matPose.m[2][1], 0.0, |
| matPose.m[0][2], matPose.m[1][2], matPose.m[2][2], 0.0, |
| matPose.m[0][3], matPose.m[1][3], matPose.m[2][3], 1.0f |
| ); |
| return matrixObj; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Create/destroy Vulkan Render Models |
| //----------------------------------------------------------------------------- |
| VulkanRenderModel::VulkanRenderModel( const std::string & sRenderModelName ) |
| : m_sModelName( sRenderModelName ) |
| , m_pDevice( VK_NULL_HANDLE ) |
| , m_pVertexBuffer( VK_NULL_HANDLE ) |
| , m_pVertexBufferMemory( VK_NULL_HANDLE ) |
| , m_pIndexBuffer( VK_NULL_HANDLE ) |
| , m_pIndexBufferMemory( VK_NULL_HANDLE ) |
| , m_pImage( VK_NULL_HANDLE ) |
| , m_pImageMemory( VK_NULL_HANDLE ) |
| , m_pImageView( VK_NULL_HANDLE ) |
| , m_pImageStagingBuffer( VK_NULL_HANDLE ) |
| , m_pImageStagingBufferMemory( VK_NULL_HANDLE ) |
| , m_pSampler( VK_NULL_HANDLE ) |
| { |
| memset( m_pConstantBuffer, 0, sizeof( m_pConstantBuffer ) ); |
| memset( m_pConstantBufferMemory, 0, sizeof( m_pConstantBufferMemory ) ); |
| memset( m_pConstantBufferData, 0, sizeof( m_pConstantBufferData ) ); |
| memset( m_pDescriptorSets, 0, sizeof( m_pDescriptorSets ) ); |
| } |
| |
| VulkanRenderModel::~VulkanRenderModel() |
| { |
| Cleanup(); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Allocates and populates the Vulkan resources for a render model |
| //----------------------------------------------------------------------------- |
| bool VulkanRenderModel::BInit( VkDevice pDevice, const VkPhysicalDeviceMemoryProperties &memoryProperties, VkCommandBuffer pCommandBuffer, vr::TrackedDeviceIndex_t unTrackedDeviceIndex, VkDescriptorSet pDescriptorSets[ 2 ], const vr::RenderModel_t & vrModel, const vr::RenderModel_TextureMap_t & vrDiffuseTexture ) |
| { |
| m_pDevice = pDevice; |
| m_physicalDeviceMemoryProperties = memoryProperties; |
| m_unTrackedDeviceIndex = unTrackedDeviceIndex; |
| m_pDescriptorSets[ 0 ] = pDescriptorSets[ 0 ]; |
| m_pDescriptorSets[ 1 ] = pDescriptorSets[ 1 ]; |
| |
| // Create and populate the vertex buffer |
| { |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, vrModel.rVertexData, sizeof( vr::RenderModel_Vertex_t ) * vrModel.unVertexCount, |
| VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, &m_pVertexBuffer, &m_pVertexBufferMemory ) ) |
| { |
| return false; |
| } |
| } |
| |
| // Create and populate the index buffer |
| { |
| if ( !CreateVulkanBuffer( m_pDevice, m_physicalDeviceMemoryProperties, vrModel.rIndexData, sizeof( uint16_t ) * vrModel.unTriangleCount * 3, |
| VK_BUFFER_USAGE_INDEX_BUFFER_BIT, &m_pIndexBuffer, &m_pIndexBufferMemory ) ) |
| { |
| return false; |
| } |
| } |
| |
| // create and populate the texture |
| { |
| int nImageWidth = vrDiffuseTexture.unWidth; |
| int nImageHeight = vrDiffuseTexture.unHeight; |
| |
| // Copy the base level to a buffer, reserve space for mips (overreserve by a bit to avoid having to calc mipchain size ahead of time) |
| VkDeviceSize nBufferSize = 0; |
| uint8_t *pBuffer = new uint8_t[ nImageWidth * nImageHeight * 4 * 2 ]; |
| uint8_t *pPrevBuffer = pBuffer; |
| uint8_t *pCurBuffer = pBuffer; |
| memcpy( pCurBuffer, vrDiffuseTexture.rubTextureMapData, sizeof( uint8_t ) * nImageWidth * nImageHeight * 4 ); |
| pCurBuffer += sizeof( uint8_t ) * nImageWidth * nImageHeight * 4; |
| |
| std::vector< VkBufferImageCopy > bufferImageCopies; |
| VkBufferImageCopy bufferImageCopy = {}; |
| bufferImageCopy.bufferOffset = 0; |
| bufferImageCopy.bufferRowLength = 0; |
| bufferImageCopy.bufferImageHeight = 0; |
| bufferImageCopy.imageSubresource.baseArrayLayer = 0; |
| bufferImageCopy.imageSubresource.layerCount = 1; |
| bufferImageCopy.imageSubresource.mipLevel = 0; |
| bufferImageCopy.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| bufferImageCopy.imageOffset.x = 0; |
| bufferImageCopy.imageOffset.y = 0; |
| bufferImageCopy.imageOffset.z = 0; |
| bufferImageCopy.imageExtent.width = nImageWidth; |
| bufferImageCopy.imageExtent.height = nImageHeight; |
| bufferImageCopy.imageExtent.depth = 1; |
| bufferImageCopies.push_back( bufferImageCopy ); |
| |
| int nMipWidth = nImageWidth; |
| int nMipHeight = nImageHeight; |
| |
| while( nMipWidth > 1 && nMipHeight > 1 ) |
| { |
| CMainApplication::GenMipMapRGBA( pPrevBuffer, pCurBuffer, nMipWidth, nMipHeight, &nMipWidth, &nMipHeight ); |
| bufferImageCopy.bufferOffset = pCurBuffer - pBuffer; |
| bufferImageCopy.imageSubresource.mipLevel++; |
| bufferImageCopy.imageExtent.width = nMipWidth; |
| bufferImageCopy.imageExtent.height = nMipHeight; |
| bufferImageCopies.push_back( bufferImageCopy ); |
| pPrevBuffer = pCurBuffer; |
| pCurBuffer += ( nMipWidth * nMipHeight * 4 * sizeof( uint8_t ) ); |
| } |
| nBufferSize = pCurBuffer - pBuffer; |
| |
| // Create the image |
| VkImageCreateInfo imageCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO }; |
| imageCreateInfo.imageType = VK_IMAGE_TYPE_2D; |
| imageCreateInfo.extent.width = nImageWidth; |
| imageCreateInfo.extent.height = nImageHeight; |
| imageCreateInfo.extent.depth = 1; |
| imageCreateInfo.mipLevels = ( uint32_t ) bufferImageCopies.size(); |
| imageCreateInfo.arrayLayers = 1; |
| imageCreateInfo.format = VK_FORMAT_R8G8B8A8_UNORM; |
| imageCreateInfo.tiling = VK_IMAGE_TILING_OPTIMAL; |
| imageCreateInfo.samples = VK_SAMPLE_COUNT_1_BIT; |
| imageCreateInfo.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; |
| imageCreateInfo.flags = 0; |
| vkCreateImage( m_pDevice, &imageCreateInfo, nullptr, &m_pImage ); |
| |
| VkMemoryRequirements memoryRequirements = {}; |
| vkGetImageMemoryRequirements( m_pDevice, m_pImage, &memoryRequirements ); |
| |
| VkMemoryAllocateInfo memoryAllocateInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| memoryAllocateInfo.allocationSize = memoryRequirements.size; |
| MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &memoryAllocateInfo.memoryTypeIndex ); |
| vkAllocateMemory( m_pDevice, &memoryAllocateInfo, nullptr, &m_pImageMemory ); |
| vkBindImageMemory( m_pDevice, m_pImage, m_pImageMemory, 0 ); |
| |
| VkImageViewCreateInfo imageViewCreateInfo = { VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO }; |
| imageViewCreateInfo.flags = 0; |
| imageViewCreateInfo.image = m_pImage; |
| imageViewCreateInfo.viewType = VK_IMAGE_VIEW_TYPE_2D; |
| imageViewCreateInfo.format = imageCreateInfo.format; |
| imageViewCreateInfo.components = { VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY, VK_COMPONENT_SWIZZLE_IDENTITY }; |
| imageViewCreateInfo.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageViewCreateInfo.subresourceRange.baseMipLevel = 0; |
| imageViewCreateInfo.subresourceRange.levelCount = imageCreateInfo.mipLevels; |
| imageViewCreateInfo.subresourceRange.baseArrayLayer = 0; |
| imageViewCreateInfo.subresourceRange.layerCount = 1; |
| vkCreateImageView( m_pDevice, &imageViewCreateInfo, nullptr, &m_pImageView ); |
| |
| // Create a staging buffer |
| VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
| bufferCreateInfo.size = nBufferSize; |
| bufferCreateInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; |
| vkCreateBuffer( m_pDevice, &bufferCreateInfo, nullptr, &m_pImageStagingBuffer ); |
| vkGetBufferMemoryRequirements( m_pDevice, m_pImageStagingBuffer, &memoryRequirements ); |
| |
| VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, &allocInfo.memoryTypeIndex ); |
| allocInfo.allocationSize = memoryRequirements.size; |
| |
| vkAllocateMemory( m_pDevice, &allocInfo, nullptr, &m_pImageStagingBufferMemory ); |
| vkBindBufferMemory( m_pDevice, m_pImageStagingBuffer, m_pImageStagingBufferMemory, 0 ); |
| |
| // Copy memory to the staging buffer |
| void *pData; |
| VkResult nResult = vkMapMemory( m_pDevice, m_pImageStagingBufferMemory, 0, VK_WHOLE_SIZE, 0, &pData ); |
| if ( nResult != VK_SUCCESS ) |
| { |
| dprintf( "vkMapMemory returned error %d\n", nResult ); |
| return false; |
| } |
| memcpy( pData, pBuffer, nBufferSize ); |
| vkUnmapMemory( m_pDevice, m_pImageStagingBufferMemory ); |
| |
| VkMappedMemoryRange memoryRange = { VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE }; |
| memoryRange.memory = m_pImageStagingBufferMemory; |
| memoryRange.size = VK_WHOLE_SIZE; |
| vkFlushMappedMemoryRanges( m_pDevice, 1, &memoryRange ); |
| |
| // Transition the image to TRANSFER_DST to receive image |
| VkImageMemoryBarrier imageMemoryBarrier = { VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER }; |
| imageMemoryBarrier.srcAccessMask = 0; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| imageMemoryBarrier.image = m_pImage; |
| imageMemoryBarrier.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; |
| imageMemoryBarrier.subresourceRange.baseMipLevel = 0; |
| imageMemoryBarrier.subresourceRange.levelCount = imageCreateInfo.mipLevels; |
| imageMemoryBarrier.subresourceRange.baseArrayLayer = 0; |
| imageMemoryBarrier.subresourceRange.layerCount = 1; |
| vkCmdPipelineBarrier( pCommandBuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Issue the copy to fill the image data |
| vkCmdCopyBufferToImage( pCommandBuffer, m_pImageStagingBuffer, m_pImage, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, ( uint32_t ) bufferImageCopies.size(), &bufferImageCopies[ 0 ] ); |
| |
| // Transition the image to SHADER_READ_OPTIMAL for reading |
| imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; |
| imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT; |
| imageMemoryBarrier.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; |
| imageMemoryBarrier.newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| vkCmdPipelineBarrier( pCommandBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, &imageMemoryBarrier ); |
| |
| // Create a sampler |
| VkSamplerCreateInfo samplerCreateInfo = { VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO }; |
| samplerCreateInfo.magFilter = VK_FILTER_LINEAR; |
| samplerCreateInfo.minFilter = VK_FILTER_LINEAR; |
| samplerCreateInfo.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; |
| samplerCreateInfo.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| samplerCreateInfo.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; |
| samplerCreateInfo.anisotropyEnable = VK_TRUE; |
| samplerCreateInfo.maxAnisotropy = 16.0f; |
| samplerCreateInfo.minLod = 0.0f; |
| samplerCreateInfo.maxLod = ( float ) imageCreateInfo.mipLevels; |
| vkCreateSampler( m_pDevice, &samplerCreateInfo, nullptr, &m_pSampler ); |
| } |
| |
| // Create a constant buffer to hold the transform (one for each eye) |
| for ( uint32_t nEye = 0; nEye < 2; nEye++ ) |
| { |
| VkBufferCreateInfo bufferCreateInfo = { VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO }; |
| bufferCreateInfo.size = sizeof( Matrix4 ); |
| bufferCreateInfo.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| vkCreateBuffer( m_pDevice, &bufferCreateInfo, nullptr, &m_pConstantBuffer[ nEye ] ); |
| |
| VkMemoryRequirements memoryRequirements = {}; |
| vkGetBufferMemoryRequirements( m_pDevice, m_pConstantBuffer[ nEye ], &memoryRequirements ); |
| VkMemoryAllocateInfo allocInfo = { VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO }; |
| MemoryTypeFromProperties( m_physicalDeviceMemoryProperties, memoryRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT, &allocInfo.memoryTypeIndex ); |
| allocInfo.allocationSize = memoryRequirements.size; |
| |
| vkAllocateMemory( m_pDevice, &allocInfo, nullptr, &m_pConstantBufferMemory[ nEye ] ); |
| vkBindBufferMemory( m_pDevice, m_pConstantBuffer[ nEye ], m_pConstantBufferMemory[ nEye ], 0 ); |
| |
| // Map and keep mapped persistently |
| vkMapMemory( m_pDevice, m_pConstantBufferMemory[ nEye ], 0, VK_WHOLE_SIZE, 0, &m_pConstantBufferData[ nEye ] ); |
| |
| // Bake the descriptor set |
| VkDescriptorBufferInfo bufferInfo = {}; |
| bufferInfo.buffer = m_pConstantBuffer[ nEye ]; |
| bufferInfo.offset = 0; |
| bufferInfo.range = VK_WHOLE_SIZE; |
| |
| VkDescriptorImageInfo imageInfo = {}; |
| imageInfo.imageView = m_pImageView; |
| imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; |
| |
| VkDescriptorImageInfo samplerInfo = {}; |
| samplerInfo.sampler = m_pSampler; |
| |
| VkWriteDescriptorSet writeDescriptorSets[ 3 ] = { }; |
| writeDescriptorSets[ 0 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 0 ].dstSet = m_pDescriptorSets[ nEye ]; |
| writeDescriptorSets[ 0 ].dstBinding = 0; |
| writeDescriptorSets[ 0 ].descriptorCount = 1; |
| writeDescriptorSets[ 0 ].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; |
| writeDescriptorSets[ 0 ].pBufferInfo = &bufferInfo; |
| writeDescriptorSets[ 1 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 1 ].dstSet = m_pDescriptorSets[ nEye ]; |
| writeDescriptorSets[ 1 ].dstBinding = 1; |
| writeDescriptorSets[ 1 ].descriptorCount = 1; |
| writeDescriptorSets[ 1 ].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE; |
| writeDescriptorSets[ 1 ].pImageInfo = &imageInfo; |
| writeDescriptorSets[ 2 ].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; |
| writeDescriptorSets[ 2 ].dstSet = m_pDescriptorSets[ nEye ]; |
| writeDescriptorSets[ 2 ].dstBinding = 2; |
| writeDescriptorSets[ 2 ].descriptorCount = 1; |
| writeDescriptorSets[ 2 ].descriptorType = VK_DESCRIPTOR_TYPE_SAMPLER; |
| writeDescriptorSets[ 2 ].pImageInfo = &samplerInfo; |
| |
| vkUpdateDescriptorSets( m_pDevice, _countof( writeDescriptorSets ), writeDescriptorSets, 0, nullptr ); |
| } |
| |
| m_unVertexCount = vrModel.unTriangleCount * 3; |
| |
| return true; |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Frees the Vulkan resources for a render model |
| //----------------------------------------------------------------------------- |
| void VulkanRenderModel::Cleanup() |
| { |
| if ( m_pVertexBuffer != VK_NULL_HANDLE ) |
| { |
| vkDestroyBuffer( m_pDevice, m_pVertexBuffer, nullptr ); |
| m_pVertexBuffer = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pVertexBufferMemory != VK_NULL_HANDLE ) |
| { |
| vkFreeMemory( m_pDevice, m_pVertexBufferMemory, nullptr ); |
| m_pVertexBufferMemory = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pIndexBuffer != VK_NULL_HANDLE ) |
| { |
| vkDestroyBuffer( m_pDevice, m_pIndexBuffer, nullptr ); |
| m_pIndexBuffer = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pIndexBufferMemory != VK_NULL_HANDLE ) |
| { |
| vkFreeMemory( m_pDevice, m_pIndexBufferMemory, nullptr ); |
| m_pIndexBufferMemory = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pImage != VK_NULL_HANDLE ) |
| { |
| vkDestroyImage( m_pDevice, m_pImage, nullptr ); |
| m_pImage = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pImageMemory != VK_NULL_HANDLE ) |
| { |
| vkFreeMemory( m_pDevice, m_pImageMemory, nullptr ); |
| m_pImageMemory = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pImageView != VK_NULL_HANDLE ) |
| { |
| vkDestroyImageView( m_pDevice, m_pImageView, nullptr ); |
| m_pImageView = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pImageStagingBuffer != VK_NULL_HANDLE ) |
| { |
| vkDestroyBuffer( m_pDevice, m_pImageStagingBuffer, nullptr ); |
| m_pImageStagingBuffer = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pImageStagingBufferMemory != VK_NULL_HANDLE ) |
| { |
| vkFreeMemory( m_pDevice, m_pImageStagingBufferMemory, nullptr ); |
| m_pImageStagingBufferMemory = VK_NULL_HANDLE; |
| } |
| |
| for ( uint32_t nEye = 0; nEye < 2; nEye++ ) |
| { |
| if ( m_pConstantBuffer[ nEye ] != VK_NULL_HANDLE ) |
| { |
| vkDestroyBuffer( m_pDevice, m_pConstantBuffer[ nEye ], nullptr ); |
| m_pConstantBuffer[ nEye ] = VK_NULL_HANDLE; |
| } |
| |
| if ( m_pConstantBufferMemory != VK_NULL_HANDLE ) |
| { |
| vkFreeMemory( m_pDevice, m_pConstantBufferMemory[ nEye ], nullptr ); |
| m_pConstantBufferMemory[ nEye ] = VK_NULL_HANDLE; |
| } |
| } |
| |
| if ( m_pSampler != VK_NULL_HANDLE ) |
| { |
| vkDestroySampler( m_pDevice, m_pSampler, nullptr ); |
| m_pSampler = VK_NULL_HANDLE; |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: Draws the render model |
| //----------------------------------------------------------------------------- |
| void VulkanRenderModel::Draw( vr::EVREye nEye, VkCommandBuffer pCommandBuffer, VkPipelineLayout pPipelineLayout, const Matrix4 &matMVP ) |
| { |
| // Update the CB with the transform |
| memcpy( m_pConstantBufferData[ nEye ], &matMVP, sizeof( matMVP ) ); |
| |
| // Bind the descriptor set |
| vkCmdBindDescriptorSets( pCommandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pPipelineLayout, 0, 1, &m_pDescriptorSets[ nEye ], 0, nullptr ); |
| |
| // Bind the VB/IB and draw |
| VkDeviceSize nOffsets[ 1 ] = { 0 }; |
| vkCmdBindVertexBuffers( pCommandBuffer, 0, 1, &m_pVertexBuffer, &nOffsets[ 0 ] ); |
| vkCmdBindIndexBuffer( pCommandBuffer, m_pIndexBuffer, 0, VK_INDEX_TYPE_UINT16 ); |
| vkCmdDrawIndexed( pCommandBuffer, m_unVertexCount, 1, 0, 0, 0 ); |
| } |
| |
| //----------------------------------------------------------------------------- |
| // Purpose: |
| //----------------------------------------------------------------------------- |
| int main(int argc, char *argv[]) |
| { |
| CMainApplication *pMainApplication = new CMainApplication( argc, argv ); |
| |
| if ( !pMainApplication->BInit() ) |
| { |
| pMainApplication->Shutdown(); |
| return 1; |
| } |
| |
| pMainApplication->RunMainLoop(); |
| |
| pMainApplication->Shutdown(); |
| |
| return 0; |
| } |