blob: 07aa24569069d56ef1bbf363a1d77d46298f66ba [file] [log] [blame]
//========= 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;
}