1027 lines
No EOL
44 KiB
C++
1027 lines
No EOL
44 KiB
C++
////////////////////////////////////////////////////////////////////////////////
|
|
// (c) Copyright, Ubisoft Entertainment Inc. || Created by Harbour //
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
#include "tests/ggp/vulkanTestHelper.h"
|
|
#if defined(US_TARGET_GGP)
|
|
#include "tests/ggp/ps_spv.h"
|
|
#include "tests/ggp/vs_spv.h"
|
|
#include <ggp_c/media_stream_types.h>
|
|
#include <ggp_c/frame_token_types.h>
|
|
#include <vulkan/vulkan_ggp.h>
|
|
#include <ggp_c/application.h>
|
|
|
|
#define Abort(message, ...) \
|
|
{ \
|
|
PRINT_LINE("%s:%u:%s:" message, __FILE__, __LINE__, \
|
|
__FUNCTION__, ##__VA_ARGS__); \
|
|
abort(); \
|
|
}
|
|
|
|
// Vulkan result helper macro
|
|
#define CHECK_VK(call) \
|
|
{ \
|
|
VkResult res = call; \
|
|
if (res != VK_SUCCESS) { \
|
|
Abort("Error in vulkan: %d", res); \
|
|
} \
|
|
}
|
|
|
|
// Macro for getting and checking the results of Instance extension functions.
|
|
#define VK_GET_INSTANCE_PROC_ADDR(fp, instance, entryPoint) \
|
|
static PFN_##entryPoint fp; \
|
|
\
|
|
if (fp == NULL) { \
|
|
fp = (PFN_##entryPoint)vkGetInstanceProcAddr(instance, #entryPoint); \
|
|
if (fp == NULL) { \
|
|
Abort("Error getting VkInstance function: " #entryPoint); \
|
|
} \
|
|
}
|
|
// Macro for getting and checking the results of Device extension functions.
|
|
#define VK_GET_DEVICE_PROC_ADDR(fp, device, entryPoint) \
|
|
static PFN_##entryPoint fp; \
|
|
\
|
|
if (fp == NULL) { \
|
|
fp = (PFN_##entryPoint)vkGetDeviceProcAddr(device, #entryPoint); \
|
|
if (fp == NULL) { \
|
|
Abort("Error getting VkDevice function: " #entryPoint); \
|
|
} \
|
|
}
|
|
|
|
// Simple alloc array macro to clean-up the code
|
|
#define ALLOC_ARRAY(num, type) (type*)malloc((num) * sizeof(type));
|
|
|
|
#define VK_DBG_OFF 0
|
|
#define VK_DBG_ON 1
|
|
#define VK_DBG_REPORT_INFO VK_DBG_OFF
|
|
#define VK_DBG_REPORT_WARN VK_DBG_OFF
|
|
#define VK_DBG_REPORT_PERF VK_DBG_OFF
|
|
#define VK_DBG_REPORT_DEBUG VK_DBG_ON
|
|
#define VK_DBG_REPORT_ERROR VK_DBG_ON
|
|
|
|
// Simple Debug Report Callback.
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL DebugReportCallback(
|
|
VkDebugReportFlagsEXT flags, VkDebugReportObjectTypeEXT objectType,
|
|
uint64_t object, size_t location, int32_t messageCode,
|
|
const char* pLayerPrefix, const char* pMessage, void* pUserData)
|
|
{
|
|
if (flags & VK_DEBUG_REPORT_INFORMATION_BIT_EXT) {
|
|
#if (VK_DBG_REPORT_INFO == VK_DBG_ON)
|
|
PRINT_LINE("[%s] : %s (%d)", "INFO", pMessage, messageCode);
|
|
#endif
|
|
}
|
|
else if (flags & VK_DEBUG_REPORT_WARNING_BIT_EXT) {
|
|
#if (VK_DBG_REPORT_WARN == VK_DBG_ON)
|
|
PRINT_LINE("[%s] : %s (%d)", "WARN", pMessage, messageCode);
|
|
#endif
|
|
}
|
|
else if (flags & VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) {
|
|
#if (VK_DBG_REPORT_PERF == VK_DBG_ON)
|
|
PRINT_LINE("[%s] : %s (%d)", "PERF", pMessage, messageCode);
|
|
#endif
|
|
}
|
|
else if (flags & VK_DEBUG_REPORT_ERROR_BIT_EXT) {
|
|
#if (VK_DBG_REPORT_ERROR == VK_DBG_ON)
|
|
PRINT_LINE("[%s] : %s (%d)", "ERROR", pMessage, messageCode);
|
|
#endif
|
|
return VK_TRUE;
|
|
}
|
|
else if (flags & VK_DEBUG_REPORT_DEBUG_BIT_EXT) {
|
|
#if (VK_DBG_REPORT_DEBUG == VK_DBG_ON)
|
|
PRINT_LINE("[%s] : %s (%d)", "DEBUG", pMessage, messageCode);
|
|
#endif
|
|
}
|
|
return VK_FALSE;
|
|
}
|
|
|
|
typedef struct Vertex {
|
|
float x, y, z, w;
|
|
float r, g, b, a;
|
|
} Vertex;
|
|
|
|
VulkanTestHelper::VulkanTestHelper()
|
|
: m_data({0})
|
|
{
|
|
}
|
|
|
|
void VulkanTestHelper::init()
|
|
{
|
|
initializeVulkan();
|
|
setupEnvironment();
|
|
createSwapchain();
|
|
createBuffers();
|
|
createGraphicsPipeline();
|
|
recordCommands();
|
|
}
|
|
|
|
void VulkanTestHelper::uninit()
|
|
{
|
|
// First grab extension function pointers.
|
|
VK_GET_INSTANCE_PROC_ADDR(fpDestroyDebugReportCallbackEXT, m_data.instance,
|
|
vkDestroyDebugReportCallbackEXT);
|
|
VK_GET_INSTANCE_PROC_ADDR(fpDestroySurfaceKHR, m_data.instance,
|
|
vkDestroySurfaceKHR);
|
|
VK_GET_DEVICE_PROC_ADDR(fpDestroySwapchainKHR, m_data.device,
|
|
vkDestroySwapchainKHR);
|
|
|
|
// Tearing down Vulkan is somewhat minor in this example. Mostly it's just
|
|
// destroying the objects in the reverse order that they were created. Note:
|
|
// When destroying these objects, wait for the device to become idle first,
|
|
// otherwise it'll likely crash the program if resources are in flight when
|
|
// destroyed.
|
|
vkDeviceWaitIdle(m_data.device);
|
|
|
|
vkDestroyCommandPool(m_data.device, m_data.command_pool, NULL);
|
|
|
|
vkDestroyPipeline(m_data.device, m_data.graphics_pipeline, NULL);
|
|
vkDestroyPipelineLayout(m_data.device, m_data.pipeline_layout, NULL);
|
|
|
|
vkDestroyBuffer(m_data.device, m_data.vertex_buffer.buffer, NULL);
|
|
vkFreeMemory(m_data.device, m_data.buffer_memory, NULL);
|
|
|
|
for (uint32_t i = 0; i < SWAP_CHAIN_IMAGE_COUNT; ++i) {
|
|
vkDestroyFramebuffer(m_data.device, m_data.swapchain_framebuffers[i],
|
|
NULL);
|
|
vkDestroyImageView(m_data.device, m_data.swapchain_image_views[i],
|
|
NULL);
|
|
}
|
|
|
|
vkDestroyRenderPass(m_data.device, m_data.render_pass, NULL);
|
|
|
|
fpDestroySwapchainKHR(m_data.device, m_data.swapchain, NULL);
|
|
|
|
vkDestroySemaphore(m_data.device, m_data.image_available_semaphore,
|
|
NULL);
|
|
vkDestroySemaphore(m_data.device, m_data.render_finished_semaphore,
|
|
NULL);
|
|
|
|
vkDestroyDevice(m_data.device, NULL);
|
|
|
|
fpDestroySurfaceKHR(m_data.instance, m_data.surface, NULL);
|
|
if (m_data.debug_callback)
|
|
fpDestroyDebugReportCallbackEXT(m_data.instance, m_data.debug_callback,
|
|
NULL);
|
|
vkDestroyInstance(m_data.instance, NULL);
|
|
}
|
|
|
|
void VulkanTestHelper::updateFrameToken(const GgpFrameToken& frameToken)
|
|
{
|
|
m_data.m_frame_token = frameToken;
|
|
}
|
|
|
|
void VulkanTestHelper::drawFrame()
|
|
{
|
|
// Cache the extension functions from the device to use the Surface and
|
|
// present
|
|
// to the screen.
|
|
VK_GET_DEVICE_PROC_ADDR(fpAcquireNextImageKHR, m_data.device,
|
|
vkAcquireNextImageKHR);
|
|
VK_GET_DEVICE_PROC_ADDR(fpQueuePresentKHR, m_data.device,
|
|
vkQueuePresentKHR);
|
|
|
|
// First thing to do is aqcuire the image from the swapchain. This index
|
|
// represents the index of usable image. Also, for the purposes of this
|
|
// example,
|
|
// it represents which command buffer should be queued. Pass a semaphore in,
|
|
// to
|
|
// be signaled when the image has been acquired from the swap chain.
|
|
uint32_t image_index;
|
|
CHECK_VK(fpAcquireNextImageKHR(
|
|
m_data.device, m_data.swapchain, UINT64_MAX,
|
|
m_data.image_available_semaphore, NULL, &image_index));
|
|
|
|
VkSemaphore wait_semaphores[1] = { m_data.image_available_semaphore };
|
|
VkPipelineStageFlags wait_stages[1] = {
|
|
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT };
|
|
VkSemaphore signal_semaphors[1] = { m_data.render_finished_semaphore };
|
|
|
|
// Build the Queue submit struct with the command buffer to queue, the
|
|
// semaphores to wait on before processing and the semaphores to signal when
|
|
// the processing is over.
|
|
VkSubmitInfo submit_info = { VK_STRUCTURE_TYPE_SUBMIT_INFO };
|
|
submit_info.pCommandBuffers = &m_data.command_buffers[image_index];
|
|
submit_info.commandBufferCount = 1;
|
|
submit_info.pWaitSemaphores = wait_semaphores;
|
|
submit_info.waitSemaphoreCount = 1;
|
|
submit_info.pSignalSemaphores = signal_semaphors;
|
|
submit_info.signalSemaphoreCount = 1;
|
|
submit_info.pWaitDstStageMask = wait_stages;
|
|
|
|
CHECK_VK(
|
|
vkQueueSubmit(m_data.graphics_queue, 1, &submit_info, VK_NULL_HANDLE));
|
|
|
|
// Once the queue has processed the command buffer, present it to the screen.
|
|
// In this case, the semaphore we wait on is the semaphore we asked to signal
|
|
// above.
|
|
VkPresentInfoKHR present_info = { VK_STRUCTURE_TYPE_PRESENT_INFO_KHR };
|
|
present_info.pSwapchains = &m_data.swapchain;
|
|
present_info.swapchainCount = 1;
|
|
present_info.pImageIndices = &image_index;
|
|
present_info.pResults = NULL;
|
|
present_info.pWaitSemaphores = signal_semaphors;
|
|
present_info.waitSemaphoreCount = 1;
|
|
|
|
// Attach a metadata struct to the Present call with the frame token
|
|
// issued in the main loop.
|
|
VkPresentFrameTokenGGP frame_token_metadata = {
|
|
VK_STRUCTURE_TYPE_PRESENT_FRAME_TOKEN_GGP };
|
|
frame_token_metadata.frameToken = m_data.m_frame_token;
|
|
present_info.pNext = &frame_token_metadata;
|
|
|
|
|
|
CHECK_VK(vkQueuePresentKHR(m_data.present_queue, &present_info));
|
|
|
|
vkDeviceWaitIdle(m_data.device);
|
|
}
|
|
|
|
void VulkanTestHelper::initializeVulkan()
|
|
{
|
|
//IsRenderDocLoaded();
|
|
|
|
static const uint32_t kLayerCount = 1;
|
|
static const char* kLayerNames[kLayerCount] = {
|
|
"VK_LAYER_LUNARG_standard_validation" };
|
|
|
|
static const uint32_t kExtensionCount = 3;
|
|
static const char* kExtensionNames[kExtensionCount] = {
|
|
VK_EXT_DEBUG_REPORT_EXTENSION_NAME, VK_KHR_SURFACE_EXTENSION_NAME,
|
|
VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME };
|
|
|
|
VkApplicationInfo app_info = { VK_STRUCTURE_TYPE_APPLICATION_INFO };
|
|
app_info.apiVersion = VK_API_VERSION_1_0;
|
|
app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
app_info.pApplicationName = "ubiservices_test";
|
|
app_info.engineVersion = VK_MAKE_VERSION(1, 0, 0);
|
|
app_info.pEngineName = "GGP SDK";
|
|
|
|
VkInstanceCreateInfo create_info = { VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO };
|
|
create_info.pApplicationInfo = &app_info;
|
|
create_info.enabledLayerCount = kLayerCount;
|
|
create_info.ppEnabledLayerNames = kLayerNames;
|
|
create_info.enabledExtensionCount = kExtensionCount;
|
|
create_info.ppEnabledExtensionNames = kExtensionNames;
|
|
|
|
CHECK_VK(vkCreateInstance(&create_info, NULL, &m_data.instance));
|
|
|
|
// For extension functions, those with suffixes such as KHR, GOOGLE, EXT,
|
|
// etc, should be retrieved from the loader. vkCreateDebugReportCallbackEXT
|
|
// will be retrieved from the Instance. Below there's a corresponding Destroy
|
|
// function retrieved from the instance at the time of cleanup.
|
|
VK_GET_INSTANCE_PROC_ADDR(fpCreateDebugReportCallbackEXT, m_data.instance,
|
|
vkCreateDebugReportCallbackEXT);
|
|
|
|
// The DebugReportCallbackEXT, will give us all the information available to
|
|
// the various enabled layers and extensions to help track/debug the
|
|
// application.
|
|
VkDebugReportCallbackCreateInfoEXT debug_create_info = {
|
|
VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT };
|
|
// Attach the callback, which can be found above.
|
|
debug_create_info.pfnCallback = DebugReportCallback;
|
|
debug_create_info.pUserData = this;
|
|
// Select the level of debug information called back.
|
|
debug_create_info.flags =
|
|
VK_DEBUG_REPORT_INFORMATION_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
|
|
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT |
|
|
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_DEBUG_BIT_EXT;
|
|
CHECK_VK(fpCreateDebugReportCallbackEXT(
|
|
m_data.instance, &debug_create_info, NULL, &m_data.debug_callback));
|
|
|
|
// Once the instance is created, the surface can be created. The surface is
|
|
// the platform specific system window. In GGP, it's the object that takes
|
|
// your rendered data and compresses and transmits it to the client.
|
|
VkStreamDescriptorSurfaceCreateInfoGGP surface_create_info = {
|
|
VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP };
|
|
surface_create_info.streamDescriptor = kGgpPrimaryStreamDescriptor;
|
|
VK_GET_INSTANCE_PROC_ADDR(fpCreateStreamDescriptorSurfaceGGP,
|
|
m_data.instance,
|
|
vkCreateStreamDescriptorSurfaceGGP);
|
|
CHECK_VK(fpCreateStreamDescriptorSurfaceGGP(
|
|
m_data.instance, &surface_create_info, NULL, &m_data.surface));
|
|
}
|
|
|
|
void VulkanTestHelper::setupEnvironment()
|
|
{
|
|
VK_GET_INSTANCE_PROC_ADDR(fpGetPhysicalDeviceSurfaceSupportKHR,
|
|
m_data.instance,
|
|
vkGetPhysicalDeviceSurfaceSupportKHR);
|
|
VK_GET_INSTANCE_PROC_ADDR(fpGetPhysicalDeviceSurfaceCapabilitiesKHR,
|
|
m_data.instance,
|
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR);
|
|
VK_GET_INSTANCE_PROC_ADDR(fpGetPhysicalDeviceSurfaceFormatsKHR,
|
|
m_data.instance,
|
|
vkGetPhysicalDeviceSurfaceFormatsKHR);
|
|
VK_GET_INSTANCE_PROC_ADDR(fpGetPhysicalDeviceSurfacePresentModesKHR,
|
|
m_data.instance,
|
|
vkGetPhysicalDeviceSurfacePresentModesKHR);
|
|
|
|
// Enumerate physical devices associated with this instance.
|
|
VkPhysicalDevice* devices;
|
|
uint32_t device_count;
|
|
CHECK_VK(vkEnumeratePhysicalDevices(m_data.instance, &device_count, NULL));
|
|
devices = ALLOC_ARRAY(device_count, VkPhysicalDevice);
|
|
CHECK_VK(
|
|
vkEnumeratePhysicalDevices(m_data.instance, &device_count, devices));
|
|
// On the GGP platform, the first device is the GPU. Select that to setup
|
|
// the environment.
|
|
VkPhysicalDevice ggp_device = devices[0];
|
|
free(devices);
|
|
|
|
// Cache the features to enable. For the purposes of this example, enable all
|
|
// features.
|
|
VkPhysicalDeviceFeatures enable_features;
|
|
vkGetPhysicalDeviceFeatures(ggp_device, &enable_features);
|
|
|
|
// Cache the graphics and present queue indices.
|
|
m_data.graphics_queue_idx = -1;
|
|
m_data.present_queue_idx = -1;
|
|
VkQueueFamilyProperties* queue_props;
|
|
uint32_t queue_prop_count;
|
|
vkGetPhysicalDeviceQueueFamilyProperties(ggp_device, &queue_prop_count,
|
|
NULL);
|
|
queue_props = ALLOC_ARRAY(queue_prop_count, VkQueueFamilyProperties);
|
|
vkGetPhysicalDeviceQueueFamilyProperties(ggp_device, &queue_prop_count,
|
|
queue_props);
|
|
for (uint32_t i = 0; i < queue_prop_count; ++i) {
|
|
VkQueueFamilyProperties* prop = &queue_props[i];
|
|
if (prop->queueCount > 0) {
|
|
// If this queue supports graphics, cache the index of the queue.
|
|
if (prop->queueFlags & VK_QUEUE_GRAPHICS_BIT) {
|
|
m_data.graphics_queue_idx = i;
|
|
}
|
|
// Use the function pointer retrieved from the instance above to query
|
|
// 'Present' support.
|
|
VkBool32 supports_present = VK_FALSE;
|
|
CHECK_VK(fpGetPhysicalDeviceSurfaceSupportKHR(
|
|
ggp_device, i, m_data.surface, &supports_present));
|
|
if (supports_present == VK_TRUE) {
|
|
m_data.present_queue_idx = i;
|
|
}
|
|
if (m_data.graphics_queue_idx >= 0 &&
|
|
m_data.present_queue_idx >= 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
free(queue_props);
|
|
if (m_data.graphics_queue_idx < 0 || m_data.present_queue_idx < 0) {
|
|
Abort("Couldn't find Graphics Queue: [%d] or Present Queue: [%d}",
|
|
m_data.graphics_queue_idx, m_data.present_queue_idx);
|
|
}
|
|
|
|
// Now gather the remaining information of the GGP surface to build the
|
|
// Swapchain, ImageViews and Framebuffers below.
|
|
|
|
CHECK_VK(fpGetPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
ggp_device, m_data.surface, &m_data.surface_capabilities));
|
|
|
|
VkSurfaceFormatKHR* surf_formats;
|
|
uint32_t surf_format_count;
|
|
CHECK_VK(fpGetPhysicalDeviceSurfaceFormatsKHR(ggp_device, m_data.surface,
|
|
&surf_format_count, NULL));
|
|
surf_formats = ALLOC_ARRAY(surf_format_count, VkSurfaceFormatKHR);
|
|
CHECK_VK(fpGetPhysicalDeviceSurfaceFormatsKHR(
|
|
ggp_device, m_data.surface, &surf_format_count, surf_formats));
|
|
|
|
VkPresentModeKHR* surf_present_modes;
|
|
uint32_t surf_mode_count;
|
|
CHECK_VK(fpGetPhysicalDeviceSurfacePresentModesKHR(
|
|
ggp_device, m_data.surface, &surf_mode_count, NULL));
|
|
surf_present_modes = ALLOC_ARRAY(surf_mode_count, VkPresentModeKHR);
|
|
CHECK_VK(fpGetPhysicalDeviceSurfacePresentModesKHR(
|
|
ggp_device, m_data.surface, &surf_mode_count, surf_present_modes));
|
|
|
|
// Build the SwapchainCreateInfo struct with the queried attributes.
|
|
// This allows the application to recreate the swap chain multiple times
|
|
// if there are any changes to the surface, i.e. the window is resized, etc,
|
|
// without having to query the devices again.
|
|
m_data.swapchain_create_info.sType =
|
|
VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
|
m_data.swapchain_create_info.pNext = NULL;
|
|
m_data.swapchain_create_info.flags = 0;
|
|
m_data.swapchain_create_info.imageExtent =
|
|
m_data.surface_capabilities.currentExtent;
|
|
m_data.swapchain_create_info.preTransform =
|
|
m_data.surface_capabilities.currentTransform;
|
|
m_data.swapchain_create_info.imageFormat = surf_formats[0].format;
|
|
m_data.swapchain_create_info.imageColorSpace = surf_formats[0].colorSpace;
|
|
m_data.swapchain_create_info.presentMode = surf_present_modes[0];
|
|
m_data.swapchain_create_info.imageArrayLayers = 1;
|
|
m_data.swapchain_create_info.minImageCount = SWAP_CHAIN_IMAGE_COUNT;
|
|
m_data.swapchain_create_info.compositeAlpha =
|
|
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
|
m_data.swapchain_create_info.clipped = VK_TRUE;
|
|
m_data.swapchain_create_info.oldSwapchain = VK_NULL_HANDLE;
|
|
m_data.swapchain_create_info.imageUsage =
|
|
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
|
m_data.swapchain_create_info.surface = m_data.surface;
|
|
|
|
// If the graphics queue and the present queue don't have the same index,
|
|
// then the swapchain would need to be shared between both queues. In that
|
|
// case, imageSharingMode would be CONCURRENT. Otherwise, it's exclusive
|
|
// and the queueFamilyIndices is ignored.
|
|
if (m_data.graphics_queue_idx != m_data.present_queue_idx) {
|
|
uint32_t queue_family_indices[2] = { (uint32_t)m_data.graphics_queue_idx,
|
|
(uint32_t)m_data.present_queue_idx };
|
|
|
|
m_data.swapchain_create_info.imageSharingMode =
|
|
VK_SHARING_MODE_CONCURRENT;
|
|
m_data.swapchain_create_info.pQueueFamilyIndices = queue_family_indices;
|
|
m_data.swapchain_create_info.queueFamilyIndexCount = 2;
|
|
}
|
|
else {
|
|
m_data.swapchain_create_info.imageSharingMode =
|
|
VK_SHARING_MODE_EXCLUSIVE;
|
|
m_data.swapchain_create_info.pQueueFamilyIndices = NULL;
|
|
m_data.swapchain_create_info.queueFamilyIndexCount = 0;
|
|
}
|
|
free(surf_formats);
|
|
free(surf_present_modes);
|
|
|
|
// Query the devices' memory properties. This example caches the heap that
|
|
// has
|
|
// the attributes of device local, host visible and coherent memory.
|
|
VkPhysicalDeviceMemoryProperties mem_props;
|
|
vkGetPhysicalDeviceMemoryProperties(ggp_device, &mem_props);
|
|
|
|
const VkMemoryPropertyFlags heap_flag = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT |
|
|
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |
|
|
VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
|
|
|
for (uint32_t i = 0, end = mem_props.memoryTypeCount; i < end; ++i) {
|
|
VkMemoryPropertyFlags props = mem_props.memoryTypes[i].propertyFlags;
|
|
if ((props & heap_flag) == heap_flag) {
|
|
m_data.memory_type_index = mem_props.memoryTypes[i].heapIndex;
|
|
}
|
|
}
|
|
|
|
// Rather than enumerating all physical device extensions, we only request
|
|
// the minimum we need. If they're unavailable, initialization will fail
|
|
// anyway.
|
|
const uint32_t total_ext_names = 3;
|
|
const char** ext_names = ALLOC_ARRAY(total_ext_names, const char*);
|
|
ext_names[0] = VK_KHR_SWAPCHAIN_EXTENSION_NAME;
|
|
ext_names[1] = VK_KHR_MAINTENANCE1_EXTENSION_NAME;
|
|
ext_names[2] = VK_GGP_FRAME_TOKEN_EXTENSION_NAME;
|
|
|
|
// Build the VkQueueCreateInfo, which sets up the queues used on the device.
|
|
// Only submit CreateInfo for unique queue indices. If graphics and present
|
|
// are the same index only create the graphics.
|
|
uint32_t queues_to_create_count =
|
|
m_data.graphics_queue_idx != m_data.present_queue_idx ? 2 : 1;
|
|
VkDeviceQueueCreateInfo* queue_create_infos =
|
|
ALLOC_ARRAY(queues_to_create_count, VkDeviceQueueCreateInfo);
|
|
uint32_t queue_indices[2] = { (uint32_t)m_data.graphics_queue_idx,
|
|
(uint32_t)m_data.present_queue_idx };
|
|
float priorities = 1.0f;
|
|
for (uint32_t i = 0; i < queues_to_create_count; ++i) {
|
|
queue_create_infos[i].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
|
queue_create_infos[i].pNext = NULL;
|
|
queue_create_infos[i].flags = 0;
|
|
queue_create_infos[i].queueFamilyIndex = queue_indices[i];
|
|
queue_create_infos[i].queueCount = 1;
|
|
queue_create_infos[i].pQueuePriorities = &priorities;
|
|
}
|
|
|
|
VkDeviceCreateInfo device_create_info = {
|
|
VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO };
|
|
device_create_info.queueCreateInfoCount = queues_to_create_count;
|
|
device_create_info.pQueueCreateInfos = queue_create_infos;
|
|
device_create_info.pEnabledFeatures = &enable_features;
|
|
// Note: Layers for VkDevice deprecated.
|
|
device_create_info.ppEnabledLayerNames = NULL;
|
|
device_create_info.enabledLayerCount = 0;
|
|
device_create_info.enabledExtensionCount = total_ext_names;
|
|
device_create_info.ppEnabledExtensionNames = ext_names;
|
|
|
|
// After blindly loading extensions here without checking for support first,
|
|
// handle the
|
|
// potential error result with a useful log message.
|
|
VkResult res =
|
|
vkCreateDevice(ggp_device, &device_create_info, NULL, &m_data.device);
|
|
if (res == VK_ERROR_EXTENSION_NOT_PRESENT) {
|
|
PRINT_LINE("vkCreateDevice failed: required extension(s) not supported by "
|
|
"physical device.");
|
|
PRINT_LINE("Required extensions:");
|
|
for (uint32_t i = 0; i < device_create_info.enabledExtensionCount; ++i) {
|
|
PRINT_LINE("- %s", device_create_info.ppEnabledExtensionNames[i]);
|
|
}
|
|
abort();
|
|
}
|
|
else if (res != VK_SUCCESS) {
|
|
Abort("vkCreateDevice failed: %d", res);
|
|
}
|
|
free(queue_create_infos);
|
|
free(ext_names);
|
|
|
|
// Once the device is created, cache the requested queues by index.
|
|
vkGetDeviceQueue(m_data.device, m_data.graphics_queue_idx, 0,
|
|
&m_data.graphics_queue);
|
|
vkGetDeviceQueue(m_data.device, m_data.present_queue_idx, 0,
|
|
&m_data.present_queue);
|
|
|
|
// Create a command pool that is used to allocate device commands, which are
|
|
// submitted to these queues.
|
|
VkCommandPoolCreateInfo command_pool_create_info = {
|
|
VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO };
|
|
// Need the queue index that CommandBuffers from this pool will be submitted
|
|
// to. In this case, all CommandBuffers allocated from this Pool will be
|
|
// submitted to the Graphics Queue.
|
|
command_pool_create_info.queueFamilyIndex = m_data.graphics_queue_idx;
|
|
CHECK_VK(vkCreateCommandPool(m_data.device, &command_pool_create_info,
|
|
NULL, &m_data.command_pool));
|
|
|
|
// Finally, create the two Semaphores needed by each frame to synchronize
|
|
// swapchain access and present ready.
|
|
VkSemaphoreCreateInfo semaphore_create_info = {
|
|
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO };
|
|
CHECK_VK(vkCreateSemaphore(m_data.device, &semaphore_create_info, NULL,
|
|
&m_data.image_available_semaphore));
|
|
CHECK_VK(vkCreateSemaphore(m_data.device, &semaphore_create_info, NULL,
|
|
&m_data.render_finished_semaphore));
|
|
}
|
|
|
|
void VulkanTestHelper::createSwapchain()
|
|
{
|
|
VK_GET_DEVICE_PROC_ADDR(fpCreateSwapchainKHR, m_data.device,
|
|
vkCreateSwapchainKHR);
|
|
VK_GET_DEVICE_PROC_ADDR(fpGetSwapchainImagesKHR, m_data.device,
|
|
vkGetSwapchainImagesKHR);
|
|
|
|
// Use the cached swapchainCreateInfo struct to create the Swapchain.
|
|
CHECK_VK(vkCreateSwapchainKHR(m_data.device,
|
|
&m_data.swapchain_create_info, NULL,
|
|
&m_data.swapchain));
|
|
|
|
// Next, the Swapchain Images need to be cached.
|
|
uint32_t reported_images_count;
|
|
CHECK_VK(vkGetSwapchainImagesKHR(m_data.device, m_data.swapchain,
|
|
&reported_images_count, NULL));
|
|
if (SWAP_CHAIN_IMAGE_COUNT != reported_images_count) {
|
|
Abort("Number of reported Swapchain images not the same as expected.");
|
|
}
|
|
CHECK_VK(vkGetSwapchainImagesKHR(m_data.device, m_data.swapchain,
|
|
&reported_images_count,
|
|
m_data.swapchain_images));
|
|
|
|
// Now create the RenderPass. This object contains all the relevant
|
|
// color/depth
|
|
// /stencil/multisampling/attachment information for rendering as well as
|
|
// possible dependencies on other subpasses.
|
|
VkRenderPassCreateInfo render_pass_create_info = {
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO };
|
|
|
|
VkAttachmentDescription attach_description = {};
|
|
attach_description.format = m_data.swapchain_create_info.imageFormat;
|
|
attach_description.samples = VK_SAMPLE_COUNT_1_BIT;
|
|
attach_description.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
|
attach_description.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
|
|
attach_description.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
|
attach_description.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
|
attach_description.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
|
|
attach_description.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
|
|
|
render_pass_create_info.pAttachments = &attach_description;
|
|
render_pass_create_info.attachmentCount = 1;
|
|
|
|
// Then describe the layout and bind points of each attachment.
|
|
VkAttachmentReference color_attachment_reference = {};
|
|
color_attachment_reference.attachment = 0;
|
|
color_attachment_reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
|
|
|
VkSubpassDescription subpass_description = {};
|
|
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
|
subpass_description.pColorAttachments = &color_attachment_reference;
|
|
subpass_description.colorAttachmentCount = 1;
|
|
|
|
render_pass_create_info.subpassCount = 1;
|
|
render_pass_create_info.pSubpasses = &subpass_description;
|
|
|
|
CHECK_VK(vkCreateRenderPass(m_data.device, &render_pass_create_info, NULL,
|
|
&m_data.render_pass));
|
|
|
|
// To use the swapchain images as targets for rendering, ImageViews and
|
|
// Framebuffers for each SwapchainImage must be created.
|
|
for (uint32_t i = 0; i < SWAP_CHAIN_IMAGE_COUNT; ++i) {
|
|
// The ImageView needs an Image to use, as well as the format and
|
|
// components
|
|
// to expect.
|
|
VkImageViewCreateInfo image_view_create_info = {
|
|
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO };
|
|
image_view_create_info.image = m_data.swapchain_images[i];
|
|
image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
image_view_create_info.format = m_data.swapchain_create_info.imageFormat;
|
|
image_view_create_info.subresourceRange.aspectMask =
|
|
VK_IMAGE_ASPECT_COLOR_BIT;
|
|
image_view_create_info.subresourceRange.baseArrayLayer = 0;
|
|
image_view_create_info.subresourceRange.layerCount = 1;
|
|
image_view_create_info.subresourceRange.baseMipLevel = 0;
|
|
image_view_create_info.subresourceRange.levelCount = 1;
|
|
image_view_create_info.components.r = VK_COMPONENT_SWIZZLE_R;
|
|
image_view_create_info.components.g = VK_COMPONENT_SWIZZLE_G;
|
|
image_view_create_info.components.b = VK_COMPONENT_SWIZZLE_B;
|
|
image_view_create_info.components.a = VK_COMPONENT_SWIZZLE_A;
|
|
|
|
CHECK_VK(vkCreateImageView(m_data.device, &image_view_create_info, NULL,
|
|
&m_data.swapchain_image_views[i]));
|
|
|
|
// Now that the ImageView is created, set it as the attachment to the
|
|
// Framebuffer. Also, set the RenderPass, created above, which explains
|
|
// the attachements.
|
|
VkImageView attachments[1] = { m_data.swapchain_image_views[i] };
|
|
VkFramebufferCreateInfo framebuffer_create_info = {
|
|
VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO };
|
|
framebuffer_create_info.renderPass = m_data.render_pass;
|
|
framebuffer_create_info.pAttachments = attachments;
|
|
framebuffer_create_info.attachmentCount = 1;
|
|
framebuffer_create_info.width =
|
|
m_data.surface_capabilities.currentExtent.width;
|
|
framebuffer_create_info.height =
|
|
m_data.surface_capabilities.currentExtent.height;
|
|
framebuffer_create_info.layers = 1;
|
|
|
|
CHECK_VK(vkCreateFramebuffer(m_data.device, &framebuffer_create_info,
|
|
NULL, &m_data.swapchain_framebuffers[i]));
|
|
}
|
|
|
|
// Finally, allocate Command buffers for each SwapchainImage to allow one
|
|
// frame
|
|
// to be processed while another is being presented.
|
|
VkCommandBufferAllocateInfo command_buffer_alloc_info = {
|
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO };
|
|
command_buffer_alloc_info.commandPool = m_data.command_pool;
|
|
command_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
|
command_buffer_alloc_info.commandBufferCount = SWAP_CHAIN_IMAGE_COUNT;
|
|
|
|
CHECK_VK(vkAllocateCommandBuffers(
|
|
m_data.device, &command_buffer_alloc_info, m_data.command_buffers));
|
|
}
|
|
|
|
void VulkanTestHelper::createBuffers()
|
|
{
|
|
VkDeviceSize total_mem_size = 0;
|
|
|
|
// This sample only uses one buffer (a vertex buffer with the triangle
|
|
// list). To create the buffer, set the queues that'll consume them and the
|
|
// size in bytes and usage of the Buffer.
|
|
VkBufferCreateInfo buffer_create_info = {
|
|
VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO };
|
|
buffer_create_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
|
|
|
buffer_create_info.size = sizeof(Vertex) * 3;
|
|
buffer_create_info.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT;
|
|
|
|
CHECK_VK(vkCreateBuffer(m_data.device, &buffer_create_info, NULL,
|
|
&m_data.vertex_buffer.buffer));
|
|
|
|
// After the buffer is created, query the Memory Requirements of the buffer.
|
|
// This contains the alignment requirements and the device size in bytes.
|
|
VkMemoryRequirements mem_reqs;
|
|
vkGetBufferMemoryRequirements(m_data.device, m_data.vertex_buffer.buffer,
|
|
&mem_reqs);
|
|
|
|
m_data.vertex_buffer.offset = 0;
|
|
m_data.vertex_buffer.size = mem_reqs.size;
|
|
total_mem_size = m_data.vertex_buffer.size;
|
|
|
|
// Allocate the memory from the earlier chosen heap.
|
|
VkMemoryAllocateInfo mem_alloc_info = {
|
|
VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO };
|
|
mem_alloc_info.allocationSize = total_mem_size;
|
|
mem_alloc_info.memoryTypeIndex = m_data.memory_type_index;
|
|
|
|
CHECK_VK(vkAllocateMemory(m_data.device, &mem_alloc_info, NULL,
|
|
&m_data.buffer_memory));
|
|
CHECK_VK(vkBindBufferMemory(m_data.device, m_data.vertex_buffer.buffer,
|
|
m_data.buffer_memory,
|
|
m_data.vertex_buffer.offset));
|
|
// Simple triangle vertices
|
|
Vertex vertices[3] = { {0.0f, 0.75f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f},
|
|
{-0.5f, -0.75f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f},
|
|
{0.5f, -0.75f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f} };
|
|
|
|
// Once the memory for the buffers is bound, map the memory to ingest the
|
|
// vert
|
|
// data.
|
|
uint8_t* mapped_mem;
|
|
CHECK_VK(vkMapMemory(m_data.device, m_data.buffer_memory, 0,
|
|
VK_WHOLE_SIZE, 0, (void**)&mapped_mem));
|
|
|
|
memcpy(mapped_mem, &vertices[0].x, sizeof(Vertex) * 3);
|
|
|
|
vkUnmapMemory(m_data.device, m_data.buffer_memory);
|
|
}
|
|
|
|
void VulkanTestHelper::createGraphicsPipeline()
|
|
{
|
|
// The Graphics Pipeline is the largest struct yet. It contains all of the
|
|
// programmable and fixed-function settings for the hardware.
|
|
VkGraphicsPipelineCreateInfo pipeline_create_info = {
|
|
VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO };
|
|
|
|
// There are 2 shader stages, Vertex and Pixel, in this example. First, the
|
|
// ShaderModules are created. Then they're used to setup the
|
|
// PipelineShaderStage
|
|
// structure.
|
|
const uint32_t kShaderStages = 2;
|
|
VkPipelineShaderStageCreateInfo shader_stage_create_info[kShaderStages] = {};
|
|
|
|
// Create the Vertex ShaderModule.
|
|
const char* vs_relative_file_path = "shaders/hello_ggp_vs.spv";
|
|
VkShaderModule vs_shader_module;
|
|
createShaderModule(vs_relative_file_path, &vs_shader_module);
|
|
shader_stage_create_info[0].sType =
|
|
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shader_stage_create_info[0].pNext = NULL;
|
|
shader_stage_create_info[0].flags = 0;
|
|
shader_stage_create_info[0].pName = "VSMain";
|
|
shader_stage_create_info[0].module = vs_shader_module;
|
|
shader_stage_create_info[0].stage = VK_SHADER_STAGE_VERTEX_BIT;
|
|
shader_stage_create_info[0].pSpecializationInfo = NULL;
|
|
|
|
// Create the Pixel ShaderModule.
|
|
const char* ps_relative_file_path = "shaders/hello_ggp_ps.spv";
|
|
VkShaderModule ps_shader_module;
|
|
createShaderModule(ps_relative_file_path, &ps_shader_module);
|
|
shader_stage_create_info[1].sType =
|
|
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
|
shader_stage_create_info[1].pNext = NULL;
|
|
shader_stage_create_info[1].flags = 0;
|
|
shader_stage_create_info[1].pName = "PSMain";
|
|
shader_stage_create_info[1].module = ps_shader_module;
|
|
shader_stage_create_info[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
|
shader_stage_create_info[1].pSpecializationInfo = NULL;
|
|
|
|
pipeline_create_info.stageCount = kShaderStages;
|
|
pipeline_create_info.pStages = shader_stage_create_info;
|
|
|
|
// Next is the unfortunately named PipelineLayout, which is concerned with
|
|
// ShaderStage resources. Since there's no DescriptorSets or PushConstants in
|
|
// use, create it with nulls.
|
|
VkPipelineLayoutCreateInfo pipeline_layout_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO };
|
|
|
|
CHECK_VK(vkCreatePipelineLayout(m_data.device,
|
|
&pipeline_layout_create_info, NULL,
|
|
&m_data.pipeline_layout));
|
|
|
|
pipeline_create_info.layout = m_data.pipeline_layout;
|
|
|
|
// Lay out the Vertex attribs and binding information. These structures
|
|
// describe the vertices that the ShaderStage will consume.
|
|
VkVertexInputBindingDescription vert_binding_desc = {};
|
|
vert_binding_desc.binding = 0;
|
|
vert_binding_desc.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;
|
|
vert_binding_desc.stride = sizeof(Vertex);
|
|
|
|
VkVertexInputAttributeDescription vert_attribute_desc[2];
|
|
vert_attribute_desc[0].binding = 0;
|
|
vert_attribute_desc[0].format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
vert_attribute_desc[0].location = 0;
|
|
vert_attribute_desc[0].offset = offsetof(Vertex, x);
|
|
vert_attribute_desc[1].binding = 0;
|
|
vert_attribute_desc[1].format = VK_FORMAT_R32G32B32A32_SFLOAT;
|
|
vert_attribute_desc[1].location = 1;
|
|
vert_attribute_desc[1].offset = offsetof(Vertex, r);
|
|
|
|
VkPipelineVertexInputStateCreateInfo vert_input_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO };
|
|
vert_input_create_info.pVertexBindingDescriptions = &vert_binding_desc;
|
|
vert_input_create_info.vertexBindingDescriptionCount = 1;
|
|
vert_input_create_info.pVertexAttributeDescriptions = vert_attribute_desc;
|
|
vert_input_create_info.vertexAttributeDescriptionCount = 2;
|
|
|
|
pipeline_create_info.pVertexInputState = &vert_input_create_info;
|
|
|
|
// Next, describe the primitive to use in assembling the vertices.
|
|
VkPipelineInputAssemblyStateCreateInfo input_assembly_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO };
|
|
input_assembly_create_info.primitiveRestartEnable = VK_FALSE;
|
|
input_assembly_create_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
|
|
|
|
pipeline_create_info.pInputAssemblyState = &input_assembly_create_info;
|
|
|
|
VkPipelineViewportStateCreateInfo viewport_state_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO };
|
|
viewport_state_create_info.pViewports = NULL;
|
|
viewport_state_create_info.viewportCount = 1;
|
|
viewport_state_create_info.pScissors = NULL;
|
|
viewport_state_create_info.scissorCount = 1;
|
|
|
|
pipeline_create_info.pViewportState = &viewport_state_create_info;
|
|
|
|
// Describe the rasterization state for the pipeline.
|
|
VkPipelineRasterizationStateCreateInfo raster_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO };
|
|
raster_create_info.polygonMode = VK_POLYGON_MODE_FILL;
|
|
raster_create_info.lineWidth = 1.0f;
|
|
raster_create_info.cullMode = VK_CULL_MODE_NONE;
|
|
raster_create_info.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE;
|
|
|
|
pipeline_create_info.pRasterizationState = &raster_create_info;
|
|
|
|
// Dynamic states are those that can be updated during the command buffer
|
|
// pass,
|
|
// such as viewport and scissor.
|
|
const uint32_t kDynamicStatesCount = 2;
|
|
VkDynamicState states[kDynamicStatesCount] = { VK_DYNAMIC_STATE_VIEWPORT,
|
|
VK_DYNAMIC_STATE_SCISSOR };
|
|
|
|
VkPipelineDynamicStateCreateInfo dynamic_state_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
|
|
dynamic_state_create_info.pDynamicStates = states;
|
|
dynamic_state_create_info.dynamicStateCount = kDynamicStatesCount;
|
|
|
|
pipeline_create_info.pDynamicState = &dynamic_state_create_info;
|
|
|
|
// Describe the multi-sample state. For the purposes of this example,
|
|
// multi-sampling will be disabled.
|
|
VkPipelineMultisampleStateCreateInfo multi_sample_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO };
|
|
multi_sample_create_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
|
|
|
|
pipeline_create_info.pMultisampleState = &multi_sample_create_info;
|
|
|
|
// Describe the depth/stencil state. For the purposes of this example,
|
|
// depth/stencil will be disabled
|
|
VkPipelineDepthStencilStateCreateInfo depth_stencil_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO };
|
|
depth_stencil_create_info.maxDepthBounds = 1.0f;
|
|
depth_stencil_create_info.minDepthBounds = 0.0f;
|
|
depth_stencil_create_info.depthCompareOp = VK_COMPARE_OP_ALWAYS;
|
|
|
|
pipeline_create_info.pDepthStencilState = &depth_stencil_create_info;
|
|
|
|
// Describe the blending state for each attachment. For this example, there's
|
|
// only one attachment and blending is disabled but need to set the color
|
|
// write
|
|
// mask.
|
|
VkPipelineColorBlendAttachmentState color_blend_attach_state = {};
|
|
color_blend_attach_state.colorWriteMask =
|
|
VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
|
|
VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;
|
|
|
|
VkPipelineColorBlendStateCreateInfo color_blend_create_info = {
|
|
VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO };
|
|
color_blend_create_info.logicOpEnable = VK_FALSE;
|
|
color_blend_create_info.logicOp = VK_LOGIC_OP_NO_OP;
|
|
color_blend_create_info.attachmentCount = 1;
|
|
color_blend_create_info.pAttachments = &color_blend_attach_state;
|
|
color_blend_create_info.blendConstants[0] = 0.0f;
|
|
color_blend_create_info.blendConstants[1] = 0.0f;
|
|
color_blend_create_info.blendConstants[2] = 0.0f;
|
|
color_blend_create_info.blendConstants[3] = 0.0f;
|
|
|
|
pipeline_create_info.pColorBlendState = &color_blend_create_info;
|
|
|
|
// Set the RenderPass that was created earlier.
|
|
pipeline_create_info.renderPass = m_data.render_pass;
|
|
pipeline_create_info.subpass = 0;
|
|
// Tessellation is disabled because it isn't used.
|
|
pipeline_create_info.pTessellationState = NULL;
|
|
// From the spec, deriving from a parent pipeline will allow it to be more
|
|
// efficient to switch/bind between children of the same parent.
|
|
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
|
pipeline_create_info.basePipelineIndex = 0;
|
|
|
|
CHECK_VK(vkCreateGraphicsPipelines(m_data.device, NULL, 1,
|
|
&pipeline_create_info, NULL,
|
|
&m_data.graphics_pipeline));
|
|
|
|
// Once the pipeline is built, destroy the shader modules.
|
|
vkDestroyShaderModule(m_data.device, vs_shader_module, NULL);
|
|
vkDestroyShaderModule(m_data.device, ps_shader_module, NULL);
|
|
}
|
|
|
|
void VulkanTestHelper::recordCommands()
|
|
{
|
|
// This example builds a command buffer per frame to keep one processing
|
|
// while
|
|
// the other is presenting.
|
|
VkRect2D render_area = {};
|
|
render_area.offset.x = 0;
|
|
render_area.offset.y = 0;
|
|
render_area.extent.width = m_data.surface_capabilities.currentExtent.width;
|
|
render_area.extent.height =
|
|
m_data.surface_capabilities.currentExtent.height;
|
|
|
|
VkClearValue clear_value = {};
|
|
// clear_value.color.float32[0] = 1.0f;
|
|
// clear_value.color.float32[1] = 0.375f;
|
|
// clear_value.color.float32[2] = 0.308f;
|
|
clear_value.color.float32[0] = 0.0f;
|
|
clear_value.color.float32[1] = 0.75f;
|
|
clear_value.color.float32[2] = 1.0f;
|
|
clear_value.color.float32[3] = 1.0f;
|
|
|
|
// Note: the negative viewport here requires the VK_KHR_maintenance1
|
|
// extension, or similar functionality.
|
|
VkViewport viewport = {};
|
|
viewport.x = 0.0f;
|
|
viewport.y = (float)m_data.surface_capabilities.currentExtent.height;
|
|
viewport.width = (float)m_data.surface_capabilities.currentExtent.width;
|
|
viewport.height = -(float)m_data.surface_capabilities.currentExtent.height;
|
|
viewport.minDepth = 0.0f;
|
|
viewport.maxDepth = 1.0f;
|
|
|
|
VkRect2D scissor = {};
|
|
scissor.offset.x = 0;
|
|
scissor.offset.y = 0;
|
|
scissor.extent.width = m_data.surface_capabilities.currentExtent.width;
|
|
scissor.extent.height = m_data.surface_capabilities.currentExtent.height;
|
|
|
|
VkBuffer buffers[1] = { m_data.vertex_buffer.buffer };
|
|
VkDeviceSize offsets[1] = {};
|
|
|
|
// Iterate the number of images.
|
|
for (uint32_t i = 0; i < SWAP_CHAIN_IMAGE_COUNT; ++i) {
|
|
// Begin the Command Buffer recording.
|
|
VkCommandBufferBeginInfo command_begin_info = {
|
|
VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO };
|
|
command_begin_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT;
|
|
command_begin_info.pInheritanceInfo = NULL;
|
|
|
|
CHECK_VK(vkBeginCommandBuffer(m_data.command_buffers[i],
|
|
&command_begin_info));
|
|
// Begin the render pass using the framebuffer, renderPass, render area,
|
|
// and
|
|
// clear value.
|
|
VkRenderPassBeginInfo render_pass_begin_info = {
|
|
VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO };
|
|
render_pass_begin_info.renderPass = m_data.render_pass;
|
|
render_pass_begin_info.renderArea = render_area;
|
|
render_pass_begin_info.framebuffer = m_data.swapchain_framebuffers[i];
|
|
render_pass_begin_info.pClearValues = &clear_value;
|
|
render_pass_begin_info.clearValueCount = 1;
|
|
|
|
vkCmdBeginRenderPass(m_data.command_buffers[i], &render_pass_begin_info,
|
|
VK_SUBPASS_CONTENTS_INLINE);
|
|
// Bind the vertices with the expected layout given to the pipeline.
|
|
vkCmdBindVertexBuffers(m_data.command_buffers[i], 0, 1, buffers,
|
|
offsets);
|
|
// Bind the pipeline created above. Make sure it's bound to the correct
|
|
// bind
|
|
// point.
|
|
vkCmdBindPipeline(m_data.command_buffers[i],
|
|
VK_PIPELINE_BIND_POINT_GRAPHICS,
|
|
m_data.graphics_pipeline);
|
|
vkCmdSetViewport(m_data.command_buffers[i], 0, 1, &viewport);
|
|
vkCmdSetScissor(m_data.command_buffers[i], 0, 1, &scissor);
|
|
// Then draw. In this case we have 3 vertices and 1 instance of it.
|
|
vkCmdDraw(m_data.command_buffers[i], 3, 1, 0, 0);
|
|
// End the render pass.
|
|
vkCmdEndRenderPass(m_data.command_buffers[i]);
|
|
// And finally, if there are no other commands, end the command buffer
|
|
// recording.
|
|
CHECK_VK(vkEndCommandBuffer(m_data.command_buffers[i]));
|
|
}
|
|
}
|
|
|
|
void VulkanTestHelper::createShaderModule(const char* relative_file_path, VkShaderModule* module)
|
|
{
|
|
uint8_t* binary = NULL;
|
|
size_t file_size = 0;
|
|
|
|
//#if !defined(INCLUDE_SPV_IN_EXECUTABLE)
|
|
// char file_path[256];
|
|
// const char* asset_dir = GgpAssetDirectory();
|
|
//
|
|
// memset(file_path, 0, sizeof(file_path));
|
|
// strcat(file_path, asset_dir);
|
|
// strcat(file_path, relative_file_path);
|
|
//
|
|
// FILE* file = fopen(file_path, "r");
|
|
// if (!file) Abort("could not open file for reading: \"%s\"", file_path);
|
|
// fseek(file, 0L, SEEK_END);
|
|
// file_size = ftell(file);
|
|
// rewind(file);
|
|
// binary = ALLOC_ARRAY(file_size, uint8_t);
|
|
// fread((char*)binary, 1, file_size, file);
|
|
// fclose(file);
|
|
// const bool should_free_binary = true;
|
|
//#else
|
|
if (!strcmp("shaders/hello_ggp_vs.spv", relative_file_path)) {
|
|
binary = hello_ggp_vs_spv;
|
|
file_size = hello_ggp_vs_spv_size;
|
|
}
|
|
else {
|
|
binary = hello_ggp_ps_spv;
|
|
file_size = hello_ggp_ps_spv_size;
|
|
}
|
|
const bool should_free_binary = false;
|
|
//#endif
|
|
|
|
// Create the ShaderModule by casting the binary to a uint32_t ptr and
|
|
// setting
|
|
// the codesize to the size in bytes.
|
|
VkShaderModuleCreateInfo shader_module_create_info = {
|
|
VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO };
|
|
shader_module_create_info.pCode = (uint32_t*)binary;
|
|
shader_module_create_info.codeSize = file_size;
|
|
|
|
CHECK_VK(vkCreateShaderModule(m_data.device, &shader_module_create_info,
|
|
NULL, module));
|
|
if (should_free_binary) {
|
|
free(binary);
|
|
}
|
|
}
|
|
|
|
#endif // defined(US_TARGET_GGP)
|