mirror of
https://github.com/NVIDIA/cuda-samples.git
synced 2026-10-23 13:18:16 +08:00
add and update samples with CUDA 11.3 support
This commit is contained in:
@@ -30,7 +30,7 @@ cudaImportExternalMemory, cudaExternalMemoryGetMappedBuffer, cudaImportExternalS
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Download and install the [CUDA Toolkit 11.2](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Download and install the [CUDA Toolkit 11.3](https://developer.nvidia.com/cuda-downloads) for your corresponding platform.
|
||||
Make sure the dependencies mentioned in [Dependencies]() section above are installed.
|
||||
|
||||
## Build and Run
|
||||
|
||||
@@ -29,110 +29,106 @@
|
||||
#include <algorithm>
|
||||
#include <helper_cuda.h>
|
||||
|
||||
__global__ void sinewave(float *heightMap, unsigned int width, unsigned int height, float time)
|
||||
{
|
||||
const float freq = 4.0f;
|
||||
const size_t stride = gridDim.x * blockDim.x;
|
||||
__global__ void sinewave(float *heightMap, unsigned int width,
|
||||
unsigned int height, float time) {
|
||||
const float freq = 4.0f;
|
||||
const size_t stride = gridDim.x * blockDim.x;
|
||||
|
||||
// Iterate through the entire array in a way that is
|
||||
// independent of the grid configuration
|
||||
for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < width * height; tid += stride) {
|
||||
// Calculate the x, y coordinates
|
||||
const size_t y = tid / width;
|
||||
const size_t x = tid - y * width;
|
||||
// Normalize x, y to [0,1]
|
||||
const float u = ((2.0f * x) / width) - 1.0f;
|
||||
const float v = ((2.0f * y) / height) - 1.0f;
|
||||
// Calculate the new height value
|
||||
const float w = 0.5f * sinf(u * freq + time) * cosf(v * freq + time);
|
||||
// Store this new height value
|
||||
heightMap[tid] = w;
|
||||
}
|
||||
// Iterate through the entire array in a way that is
|
||||
// independent of the grid configuration
|
||||
for (size_t tid = blockIdx.x * blockDim.x + threadIdx.x; tid < width * height;
|
||||
tid += stride) {
|
||||
// Calculate the x, y coordinates
|
||||
const size_t y = tid / width;
|
||||
const size_t x = tid - y * width;
|
||||
// Normalize x, y to [0,1]
|
||||
const float u = ((2.0f * x) / width) - 1.0f;
|
||||
const float v = ((2.0f * y) / height) - 1.0f;
|
||||
// Calculate the new height value
|
||||
const float w = 0.5f * sinf(u * freq + time) * cosf(v * freq + time);
|
||||
// Store this new height value
|
||||
heightMap[tid] = w;
|
||||
}
|
||||
}
|
||||
|
||||
SineWaveSimulation::SineWaveSimulation(size_t width, size_t height)
|
||||
: m_heightMap(nullptr), m_width(width), m_height(height)
|
||||
{
|
||||
SineWaveSimulation::SineWaveSimulation(size_t width, size_t height)
|
||||
: m_heightMap(nullptr), m_width(width), m_height(height) {}
|
||||
|
||||
void SineWaveSimulation::initCudaLaunchConfig(int device) {
|
||||
cudaDeviceProp prop = {};
|
||||
checkCudaErrors(cudaSetDevice(device));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&prop, device));
|
||||
|
||||
// We don't need large block sizes, since there's not much inter-thread
|
||||
// communication
|
||||
m_threads = prop.warpSize;
|
||||
|
||||
// Use the occupancy calculator and fill the gpu as best as we can
|
||||
checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(
|
||||
&m_blocks, sinewave, prop.warpSize, 0));
|
||||
m_blocks *= prop.multiProcessorCount;
|
||||
|
||||
// Go ahead and the clamp the blocks to the minimum needed for this
|
||||
// height/width
|
||||
m_blocks = std::min(m_blocks,
|
||||
(int)((m_width * m_height + m_threads - 1) / m_threads));
|
||||
}
|
||||
|
||||
void SineWaveSimulation::initCudaLaunchConfig(int device)
|
||||
{
|
||||
cudaDeviceProp prop = {};
|
||||
checkCudaErrors(cudaSetDevice(device));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&prop, device));
|
||||
int SineWaveSimulation::initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE) {
|
||||
int current_device = 0;
|
||||
int device_count = 0;
|
||||
int devices_prohibited = 0;
|
||||
|
||||
// We don't need large block sizes, since there's not much inter-thread communication
|
||||
m_threads = prop.warpSize;
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceCount(&device_count));
|
||||
|
||||
// Use the occupancy calculator and fill the gpu as best as we can
|
||||
checkCudaErrors(cudaOccupancyMaxActiveBlocksPerMultiprocessor(&m_blocks, sinewave, prop.warpSize, 0));
|
||||
m_blocks *= prop.multiProcessorCount;
|
||||
if (device_count == 0) {
|
||||
fprintf(stderr, "CUDA error: no devices supporting CUDA.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// Go ahead and the clamp the blocks to the minimum needed for this height/width
|
||||
m_blocks = std::min(m_blocks, (int)((m_width * m_height + m_threads - 1) / m_threads));
|
||||
}
|
||||
// Find the GPU which is selected by Vulkan
|
||||
while (current_device < device_count) {
|
||||
cudaGetDeviceProperties(&deviceProp, current_device);
|
||||
|
||||
int SineWaveSimulation::initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE)
|
||||
{
|
||||
int current_device = 0;
|
||||
int device_count = 0;
|
||||
int devices_prohibited = 0;
|
||||
if ((deviceProp.computeMode != cudaComputeModeProhibited)) {
|
||||
// Compare the cuda device UUID with vulkan UUID
|
||||
int ret = memcmp((void *)&deviceProp.uuid, vkDeviceUUID, UUID_SIZE);
|
||||
if (ret == 0) {
|
||||
checkCudaErrors(cudaSetDevice(current_device));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));
|
||||
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n",
|
||||
current_device, deviceProp.name, deviceProp.major,
|
||||
deviceProp.minor);
|
||||
|
||||
cudaDeviceProp deviceProp;
|
||||
checkCudaErrors(cudaGetDeviceCount(&device_count));
|
||||
return current_device;
|
||||
}
|
||||
|
||||
if (device_count == 0) {
|
||||
fprintf(stderr, "CUDA error: no devices supporting CUDA.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
} else {
|
||||
devices_prohibited++;
|
||||
}
|
||||
|
||||
// Find the GPU which is selected by Vulkan
|
||||
while (current_device < device_count) {
|
||||
cudaGetDeviceProperties(&deviceProp, current_device);
|
||||
current_device++;
|
||||
}
|
||||
|
||||
if ((deviceProp.computeMode != cudaComputeModeProhibited)) {
|
||||
// Compare the cuda device UUID with vulkan UUID
|
||||
int ret = memcmp((void*)&deviceProp.uuid, vkDeviceUUID, UUID_SIZE);
|
||||
if (ret == 0)
|
||||
{
|
||||
checkCudaErrors(cudaSetDevice(current_device));
|
||||
checkCudaErrors(cudaGetDeviceProperties(&deviceProp, current_device));
|
||||
printf("GPU Device %d: \"%s\" with compute capability %d.%d\n\n",
|
||||
current_device, deviceProp.name, deviceProp.major,
|
||||
deviceProp.minor);
|
||||
if (devices_prohibited == device_count) {
|
||||
fprintf(stderr,
|
||||
"CUDA error:"
|
||||
" No Vulkan-CUDA Interop capable GPU found.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return current_device;
|
||||
}
|
||||
|
||||
} else {
|
||||
devices_prohibited++;
|
||||
}
|
||||
|
||||
current_device++;
|
||||
}
|
||||
|
||||
if (devices_prohibited == device_count) {
|
||||
fprintf(stderr,
|
||||
"CUDA error:"
|
||||
" No Vulkan-CUDA Interop capable GPU found.\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
return -1;
|
||||
return -1;
|
||||
}
|
||||
|
||||
SineWaveSimulation::~SineWaveSimulation()
|
||||
{
|
||||
m_heightMap = NULL;
|
||||
SineWaveSimulation::~SineWaveSimulation() { m_heightMap = NULL; }
|
||||
|
||||
void SineWaveSimulation::initSimulation(float *heights) {
|
||||
m_heightMap = heights;
|
||||
}
|
||||
|
||||
void SineWaveSimulation::initSimulation(float *heights)
|
||||
{
|
||||
m_heightMap = heights;
|
||||
}
|
||||
|
||||
void SineWaveSimulation::stepSimulation(float time, cudaStream_t stream)
|
||||
{
|
||||
sinewave <<< m_blocks, m_threads, 0, stream >>> (m_heightMap, m_width, m_height, time);
|
||||
getLastCudaError("Failed to launch CUDA simulation");
|
||||
void SineWaveSimulation::stepSimulation(float time, cudaStream_t stream) {
|
||||
sinewave<<<m_blocks, m_threads, 0, stream>>>(m_heightMap, m_width, m_height,
|
||||
time);
|
||||
getLastCudaError("Failed to launch CUDA simulation");
|
||||
}
|
||||
|
||||
@@ -34,25 +34,21 @@
|
||||
#include <stdint.h>
|
||||
#include "linmath.h"
|
||||
|
||||
class SineWaveSimulation
|
||||
{
|
||||
float *m_heightMap;
|
||||
size_t m_width, m_height;
|
||||
int m_blocks, m_threads;
|
||||
public:
|
||||
SineWaveSimulation(size_t width, size_t height);
|
||||
~SineWaveSimulation();
|
||||
void initSimulation(float *heightMap);
|
||||
void stepSimulation(float time, cudaStream_t stream = 0);
|
||||
void initCudaLaunchConfig(int device);
|
||||
int initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE);
|
||||
class SineWaveSimulation {
|
||||
float *m_heightMap;
|
||||
size_t m_width, m_height;
|
||||
int m_blocks, m_threads;
|
||||
|
||||
size_t getWidth() const {
|
||||
return m_width;
|
||||
}
|
||||
size_t getHeight() const {
|
||||
return m_height;
|
||||
}
|
||||
public:
|
||||
SineWaveSimulation(size_t width, size_t height);
|
||||
~SineWaveSimulation();
|
||||
void initSimulation(float *heightMap);
|
||||
void stepSimulation(float time, cudaStream_t stream = 0);
|
||||
void initCudaLaunchConfig(int device);
|
||||
int initCuda(uint8_t *vkDeviceUUID, size_t UUID_SIZE);
|
||||
|
||||
size_t getWidth() const { return m_width; }
|
||||
size_t getHeight() const { return m_height; }
|
||||
};
|
||||
|
||||
#endif // __SINESIM_H__
|
||||
#endif // __SINESIM_H__
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,101 +38,125 @@
|
||||
#include <vulkan/vulkan_win32.h>
|
||||
#endif /* _WIN64 */
|
||||
|
||||
/* remove _VK_TIMELINE_SEMAPHORE to use binary semaphores */
|
||||
// use vulkan timeline semaphore
|
||||
#define _VK_TIMELINE_SEMAPHORE
|
||||
|
||||
struct GLFWwindow;
|
||||
|
||||
class VulkanBaseApp
|
||||
{
|
||||
public:
|
||||
VulkanBaseApp(const std::string& appName, bool enableValidation = false);
|
||||
static VkExternalSemaphoreHandleTypeFlagBits getDefaultSemaphoreHandleType();
|
||||
static VkExternalMemoryHandleTypeFlagBits getDefaultMemHandleType();
|
||||
virtual ~VulkanBaseApp();
|
||||
void init();
|
||||
void *getMemHandle(VkDeviceMemory memory, VkExternalMemoryHandleTypeFlagBits handleType);
|
||||
void *getSemaphoreHandle(VkSemaphore semaphore, VkExternalSemaphoreHandleTypeFlagBits handleType);
|
||||
void createExternalSemaphore(VkSemaphore& semaphore, VkExternalSemaphoreHandleTypeFlagBits handleType);
|
||||
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
||||
void createExternalBuffer(VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkExternalMemoryHandleTypeFlagsKHR extMemHandleType, VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
||||
void importExternalBuffer(void *handle, VkExternalMemoryHandleTypeFlagBits handleType, size_t size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkBuffer& buffer, VkDeviceMemory& memory);
|
||||
void copyBuffer(VkBuffer dst, VkBuffer src, VkDeviceSize size);
|
||||
VkCommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands(VkCommandBuffer commandBuffer);
|
||||
void mainLoop();
|
||||
protected:
|
||||
const std::string m_appName;
|
||||
const bool m_enableValidation;
|
||||
VkInstance m_instance;
|
||||
VkDebugUtilsMessengerEXT m_debugMessenger;
|
||||
VkSurfaceKHR m_surface;
|
||||
VkPhysicalDevice m_physicalDevice;
|
||||
VkDevice m_device;
|
||||
VkQueue m_graphicsQueue;
|
||||
VkQueue m_presentQueue;
|
||||
VkSwapchainKHR m_swapChain;
|
||||
std::vector<VkImage> m_swapChainImages;
|
||||
VkFormat m_swapChainFormat;
|
||||
VkExtent2D m_swapChainExtent;
|
||||
std::vector<VkImageView> m_swapChainImageViews;
|
||||
std::vector<std::pair<VkShaderStageFlagBits, std::string> > m_shaderFiles;
|
||||
VkRenderPass m_renderPass;
|
||||
VkPipelineLayout m_pipelineLayout;
|
||||
VkPipeline m_graphicsPipeline;
|
||||
std::vector<VkFramebuffer> m_swapChainFramebuffers;
|
||||
VkCommandPool m_commandPool;
|
||||
std::vector<VkCommandBuffer> m_commandBuffers;
|
||||
std::vector<VkSemaphore> m_imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> m_renderFinishedSemaphores;
|
||||
std::vector<VkFence> m_inFlightFences;
|
||||
std::vector<VkBuffer> m_uniformBuffers;
|
||||
std::vector<VkDeviceMemory> m_uniformMemory;
|
||||
VkDescriptorSetLayout m_descriptorSetLayout;
|
||||
VkDescriptorPool m_descriptorPool;
|
||||
std::vector<VkDescriptorSet> m_descriptorSets;
|
||||
VkImage m_depthImage;
|
||||
VkDeviceMemory m_depthImageMemory;
|
||||
VkImageView m_depthImageView;
|
||||
size_t m_currentFrame;
|
||||
bool m_framebufferResized;
|
||||
uint8_t m_vkDeviceUUID[VK_UUID_SIZE];
|
||||
class VulkanBaseApp {
|
||||
public:
|
||||
VulkanBaseApp(const std::string& appName, bool enableValidation = false);
|
||||
static VkExternalSemaphoreHandleTypeFlagBits getDefaultSemaphoreHandleType();
|
||||
static VkExternalMemoryHandleTypeFlagBits getDefaultMemHandleType();
|
||||
virtual ~VulkanBaseApp();
|
||||
void init();
|
||||
void* getMemHandle(VkDeviceMemory memory,
|
||||
VkExternalMemoryHandleTypeFlagBits handleType);
|
||||
void* getSemaphoreHandle(VkSemaphore semaphore,
|
||||
VkExternalSemaphoreHandleTypeFlagBits handleType);
|
||||
void createExternalSemaphore(
|
||||
VkSemaphore& semaphore, VkExternalSemaphoreHandleTypeFlagBits handleType);
|
||||
void createBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties, VkBuffer& buffer,
|
||||
VkDeviceMemory& bufferMemory);
|
||||
void createExternalBuffer(VkDeviceSize size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties,
|
||||
VkExternalMemoryHandleTypeFlagsKHR extMemHandleType,
|
||||
VkBuffer& buffer, VkDeviceMemory& bufferMemory);
|
||||
void importExternalBuffer(void* handle,
|
||||
VkExternalMemoryHandleTypeFlagBits handleType,
|
||||
size_t size, VkBufferUsageFlags usage,
|
||||
VkMemoryPropertyFlags properties, VkBuffer& buffer,
|
||||
VkDeviceMemory& memory);
|
||||
void copyBuffer(VkBuffer dst, VkBuffer src, VkDeviceSize size);
|
||||
VkCommandBuffer beginSingleTimeCommands();
|
||||
void endSingleTimeCommands(VkCommandBuffer commandBuffer);
|
||||
void mainLoop();
|
||||
|
||||
virtual void initVulkanApp() {}
|
||||
virtual void fillRenderingCommandBuffer(VkCommandBuffer& buffer) {}
|
||||
virtual std::vector<const char *> getRequiredExtensions() const;
|
||||
virtual std::vector<const char *> getRequiredDeviceExtensions() const;
|
||||
virtual void getVertexDescriptions(std::vector<VkVertexInputBindingDescription>& bindingDesc, std::vector<VkVertexInputAttributeDescription>& attribDesc);
|
||||
virtual void getAssemblyStateInfo(VkPipelineInputAssemblyStateCreateInfo& info);
|
||||
virtual void getWaitFrameSemaphores(std::vector<VkSemaphore>& wait, std::vector< VkPipelineStageFlags>& waitStages) const;
|
||||
virtual void getSignalFrameSemaphores(std::vector<VkSemaphore>& signal) const;
|
||||
virtual VkDeviceSize getUniformSize() const;
|
||||
virtual void updateUniformBuffer(uint32_t imageIndex);
|
||||
virtual void drawFrame();
|
||||
private:
|
||||
GLFWwindow *m_window;
|
||||
protected:
|
||||
const std::string m_appName;
|
||||
const bool m_enableValidation;
|
||||
VkInstance m_instance;
|
||||
VkDebugUtilsMessengerEXT m_debugMessenger;
|
||||
VkSurfaceKHR m_surface;
|
||||
VkPhysicalDevice m_physicalDevice;
|
||||
VkDevice m_device;
|
||||
VkQueue m_graphicsQueue;
|
||||
VkQueue m_presentQueue;
|
||||
VkSwapchainKHR m_swapChain;
|
||||
std::vector<VkImage> m_swapChainImages;
|
||||
VkFormat m_swapChainFormat;
|
||||
VkExtent2D m_swapChainExtent;
|
||||
std::vector<VkImageView> m_swapChainImageViews;
|
||||
std::vector<std::pair<VkShaderStageFlagBits, std::string> > m_shaderFiles;
|
||||
VkRenderPass m_renderPass;
|
||||
VkPipelineLayout m_pipelineLayout;
|
||||
VkPipeline m_graphicsPipeline;
|
||||
std::vector<VkFramebuffer> m_swapChainFramebuffers;
|
||||
VkCommandPool m_commandPool;
|
||||
std::vector<VkCommandBuffer> m_commandBuffers;
|
||||
std::vector<VkSemaphore> m_imageAvailableSemaphores;
|
||||
std::vector<VkSemaphore> m_renderFinishedSemaphores;
|
||||
std::vector<VkFence> m_inFlightFences;
|
||||
std::vector<VkBuffer> m_uniformBuffers;
|
||||
std::vector<VkDeviceMemory> m_uniformMemory;
|
||||
VkSemaphore m_vkPresentationSemaphore;
|
||||
VkSemaphore m_vkTimelineSemaphore;
|
||||
VkDescriptorSetLayout m_descriptorSetLayout;
|
||||
VkDescriptorPool m_descriptorPool;
|
||||
std::vector<VkDescriptorSet> m_descriptorSets;
|
||||
VkImage m_depthImage;
|
||||
VkDeviceMemory m_depthImageMemory;
|
||||
VkImageView m_depthImageView;
|
||||
size_t m_currentFrame;
|
||||
bool m_framebufferResized;
|
||||
uint8_t m_vkDeviceUUID[VK_UUID_SIZE];
|
||||
|
||||
void initWindow();
|
||||
void initVulkan();
|
||||
void createInstance();
|
||||
void createSurface();
|
||||
void createDevice();
|
||||
void createSwapChain();
|
||||
void createImageViews();
|
||||
void createRenderPass();
|
||||
void createDescriptorSetLayout();
|
||||
void createGraphicsPipeline();
|
||||
void createFramebuffers();
|
||||
void createCommandPool();
|
||||
void createDepthResources();
|
||||
void createUniformBuffers();
|
||||
void createDescriptorPool();
|
||||
void createDescriptorSets();
|
||||
void createCommandBuffers();
|
||||
void createSyncObjects();
|
||||
virtual void initVulkanApp() {}
|
||||
virtual void fillRenderingCommandBuffer(VkCommandBuffer& buffer) {}
|
||||
virtual std::vector<const char*> getRequiredExtensions() const;
|
||||
virtual std::vector<const char*> getRequiredDeviceExtensions() const;
|
||||
virtual void getVertexDescriptions(
|
||||
std::vector<VkVertexInputBindingDescription>& bindingDesc,
|
||||
std::vector<VkVertexInputAttributeDescription>& attribDesc);
|
||||
virtual void getAssemblyStateInfo(
|
||||
VkPipelineInputAssemblyStateCreateInfo& info);
|
||||
virtual void getWaitFrameSemaphores(
|
||||
std::vector<VkSemaphore>& wait,
|
||||
std::vector<VkPipelineStageFlags>& waitStages) const;
|
||||
virtual void getSignalFrameSemaphores(std::vector<VkSemaphore>& signal) const;
|
||||
virtual VkDeviceSize getUniformSize() const;
|
||||
virtual void updateUniformBuffer(uint32_t imageIndex);
|
||||
virtual void drawFrame();
|
||||
|
||||
void cleanupSwapChain();
|
||||
void recreateSwapChain();
|
||||
private:
|
||||
GLFWwindow* m_window;
|
||||
|
||||
bool isSuitableDevice(VkPhysicalDevice dev) const;
|
||||
static void resizeCallback(GLFWwindow *window, int width, int height);
|
||||
void initWindow();
|
||||
void initVulkan();
|
||||
void createInstance();
|
||||
void createSurface();
|
||||
void createDevice();
|
||||
void createSwapChain();
|
||||
void createImageViews();
|
||||
void createRenderPass();
|
||||
void createDescriptorSetLayout();
|
||||
void createGraphicsPipeline();
|
||||
void createFramebuffers();
|
||||
void createCommandPool();
|
||||
void createDepthResources();
|
||||
void createUniformBuffers();
|
||||
void createDescriptorPool();
|
||||
void createDescriptorSets();
|
||||
void createCommandBuffers();
|
||||
void createSyncObjects();
|
||||
|
||||
void cleanupSwapChain();
|
||||
void recreateSwapChain();
|
||||
|
||||
bool isSuitableDevice(VkPhysicalDevice dev) const;
|
||||
static void resizeCallback(GLFWwindow* window, int width, int height);
|
||||
};
|
||||
|
||||
void readFile(std::istream& s, std::vector<char>& data);
|
||||
|
||||
@@ -46,28 +46,28 @@ std::string execution_path;
|
||||
#define ENABLE_VALIDATION (true)
|
||||
#endif
|
||||
|
||||
class VulkanCudaSineWave : public VulkanBaseApp
|
||||
{
|
||||
class VulkanCudaSineWave : public VulkanBaseApp {
|
||||
typedef struct UniformBufferObject_st {
|
||||
mat4x4 modelViewProj;
|
||||
} UniformBufferObject;
|
||||
|
||||
typedef struct UniformBufferObject_st {
|
||||
mat4x4 modelViewProj;
|
||||
} UniformBufferObject;
|
||||
VkBuffer m_heightBuffer, m_xyBuffer, m_indexBuffer;
|
||||
VkDeviceMemory m_heightMemory, m_xyMemory, m_indexMemory;
|
||||
UniformBufferObject m_ubo;
|
||||
VkSemaphore m_vkWaitSemaphore, m_vkSignalSemaphore;
|
||||
SineWaveSimulation m_sim;
|
||||
cudaStream_t m_stream;
|
||||
cudaExternalSemaphore_t m_cudaWaitSemaphore, m_cudaSignalSemaphore,
|
||||
m_cudaTimelineSemaphore;
|
||||
cudaExternalMemory_t m_cudaVertMem;
|
||||
float *m_cudaHeightMap;
|
||||
using chrono_tp = std::chrono::time_point<std::chrono::high_resolution_clock>;
|
||||
chrono_tp m_lastTime;
|
||||
size_t m_lastFrame;
|
||||
|
||||
VkBuffer m_heightBuffer, m_xyBuffer, m_indexBuffer;
|
||||
VkDeviceMemory m_heightMemory, m_xyMemory, m_indexMemory;
|
||||
UniformBufferObject m_ubo;
|
||||
VkSemaphore m_vkWaitSemaphore, m_vkSignalSemaphore;
|
||||
SineWaveSimulation m_sim;
|
||||
cudaStream_t m_stream;
|
||||
cudaExternalSemaphore_t m_cudaWaitSemaphore, m_cudaSignalSemaphore;
|
||||
cudaExternalMemory_t m_cudaVertMem;
|
||||
float *m_cudaHeightMap;
|
||||
using chrono_tp = std::chrono::time_point<std::chrono::high_resolution_clock>;
|
||||
chrono_tp m_lastTime;
|
||||
size_t m_lastFrame;
|
||||
public:
|
||||
VulkanCudaSineWave(size_t width, size_t height) :
|
||||
VulkanBaseApp("vulkanCudaSineWave", ENABLE_VALIDATION),
|
||||
public:
|
||||
VulkanCudaSineWave(size_t width, size_t height)
|
||||
: VulkanBaseApp("vulkanCudaSineWave", ENABLE_VALIDATION),
|
||||
m_heightBuffer(VK_NULL_HANDLE),
|
||||
m_xyBuffer(VK_NULL_HANDLE),
|
||||
m_indexBuffer(VK_NULL_HANDLE),
|
||||
@@ -81,361 +81,458 @@ public:
|
||||
m_vkSignalSemaphore(VK_NULL_HANDLE),
|
||||
m_cudaWaitSemaphore(),
|
||||
m_cudaSignalSemaphore(),
|
||||
m_cudaTimelineSemaphore(),
|
||||
m_cudaVertMem(),
|
||||
m_cudaHeightMap(nullptr),
|
||||
m_lastFrame(0) {
|
||||
// Our index buffer can only index 32-bits of the vertex buffer
|
||||
if ((width * height) > (1ULL << 32ULL)) {
|
||||
throw std::runtime_error("Requested height and width is too large for this sample!");
|
||||
}
|
||||
// Add our compiled vulkan shader files
|
||||
char* vertex_shader_path = sdkFindFilePath("sinewave.vert", execution_path.c_str());
|
||||
char* fragment_shader_path = sdkFindFilePath("sinewave.frag", execution_path.c_str());
|
||||
m_shaderFiles.push_back(std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertex_shader_path));
|
||||
m_shaderFiles.push_back(std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader_path));
|
||||
|
||||
// Our index buffer can only index 32-bits of the vertex buffer
|
||||
if ((width * height) > (1ULL << 32ULL)) {
|
||||
throw std::runtime_error(
|
||||
"Requested height and width is too large for this sample!");
|
||||
}
|
||||
~VulkanCudaSineWave() {
|
||||
// Make sure there's no pending work before we start tearing down
|
||||
checkCudaErrors(cudaStreamSynchronize(m_stream));
|
||||
// Add our compiled vulkan shader files
|
||||
char *vertex_shader_path =
|
||||
sdkFindFilePath("sinewave.vert", execution_path.c_str());
|
||||
char *fragment_shader_path =
|
||||
sdkFindFilePath("sinewave.frag", execution_path.c_str());
|
||||
m_shaderFiles.push_back(
|
||||
std::make_pair(VK_SHADER_STAGE_VERTEX_BIT, vertex_shader_path));
|
||||
m_shaderFiles.push_back(
|
||||
std::make_pair(VK_SHADER_STAGE_FRAGMENT_BIT, fragment_shader_path));
|
||||
}
|
||||
~VulkanCudaSineWave() {
|
||||
// Make sure there's no pending work before we start tearing down
|
||||
checkCudaErrors(cudaStreamSynchronize(m_stream));
|
||||
|
||||
if (m_vkSignalSemaphore != VK_NULL_HANDLE) {
|
||||
checkCudaErrors(cudaDestroyExternalSemaphore(m_cudaSignalSemaphore));
|
||||
vkDestroySemaphore(m_device, m_vkSignalSemaphore, nullptr);
|
||||
}
|
||||
if (m_vkWaitSemaphore != VK_NULL_HANDLE) {
|
||||
checkCudaErrors(cudaDestroyExternalSemaphore(m_cudaWaitSemaphore));
|
||||
vkDestroySemaphore(m_device, m_vkWaitSemaphore, nullptr);
|
||||
}
|
||||
#ifdef _VK_TIMELINE_SEMAPHORE
|
||||
if (m_vkTimelineSemaphore != VK_NULL_HANDLE) {
|
||||
checkCudaErrors(cudaDestroyExternalSemaphore(m_cudaTimelineSemaphore));
|
||||
vkDestroySemaphore(m_device, m_vkTimelineSemaphore, nullptr);
|
||||
}
|
||||
#endif /* _VK_TIMELINE_SEMAPHORE */
|
||||
|
||||
if (m_xyBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_xyBuffer, nullptr);
|
||||
}
|
||||
if (m_xyMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_xyMemory, nullptr);
|
||||
}
|
||||
|
||||
if (m_heightBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_heightBuffer, nullptr);
|
||||
}
|
||||
if (m_heightMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_heightMemory, nullptr);
|
||||
}
|
||||
if (m_cudaHeightMap) {
|
||||
checkCudaErrors(cudaDestroyExternalMemory(m_cudaVertMem));
|
||||
}
|
||||
|
||||
if (m_indexBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_indexBuffer, nullptr);
|
||||
}
|
||||
if (m_indexMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_indexMemory, nullptr);
|
||||
}
|
||||
if (m_vkSignalSemaphore != VK_NULL_HANDLE) {
|
||||
checkCudaErrors(cudaDestroyExternalSemaphore(m_cudaSignalSemaphore));
|
||||
vkDestroySemaphore(m_device, m_vkSignalSemaphore, nullptr);
|
||||
}
|
||||
if (m_vkWaitSemaphore != VK_NULL_HANDLE) {
|
||||
checkCudaErrors(cudaDestroyExternalSemaphore(m_cudaWaitSemaphore));
|
||||
vkDestroySemaphore(m_device, m_vkWaitSemaphore, nullptr);
|
||||
}
|
||||
|
||||
void fillRenderingCommandBuffer(VkCommandBuffer& commandBuffer) {
|
||||
VkBuffer vertexBuffers[] = { m_heightBuffer, m_xyBuffer };
|
||||
VkDeviceSize offsets[] = { 0, 0 };
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0, sizeof(vertexBuffers) / sizeof(vertexBuffers[0]), vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdDrawIndexed(commandBuffer, (uint32_t)((m_sim.getWidth() - 1) * (m_sim.getHeight() - 1) * 6), 1, 0, 0, 0);
|
||||
if (m_xyBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_xyBuffer, nullptr);
|
||||
}
|
||||
if (m_xyMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_xyMemory, nullptr);
|
||||
}
|
||||
|
||||
void getVertexDescriptions(std::vector<VkVertexInputBindingDescription>& bindingDesc, std::vector<VkVertexInputAttributeDescription>& attribDesc) {
|
||||
bindingDesc.resize(2);
|
||||
attribDesc.resize(2);
|
||||
|
||||
bindingDesc[0].binding = 0;
|
||||
bindingDesc[0].stride = sizeof(float);
|
||||
bindingDesc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
bindingDesc[1].binding = 1;
|
||||
bindingDesc[1].stride = sizeof(vec2);
|
||||
bindingDesc[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
attribDesc[0].binding = 0;
|
||||
attribDesc[0].location = 0;
|
||||
attribDesc[0].format = VK_FORMAT_R32_SFLOAT;
|
||||
attribDesc[0].offset = 0;
|
||||
|
||||
attribDesc[1].binding = 1;
|
||||
attribDesc[1].location = 1;
|
||||
attribDesc[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribDesc[1].offset = 0;
|
||||
if (m_heightBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_heightBuffer, nullptr);
|
||||
}
|
||||
if (m_heightMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_heightMemory, nullptr);
|
||||
}
|
||||
if (m_cudaHeightMap) {
|
||||
checkCudaErrors(cudaDestroyExternalMemory(m_cudaVertMem));
|
||||
}
|
||||
|
||||
void getAssemblyStateInfo(VkPipelineInputAssemblyStateCreateInfo& info) {
|
||||
info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
info.primitiveRestartEnable = VK_FALSE;
|
||||
if (m_indexBuffer != VK_NULL_HANDLE) {
|
||||
vkDestroyBuffer(m_device, m_indexBuffer, nullptr);
|
||||
}
|
||||
if (m_indexMemory != VK_NULL_HANDLE) {
|
||||
vkFreeMemory(m_device, m_indexMemory, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void fillRenderingCommandBuffer(VkCommandBuffer &commandBuffer) {
|
||||
VkBuffer vertexBuffers[] = {m_heightBuffer, m_xyBuffer};
|
||||
VkDeviceSize offsets[] = {0, 0};
|
||||
vkCmdBindVertexBuffers(commandBuffer, 0,
|
||||
sizeof(vertexBuffers) / sizeof(vertexBuffers[0]),
|
||||
vertexBuffers, offsets);
|
||||
vkCmdBindIndexBuffer(commandBuffer, m_indexBuffer, 0, VK_INDEX_TYPE_UINT32);
|
||||
vkCmdDrawIndexed(commandBuffer, (uint32_t)((m_sim.getWidth() - 1) *
|
||||
(m_sim.getHeight() - 1) * 6),
|
||||
1, 0, 0, 0);
|
||||
}
|
||||
|
||||
void getVertexDescriptions(
|
||||
std::vector<VkVertexInputBindingDescription> &bindingDesc,
|
||||
std::vector<VkVertexInputAttributeDescription> &attribDesc) {
|
||||
bindingDesc.resize(2);
|
||||
attribDesc.resize(2);
|
||||
|
||||
bindingDesc[0].binding = 0;
|
||||
bindingDesc[0].stride = sizeof(float);
|
||||
bindingDesc[0].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
bindingDesc[1].binding = 1;
|
||||
bindingDesc[1].stride = sizeof(vec2);
|
||||
bindingDesc[1].inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
||||
|
||||
attribDesc[0].binding = 0;
|
||||
attribDesc[0].location = 0;
|
||||
attribDesc[0].format = VK_FORMAT_R32_SFLOAT;
|
||||
attribDesc[0].offset = 0;
|
||||
|
||||
attribDesc[1].binding = 1;
|
||||
attribDesc[1].location = 1;
|
||||
attribDesc[1].format = VK_FORMAT_R32G32_SFLOAT;
|
||||
attribDesc[1].offset = 0;
|
||||
}
|
||||
|
||||
void getAssemblyStateInfo(VkPipelineInputAssemblyStateCreateInfo &info) {
|
||||
info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
||||
info.primitiveRestartEnable = VK_FALSE;
|
||||
}
|
||||
|
||||
void getWaitFrameSemaphores(
|
||||
std::vector<VkSemaphore> &wait,
|
||||
std::vector<VkPipelineStageFlags> &waitStages) const {
|
||||
if (m_currentFrame != 0) {
|
||||
// Have vulkan wait until cuda is done with the vertex buffer before
|
||||
// rendering, We don't do this on the first frame, as the wait semaphore
|
||||
// hasn't been initialized yet
|
||||
wait.push_back(m_vkWaitSemaphore);
|
||||
// We want to wait until all the pipeline commands are complete before
|
||||
// letting cuda work
|
||||
waitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void getSignalFrameSemaphores(std::vector<VkSemaphore> &signal) const {
|
||||
// Add this semaphore for vulkan to signal once the vertex buffer is ready
|
||||
// for cuda to modify
|
||||
signal.push_back(m_vkSignalSemaphore);
|
||||
}
|
||||
|
||||
void initVulkanApp() {
|
||||
int cuda_device = -1;
|
||||
|
||||
// Select cuda device where vulkan is running.
|
||||
cuda_device = m_sim.initCuda(m_vkDeviceUUID, VK_UUID_SIZE);
|
||||
if (cuda_device == -1) {
|
||||
printf("Error: No CUDA-Vulkan interop capable device found\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void getWaitFrameSemaphores(std::vector<VkSemaphore>& wait, std::vector< VkPipelineStageFlags>& waitStages) const {
|
||||
if (m_currentFrame != 0) {
|
||||
// Have vulkan wait until cuda is done with the vertex buffer before rendering
|
||||
// We don't do this on the first frame, as the wait semaphore hasn't been initialized yet
|
||||
wait.push_back(m_vkWaitSemaphore);
|
||||
// We want to wait until all the pipeline commands are complete before letting cuda work
|
||||
waitStages.push_back(VK_PIPELINE_STAGE_ALL_COMMANDS_BIT);
|
||||
m_sim.initCudaLaunchConfig(cuda_device);
|
||||
|
||||
// Create the cuda stream we'll be using
|
||||
checkCudaErrors(
|
||||
cudaStreamCreateWithFlags(&m_stream, cudaStreamNonBlocking));
|
||||
|
||||
const size_t nVerts = m_sim.getWidth() * m_sim.getHeight();
|
||||
const size_t nInds = (m_sim.getWidth() - 1) * (m_sim.getHeight() - 1) * 6;
|
||||
|
||||
// Create the height map cuda will write to
|
||||
createExternalBuffer(
|
||||
nVerts * sizeof(float),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, getDefaultMemHandleType(),
|
||||
m_heightBuffer, m_heightMemory);
|
||||
|
||||
// Create the vertex buffer that will hold the xy coordinates for the grid
|
||||
createBuffer(nVerts * sizeof(vec2), VK_BUFFER_USAGE_TRANSFER_DST_BIT |
|
||||
VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_xyBuffer, m_xyMemory);
|
||||
|
||||
// Create the index buffer that references from both buffers above
|
||||
createBuffer(
|
||||
nInds * sizeof(uint32_t),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, m_indexBuffer, m_indexMemory);
|
||||
|
||||
// Import the height map into cuda and retrieve a device pointer to use
|
||||
importCudaExternalMemory((void **)&m_cudaHeightMap, m_cudaVertMem,
|
||||
m_heightMemory, nVerts * sizeof(*m_cudaHeightMap),
|
||||
getDefaultMemHandleType());
|
||||
// Set the height map to use in the simulation
|
||||
m_sim.initSimulation(m_cudaHeightMap);
|
||||
|
||||
{
|
||||
// Set up the initial values for the vertex buffers with Vulkan
|
||||
void *stagingBase;
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingMemory;
|
||||
VkDeviceSize stagingSz =
|
||||
std::max(nVerts * sizeof(vec2), nInds * sizeof(uint32_t));
|
||||
createBuffer(stagingSz, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
||||
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,
|
||||
stagingBuffer, stagingMemory);
|
||||
|
||||
vkMapMemory(m_device, stagingMemory, 0, stagingSz, 0, &stagingBase);
|
||||
|
||||
memset(stagingBase, 0, nVerts * sizeof(float));
|
||||
copyBuffer(m_heightBuffer, stagingBuffer, nVerts * sizeof(float));
|
||||
|
||||
for (size_t y = 0; y < m_sim.getHeight(); y++) {
|
||||
for (size_t x = 0; x < m_sim.getWidth(); x++) {
|
||||
vec2 *stagedVert = (vec2 *)stagingBase;
|
||||
stagedVert[y * m_sim.getWidth() + x][0] =
|
||||
(2.0f * x) / (m_sim.getWidth() - 1) - 1;
|
||||
stagedVert[y * m_sim.getWidth() + x][1] =
|
||||
(2.0f * y) / (m_sim.getHeight() - 1) - 1;
|
||||
}
|
||||
}
|
||||
copyBuffer(m_xyBuffer, stagingBuffer, nVerts * sizeof(vec2));
|
||||
|
||||
{
|
||||
uint32_t *indices = (uint32_t *)stagingBase;
|
||||
for (size_t y = 0; y < m_sim.getHeight() - 1; y++) {
|
||||
for (size_t x = 0; x < m_sim.getWidth() - 1; x++) {
|
||||
indices[0] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 0));
|
||||
indices[1] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 0));
|
||||
indices[2] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 1));
|
||||
indices[3] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 0));
|
||||
indices[4] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 1));
|
||||
indices[5] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 1));
|
||||
indices += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
copyBuffer(m_indexBuffer, stagingBuffer, nInds * sizeof(uint32_t));
|
||||
|
||||
vkUnmapMemory(m_device, stagingMemory);
|
||||
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(m_device, stagingMemory, nullptr);
|
||||
}
|
||||
|
||||
void getSignalFrameSemaphores(std::vector<VkSemaphore>& signal) const {
|
||||
// Add this semaphore for vulkan to signal once the vertex buffer is ready for cuda to modify
|
||||
signal.push_back(m_vkSignalSemaphore);
|
||||
#ifdef _VK_TIMELINE_SEMAPHORE
|
||||
// Create the timeline semaphore to sync cuda and vulkan access to vertex
|
||||
// buffer
|
||||
createExternalSemaphore(m_vkTimelineSemaphore,
|
||||
getDefaultSemaphoreHandleType());
|
||||
// Import the timeline semaphore cuda will use to sync cuda and vulkan
|
||||
// access to vertex buffer
|
||||
importCudaExternalSemaphore(m_cudaTimelineSemaphore, m_vkTimelineSemaphore,
|
||||
getDefaultSemaphoreHandleType());
|
||||
#else
|
||||
|
||||
// Create the semaphore vulkan will signal when it's done with the vertex
|
||||
// buffer
|
||||
createExternalSemaphore(m_vkSignalSemaphore,
|
||||
getDefaultSemaphoreHandleType());
|
||||
// Create the semaphore vulkan will wait for before using the vertex buffer
|
||||
createExternalSemaphore(m_vkWaitSemaphore, getDefaultSemaphoreHandleType());
|
||||
// Import the semaphore cuda will use -- vulkan's signal will be cuda's wait
|
||||
importCudaExternalSemaphore(m_cudaWaitSemaphore, m_vkSignalSemaphore,
|
||||
getDefaultSemaphoreHandleType());
|
||||
// Import the semaphore cuda will use -- cuda's signal will be vulkan's wait
|
||||
importCudaExternalSemaphore(m_cudaSignalSemaphore, m_vkWaitSemaphore,
|
||||
getDefaultSemaphoreHandleType());
|
||||
#endif /* _VK_TIMELINE_SEMAPHORE */
|
||||
}
|
||||
|
||||
void importCudaExternalMemory(void **cudaPtr, cudaExternalMemory_t &cudaMem,
|
||||
VkDeviceMemory &vkMem, VkDeviceSize size,
|
||||
VkExternalMemoryHandleTypeFlagBits handleType) {
|
||||
cudaExternalMemoryHandleDesc externalMemoryHandleDesc = {};
|
||||
|
||||
if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) {
|
||||
externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueWin32;
|
||||
} else if (handleType &
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {
|
||||
externalMemoryHandleDesc.type =
|
||||
cudaExternalMemoryHandleTypeOpaqueWin32Kmt;
|
||||
} else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueFd;
|
||||
} else {
|
||||
throw std::runtime_error("Unknown handle type requested!");
|
||||
}
|
||||
|
||||
void initVulkanApp() {
|
||||
int cuda_device = -1;
|
||||
|
||||
// Select cuda device where vulkan is running.
|
||||
cuda_device = m_sim.initCuda(m_vkDeviceUUID, VK_UUID_SIZE);
|
||||
if (cuda_device == -1)
|
||||
{
|
||||
printf("Error: No CUDA-Vulkan interop capable device found\n");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
m_sim.initCudaLaunchConfig(cuda_device);
|
||||
|
||||
// Create the cuda stream we'll be using
|
||||
checkCudaErrors(cudaStreamCreateWithFlags(&m_stream, cudaStreamNonBlocking));
|
||||
|
||||
const size_t nVerts = m_sim.getWidth() * m_sim.getHeight();
|
||||
const size_t nInds = (m_sim.getWidth() - 1) * (m_sim.getHeight() - 1) * 6;
|
||||
|
||||
// Create the height map cuda will write to
|
||||
createExternalBuffer(nVerts * sizeof(float),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
getDefaultMemHandleType(),
|
||||
m_heightBuffer, m_heightMemory);
|
||||
|
||||
// Create the vertex buffer that will hold the xy coordinates for the grid
|
||||
createBuffer(nVerts * sizeof(vec2),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
m_xyBuffer, m_xyMemory);
|
||||
|
||||
// Create the index buffer that references from both buffers above
|
||||
createBuffer(nInds * sizeof(uint32_t),
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
|
||||
m_indexBuffer, m_indexMemory);
|
||||
|
||||
// Import the height map into cuda and retrieve a device pointer to use
|
||||
importCudaExternalMemory((void **)&m_cudaHeightMap, m_cudaVertMem, m_heightMemory, nVerts * sizeof(*m_cudaHeightMap), getDefaultMemHandleType());
|
||||
// Set the height map to use in the simulation
|
||||
m_sim.initSimulation(m_cudaHeightMap);
|
||||
|
||||
{
|
||||
// Set up the initial values for the vertex buffers with Vulkan
|
||||
void *stagingBase;
|
||||
VkBuffer stagingBuffer;
|
||||
VkDeviceMemory stagingMemory;
|
||||
VkDeviceSize stagingSz = std::max(nVerts * sizeof(vec2), nInds * sizeof(uint32_t));
|
||||
createBuffer(stagingSz, VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
|
||||
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, stagingBuffer, stagingMemory);
|
||||
|
||||
vkMapMemory(m_device, stagingMemory, 0, stagingSz, 0, &stagingBase);
|
||||
|
||||
memset(stagingBase, 0, nVerts * sizeof(float));
|
||||
copyBuffer(m_heightBuffer, stagingBuffer, nVerts * sizeof(float));
|
||||
|
||||
for (size_t y = 0; y < m_sim.getHeight(); y++) {
|
||||
for (size_t x = 0; x < m_sim.getWidth(); x++) {
|
||||
vec2 *stagedVert = (vec2 *)stagingBase;
|
||||
stagedVert[y * m_sim.getWidth() + x][0] = (2.0f * x) / (m_sim.getWidth() - 1) - 1;
|
||||
stagedVert[y * m_sim.getWidth() + x][1] = (2.0f * y) / (m_sim.getHeight() - 1) - 1;
|
||||
}
|
||||
}
|
||||
copyBuffer(m_xyBuffer, stagingBuffer, nVerts * sizeof(vec2));
|
||||
|
||||
{
|
||||
uint32_t *indices = (uint32_t *)stagingBase;
|
||||
for (size_t y = 0; y < m_sim.getHeight() - 1; y++) {
|
||||
for (size_t x = 0; x < m_sim.getWidth() - 1; x++) {
|
||||
indices[0] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 0));
|
||||
indices[1] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 0));
|
||||
indices[2] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 1));
|
||||
indices[3] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 0));
|
||||
indices[4] = (uint32_t)((y + 1) * m_sim.getWidth() + (x + 1));
|
||||
indices[5] = (uint32_t)((y + 0) * m_sim.getWidth() + (x + 1));
|
||||
indices += 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
copyBuffer(m_indexBuffer, stagingBuffer, nInds * sizeof(uint32_t));
|
||||
|
||||
vkUnmapMemory(m_device, stagingMemory);
|
||||
vkDestroyBuffer(m_device, stagingBuffer, nullptr);
|
||||
vkFreeMemory(m_device, stagingMemory, nullptr);
|
||||
}
|
||||
|
||||
// Create the semaphore vulkan will signal when it's done with the vertex buffer
|
||||
createExternalSemaphore(m_vkSignalSemaphore, getDefaultSemaphoreHandleType());
|
||||
// Create the semaphore vulkan will wait for before using the vertex buffer
|
||||
createExternalSemaphore(m_vkWaitSemaphore, getDefaultSemaphoreHandleType());
|
||||
// Import the semaphore cuda will use -- vulkan's signal will be cuda's wait
|
||||
importCudaExternalSemaphore(m_cudaWaitSemaphore, m_vkSignalSemaphore, getDefaultSemaphoreHandleType());
|
||||
// Import the semaphore cuda will use -- cuda's signal will be vulkan's wait
|
||||
importCudaExternalSemaphore(m_cudaSignalSemaphore, m_vkWaitSemaphore, getDefaultSemaphoreHandleType());
|
||||
}
|
||||
|
||||
void importCudaExternalMemory(void **cudaPtr, cudaExternalMemory_t& cudaMem, VkDeviceMemory& vkMem, VkDeviceSize size, VkExternalMemoryHandleTypeFlagBits handleType) {
|
||||
cudaExternalMemoryHandleDesc externalMemoryHandleDesc = {};
|
||||
|
||||
if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) {
|
||||
externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueWin32;
|
||||
}
|
||||
else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {
|
||||
externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueWin32Kmt;
|
||||
}
|
||||
else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
externalMemoryHandleDesc.type = cudaExternalMemoryHandleTypeOpaqueFd;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Unknown handle type requested!");
|
||||
}
|
||||
|
||||
externalMemoryHandleDesc.size = size;
|
||||
externalMemoryHandleDesc.size = size;
|
||||
|
||||
#ifdef _WIN64
|
||||
externalMemoryHandleDesc.handle.win32.handle = (HANDLE)getMemHandle(vkMem, handleType);
|
||||
externalMemoryHandleDesc.handle.win32.handle =
|
||||
(HANDLE)getMemHandle(vkMem, handleType);
|
||||
#else
|
||||
externalMemoryHandleDesc.handle.fd = (int)(uintptr_t)getMemHandle(vkMem, handleType);
|
||||
externalMemoryHandleDesc.handle.fd =
|
||||
(int)(uintptr_t)getMemHandle(vkMem, handleType);
|
||||
#endif
|
||||
|
||||
checkCudaErrors(cudaImportExternalMemory(&cudaMem, &externalMemoryHandleDesc));
|
||||
checkCudaErrors(
|
||||
cudaImportExternalMemory(&cudaMem, &externalMemoryHandleDesc));
|
||||
|
||||
cudaExternalMemoryBufferDesc externalMemBufferDesc = {};
|
||||
externalMemBufferDesc.offset = 0;
|
||||
externalMemBufferDesc.size = size;
|
||||
externalMemBufferDesc.flags = 0;
|
||||
cudaExternalMemoryBufferDesc externalMemBufferDesc = {};
|
||||
externalMemBufferDesc.offset = 0;
|
||||
externalMemBufferDesc.size = size;
|
||||
externalMemBufferDesc.flags = 0;
|
||||
|
||||
checkCudaErrors(cudaExternalMemoryGetMappedBuffer(cudaPtr, cudaMem, &externalMemBufferDesc));
|
||||
checkCudaErrors(cudaExternalMemoryGetMappedBuffer(cudaPtr, cudaMem,
|
||||
&externalMemBufferDesc));
|
||||
}
|
||||
|
||||
void importCudaExternalSemaphore(
|
||||
cudaExternalSemaphore_t &cudaSem, VkSemaphore &vkSem,
|
||||
VkExternalSemaphoreHandleTypeFlagBits handleType) {
|
||||
cudaExternalSemaphoreHandleDesc externalSemaphoreHandleDesc = {};
|
||||
|
||||
#ifdef _VK_TIMELINE_SEMAPHORE
|
||||
if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeTimelineSemaphoreWin32;
|
||||
} else if (handleType &
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeTimelineSemaphoreWin32;
|
||||
} else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeTimelineSemaphoreFd;
|
||||
}
|
||||
#else
|
||||
if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeOpaqueWin32;
|
||||
} else if (handleType &
|
||||
VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeOpaqueWin32Kmt;
|
||||
} else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
externalSemaphoreHandleDesc.type =
|
||||
cudaExternalSemaphoreHandleTypeOpaqueFd;
|
||||
}
|
||||
#endif /* _VK_TIMELINE_SEMAPHORE */
|
||||
else {
|
||||
throw std::runtime_error("Unknown handle type requested!");
|
||||
}
|
||||
|
||||
void importCudaExternalSemaphore(cudaExternalSemaphore_t& cudaSem, VkSemaphore& vkSem, VkExternalSemaphoreHandleTypeFlagBits handleType) {
|
||||
cudaExternalSemaphoreHandleDesc externalSemaphoreHandleDesc = {};
|
||||
|
||||
if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_BIT) {
|
||||
externalSemaphoreHandleDesc.type = cudaExternalSemaphoreHandleTypeOpaqueWin32;
|
||||
}
|
||||
else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_WIN32_KMT_BIT) {
|
||||
externalSemaphoreHandleDesc.type = cudaExternalSemaphoreHandleTypeOpaqueWin32Kmt;
|
||||
}
|
||||
else if (handleType & VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT) {
|
||||
externalSemaphoreHandleDesc.type = cudaExternalSemaphoreHandleTypeOpaqueFd;
|
||||
}
|
||||
else {
|
||||
throw std::runtime_error("Unknown handle type requested!");
|
||||
}
|
||||
|
||||
#ifdef _WIN64
|
||||
externalSemaphoreHandleDesc.handle.win32.handle = (HANDLE)getSemaphoreHandle(vkSem, handleType);
|
||||
externalSemaphoreHandleDesc.handle.win32.handle =
|
||||
(HANDLE)getSemaphoreHandle(vkSem, handleType);
|
||||
#else
|
||||
externalSemaphoreHandleDesc.handle.fd = (int)(uintptr_t)getSemaphoreHandle(vkSem, handleType);
|
||||
externalSemaphoreHandleDesc.handle.fd =
|
||||
(int)(uintptr_t)getSemaphoreHandle(vkSem, handleType);
|
||||
#endif
|
||||
|
||||
externalSemaphoreHandleDesc.flags = 0;
|
||||
externalSemaphoreHandleDesc.flags = 0;
|
||||
|
||||
checkCudaErrors(cudaImportExternalSemaphore(&cudaSem, &externalSemaphoreHandleDesc));
|
||||
checkCudaErrors(
|
||||
cudaImportExternalSemaphore(&cudaSem, &externalSemaphoreHandleDesc));
|
||||
}
|
||||
|
||||
VkDeviceSize getUniformSize() const { return sizeof(UniformBufferObject); }
|
||||
|
||||
void updateUniformBuffer(uint32_t imageIndex) {
|
||||
{
|
||||
mat4x4 view, proj;
|
||||
vec3 eye = {1.75f, 1.75f, 1.25f};
|
||||
vec3 center = {0.0f, 0.0f, -0.25f};
|
||||
vec3 up = {0.0f, 0.0f, 1.0f};
|
||||
|
||||
mat4x4_perspective(
|
||||
proj, (float)degreesToRadians(45.0f),
|
||||
m_swapChainExtent.width / (float)m_swapChainExtent.height, 0.1f,
|
||||
10.0f);
|
||||
proj[1][1] *= -1.0f; // Flip y axis
|
||||
|
||||
mat4x4_look_at(view, eye, center, up);
|
||||
mat4x4_mul(m_ubo.modelViewProj, proj, view);
|
||||
}
|
||||
|
||||
VkDeviceSize getUniformSize() const {
|
||||
return sizeof(UniformBufferObject);
|
||||
}
|
||||
void *data;
|
||||
vkMapMemory(m_device, m_uniformMemory[imageIndex], 0, getUniformSize(), 0,
|
||||
&data);
|
||||
memcpy(data, &m_ubo, sizeof(m_ubo));
|
||||
vkUnmapMemory(m_device, m_uniformMemory[imageIndex]);
|
||||
}
|
||||
|
||||
void updateUniformBuffer(uint32_t imageIndex) {
|
||||
{
|
||||
mat4x4 view, proj;
|
||||
vec3 eye = { 1.75f, 1.75f, 1.25f };
|
||||
vec3 center = { 0.0f, 0.0f, -0.25f };
|
||||
vec3 up = { 0.0f, 0.0f, 1.0f };
|
||||
std::vector<const char *> getRequiredExtensions() const {
|
||||
std::vector<const char *> extensions;
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
mat4x4_perspective(proj, (float)degreesToRadians(45.0f), m_swapChainExtent.width / (float)m_swapChainExtent.height, 0.1f, 10.0f);
|
||||
proj[1][1] *= -1.0f; // Flip y axis
|
||||
|
||||
mat4x4_look_at(view, eye, center, up);
|
||||
mat4x4_mul(m_ubo.modelViewProj, proj, view);
|
||||
}
|
||||
|
||||
void *data;
|
||||
vkMapMemory(m_device, m_uniformMemory[imageIndex], 0, getUniformSize(), 0, &data);
|
||||
memcpy(data, &m_ubo, sizeof(m_ubo));
|
||||
vkUnmapMemory(m_device, m_uniformMemory[imageIndex]);
|
||||
}
|
||||
|
||||
std::vector<const char *> getRequiredExtensions() const {
|
||||
std::vector<const char *> extensions;
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_CAPABILITIES_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_CAPABILITIES_EXTENSION_NAME);
|
||||
return extensions;
|
||||
}
|
||||
|
||||
std::vector<const char *> getRequiredDeviceExtensions() const {
|
||||
std::vector<const char *> extensions;
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
|
||||
std::vector<const char *> getRequiredDeviceExtensions() const {
|
||||
std::vector<const char *> extensions;
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME);
|
||||
#ifdef _WIN64
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_WIN32_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_WIN32_EXTENSION_NAME);
|
||||
#else
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME);
|
||||
extensions.push_back(VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME);
|
||||
#endif /* _WIN64 */
|
||||
return extensions;
|
||||
return extensions;
|
||||
}
|
||||
|
||||
void drawFrame() {
|
||||
static chrono_tp startTime = std::chrono::high_resolution_clock::now();
|
||||
|
||||
chrono_tp currentTime = std::chrono::high_resolution_clock::now();
|
||||
float time = std::chrono::duration<float, std::chrono::seconds::period>(
|
||||
currentTime - startTime)
|
||||
.count();
|
||||
|
||||
if (m_currentFrame == 0) {
|
||||
m_lastTime = startTime;
|
||||
}
|
||||
|
||||
void drawFrame() {
|
||||
static chrono_tp startTime = std::chrono::high_resolution_clock::now();
|
||||
float frame_time =
|
||||
std::chrono::duration<float, std::chrono::seconds::period>(currentTime -
|
||||
m_lastTime)
|
||||
.count();
|
||||
|
||||
chrono_tp currentTime = std::chrono::high_resolution_clock::now();
|
||||
float time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - startTime).count();
|
||||
// Have vulkan draw the current frame...
|
||||
VulkanBaseApp::drawFrame();
|
||||
|
||||
if (m_currentFrame == 0) {
|
||||
m_lastTime = startTime;
|
||||
}
|
||||
#ifdef _VK_TIMELINE_SEMAPHORE
|
||||
cudaExternalSemaphoreWaitParams waitParams = {};
|
||||
waitParams.flags = 0;
|
||||
waitParams.params.fence.value = 1;
|
||||
|
||||
float frame_time = std::chrono::duration<float, std::chrono::seconds::period>(currentTime - m_lastTime).count();
|
||||
cudaExternalSemaphoreSignalParams signalParams = {};
|
||||
signalParams.flags = 0;
|
||||
signalParams.params.fence.value = 0;
|
||||
// Wait for vulkan to complete it's work
|
||||
checkCudaErrors(cudaWaitExternalSemaphoresAsync(&m_cudaTimelineSemaphore,
|
||||
&waitParams, 1, m_stream));
|
||||
// Now step the simulation
|
||||
m_sim.stepSimulation(time, m_stream);
|
||||
// Signal vulkan to continue with the updated buffers
|
||||
checkCudaErrors(cudaSignalExternalSemaphoresAsync(
|
||||
&m_cudaTimelineSemaphore, &signalParams, 1, m_stream));
|
||||
#else
|
||||
cudaExternalSemaphoreWaitParams waitParams = {};
|
||||
waitParams.flags = 0;
|
||||
waitParams.params.fence.value = 0;
|
||||
|
||||
cudaExternalSemaphoreWaitParams waitParams = {};
|
||||
waitParams.flags = 0;
|
||||
waitParams.params.fence.value = 0;
|
||||
cudaExternalSemaphoreSignalParams signalParams = {};
|
||||
signalParams.flags = 0;
|
||||
signalParams.params.fence.value = 0;
|
||||
|
||||
cudaExternalSemaphoreSignalParams signalParams = {};
|
||||
signalParams.flags = 0;
|
||||
signalParams.params.fence.value = 0;
|
||||
// Wait for vulkan to complete it's work
|
||||
checkCudaErrors(cudaWaitExternalSemaphoresAsync(&m_cudaWaitSemaphore,
|
||||
&waitParams, 1, m_stream));
|
||||
// Now step the simulation
|
||||
m_sim.stepSimulation(time, m_stream);
|
||||
// Signal vulkan to continue with the updated buffers
|
||||
checkCudaErrors(cudaSignalExternalSemaphoresAsync(
|
||||
&m_cudaSignalSemaphore, &signalParams, 1, m_stream));
|
||||
#endif /* _VK_TIMELINE_SEMAPHORE */
|
||||
|
||||
// Have vulkan draw the current frame...
|
||||
VulkanBaseApp::drawFrame();
|
||||
// Wait for vulkan to complete it's work
|
||||
checkCudaErrors(cudaWaitExternalSemaphoresAsync(&m_cudaWaitSemaphore, &waitParams, 1, m_stream));
|
||||
// Now step the simulation
|
||||
m_sim.stepSimulation(time, m_stream);
|
||||
// Signal vulkan to continue with the updated buffers
|
||||
checkCudaErrors(cudaSignalExternalSemaphoresAsync(&m_cudaSignalSemaphore, &signalParams, 1, m_stream));
|
||||
|
||||
// Output a naive measurement of the frames per second every five seconds
|
||||
if (frame_time > 5) {
|
||||
std::cout << "Average FPS (over "
|
||||
<< std::fixed << std::setprecision(2) << frame_time
|
||||
<< " seconds): "
|
||||
<< std::fixed << std::setprecision(2)
|
||||
<< ((m_currentFrame - m_lastFrame) / frame_time)
|
||||
<< std::endl;
|
||||
m_lastFrame = m_currentFrame;
|
||||
m_lastTime = currentTime;
|
||||
}
|
||||
// Output a naive measurement of the frames per second every five seconds
|
||||
if (frame_time > 5) {
|
||||
std::cout << "Average FPS (over " << std::fixed << std::setprecision(2)
|
||||
<< frame_time << " seconds): " << std::fixed
|
||||
<< std::setprecision(2)
|
||||
<< ((m_currentFrame - m_lastFrame) / frame_time) << std::endl;
|
||||
m_lastFrame = m_currentFrame;
|
||||
m_lastTime = currentTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
execution_path = argv[0];
|
||||
VulkanCudaSineWave app((1ULL << 8ULL), (1ULL << 8ULL));
|
||||
app.init();
|
||||
app.mainLoop();
|
||||
return 0;
|
||||
int main(int argc, char **argv) {
|
||||
execution_path = argv[0];
|
||||
VulkanCudaSineWave app((1ULL << 8ULL), (1ULL << 8ULL));
|
||||
app.init();
|
||||
app.mainLoop();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.2.props" />
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.3.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" />
|
||||
@@ -122,6 +122,6 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.2.targets" />
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.3.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.2.props" />
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.3.props" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets">
|
||||
<Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" />
|
||||
@@ -118,6 +118,6 @@
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.2.targets" />
|
||||
<Import Project="$(CUDAPropsPath)\CUDA 11.3.targets" />
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
|
||||
Reference in New Issue
Block a user