First pass at VulkanViewer
BUG=skia:
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1848833005
Review URL: https://codereview.chromium.org/1848833005
diff --git a/gyp/most.gyp b/gyp/most.gyp
index 9b49c47..6c7fc02 100644
--- a/gyp/most.gyp
+++ b/gyp/most.gyp
@@ -29,12 +29,14 @@
'visualbench.gyp:visualbench',
'fuzz.gyp:fuzz',
'kilobench.gyp:kilobench',
+ 'vulkanviewer.gyp:vulkanviewer',
],
'conditions': [
[ 'skia_gpu == 0', {
'dependencies!': [
'visualbench.gyp:visualbench',
'kilobench.gyp:kilobench',
+ 'vulkanviewer.gyp:vulkanviewer',
]
}],
[ 'skia_os != "android" and skia_os != "linux"', {
@@ -73,6 +75,11 @@
'skiaserve.gyp:skiaserve',
],
}],
+ [ 'skia_vulkan == 0 or skia_os != "win"', {
+ 'dependencies!': [
+ 'vulkanviewer.gyp:vulkanviewer',
+ ],
+ }],
[ 'skia_skip_gui',
{
'dependencies!': [
diff --git a/gyp/vulkanviewer.gyp b/gyp/vulkanviewer.gyp
new file mode 100644
index 0000000..d58613d
--- /dev/null
+++ b/gyp/vulkanviewer.gyp
@@ -0,0 +1,46 @@
+# Copyright 2016 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+# GYP file to build performance testbench.
+#
+{
+ 'includes': [
+ 'apptype_console.gypi',
+ ],
+ 'targets': [
+ {
+ 'target_name': 'vulkanviewer',
+ 'type': 'executable',
+ 'includes' : [
+ 'gmslides.gypi',
+ ],
+ 'include_dirs': [
+ '../bench',
+ '../gm',
+ '../include/private',
+ '../src/core',
+ '../src/effects',
+ '../src/gpu',
+ '../src/images',
+ '../src/image',
+ ],
+ 'sources': [
+ '../gm/gm.cpp',
+ '<!@(python find.py ../tools/vulkan "*.cpp")',
+ ],
+ 'dependencies': [
+ 'flags.gyp:flags',
+ 'gputest.gyp:skgputest',
+ 'jsoncpp.gyp:jsoncpp',
+ 'skia_lib.gyp:skia_lib',
+ 'tools.gyp:crash_handler',
+ 'tools.gyp:proc_stats',
+ 'tools.gyp:resources',
+ 'tools.gyp:sk_tool_utils',
+ 'tools.gyp:timer',
+ 'tools.gyp:url_data_manager',
+ ],
+ },
+ ],
+}
diff --git a/src/gpu/vk/GrVkBackendContext.cpp b/src/gpu/vk/GrVkBackendContext.cpp
index 1ca56e8..0358618 100644
--- a/src/gpu/vk/GrVkBackendContext.cpp
+++ b/src/gpu/vk/GrVkBackendContext.cpp
@@ -24,7 +24,7 @@
"VK_LAYER_LUNARG_mem_tracker",
"VK_LAYER_LUNARG_draw_state",
"VK_LAYER_LUNARG_swapchain",
- "VK_LAYER_GOOGLE_unique_objects",
+ //"VK_LAYER_GOOGLE_unique_objects",
// not included in standard_validation
//"VK_LAYER_LUNARG_api_dump",
//"VK_LAYER_LUNARG_vktrace",
@@ -68,6 +68,8 @@
instanceExtensionNames.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
extensionFlags |= kEXT_debug_report_GrVkExtensionFlag;
}
+#endif
+
if (extensions.hasInstanceExtension(VK_KHR_SURFACE_EXTENSION_NAME)) {
instanceExtensionNames.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
extensionFlags |= kKHR_surface_GrVkExtensionFlag;
@@ -92,7 +94,6 @@
extensionFlags |= kKHR_xlib_surface_GrVkExtensionFlag;
}
#endif
-#endif
const VkInstanceCreateInfo instance_create = {
VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO, // sType
@@ -159,6 +160,10 @@
}
}
#endif
+ if (extensions.hasDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME)) {
+ deviceExtensionNames.push_back(VK_KHR_SWAPCHAIN_EXTENSION_NAME);
+ extensionFlags |= kKHR_swapchain_GrVkExtensionFlag;
+ }
if (extensions.hasDeviceExtension("VK_NV_glsl_shader")) {
deviceExtensionNames.push_back("VK_NV_glsl_shader");
extensionFlags |= kNV_glsl_shader_GrVkExtensionFlag;
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index e1e99ed..62cf580 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -95,10 +95,10 @@
callbackCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT;
callbackCreateInfo.pNext = nullptr;
callbackCreateInfo.flags = VK_DEBUG_REPORT_ERROR_BIT_EXT |
- VK_DEBUG_REPORT_WARNING_BIT_EXT |
+ VK_DEBUG_REPORT_WARNING_BIT_EXT;// |
//VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
//VK_DEBUG_REPORT_DEBUG_BIT_EXT |
- VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
+ //VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
callbackCreateInfo.pfnCallback = &DebugReportCallback;
callbackCreateInfo.pUserData = nullptr;
diff --git a/src/gpu/vk/GrVkUtil.cpp b/src/gpu/vk/GrVkUtil.cpp
index 5ab7966..a6a9d4c 100644
--- a/src/gpu/vk/GrVkUtil.cpp
+++ b/src/gpu/vk/GrVkUtil.cpp
@@ -23,6 +23,9 @@
case kSRGBA_8888_GrPixelConfig:
*format = VK_FORMAT_R8G8B8A8_SRGB;
break;
+ case kSBGRA_8888_GrPixelConfig:
+ *format = VK_FORMAT_B8G8R8A8_SRGB;
+ break;
case kRGB_565_GrPixelConfig:
*format = VK_FORMAT_R5G6B5_UNORM_PACK16;
break;
@@ -30,7 +33,7 @@
*format = VK_FORMAT_R4G4B4A4_UNORM_PACK16;
break;
case kIndex_8_GrPixelConfig:
- // No current rad support for this config
+ // No current vulkan support for this config
return false;
case kAlpha_8_GrPixelConfig:
*format = VK_FORMAT_R8_UNORM;
@@ -40,7 +43,7 @@
*format = VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK;
break;
case kLATC_GrPixelConfig:
- // No current rad support for this config
+ // No current vulkan support for this config
return false;
case kR11_EAC_GrPixelConfig:
*format = VK_FORMAT_EAC_R11_UNORM_BLOCK;
@@ -63,6 +66,58 @@
return true;
}
+bool GrVkFormatToPixelConfig(VkFormat format, GrPixelConfig* config) {
+ GrPixelConfig dontCare;
+ if (!config) {
+ config = &dontCare;
+ }
+
+ switch (format) {
+ case VK_FORMAT_R8G8B8A8_UNORM:
+ *config = kRGBA_8888_GrPixelConfig;
+ break;
+ case VK_FORMAT_B8G8R8A8_UNORM:
+ *config = kBGRA_8888_GrPixelConfig;
+ break;
+ case VK_FORMAT_R8G8B8A8_SRGB:
+ *config = kSRGBA_8888_GrPixelConfig;
+ break;
+ case VK_FORMAT_B8G8R8A8_SRGB:
+ *config = kSBGRA_8888_GrPixelConfig;
+ break;
+ case VK_FORMAT_R5G6B5_UNORM_PACK16:
+ *config = kRGB_565_GrPixelConfig;
+ break;
+ case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
+ *config = kRGBA_4444_GrPixelConfig;
+ break;
+ case VK_FORMAT_R8_UNORM:
+ *config = kAlpha_8_GrPixelConfig;
+ break;
+ case VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK:
+ *config = kETC1_GrPixelConfig;
+ break;
+ case VK_FORMAT_EAC_R11_UNORM_BLOCK:
+ *config = kR11_EAC_GrPixelConfig;
+ break;
+ case VK_FORMAT_ASTC_12x12_UNORM_BLOCK:
+ *config = kASTC_12x12_GrPixelConfig;
+ break;
+ case VK_FORMAT_R32G32B32A32_SFLOAT:
+ *config = kRGBA_float_GrPixelConfig;
+ break;
+ case VK_FORMAT_R16G16B16A16_SFLOAT:
+ *config = kRGBA_half_GrPixelConfig;
+ break;
+ case VK_FORMAT_R16_SFLOAT:
+ *config = kAlpha_half_GrPixelConfig;
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
bool GrSampleCountToVkSampleCount(uint32_t samples, VkSampleCountFlagBits* vkSamples) {
switch (samples) {
case 0: // fall through
diff --git a/src/gpu/vk/GrVkUtil.h b/src/gpu/vk/GrVkUtil.h
index 4e03c75..c2227ec 100644
--- a/src/gpu/vk/GrVkUtil.h
+++ b/src/gpu/vk/GrVkUtil.h
@@ -30,6 +30,11 @@
*/
bool GrPixelConfigToVkFormat(GrPixelConfig config, VkFormat* format);
+/**
+* Returns the GrPixelConfig for the given vulkan texture format
+*/
+bool GrVkFormatToPixelConfig(VkFormat format, GrPixelConfig* config);
+
bool GrSampleCountToVkSampleCount(uint32_t samples, VkSampleCountFlagBits* vkSamples);
#endif
diff --git a/tools/vulkan/Application.h b/tools/vulkan/Application.h
new file mode 100644
index 0000000..623496d
--- /dev/null
+++ b/tools/vulkan/Application.h
@@ -0,0 +1,20 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef Application_DEFINED
+#define Application_DEFINED
+
+class Application {
+public:
+ static Application* Create(int argc, char** argv, void* platformData);
+
+ virtual ~Application() {}
+
+ virtual void onIdle(float dt) = 0;
+};
+
+#endif
diff --git a/tools/vulkan/VulkanTestContext.cpp b/tools/vulkan/VulkanTestContext.cpp
new file mode 100644
index 0000000..4a4c219
--- /dev/null
+++ b/tools/vulkan/VulkanTestContext.cpp
@@ -0,0 +1,605 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrContext.h"
+#include "SkSurface.h"
+#include "VulkanTestContext.h"
+
+#include "vk/GrVkInterface.h"
+#include "vk/GrVkUtil.h"
+#include "vk/GrVkTypes.h"
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+// windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
+#undef CreateSemaphore
+#endif
+
+VulkanTestContext::VulkanTestContext(void* platformData, int msaaSampleCount)
+ : fSurface(VK_NULL_HANDLE)
+ , fSwapchain(VK_NULL_HANDLE)
+ , fCommandPool(VK_NULL_HANDLE)
+ , fBackbuffers(nullptr) {
+
+ // any config code here (particularly for msaa)?
+
+ this->initializeContext(platformData);
+}
+
+void VulkanTestContext::initializeContext(void* platformData) {
+
+ fBackendContext.reset(GrVkBackendContext::Create());
+ fBackendContext->ref();
+
+ fContext = GrContext::Create(kVulkan_GrBackend, (GrBackendContext)fBackendContext.get());
+
+ fSurface = createVkSurface(platformData);
+ if (VK_NULL_HANDLE == fSurface) {
+ fBackendContext.reset(nullptr);
+ return;
+ }
+
+ // query to get the initial queue props size
+ uint32_t queueCount;
+ GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceQueueFamilyProperties(fBackendContext->fPhysicalDevice, &queueCount,
+ nullptr));
+ SkASSERT(queueCount >= 1);
+
+ SkAutoMalloc queuePropsAlloc(queueCount * sizeof(VkQueueFamilyProperties));
+ // now get the actual queue props
+ VkQueueFamilyProperties* queueProps = (VkQueueFamilyProperties*)queuePropsAlloc.get();
+
+ GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceQueueFamilyProperties(fBackendContext->fPhysicalDevice, &queueCount,
+ queueProps));
+
+ // iterate to find the present queue
+ fPresentQueueIndex = -1;
+ for (uint32_t i = 0; i < queueCount; i++) {
+ if ((queueProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) && canPresent(i)) {
+ fPresentQueueIndex = i;
+ break;
+ }
+ }
+ SkASSERT(0 <= fPresentQueueIndex && fPresentQueueIndex < queueCount);
+
+ VkBool32 supported;
+ VkResult res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfaceSupportKHR(fBackendContext->fPhysicalDevice,
+ fPresentQueueIndex,
+ fSurface,
+ &supported));
+ if (VK_SUCCESS != res) {
+ this->destroyContext();
+ return;
+ }
+
+ // get this info from somewhere?
+ if (!this->createSwapchain(1024, 768)) {
+ this->destroyContext();
+ return;
+ }
+
+ // create presentQueue
+ vkGetDeviceQueue(fBackendContext->fDevice, fPresentQueueIndex, 0, &fPresentQueue);
+
+
+}
+
+bool VulkanTestContext::createSwapchain(uint32_t width, uint32_t height)
+{
+ // check for capabilities
+ VkSurfaceCapabilitiesKHR caps;
+ VkResult res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfaceCapabilitiesKHR(fBackendContext->fPhysicalDevice,
+ fSurface,
+ &caps));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t surfaceFormatCount;
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice,
+ fSurface,
+ &surfaceFormatCount,
+ nullptr));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR));
+ VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get();
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice,
+ fSurface,
+ &surfaceFormatCount,
+ surfaceFormats));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ uint32_t presentModeCount;
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice,
+ fSurface,
+ &presentModeCount,
+ nullptr));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR));
+ VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get();
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice,
+ fSurface,
+ &presentModeCount,
+ presentModes));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ VkExtent2D extent = caps.currentExtent;
+ // use the hints
+ if (extent.width == (uint32_t)-1) {
+ extent.width = width;
+ extent.height = height;
+ }
+
+ // clamp width; to protect us from broken hints?
+ if (extent.width < caps.minImageExtent.width) {
+ extent.width = caps.minImageExtent.width;
+ } else if (extent.width > caps.maxImageExtent.width) {
+ extent.width = caps.maxImageExtent.width;
+ }
+ // clamp height
+ if (extent.height < caps.minImageExtent.height) {
+ extent.height = caps.minImageExtent.height;
+ } else if (extent.height > caps.maxImageExtent.height) {
+ extent.height = caps.maxImageExtent.height;
+ }
+ fWidth = (int)extent.width;
+ fHeight = (int)extent.height;
+
+ uint32_t imageCount = caps.minImageCount + 2;
+ if (caps.maxImageCount > 0 && imageCount > caps.maxImageCount) {
+ // Application must settle for fewer images than desired:
+ imageCount = caps.maxImageCount;
+ }
+
+ VkImageUsageFlags usageFlags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
+ VK_IMAGE_USAGE_TRANSFER_SRC_BIT |
+ VK_IMAGE_USAGE_TRANSFER_DST_BIT;
+ SkASSERT((caps.supportedUsageFlags & usageFlags) == usageFlags);
+ SkASSERT(caps.supportedTransforms & caps.currentTransform);
+ SkASSERT(caps.supportedCompositeAlpha & (VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR |
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR));
+ VkCompositeAlphaFlagBitsKHR composite_alpha =
+ (caps.supportedCompositeAlpha & VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR) ?
+ VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR :
+ VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
+
+ // FIFO is the only mode universally supported
+ VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR;
+ bool vsync = false;
+ for (uint32_t i = 0; i < presentModeCount; ++i) {
+ if ((vsync && VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) ||
+ (!vsync && VK_PRESENT_MODE_IMMEDIATE_KHR == presentModes[i])) {
+ mode = presentModes[i];
+ }
+ }
+
+ VkSwapchainCreateInfoKHR swapchainCreateInfo;
+ memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR));
+ swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
+ swapchainCreateInfo.surface = fSurface;
+ swapchainCreateInfo.minImageCount = imageCount;
+ swapchainCreateInfo.imageFormat = surfaceFormats[0].format; // for now, use the first one
+ swapchainCreateInfo.imageColorSpace = surfaceFormats[0].colorSpace;
+ swapchainCreateInfo.imageExtent = extent;
+ swapchainCreateInfo.imageArrayLayers = 1;
+ swapchainCreateInfo.imageUsage = usageFlags;
+
+ uint32_t queueFamilies[] = { fBackendContext->fQueueFamilyIndex, fPresentQueueIndex };
+ if (fBackendContext->fQueueFamilyIndex != fPresentQueueIndex) {
+ swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
+ swapchainCreateInfo.queueFamilyIndexCount = 2;
+ swapchainCreateInfo.pQueueFamilyIndices = queueFamilies;
+ } else {
+ swapchainCreateInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
+ swapchainCreateInfo.queueFamilyIndexCount = 0;
+ swapchainCreateInfo.pQueueFamilyIndices = nullptr;
+ }
+
+ swapchainCreateInfo.preTransform = caps.currentTransform;;
+ swapchainCreateInfo.compositeAlpha = composite_alpha;
+ swapchainCreateInfo.presentMode = mode;
+ swapchainCreateInfo.clipped = true;
+ swapchainCreateInfo.oldSwapchain = fSwapchain;
+
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ CreateSwapchainKHR(fBackendContext->fDevice,
+ &swapchainCreateInfo, nullptr, &fSwapchain));
+ if (VK_SUCCESS != res) {
+ return false;
+ }
+
+ // destroy the old swapchain
+ if (swapchainCreateInfo.oldSwapchain != VK_NULL_HANDLE) {
+ GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice));
+
+ this->destroyBuffers();
+
+ GR_VK_CALL(fBackendContext->fInterface, DestroySwapchainKHR(fBackendContext->fDevice,
+ swapchainCreateInfo.oldSwapchain,
+ nullptr));
+ }
+
+ GrVkFormatToPixelConfig(swapchainCreateInfo.imageFormat, &fPixelConfig);
+
+ this->createBuffers();
+
+ return true;
+}
+
+void VulkanTestContext::createBuffers() {
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, GetSwapchainImagesKHR(fBackendContext->fDevice,
+ fSwapchain,
+ &fImageCount,
+ nullptr));
+ SkASSERT(fImageCount);
+ fImages = new VkImage[fImageCount];
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface, GetSwapchainImagesKHR(fBackendContext->fDevice,
+ fSwapchain,
+ &fImageCount,
+ fImages));
+
+ // set up initial image layouts and create surfaces
+ fImageLayouts = new VkImageLayout[fImageCount];
+ fSurfaces = new sk_sp<SkSurface>[fImageCount];
+ for (uint32_t i = 0; i < fImageCount; ++i) {
+ fImageLayouts[i] = VK_IMAGE_LAYOUT_UNDEFINED;
+
+ GrBackendRenderTargetDesc desc;
+ GrVkTextureInfo info;
+ info.fImage = fImages[i];
+ info.fAlloc = nullptr;
+ info.fImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ info.fImageTiling = VK_IMAGE_TILING_OPTIMAL;
+ desc.fWidth = fWidth;
+ desc.fHeight = fHeight;
+ desc.fConfig = fPixelConfig;
+ desc.fOrigin = kTopLeft_GrSurfaceOrigin;
+ desc.fSampleCnt = 0;
+ desc.fStencilBits = 0;
+ desc.fRenderTargetHandle = (GrBackendObject) &info;
+ SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
+ fSurfaces[i] = SkSurface::MakeFromBackendRenderTarget(fContext, desc, &props);
+ }
+
+ // create the command pool for the command buffers
+ if (VK_NULL_HANDLE == fCommandPool) {
+ VkCommandPoolCreateInfo commandPoolInfo;
+ memset(&commandPoolInfo, 0, sizeof(VkCommandPoolCreateInfo));
+ commandPoolInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
+ // this needs to be on the render queue
+ commandPoolInfo.queueFamilyIndex = fBackendContext->fQueueFamilyIndex;
+ commandPoolInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT;
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ CreateCommandPool(fBackendContext->fDevice, &commandPoolInfo,
+ nullptr, &fCommandPool));
+ }
+
+ // set up the backbuffers
+ VkSemaphoreCreateInfo semaphoreInfo;
+ memset(&semaphoreInfo, 0, sizeof(VkSemaphoreCreateInfo));
+ semaphoreInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
+ semaphoreInfo.pNext = nullptr;
+ semaphoreInfo.flags = 0;
+ VkCommandBufferAllocateInfo commandBuffersInfo;
+ memset(&commandBuffersInfo, 0, sizeof(VkCommandBufferAllocateInfo));
+ commandBuffersInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
+ commandBuffersInfo.pNext = nullptr;
+ commandBuffersInfo.commandPool = fCommandPool;
+ commandBuffersInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
+ commandBuffersInfo.commandBufferCount = 2;
+ VkFenceCreateInfo fenceInfo;
+ memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
+ fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+ fenceInfo.pNext = nullptr;
+ fenceInfo.flags = VK_FENCE_CREATE_SIGNALED_BIT;
+
+ // we create one additional backbuffer structure here, because we want to
+ // give the command buffers they contain a chance to finish before we cycle back
+ fBackbuffers = new BackbufferInfo[fImageCount + 1];
+ for (uint32_t i = 0; i < fImageCount + 1; ++i) {
+ fBackbuffers[i].fImageIndex = -1;
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo,
+ nullptr, &fBackbuffers[i].fAcquireSemaphore));
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ CreateSemaphore(fBackendContext->fDevice, &semaphoreInfo,
+ nullptr, &fBackbuffers[i].fRenderSemaphore));
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ AllocateCommandBuffers(fBackendContext->fDevice, &commandBuffersInfo,
+ fBackbuffers[i].fTransitionCmdBuffers));
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr,
+ &fBackbuffers[i].fUsageFences[0]));
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ CreateFence(fBackendContext->fDevice, &fenceInfo, nullptr,
+ &fBackbuffers[i].fUsageFences[1]));
+ }
+ fCurrentBackbufferIndex = fImageCount;
+}
+
+void VulkanTestContext::destroyBuffers() {
+
+ if (fBackbuffers) {
+ for (uint32_t i = 0; i < fImageCount + 1; ++i) {
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ WaitForFences(fBackendContext->fDevice, 2,
+ fBackbuffers[i].fUsageFences,
+ true, UINT64_MAX));
+ fBackbuffers[i].fImageIndex = -1;
+ GR_VK_CALL(fBackendContext->fInterface,
+ DestroySemaphore(fBackendContext->fDevice,
+ fBackbuffers[i].fAcquireSemaphore,
+ nullptr));
+ GR_VK_CALL(fBackendContext->fInterface,
+ DestroySemaphore(fBackendContext->fDevice,
+ fBackbuffers[i].fRenderSemaphore,
+ nullptr));
+ GR_VK_CALL(fBackendContext->fInterface,
+ FreeCommandBuffers(fBackendContext->fDevice, fCommandPool, 2,
+ fBackbuffers[i].fTransitionCmdBuffers));
+ GR_VK_CALL(fBackendContext->fInterface,
+ DestroyFence(fBackendContext->fDevice, fBackbuffers[i].fUsageFences[0], 0));
+ GR_VK_CALL(fBackendContext->fInterface,
+ DestroyFence(fBackendContext->fDevice, fBackbuffers[i].fUsageFences[1], 0));
+ }
+ }
+
+ delete[] fBackbuffers;
+ fBackbuffers = nullptr;
+
+ delete[] fSurfaces;
+ fSurfaces = nullptr;
+ delete[] fImageLayouts;
+ fImageLayouts = nullptr;
+ delete[] fImages;
+ fImages = nullptr;
+}
+
+VulkanTestContext::~VulkanTestContext() {
+ this->destroyContext();
+}
+
+void VulkanTestContext::destroyContext() {
+ if (!fBackendContext.get()) {
+ return;
+ }
+
+ GR_VK_CALL(fBackendContext->fInterface, DeviceWaitIdle(fBackendContext->fDevice));
+
+ this->destroyBuffers();
+
+ if (VK_NULL_HANDLE != fCommandPool) {
+ GR_VK_CALL(fBackendContext->fInterface, DestroyCommandPool(fBackendContext->fDevice,
+ fCommandPool, nullptr));
+ fCommandPool = VK_NULL_HANDLE;
+ }
+
+ if (VK_NULL_HANDLE != fSwapchain) {
+ GR_VK_CALL(fBackendContext->fInterface, DestroySwapchainKHR(fBackendContext->fDevice,
+ fSwapchain, nullptr));
+ fSwapchain = VK_NULL_HANDLE;
+ }
+
+ if (VK_NULL_HANDLE != fSurface) {
+ GR_VK_CALL(fBackendContext->fInterface, DestroySurfaceKHR(fBackendContext->fInstance,
+ fSurface, nullptr));
+ fSurface = VK_NULL_HANDLE;
+ }
+
+ delete fContext;
+
+ fBackendContext.reset(nullptr);
+}
+
+VulkanTestContext::BackbufferInfo* VulkanTestContext::getAvailableBackbuffer() {
+ SkASSERT(fBackbuffers);
+
+ ++fCurrentBackbufferIndex;
+ if (fCurrentBackbufferIndex > fImageCount) {
+ fCurrentBackbufferIndex = 0;
+ }
+
+ BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
+
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ WaitForFences(fBackendContext->fDevice, 2, backbuffer->fUsageFences,
+ true, UINT64_MAX));
+ return backbuffer;
+}
+
+SkSurface* VulkanTestContext::getBackbufferSurface() {
+ BackbufferInfo* backbuffer = this->getAvailableBackbuffer();
+ SkASSERT(backbuffer);
+
+ // reset the fence
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ ResetFences(fBackendContext->fDevice, 2, backbuffer->fUsageFences));
+ // semaphores should be in unsignaled state
+
+ // acquire the image
+ VkResult res = GR_VK_CALL(fBackendContext->fInterface,
+ AcquireNextImageKHR(fBackendContext->fDevice,
+ fSwapchain,
+ UINT64_MAX,
+ backbuffer->fAcquireSemaphore,
+ VK_NULL_HANDLE,
+ &backbuffer->fImageIndex));
+ if (VK_ERROR_SURFACE_LOST_KHR == res) {
+ // need to figure out how to create a new vkSurface without the platformData*
+ return nullptr;
+ }
+ if (VK_ERROR_OUT_OF_DATE_KHR == res || VK_ERROR_SURFACE_LOST_KHR == res) {
+ // tear swapchain down and try again
+ if (!this->createSwapchain(0, 0)) {
+ return nullptr;
+ }
+
+ // acquire the image
+ res = GR_VK_CALL(fBackendContext->fInterface,
+ AcquireNextImageKHR(fBackendContext->fDevice,
+ fSwapchain,
+ UINT64_MAX,
+ backbuffer->fAcquireSemaphore,
+ VK_NULL_HANDLE,
+ &backbuffer->fImageIndex));
+
+ if (VK_SUCCESS != res) {
+ return nullptr;
+ }
+ }
+
+ // set up layout transfer from initial to color attachment
+ VkImageLayout layout = fImageLayouts[backbuffer->fImageIndex];
+ VkPipelineStageFlags srcStageMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
+ VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT :
+ VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkAccessFlags srcAccessMask = (VK_IMAGE_LAYOUT_UNDEFINED == layout) ?
+ 0 : VK_ACCESS_MEMORY_READ_BIT;
+ VkAccessFlags dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+
+ VkImageMemoryBarrier imageMemoryBarrier = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
+ NULL, // pNext
+ srcAccessMask, // outputMask
+ dstAccessMask, // inputMask
+ layout, // oldLayout
+ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, // newLayout
+ fPresentQueueIndex, // srcQueueFamilyIndex
+ fBackendContext->fQueueFamilyIndex, // dstQueueFamilyIndex
+ fImages[backbuffer->fImageIndex], // image
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
+ };
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ ResetCommandBuffer(backbuffer->fTransitionCmdBuffers[0], 0));
+ VkCommandBufferBeginInfo info;
+ memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
+ info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ info.flags = 0;
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ BeginCommandBuffer(backbuffer->fTransitionCmdBuffers[0], &info));
+
+ GR_VK_CALL(fBackendContext->fInterface,
+ CmdPipelineBarrier(backbuffer->fTransitionCmdBuffers[0],
+ srcStageMask, dstStageMask, 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &imageMemoryBarrier));
+
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ EndCommandBuffer(backbuffer->fTransitionCmdBuffers[0]));
+
+ // insert the layout transfer into the queue and wait on the acquire
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 1;
+ submitInfo.pWaitSemaphores = &backbuffer->fAcquireSemaphore;
+ submitInfo.pWaitDstStageMask = 0;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &backbuffer->fTransitionCmdBuffers[0];
+ submitInfo.signalSemaphoreCount = 0;
+
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ QueueSubmit(fBackendContext->fQueue, 1, &submitInfo,
+ backbuffer->fUsageFences[0]));
+
+ return fSurfaces[backbuffer->fImageIndex].get();
+}
+
+
+void VulkanTestContext::swapBuffers() {
+
+ BackbufferInfo* backbuffer = fBackbuffers + fCurrentBackbufferIndex;
+
+ VkImageLayout layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
+ VkPipelineStageFlags srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+ VkPipelineStageFlags dstStageMask = VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
+ VkAccessFlags srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
+ VkAccessFlags dstAccessMask = VK_ACCESS_MEMORY_READ_BIT;
+
+ VkImageMemoryBarrier imageMemoryBarrier = {
+ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, // sType
+ NULL, // pNext
+ srcAccessMask, // outputMask
+ dstAccessMask, // inputMask
+ layout, // oldLayout
+ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, // newLayout
+ fBackendContext->fQueueFamilyIndex, // srcQueueFamilyIndex
+ fPresentQueueIndex, // dstQueueFamilyIndex
+ fImages[backbuffer->fImageIndex], // image
+ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } // subresourceRange
+ };
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ ResetCommandBuffer(backbuffer->fTransitionCmdBuffers[1], 0));
+ VkCommandBufferBeginInfo info;
+ memset(&info, 0, sizeof(VkCommandBufferBeginInfo));
+ info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+ info.flags = 0;
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ BeginCommandBuffer(backbuffer->fTransitionCmdBuffers[1], &info));
+ GR_VK_CALL(fBackendContext->fInterface,
+ CmdPipelineBarrier(backbuffer->fTransitionCmdBuffers[1],
+ srcStageMask, dstStageMask, 0,
+ 0, nullptr,
+ 0, nullptr,
+ 1, &imageMemoryBarrier));
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ EndCommandBuffer(backbuffer->fTransitionCmdBuffers[1]));
+
+ fImageLayouts[backbuffer->fImageIndex] = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
+
+ // insert the layout transfer into the queue and wait on the acquire
+ VkSubmitInfo submitInfo;
+ memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+ submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+ submitInfo.waitSemaphoreCount = 0;
+ submitInfo.pWaitDstStageMask = 0;
+ submitInfo.commandBufferCount = 1;
+ submitInfo.pCommandBuffers = &backbuffer->fTransitionCmdBuffers[1];
+ submitInfo.signalSemaphoreCount = 1;
+ submitInfo.pSignalSemaphores = &backbuffer->fRenderSemaphore;
+
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ QueueSubmit(fBackendContext->fQueue, 1, &submitInfo,
+ backbuffer->fUsageFences[1]));
+
+ // Submit present operation to present queue
+ const VkPresentInfoKHR presentInfo =
+ {
+ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, // sType
+ NULL, // pNext
+ 1, // waitSemaphoreCount
+ &backbuffer->fRenderSemaphore, // pWaitSemaphores
+ 1, // swapchainCount
+ &fSwapchain, // pSwapchains
+ &backbuffer->fImageIndex, // pImageIndices
+ NULL // pResults
+ };
+
+ GR_VK_CALL_ERRCHECK(fBackendContext->fInterface,
+ QueuePresentKHR(fPresentQueue, &presentInfo));
+
+}
diff --git a/tools/vulkan/VulkanTestContext.h b/tools/vulkan/VulkanTestContext.h
new file mode 100644
index 0000000..c1535b9
--- /dev/null
+++ b/tools/vulkan/VulkanTestContext.h
@@ -0,0 +1,92 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef VulkanTestContext_DEFINED
+#define VulkanTestContext_DEFINED
+
+#ifdef SK_VULKAN
+
+#include "GrTypes.h"
+#include "vk/GrVkBackendContext.h"
+
+class SkSurface;
+class GrContext;
+
+class VulkanTestContext {
+public:
+ ~VulkanTestContext();
+
+ // each platform will have to implement these in its CPP file
+ VkSurfaceKHR createVkSurface(void* platformData);
+ bool canPresent(uint32_t queueFamilyIndex);
+
+ static VulkanTestContext* Create(void* platformData, int msaaSampleCount) {
+ VulkanTestContext* ctx = new VulkanTestContext(platformData, msaaSampleCount);
+ if (!ctx->isValid()) {
+ delete ctx;
+ return nullptr;
+ }
+ return ctx;
+ }
+
+ SkSurface* getBackbufferSurface();
+ void swapBuffers();
+
+ bool makeCurrent() { return true; }
+ int getStencilBits() { return 0; }
+ int getSampleCount() { return 0; }
+
+ bool isValid() { return SkToBool(fBackendContext.get()); }
+
+ void resize() {
+ this->createSwapchain(0, 0);
+ }
+
+ GrBackendContext getBackendContext() { return (GrBackendContext)fBackendContext.get(); }
+
+private:
+ VulkanTestContext();
+ VulkanTestContext(void*, int msaaSampleCount);
+ void initializeContext(void*);
+ void destroyContext();
+
+ struct BackbufferInfo {
+ uint32_t fImageIndex; // image this is associated with
+ VkSemaphore fAcquireSemaphore; // we signal on this for acquisition of image
+ VkSemaphore fRenderSemaphore; // we wait on this for rendering to be done
+ VkCommandBuffer fTransitionCmdBuffers[2]; // to transition layout between present and render
+ VkFence fUsageFences[2]; // used to ensure this data is no longer used on GPU
+ };
+
+ BackbufferInfo* getAvailableBackbuffer();
+ bool createSwapchain(uint32_t width, uint32_t height);
+ void createBuffers();
+ void destroyBuffers();
+
+ SkAutoTUnref<const GrVkBackendContext> fBackendContext;
+
+ GrContext* fContext;
+ VkSurfaceKHR fSurface;
+ VkSwapchainKHR fSwapchain;
+ uint32_t fPresentQueueIndex;
+ VkQueue fPresentQueue;
+ int fWidth;
+ int fHeight;
+ GrPixelConfig fPixelConfig;
+
+ uint32_t fImageCount;
+ VkImage* fImages; // images in the swapchain
+ VkImageLayout* fImageLayouts; // layouts of these images when not color attachment
+ sk_sp<SkSurface>* fSurfaces; // wrapped surface for those images
+ VkCommandPool fCommandPool;
+ BackbufferInfo* fBackbuffers;
+ uint32_t fCurrentBackbufferIndex;
+};
+
+#endif // SK_VULKAN
+
+#endif
diff --git a/tools/vulkan/Window.cpp b/tools/vulkan/Window.cpp
new file mode 100644
index 0000000..d0a2a76
--- /dev/null
+++ b/tools/vulkan/Window.cpp
@@ -0,0 +1,52 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "Window.h"
+
+#include "SkSurface.h"
+#include "SkCanvas.h"
+#include "VulkanTestContext.h"
+
+static bool default_key_func(int key, bool down, void* userData) {
+ return false;
+}
+
+static bool default_mouse_func(int x, int y, bool down, void* userData) {
+ return false;
+}
+
+static void default_paint_func(SkCanvas*, void* userData) {}
+
+Window::Window() : fKeyFunc(default_key_func)
+ , fMouseFunc(default_mouse_func)
+ , fPaintFunc(default_paint_func) {
+}
+
+void Window::detach() {
+ delete fTestContext;
+ fTestContext = nullptr;
+}
+
+void Window::onPaint() {
+ SkSurface* backbuffer = fTestContext->getBackbufferSurface();
+ if (backbuffer) {
+ // draw into the canvas of this surface
+ SkCanvas* canvas = backbuffer->getCanvas();
+
+ fPaintFunc(canvas, fPaintUserData);
+
+ canvas->flush();
+
+ fTestContext->swapBuffers();
+ }
+
+}
+
+
+void Window::onSize() {
+ fTestContext->resize();
+}
diff --git a/tools/vulkan/Window.h b/tools/vulkan/Window.h
new file mode 100644
index 0000000..2247ca1
--- /dev/null
+++ b/tools/vulkan/Window.h
@@ -0,0 +1,73 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef Window_DEFINED
+#define Window_DEFINED
+
+class SkCanvas;
+class VulkanTestContext;
+
+class Window {
+public:
+ static Window* CreateNativeWindow(void* platformData);
+
+ virtual ~Window() {};
+
+ virtual void setTitle(const char*) = 0;
+ virtual void show() = 0;
+
+ struct AttachmentInfo {
+ int fSampleCount;
+ int fStencilBits;
+ };
+
+ enum BackEndTypes {
+ kNativeGL_BackendType,
+ kVulkan_BackendType
+ };
+
+ virtual bool attach(BackEndTypes attachType, int msaaSampleCount, AttachmentInfo*) = 0;
+ void detach();
+
+ // input handling
+ typedef bool(*OnKeyFunc)(int key, bool down, void* userData);
+ typedef bool(*OnMouseFunc)(int x, int y, bool down, void* userData);
+ typedef void(*OnPaintFunc)(SkCanvas*, void* userData);
+
+ void registerKeyFunc(OnKeyFunc func, void* userData) {
+ fKeyFunc = func;
+ fKeyUserData = userData;
+ }
+
+ void registerMouseFunc(OnMouseFunc func, void* userData) {
+ fMouseFunc = func;
+ fMouseUserData = userData;
+ }
+
+ void registerPaintFunc(OnPaintFunc func, void* userData) {
+ fPaintFunc = func;
+ fPaintUserData = userData;
+ }
+
+ void onPaint();
+ void onSize();
+
+protected:
+ Window();
+
+ OnKeyFunc fKeyFunc;
+ void* fKeyUserData;
+ OnMouseFunc fMouseFunc;
+ void* fMouseUserData;
+ OnPaintFunc fPaintFunc;
+ void* fPaintUserData;
+
+ VulkanTestContext* fTestContext;
+};
+
+
+#endif
diff --git a/tools/vulkan/viewer/InputHandler.cpp b/tools/vulkan/viewer/InputHandler.cpp
new file mode 100644
index 0000000..0e78b94
--- /dev/null
+++ b/tools/vulkan/viewer/InputHandler.cpp
@@ -0,0 +1,48 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "InputHandler.h"
+#include <ctype.h>
+
+InputHandler::InputHandler() : fMouseDown(false), fMousePressed(false), fMouseReleased(false)
+ , fMouseX(0), fMouseY(0) {
+ // clear key states
+ memset(fKeys, 0, sizeof(bool) * 256);
+ memset(fKeyPressed, 0, sizeof(bool) * 256);
+ memset(fKeyReleased, 0, sizeof(bool) * 256);
+}
+
+void InputHandler::onKeyDown(unsigned char key) {
+ if (!fKeys[key]) {
+ fKeys[key] = true;
+ fKeyPressed[key] = true;
+ }
+}
+
+void InputHandler::onKeyUp(unsigned char key) {
+ if (fKeys[key]) {
+ fKeys[key] = false;
+ fKeyReleased[key] = true;
+ }
+}
+
+void InputHandler::onMouseDown(unsigned int h, unsigned int v) {
+ if (!fMouseDown) {
+ fMouseDown = true;
+ fMousePressed = true;
+ }
+
+ fMouseX = h;
+ fMouseY = v;
+}
+
+void InputHandler::onMouseUp() {
+ if (fMouseDown) {
+ fMouseDown = false;
+ fMouseReleased = true;
+ }
+}
diff --git a/tools/vulkan/viewer/InputHandler.h b/tools/vulkan/viewer/InputHandler.h
new file mode 100644
index 0000000..05d6942
--- /dev/null
+++ b/tools/vulkan/viewer/InputHandler.h
@@ -0,0 +1,84 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef InputHandler_DEFINED
+#define InputHandler_DEFINED
+
+#include <string.h>
+
+class InputHandler
+{
+public:
+ InputHandler();
+
+ void onKeyDown(unsigned char key); // Handle key down event
+ void onKeyUp(unsigned char key); // Handle key up event
+ void onMouseDown(unsigned int h, unsigned int v); // Handle mouse down event
+ void onMouseUp(); // Handle mouse up event
+
+ bool isKeyDown(unsigned char key) const {
+ return fKeys[key];
+ }
+ bool isKeyUp(unsigned char key) const {
+ return !fKeys[key];
+ }
+ bool isKeyPressed(unsigned char key) const {
+ return fKeyPressed[key];
+ }
+
+ bool isKeyReleased(unsigned char key) const {
+ return fKeyReleased[key];
+ }
+
+ bool isMouseDown(unsigned int* h, unsigned int* v) const {
+ if (fMouseDown)
+ {
+ *h = fMouseX;
+ *v = fMouseY;
+ return true;
+ }
+ return false;
+ }
+
+ bool isMousePressed(unsigned int* h, unsigned int* v) const {
+ if (fMousePressed)
+ {
+ *h = fMouseX;
+ *v = fMouseY;
+ return true;
+ }
+ return false;
+ }
+
+ bool IsMouseReleased() const {
+ return fMouseReleased;
+ }
+
+ inline void Update() {
+ memset(fKeyPressed, 0, sizeof(bool) * 256);
+ memset(fKeyReleased, 0, sizeof(bool) * 256);
+ fMousePressed = false;
+ fMouseReleased = false;
+ }
+
+private:
+ // assumes ASCII keystrokes only
+ bool fKeys[256];
+ bool fKeyPressed[256];
+ bool fKeyReleased[256];
+
+ bool fMouseDown;
+ bool fMousePressed;
+ bool fMouseReleased;
+ unsigned int fMouseX;
+ unsigned int fMouseY;
+
+ InputHandler(const InputHandler& other);
+ InputHandler& operator=(const InputHandler& other);
+};
+
+#endif
diff --git a/tools/vulkan/viewer/VulkanViewer.cpp b/tools/vulkan/viewer/VulkanViewer.cpp
new file mode 100644
index 0000000..0af423e
--- /dev/null
+++ b/tools/vulkan/viewer/VulkanViewer.cpp
@@ -0,0 +1,94 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "VulkanViewer.h"
+
+#include "SkCanvas.h"
+#include "SkRandom.h"
+#include "SkCommonFlags.h"
+
+DEFINE_string(key, "",
+ "Space-separated key/value pairs to add to JSON identifying this builder.");
+
+Application* Application::Create(int argc, char** argv, void* platformData) {
+ return new VulkanViewer(argc, argv, platformData);
+}
+
+static bool on_key_handler(int key, bool keyDown, void* userData) {
+ VulkanViewer* vv = reinterpret_cast<VulkanViewer*>(userData);
+
+ return vv->onKey(key, keyDown);
+}
+
+static bool on_mouse_handler(int x, int y, bool mouseDown, void* userData) {
+ VulkanViewer* vv = reinterpret_cast<VulkanViewer*>(userData);
+
+ return vv->onMouse(x, y, mouseDown);
+}
+
+static void on_paint_handler(SkCanvas* canvas, void* userData) {
+ VulkanViewer* vv = reinterpret_cast<VulkanViewer*>(userData);
+
+ return vv->onPaint(canvas);
+}
+
+VulkanViewer::VulkanViewer(int argc, char** argv, void* platformData) :
+ fGMs(skiagm::GMRegistry::Head()){
+
+ fWindow = Window::CreateNativeWindow(platformData);
+ fWindow->attach(Window::kVulkan_BackendType, 0, nullptr);
+
+ // register callbacks
+ fWindow->registerKeyFunc(on_key_handler, this);
+ fWindow->registerMouseFunc(on_mouse_handler, this);
+ fWindow->registerPaintFunc(on_paint_handler, this);
+
+ fWindow->setTitle("VulkanViewer");
+ fWindow->show();
+}
+
+VulkanViewer::~VulkanViewer() {
+ fWindow->detach();
+ delete fWindow;
+}
+
+bool VulkanViewer::onKey(int key, bool keyDown) {
+ if (keyDown) {
+ fInputHandler.onKeyDown(key);
+ } else {
+ fInputHandler.onKeyUp(key);
+ }
+ return true;
+}
+
+bool VulkanViewer::onMouse(int x, int y, bool mouseDown) {
+ if (mouseDown) {
+ fInputHandler.onMouseDown(x, y);
+ } else {
+ fInputHandler.onMouseUp();
+ }
+ return true;
+}
+
+void VulkanViewer::onPaint(SkCanvas* canvas) {
+ SkAutoTDelete<skiagm::GM> gm(fGMs->factory()(nullptr));
+
+ canvas->save();
+
+ gm->draw(canvas);
+
+ canvas->restore();
+}
+
+void VulkanViewer::onIdle(float dt) {
+ if (fInputHandler.isKeyPressed('l') || fInputHandler.isKeyPressed('L')) {
+ fGMs = fGMs->next();
+ }
+ fWindow->onPaint();
+
+ fInputHandler.Update();
+}
diff --git a/tools/vulkan/viewer/VulkanViewer.h b/tools/vulkan/viewer/VulkanViewer.h
new file mode 100644
index 0000000..48ab7af
--- /dev/null
+++ b/tools/vulkan/viewer/VulkanViewer.h
@@ -0,0 +1,37 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef VulkanViewer_DEFINED
+#define VulkanViewer_DEFINED
+
+#include "../Application.h"
+#include "InputHandler.h"
+#include "../Window.h"
+#include "gm.h"
+
+class SkCanvas;
+
+class VulkanViewer : public Application {
+public:
+ VulkanViewer(int argc, char** argv, void* platformData);
+ ~VulkanViewer() override;
+
+ bool onKey(int key, bool keyDown);
+ bool onMouse(int x, int y, bool mouseDown);
+ void onPaint(SkCanvas* canvas);
+
+ void onIdle(float dt) override;
+
+private:
+ Window* fWindow;
+ InputHandler fInputHandler;
+
+ const skiagm::GMRegistry* fGMs;
+};
+
+
+#endif
diff --git a/tools/vulkan/win/VulkanTestContext_win.cpp b/tools/vulkan/win/VulkanTestContext_win.cpp
new file mode 100644
index 0000000..a4f49e3
--- /dev/null
+++ b/tools/vulkan/win/VulkanTestContext_win.cpp
@@ -0,0 +1,47 @@
+
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "VulkanTestContext_win.h"
+
+#include "vk/GrVkInterface.h"
+#include "../../src/gpu/vk/GrVkUtil.h"
+
+// Platform dependant call
+VkSurfaceKHR VulkanTestContext::createVkSurface(void* platformData) {
+ // need better error handling here
+ SkASSERT(platformData);
+ ContextPlatformData_win* winPlatformData =
+ reinterpret_cast<ContextPlatformData_win*>(platformData);
+ VkSurfaceKHR surface;
+
+ VkWin32SurfaceCreateInfoKHR surfaceCreateInfo;
+ memset(&surfaceCreateInfo, 0, sizeof(VkWin32SurfaceCreateInfoKHR));
+ surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
+ surfaceCreateInfo.pNext = nullptr;
+ surfaceCreateInfo.flags = 0;
+ surfaceCreateInfo.hinstance = winPlatformData->fHInstance;
+ surfaceCreateInfo.hwnd = winPlatformData->fHWnd;
+
+ VkResult res = GR_VK_CALL(fBackendContext->fInterface,
+ CreateWin32SurfaceKHR(fBackendContext->fInstance, &surfaceCreateInfo,
+ nullptr, &surface));
+ if (VK_SUCCESS != res) {
+ return VK_NULL_HANDLE;
+ }
+
+ return surface;
+}
+
+// Platform dependant call
+bool VulkanTestContext::canPresent(uint32_t queueFamilyIndex) {
+ VkBool32 check = GR_VK_CALL(fBackendContext->fInterface,
+ GetPhysicalDeviceWin32PresentationSupportKHR(
+ fBackendContext->fPhysicalDevice,
+ queueFamilyIndex));
+ return (VK_FALSE != check);
+}
diff --git a/tools/vulkan/win/VulkanTestContext_win.h b/tools/vulkan/win/VulkanTestContext_win.h
new file mode 100644
index 0000000..59b5c2c
--- /dev/null
+++ b/tools/vulkan/win/VulkanTestContext_win.h
@@ -0,0 +1,24 @@
+
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#ifndef VULKANTESTCONTEXT_WIN_DEFINED
+#define VULKANTESTCONTEXT_WIN_DEFINED
+
+#ifdef SK_VULKAN
+
+#include <windows.h>
+#include "../VulkanTestContext.h"
+
+// for Windows
+struct ContextPlatformData_win {
+ HINSTANCE fHInstance;
+ HWND fHWnd;
+};
+
+#endif // SK_VULKAN
+
+#endif
diff --git a/tools/vulkan/win/Window_win.cpp b/tools/vulkan/win/Window_win.cpp
new file mode 100644
index 0000000..20cf084
--- /dev/null
+++ b/tools/vulkan/win/Window_win.cpp
@@ -0,0 +1,206 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include "Window_win.h"
+
+#include <tchar.h>
+#include <windows.h>
+#include <windowsx.h>
+
+#include "VulkanTestContext_win.h"
+
+Window* Window::CreateNativeWindow(void* platformData) {
+ HINSTANCE hInstance = (HINSTANCE)platformData;
+
+ Window_win* window = new Window_win();
+ if (!window->init(hInstance)) {
+ delete window;
+ return nullptr;
+ }
+
+ return window;
+}
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+bool Window_win::init(HINSTANCE hInstance) {
+ fHInstance = hInstance ? hInstance : GetModuleHandle(nullptr);
+
+ WNDCLASSEX wcex;
+ // The main window class name
+ static const TCHAR gSZWindowClass[] = _T("SkiaApp");
+
+ wcex.cbSize = sizeof(WNDCLASSEX);
+
+ wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
+ wcex.lpfnWndProc = WndProc;
+ wcex.cbClsExtra = 0;
+ wcex.cbWndExtra = 0;
+ wcex.hInstance = fHInstance;
+ wcex.hIcon = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO);
+ wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);;
+ wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wcex.lpszMenuName = nullptr;
+ wcex.lpszClassName = gSZWindowClass;
+ wcex.hIconSm = LoadIcon(fHInstance, (LPCTSTR)IDI_WINLOGO);;
+
+ if (!RegisterClassEx(&wcex)) {
+ return false;
+ }
+
+ /*
+ if (fullscreen)
+ {
+ DEVMODE dmScreenSettings;
+ // If full screen set the screen to maximum size of the users desktop and 32bit.
+ memset(&dmScreenSettings, 0, sizeof(dmScreenSettings));
+ dmScreenSettings.dmSize = sizeof(dmScreenSettings);
+ dmScreenSettings.dmPelsWidth = (unsigned long)width;
+ dmScreenSettings.dmPelsHeight = (unsigned long)height;
+ dmScreenSettings.dmBitsPerPel = 32;
+ dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
+
+ // Change the display settings to full screen.
+ ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
+
+ // Set the position of the window to the top left corner.
+ posX = posY = 0;
+ }
+ */
+ // gIsFullscreen = fullscreen;
+
+ fHWnd = CreateWindow(gSZWindowClass, nullptr, WS_OVERLAPPEDWINDOW,
+ CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, fHInstance, nullptr);
+ if (!fHWnd)
+ {
+ return false;
+ }
+
+ SetWindowLongPtr(fHWnd, GWLP_USERDATA, (LONG_PTR)this);
+
+ return true;
+}
+
+
+LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ PAINTSTRUCT ps;
+ HDC hdc;
+
+ Window_win* window = (Window_win*) GetWindowLongPtr(hWnd, GWLP_USERDATA);
+
+ switch (message)
+ {
+ case WM_PAINT:
+ hdc = BeginPaint(hWnd, &ps);
+ window->onPaint();
+ EndPaint(hWnd, &ps);
+ break;
+
+ case WM_CLOSE:
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+
+ case WM_ACTIVATE:
+ // disable/enable rendering here, depending on wParam != WA_INACTIVE
+ break;
+
+ case WM_SIZE:
+ window->onSize();
+ break;
+
+ case WM_KEYDOWN:
+ case WM_SYSKEYDOWN:
+ {
+ DWORD dwMask = (1 << 29);
+ bool bAltDown = ((lParam & dwMask) != 0);
+ UINT theChar = MapVirtualKey((UINT)wParam, 2);
+ // Handle Extended ASCII only
+ if (theChar < 256) {
+ return window->onKeyboard(theChar, true, bAltDown);
+ }
+ }
+ break;
+
+ case WM_KEYUP:
+ case WM_SYSKEYUP:
+ {
+ DWORD dwMask = (1 << 29);
+ bool bAltDown = ((lParam & dwMask) != 0);
+ UINT theChar = MapVirtualKey((UINT)wParam, 2);
+ // Handle Extended ASCII only
+ if (theChar < 256) {
+ return window->onKeyboard(theChar, false, bAltDown);
+ }
+ }
+ break;
+
+ case WM_LBUTTONDOWN:
+ case WM_RBUTTONDOWN:
+ case WM_MBUTTONDOWN:
+ case WM_LBUTTONUP:
+ case WM_RBUTTONUP:
+ case WM_MBUTTONUP:
+ {
+ bool bLeftDown = ((wParam & MK_LBUTTON) != 0);
+ bool bRightDown = ((wParam & MK_RBUTTON) != 0);
+ bool bMiddleDown = ((wParam & MK_MBUTTON) != 0);
+
+ int xPos = GET_X_LPARAM(lParam);
+ int yPos = GET_Y_LPARAM(lParam);
+ //if (!gIsFullscreen)
+ //{
+ // RECT rc = { 0, 0, 640, 480 };
+ // AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
+ // xPos -= rc.left;
+ // yPos -= rc.top;
+ //}
+
+ return window->onMouse(bLeftDown, bRightDown, bMiddleDown, false, false, 0, xPos, yPos);
+ }
+ break;
+
+ default:
+ return DefWindowProc(hWnd, message, wParam, lParam);
+ }
+
+ return 0;
+}
+
+bool Window_win::onKeyboard(UINT nChar, bool bKeyDown, bool bAltDown) {
+ return fKeyFunc(nChar, bKeyDown, fKeyUserData);
+}
+
+bool Window_win::onMouse(bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown,
+ bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta,
+ int xPos, int yPos) {
+ return fMouseFunc(xPos, yPos, bLeftButtonDown, fMouseUserData);
+}
+
+void Window_win::setTitle(const char* title) {
+ SetWindowTextA(fHWnd, title);
+}
+
+void Window_win::show() {
+ ShowWindow(fHWnd, SW_SHOW);
+}
+
+
+bool Window_win::attach(BackEndTypes attachType, int msaaSampleCount, AttachmentInfo*) {
+ if (kVulkan_BackendType != attachType) {
+ return false;
+ }
+
+ ContextPlatformData_win platformData;
+ platformData.fHInstance = fHInstance;
+ platformData.fHWnd = fHWnd;
+
+ fTestContext = VulkanTestContext::Create((void*)&platformData, msaaSampleCount);
+
+ return (SkToBool(fTestContext));
+}
diff --git a/tools/vulkan/win/Window_win.h b/tools/vulkan/win/Window_win.h
new file mode 100644
index 0000000..90d4191
--- /dev/null
+++ b/tools/vulkan/win/Window_win.h
@@ -0,0 +1,37 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#ifndef Window_win_DEFINED
+#define Window_win_DEFINED
+
+#include <windows.h>
+#include "../Window.h"
+
+class Window_win : public Window {
+public:
+ Window_win() : Window() {}
+ ~Window_win() override {}
+
+ bool init(HINSTANCE instance);
+
+ void setTitle(const char*) override;
+ void show() override;
+
+ // event callbacks
+ bool onKeyboard(UINT nChar, bool bKeyDown, bool bAltDown);
+ bool onMouse(bool bLeftButtonDown, bool bRightButtonDown, bool bMiddleButtonDown,
+ bool bSideButton1Down, bool bSideButton2Down, int nMouseWheelDelta,
+ int xPos, int yPos);
+
+ bool attach(BackEndTypes attachType, int msaaSampleCount, AttachmentInfo*) override;
+
+private:
+ HINSTANCE fHInstance;
+ HWND fHWnd;
+};
+
+#endif
diff --git a/tools/vulkan/win/main_win.cpp b/tools/vulkan/win/main_win.cpp
new file mode 100644
index 0000000..34e522a
--- /dev/null
+++ b/tools/vulkan/win/main_win.cpp
@@ -0,0 +1,76 @@
+/*
+* Copyright 2016 Google Inc.
+*
+* Use of this source code is governed by a BSD-style license that can be
+* found in the LICENSE file.
+*/
+
+#include <windows.h>
+#include <tchar.h>
+
+#include "SkTypes.h"
+#include "Window_win.h"
+#include "../Application.h"
+
+static char* tchar_to_utf8(const TCHAR* str) {
+#ifdef _UNICODE
+ int size = WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), NULL, 0, NULL, NULL);
+ char* str8 = (char*)sk_malloc_throw(size + 1);
+ WideCharToMultiByte(CP_UTF8, 0, str, wcslen(str), str8, size, NULL, NULL);
+ str8[size] = '\0';
+ return str8;
+#else
+ return _strdup(str);
+#endif
+}
+
+// This file can work with GUI or CONSOLE subsystem types since we define _tWinMain and main().
+
+static int main_common(HINSTANCE hInstance, int show, int argc, char**argv);
+
+int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine,
+ int nCmdShow) {
+
+ // convert from lpCmdLine to argc, argv.
+ char* argv[4096];
+ int argc = 0;
+ TCHAR exename[1024], *next;
+ int exenameLen = GetModuleFileName(NULL, exename, SK_ARRAY_COUNT(exename));
+ // we're ignoring the possibility that the exe name exceeds the exename buffer
+ (void)exenameLen;
+ argv[argc++] = tchar_to_utf8(exename);
+ TCHAR* arg = _tcstok_s(lpCmdLine, _T(" "), &next);
+ while (arg != NULL) {
+ argv[argc++] = tchar_to_utf8(arg);
+ arg = _tcstok_s(NULL, _T(" "), &next);
+ }
+ int result = main_common(hInstance, nCmdShow, argc, argv);
+ for (int i = 0; i < argc; ++i) {
+ sk_free(argv[i]);
+ }
+ return result;
+}
+
+int main(int argc, char**argv) {
+ return main_common(GetModuleHandle(NULL), SW_SHOW, argc, argv);
+}
+
+static int main_common(HINSTANCE hInstance, int show, int argc, char**argv) {
+
+ Application* app = Application::Create(argc, argv, (void*)hInstance);
+
+ MSG msg = { 0 };
+ // Main message loop
+ while (WM_QUIT != msg.message) {
+ if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ }
+
+ app->onIdle(0.0f);
+ }
+
+ delete app;
+
+ return (int)msg.wParam;
+}