diff options
author | jvanverth <jvanverth@google.com> | 2016-05-05 12:32:03 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-05-05 12:32:03 -0700 |
commit | a8d0d6c8bad00e713bc33e5f0d47ca4fec104433 (patch) | |
tree | 8f78250c57c9c9d96f8e2fac7864630c97fe3cb9 /tools/viewer/sk_app | |
parent | 2d1ee7936e3536e45c963db004e3b512bb415fd8 (diff) |
More refactoring for Viewer
* Move support files into sk_app and main files up to top directory
* Rename VulkanTestContext and create WindowContext parent class
* Place VulkanWindowContext et al. in sk_app namespace.
GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1944413005
Review-Url: https://codereview.chromium.org/1944413005
Diffstat (limited to 'tools/viewer/sk_app')
-rw-r--r-- | tools/viewer/sk_app/Application.h | 24 | ||||
-rw-r--r-- | tools/viewer/sk_app/VulkanWindowContext.cpp | 571 | ||||
-rw-r--r-- | tools/viewer/sk_app/VulkanWindowContext.h | 120 | ||||
-rw-r--r-- | tools/viewer/sk_app/Window.cpp | 78 | ||||
-rw-r--r-- | tools/viewer/sk_app/Window.h | 151 | ||||
-rw-r--r-- | tools/viewer/sk_app/WindowContext.h | 36 | ||||
-rw-r--r-- | tools/viewer/sk_app/android/VulkanWindowContext_android.cpp | 47 | ||||
-rw-r--r-- | tools/viewer/sk_app/android/VulkanWindowContext_android.h | 26 | ||||
-rw-r--r-- | tools/viewer/sk_app/android/Window_android.cpp | 183 | ||||
-rw-r--r-- | tools/viewer/sk_app/android/Window_android.h | 52 | ||||
-rw-r--r-- | tools/viewer/sk_app/android/main_android.cpp | 69 | ||||
-rw-r--r-- | tools/viewer/sk_app/win/VulkanWindowContext_win.cpp | 62 | ||||
-rw-r--r-- | tools/viewer/sk_app/win/VulkanWindowContext_win.h | 28 | ||||
-rw-r--r-- | tools/viewer/sk_app/win/Window_win.cpp | 285 | ||||
-rw-r--r-- | tools/viewer/sk_app/win/Window_win.h | 37 | ||||
-rw-r--r-- | tools/viewer/sk_app/win/main_win.cpp | 87 |
16 files changed, 1856 insertions, 0 deletions
diff --git a/tools/viewer/sk_app/Application.h b/tools/viewer/sk_app/Application.h new file mode 100644 index 0000000000..235ff09ed4 --- /dev/null +++ b/tools/viewer/sk_app/Application.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 Application_DEFINED +#define Application_DEFINED + +namespace sk_app { + +class Application { +public: + static Application* Create(int argc, char** argv, void* platformData); + + virtual ~Application() {} + + virtual void onIdle(double ms) = 0; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/VulkanWindowContext.cpp b/tools/viewer/sk_app/VulkanWindowContext.cpp new file mode 100644 index 0000000000..8f5f4209a4 --- /dev/null +++ b/tools/viewer/sk_app/VulkanWindowContext.cpp @@ -0,0 +1,571 @@ + +/* + * 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 "VulkanWindowContext.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 + +#define GET_PROC(F) f ## F = (PFN_vk ## F) vkGetInstanceProcAddr(instance, "vk" #F) +#define GET_DEV_PROC(F) f ## F = (PFN_vk ## F) vkGetDeviceProcAddr(device, "vk" #F) + +namespace sk_app { + +VulkanWindowContext::VulkanWindowContext(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 VulkanWindowContext::initializeContext(void* platformData) { + + fBackendContext.reset(GrVkBackendContext::Create(&fPresentQueueIndex, canPresent)); + if (!(fBackendContext->fExtensions & kKHR_surface_GrVkExtensionFlag) || + !(fBackendContext->fExtensions & kKHR_swapchain_GrVkExtensionFlag)) { + fBackendContext.reset(nullptr); + return; + } + + VkInstance instance = fBackendContext->fInstance; + VkDevice device = fBackendContext->fDevice; + GET_PROC(DestroySurfaceKHR); + GET_PROC(GetPhysicalDeviceSurfaceSupportKHR); + GET_PROC(GetPhysicalDeviceSurfaceCapabilitiesKHR); + GET_PROC(GetPhysicalDeviceSurfaceFormatsKHR); + GET_PROC(GetPhysicalDeviceSurfacePresentModesKHR); + GET_DEV_PROC(CreateSwapchainKHR); + GET_DEV_PROC(DestroySwapchainKHR); + GET_DEV_PROC(GetSwapchainImagesKHR); + GET_DEV_PROC(AcquireNextImageKHR); + GET_DEV_PROC(QueuePresentKHR); + + fContext = GrContext::Create(kVulkan_GrBackend, (GrBackendContext) fBackendContext.get()); + + fSurface = createVkSurface(instance, platformData); + if (VK_NULL_HANDLE == fSurface) { + fBackendContext.reset(nullptr); + return; + } + + VkBool32 supported; + VkResult res = fGetPhysicalDeviceSurfaceSupportKHR(fBackendContext->fPhysicalDevice, + fPresentQueueIndex, fSurface, + &supported); + if (VK_SUCCESS != res) { + this->destroyContext(); + return; + } + + if (!this->createSwapchain(-1, -1)) { + this->destroyContext(); + return; + } + + // create presentQueue + vkGetDeviceQueue(fBackendContext->fDevice, fPresentQueueIndex, 0, &fPresentQueue); +} + +bool VulkanWindowContext::createSwapchain(uint32_t width, uint32_t height) { + // check for capabilities + VkSurfaceCapabilitiesKHR caps; + VkResult res = fGetPhysicalDeviceSurfaceCapabilitiesKHR(fBackendContext->fPhysicalDevice, + fSurface, &caps); + if (VK_SUCCESS != res) { + return false; + } + + uint32_t surfaceFormatCount; + res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface, + &surfaceFormatCount, nullptr); + if (VK_SUCCESS != res) { + return false; + } + + SkAutoMalloc surfaceFormatAlloc(surfaceFormatCount * sizeof(VkSurfaceFormatKHR)); + VkSurfaceFormatKHR* surfaceFormats = (VkSurfaceFormatKHR*)surfaceFormatAlloc.get(); + res = fGetPhysicalDeviceSurfaceFormatsKHR(fBackendContext->fPhysicalDevice, fSurface, + &surfaceFormatCount, surfaceFormats); + if (VK_SUCCESS != res) { + return false; + } + + uint32_t presentModeCount; + res = fGetPhysicalDeviceSurfacePresentModesKHR(fBackendContext->fPhysicalDevice, fSurface, + &presentModeCount, nullptr); + if (VK_SUCCESS != res) { + return false; + } + + SkAutoMalloc presentModeAlloc(presentModeCount * sizeof(VkPresentModeKHR)); + VkPresentModeKHR* presentModes = (VkPresentModeKHR*)presentModeAlloc.get(); + res = fGetPhysicalDeviceSurfacePresentModesKHR(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; + + // Pick our surface format -- for now, the first one + VkFormat surfaceFormat = surfaceFormats[0].format; + VkColorSpaceKHR colorSpace = surfaceFormats[0].colorSpace; + + // If mailbox mode is available, use it, as it is the lowest-latency non- + // tearing mode. If not, fall back to FIFO which is always available. + VkPresentModeKHR mode = VK_PRESENT_MODE_FIFO_KHR; + for (uint32_t i = 0; i < presentModeCount; ++i) { + // use mailbox + if (VK_PRESENT_MODE_MAILBOX_KHR == presentModes[i]) { + mode = presentModes[i]; + break; + } + } + + VkSwapchainCreateInfoKHR swapchainCreateInfo; + memset(&swapchainCreateInfo, 0, sizeof(VkSwapchainCreateInfoKHR)); + swapchainCreateInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; + swapchainCreateInfo.surface = fSurface; + swapchainCreateInfo.minImageCount = imageCount; + swapchainCreateInfo.imageFormat = surfaceFormat; + swapchainCreateInfo.imageColorSpace = colorSpace; + swapchainCreateInfo.imageExtent = extent; + swapchainCreateInfo.imageArrayLayers = 1; + swapchainCreateInfo.imageUsage = usageFlags; + + uint32_t queueFamilies[] = { fBackendContext->fGraphicsQueueIndex, fPresentQueueIndex }; + if (fBackendContext->fGraphicsQueueIndex != 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 = fCreateSwapchainKHR(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(); + + fDestroySwapchainKHR(fBackendContext->fDevice, swapchainCreateInfo.oldSwapchain, nullptr); + } + + this->createBuffers(swapchainCreateInfo.imageFormat); + + return true; +} + +void VulkanWindowContext::createBuffers(VkFormat format) { + GrVkFormatToPixelConfig(format, &fPixelConfig); + + fGetSwapchainImagesKHR(fBackendContext->fDevice, fSwapchain, &fImageCount, nullptr); + SkASSERT(fImageCount); + fImages = new VkImage[fImageCount]; + fGetSwapchainImagesKHR(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 = VK_NULL_HANDLE; + info.fImageLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + info.fImageTiling = VK_IMAGE_TILING_OPTIMAL; + info.fFormat = format; + 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->fGraphicsQueueIndex; + 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 VulkanWindowContext::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; +} + +VulkanWindowContext::~VulkanWindowContext() { + this->destroyContext(); +} + +void VulkanWindowContext::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) { + fDestroySwapchainKHR(fBackendContext->fDevice, fSwapchain, nullptr); + fSwapchain = VK_NULL_HANDLE; + } + + if (VK_NULL_HANDLE != fSurface) { + fDestroySurfaceKHR(fBackendContext->fInstance, fSurface, nullptr); + fSurface = VK_NULL_HANDLE; + } + + delete fContext; + + fBackendContext.reset(nullptr); +} + +VulkanWindowContext::BackbufferInfo* VulkanWindowContext::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* VulkanWindowContext::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 = fAcquireNextImageKHR(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* + // maybe use attach somehow? but need a Window + return nullptr; + } + if (VK_ERROR_OUT_OF_DATE_KHR == res) { + // tear swapchain down and try again + if (!this->createSwapchain(0, 0)) { + return nullptr; + } + + // acquire the image + res = fAcquireNextImageKHR(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->fGraphicsQueueIndex, // 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])); + + VkPipelineStageFlags waitDstStageFlags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + // 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 = &waitDstStageFlags; + 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 VulkanWindowContext::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->fGraphicsQueueIndex, // 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 + }; + + fQueuePresentKHR(fPresentQueue, &presentInfo); + +} + +} //namespace sk_app diff --git a/tools/viewer/sk_app/VulkanWindowContext.h b/tools/viewer/sk_app/VulkanWindowContext.h new file mode 100644 index 0000000000..48f8ed536b --- /dev/null +++ b/tools/viewer/sk_app/VulkanWindowContext.h @@ -0,0 +1,120 @@ + +/* + * 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 VulkanWindowContext_DEFINED +#define VulkanWindowContext_DEFINED + +#ifdef SK_VULKAN + +#include "vk/GrVkBackendContext.h" +#include "WindowContext.h" + +class SkSurface; +class GrContext; + +namespace sk_app { + +class VulkanWindowContext : public WindowContext { +public: + ~VulkanWindowContext() override; + + // each platform will have to implement these in its CPP file + static VkSurfaceKHR createVkSurface(VkInstance, void* platformData); + static bool canPresent(VkInstance, VkPhysicalDevice, uint32_t queueFamilyIndex); + + static VulkanWindowContext* Create(void* platformData, int msaaSampleCount) { + VulkanWindowContext* ctx = new VulkanWindowContext(platformData, msaaSampleCount); + if (!ctx->isValid()) { + delete ctx; + return nullptr; + } + return ctx; + } + + SkSurface* getBackbufferSurface() override; + void swapBuffers() override; + + bool makeCurrent() override { return true; } + + bool isValid() override { return SkToBool(fBackendContext.get()); } + + void resize(uint32_t w, uint32_t h) override { + this->createSwapchain(w, h); + } + + GrBackendContext getBackendContext() override { + return (GrBackendContext) fBackendContext.get(); + } + +private: + VulkanWindowContext(); + VulkanWindowContext(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(VkFormat format); + void destroyBuffers(); + + SkAutoTUnref<const GrVkBackendContext> fBackendContext; + + // simple wrapper class that exists only to initialize a pointer to NULL + template <typename FNPTR_TYPE> class VkPtr { + public: + VkPtr() : fPtr(NULL) {} + VkPtr operator=(FNPTR_TYPE ptr) { fPtr = ptr; return *this; } + operator FNPTR_TYPE() const { return fPtr; } + private: + FNPTR_TYPE fPtr; + }; + + // WSI interface functions + VkPtr<PFN_vkDestroySurfaceKHR> fDestroySurfaceKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceSupportKHR> fGetPhysicalDeviceSurfaceSupportKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR> fGetPhysicalDeviceSurfaceCapabilitiesKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfaceFormatsKHR> fGetPhysicalDeviceSurfaceFormatsKHR; + VkPtr<PFN_vkGetPhysicalDeviceSurfacePresentModesKHR> fGetPhysicalDeviceSurfacePresentModesKHR; + + VkPtr<PFN_vkCreateSwapchainKHR> fCreateSwapchainKHR; + VkPtr<PFN_vkDestroySwapchainKHR> fDestroySwapchainKHR; + VkPtr<PFN_vkGetSwapchainImagesKHR> fGetSwapchainImagesKHR; + VkPtr<PFN_vkAcquireNextImageKHR> fAcquireNextImageKHR; + VkPtr<PFN_vkQueuePresentKHR> fQueuePresentKHR; + VkPtr<PFN_vkCreateSharedSwapchainsKHR> fCreateSharedSwapchainsKHR; + + 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; +}; + +} // namespace sk_app + +#endif // SK_VULKAN + +#endif diff --git a/tools/viewer/sk_app/Window.cpp b/tools/viewer/sk_app/Window.cpp new file mode 100644 index 0000000000..a7fd6b0cf6 --- /dev/null +++ b/tools/viewer/sk_app/Window.cpp @@ -0,0 +1,78 @@ +/* +* 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 "VulkanWindowContext.h" + +namespace sk_app { + +static bool default_char_func(SkUnichar c, uint32_t modifiers, void* userData) { + return false; +} + +static bool default_key_func(Window::Key key, Window::InputState state, uint32_t modifiers, + void* userData) { + return false; +} + +static bool default_mouse_func(int x, int y, Window::InputState state, uint32_t modifiers, + void* userData) { + return false; +} + +static void default_paint_func(SkCanvas*, void* userData) {} + +Window::Window() : fCharFunc(default_char_func) + , fKeyFunc(default_key_func) + , fMouseFunc(default_mouse_func) + , fPaintFunc(default_paint_func) { +} + +void Window::detach() { + delete fWindowContext; + fWindowContext = nullptr; +} + +bool Window::onChar(SkUnichar c, uint32_t modifiers) { + return fCharFunc(c, modifiers, fCharUserData); +} + +bool Window::onKey(Key key, InputState state, uint32_t modifiers) { + return fKeyFunc(key, state, modifiers, fKeyUserData); +} + +bool Window::onMouse(int x, int y, InputState state, uint32_t modifiers) { + return fMouseFunc(x, y, state, modifiers, fMouseUserData); +} + +void Window::onPaint() { + SkSurface* backbuffer = fWindowContext->getBackbufferSurface(); + if (backbuffer) { + // draw into the canvas of this surface + SkCanvas* canvas = backbuffer->getCanvas(); + + fPaintFunc(canvas, fPaintUserData); + + canvas->flush(); + + fWindowContext->swapBuffers(); + } else { + // try recreating testcontext + } + +} + +void Window::onResize(uint32_t w, uint32_t h) { + fWidth = w; + fHeight = h; + fWindowContext->resize(w, h); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/Window.h b/tools/viewer/sk_app/Window.h new file mode 100644 index 0000000000..3dd558e75a --- /dev/null +++ b/tools/viewer/sk_app/Window.h @@ -0,0 +1,151 @@ +/* +* 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 + +#include "SkTypes.h" +#include "SkRect.h" + +class SkCanvas; + +namespace sk_app { + +class WindowContext; + +class Window { +public: + static Window* CreateNativeWindow(void* platformData); + + virtual ~Window() {}; + + virtual void setTitle(const char*) = 0; + virtual void show() = 0; + virtual void inval() = 0; + + virtual bool scaleContentToFit() const { return false; } + virtual bool supportsContentRect() const { return false; } + virtual SkRect getContentRect() { return SkRect::MakeEmpty(); } + + enum BackEndType { + kNativeGL_BackendType, + kVulkan_BackendType + }; + + virtual bool attach(BackEndType attachType, int msaaSampleCount) = 0; + void detach(); + + // input handling + enum Key { + kNONE_Key, //corresponds to android's UNKNOWN + + kLeftSoftKey_Key, + kRightSoftKey_Key, + + kHome_Key, //!< the home key - added to match android + kBack_Key, //!< (CLR) + kSend_Key, //!< the green (talk) key + kEnd_Key, //!< the red key + + k0_Key, + k1_Key, + k2_Key, + k3_Key, + k4_Key, + k5_Key, + k6_Key, + k7_Key, + k8_Key, + k9_Key, + kStar_Key, //!< the * key + kHash_Key, //!< the # key + + kUp_Key, + kDown_Key, + kLeft_Key, + kRight_Key, + + kOK_Key, //!< the center key + + kVolUp_Key, //!< volume up - match android + kVolDown_Key, //!< volume down - same + kPower_Key, //!< power button - same + kCamera_Key, //!< camera - same + + kLast_Key = kCamera_Key + }; + static const int kKeyCount = kLast_Key + 1; + + enum ModifierKeys { + kShift_ModifierKey = 1 << 0, + kControl_ModifierKey = 1 << 1, + kOption_ModifierKey = 1 << 2, // same as ALT + kCommand_ModifierKey = 1 << 3, + kFirstPress_ModifierKey = 1 << 4, + }; + + enum InputState { + kDown_InputState, + kUp_InputState, + kMove_InputState // only valid for mouse + }; + + // return value of 'true' means 'I have handled this event' + typedef bool(*OnCharFunc)(SkUnichar c, uint32_t modifiers, void* userData); + typedef bool(*OnKeyFunc)(Key key, InputState state, uint32_t modifiers, void* userData); + typedef bool(*OnMouseFunc)(int x, int y, InputState state, uint32_t modifiers, void* userData); + typedef void(*OnPaintFunc)(SkCanvas*, void* userData); + + void registerCharFunc(OnCharFunc func, void* userData) { + fCharFunc = func; + fCharUserData = 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; + } + + bool onChar(SkUnichar c, uint32_t modifiers); + bool onKey(Key key, InputState state, uint32_t modifiers); + bool onMouse(int x, int y, InputState state, uint32_t modifiers); + void onPaint(); + void onResize(uint32_t width, uint32_t height); + + uint32_t width() { return fWidth; } + uint32_t height() { return fHeight; } + +protected: + Window(); + + uint32_t fWidth; + uint32_t fHeight; + + OnCharFunc fCharFunc; + void* fCharUserData; + OnKeyFunc fKeyFunc; + void* fKeyUserData; + OnMouseFunc fMouseFunc; + void* fMouseUserData; + OnPaintFunc fPaintFunc; + void* fPaintUserData; + + WindowContext* fWindowContext; +}; + +} // namespace sk_app +#endif diff --git a/tools/viewer/sk_app/WindowContext.h b/tools/viewer/sk_app/WindowContext.h new file mode 100644 index 0000000000..5811bedd32 --- /dev/null +++ b/tools/viewer/sk_app/WindowContext.h @@ -0,0 +1,36 @@ +/* + * 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 WindowContext_DEFINED +#define WindowContext_DEFINED + +#include "GrTypes.h" + +class SkSurface; + +namespace sk_app { + +// TODO: fill this out with an interface +class WindowContext { +public: + virtual ~WindowContext() {} + + virtual SkSurface* getBackbufferSurface() = 0; + + virtual void swapBuffers() = 0; + + virtual bool makeCurrent() = 0; + + virtual bool isValid() = 0; + + virtual void resize(uint32_t w, uint32_t h) = 0; + + virtual GrBackendContext getBackendContext() = 0; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp b/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp new file mode 100644 index 0000000000..1ef994aa3f --- /dev/null +++ b/tools/viewer/sk_app/android/VulkanWindowContext_android.cpp @@ -0,0 +1,47 @@ + +/* + * 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 "VulkanWindowContext_android.h" + +#include "vk/GrVkInterface.h" +#include "vk/GrVkUtil.h" + +namespace sk_app { + +VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* platformData) { + static PFN_vkCreateAndroidSurfaceKHR createAndroidSurfaceKHR = nullptr; + if (!createAndroidSurfaceKHR) { + createAndroidSurfaceKHR = (PFN_vkCreateAndroidSurfaceKHR)vkGetInstanceProcAddr(instance, + "vkCreateAndroidSurfaceKHR"); + } + + if (!platformData) { + return VK_NULL_HANDLE; + } + ContextPlatformData_android* androidPlatformData = + reinterpret_cast<ContextPlatformData_android*>(platformData); + VkSurfaceKHR surface; + + VkAndroidSurfaceCreateInfoKHR surfaceCreateInfo; + memset(&surfaceCreateInfo, 0, sizeof(VkAndroidSurfaceCreateInfoKHR)); + surfaceCreateInfo.sType = VK_STRUCTURE_TYPE_ANDROID_SURFACE_CREATE_INFO_KHR; + surfaceCreateInfo.pNext = nullptr; + surfaceCreateInfo.flags = 0; + surfaceCreateInfo.window = androidPlatformData->fNativeWindow; + + VkResult res = createAndroidSurfaceKHR(instance, &surfaceCreateInfo, + nullptr, &surface); + return (VK_SUCCESS == res) ? surface : VK_NULL_HANDLE; +} + +bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev, + uint32_t queueFamilyIndex) { + return true; +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/android/VulkanWindowContext_android.h b/tools/viewer/sk_app/android/VulkanWindowContext_android.h new file mode 100644 index 0000000000..dd53e13b69 --- /dev/null +++ b/tools/viewer/sk_app/android/VulkanWindowContext_android.h @@ -0,0 +1,26 @@ + +/* + * 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_ANDROID_DEFINED +#define VULKANTESTCONTEXT_ANDROID_DEFINED + +#ifdef SK_VULKAN + +#include "../VulkanWindowContext.h" + +struct ANativeWindow; + +namespace sk_app { + +struct ContextPlatformData_android { + ANativeWindow* fNativeWindow; +}; + +} +#endif // SK_VULKAN + +#endif diff --git a/tools/viewer/sk_app/android/Window_android.cpp b/tools/viewer/sk_app/android/Window_android.cpp new file mode 100644 index 0000000000..94be02c933 --- /dev/null +++ b/tools/viewer/sk_app/android/Window_android.cpp @@ -0,0 +1,183 @@ +/* +* 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_android.h" + +#include "VulkanWindowContext_android.h" + +namespace sk_app { + +Window* Window::CreateNativeWindow(void* platformData) { + Window_android* window = new Window_android(); + if (!window->init((android_app*)platformData)) { + delete window; + return nullptr; + } + return window; +} + +static void handle_cmd(struct android_app* app, int32_t cmd); +static int32_t handle_input(struct android_app* app, AInputEvent* event); + +bool Window_android::init(android_app* app) { + SkASSERT(app); + mApp = app; + mApp->userData = this; + mApp->onAppCmd = handle_cmd; + mApp->onInputEvent = handle_input; + return true; +} + +void Window_android::setTitle(const char* title) { + //todo + SkDebugf("Title: %s", title); +} + +bool Window_android::attach(BackEndType attachType, int msaaSampleCount) { + if (kVulkan_BackendType != attachType) { + return false; + } + + mSampleCount = msaaSampleCount; + + // We delay the creation of fTestContext until Android informs us that + // the native window is ready to use. + return true; +} + +void Window_android::initDisplay(ANativeWindow* window) { + SkASSERT(window); + ContextPlatformData_android platformData; + platformData.fNativeWindow = window; + fWindowContext = VulkanWindowContext::Create((void*)&platformData, mSampleCount); +} + +static void android_app_write_cmd(struct android_app* android_app, int8_t cmd) { + if (write(android_app->msgwrite, &cmd, sizeof(cmd)) != sizeof(cmd)) { + SkDebugf("Failure writing android_app cmd: %s\n", strerror(errno)); + } +} + +void Window_android::inval() { + android_app_write_cmd(mApp, APP_CMD_INVAL_WINDOW); +} + +void Window_android::paintIfNeeded() { + if (mApp->window || !mContentRect.isEmpty()) { + this->onPaint(); + } +} + +/** + * Process the next main command. + */ +static void handle_cmd(struct android_app* app, int32_t cmd) { + Window_android* window = (Window_android*)app->userData; + switch (cmd) { + case APP_CMD_INIT_WINDOW: + // The window is being shown, get it ready. + SkASSERT(app->window); + window->initDisplay(app->window); + window->paintIfNeeded(); + break; + case APP_CMD_WINDOW_RESIZED: { + int width = ANativeWindow_getWidth(app->window); + int height = ANativeWindow_getHeight(app->window); + window->onResize(width, height); + break; + } + case APP_CMD_CONTENT_RECT_CHANGED: + window->setContentRect(app->contentRect.left, app->contentRect.top, + app->contentRect.right, app->contentRect.bottom); + window->paintIfNeeded(); + break; + case APP_CMD_TERM_WINDOW: + // The window is being hidden or closed, clean it up. + window->detach(); + break; + case APP_CMD_INVAL_WINDOW: + window->paintIfNeeded(); + break; + } +} + +static Window::Key get_key(int32_t keycode) { + static const struct { + int32_t fAndroidKey; + Window::Key fWindowKey; + } gPair[] = { + { AKEYCODE_BACK, Window::kBack_Key }, + { AKEYCODE_VOLUME_UP, Window::kLeft_Key }, + { AKEYCODE_VOLUME_DOWN, Window::kRight_Key } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidKey == keycode) { + return gPair[i].fWindowKey; + } + } + return Window::kNONE_Key; +} + +static Window::InputState get_action(int32_t action) { + static const struct { + int32_t fAndroidAction; + Window::InputState fInputState; + } gPair[] = { + { AKEY_STATE_DOWN, Window::kDown_InputState }, + { AKEY_STATE_UP, Window::kUp_InputState }, + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidAction == action) { + return gPair[i].fInputState; + } + } + return Window::kMove_InputState; +} + +static int32_t get_key_modifiers(AInputEvent* event) { + static const struct { + int32_t fAndroidState; + int32_t fWindowModifier; + } gPair[] = { + { AMETA_SHIFT_ON, Window::kShift_ModifierKey }, + { AMETA_CTRL_ON, Window::kControl_ModifierKey }, + }; + + int32_t metaState = AKeyEvent_getMetaState(event); + int32_t modifiers = 0; + + if (AKeyEvent_getRepeatCount(event) == 0) { + modifiers |= Window::kFirstPress_ModifierKey; + } + + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fAndroidState == metaState) { + modifiers |= gPair[i].fWindowModifier; + } + } + return modifiers; +} + +/** + * Process the next input event. + */ +static int32_t handle_input(struct android_app* app, AInputEvent* event) { + Window_android* window = (Window_android*)app->userData; + switch(AInputEvent_getType(event)) { + case AINPUT_EVENT_TYPE_MOTION: + break; + case AINPUT_EVENT_TYPE_KEY: + Window::Key key = get_key(AKeyEvent_getKeyCode(event)); + Window::InputState state = get_action(AKeyEvent_getAction(event)); + int32_t mod = get_key_modifiers(event); + window->onKey(key, state, mod); + return true; // eat all key events + } + return 0; +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/android/Window_android.h b/tools/viewer/sk_app/android/Window_android.h new file mode 100644 index 0000000000..d41f0a54c2 --- /dev/null +++ b/tools/viewer/sk_app/android/Window_android.h @@ -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. +*/ + +#ifndef Window_android_DEFINED +#define Window_android_DEFINED + +#include "../Window.h" +#include <android_native_app_glue.h> + +namespace sk_app { + +enum { + /** + * Leave plenty of space between this item and the ones defined in the glue layer + */ + APP_CMD_INVAL_WINDOW = 64, +}; + +class Window_android : public Window { +public: + Window_android() : Window() {} + ~Window_android() override {} + + bool init(android_app* app_state); + void initDisplay(ANativeWindow* window); + + void setTitle(const char*) override; + void show() override {} + + bool attach(BackEndType attachType, int msaaSampleCount, bool deepColor) override; + void inval() override; + + void paintIfNeeded(); + + bool scaleContentToFit() const override { return true; } + bool supportsContentRect() const override { return true; } + SkRect getContentRect() override { return mContentRect; } + void setContentRect(int l, int t, int r, int b) { mContentRect.set(l,t,r,b); } + +private: + android_app* mApp = nullptr; + SkRect mContentRect; + int mSampleCount = 0; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/android/main_android.cpp b/tools/viewer/sk_app/android/main_android.cpp new file mode 100644 index 0000000000..9334f0ccd3 --- /dev/null +++ b/tools/viewer/sk_app/android/main_android.cpp @@ -0,0 +1,69 @@ +/* +* 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 <jni.h> +#include <errno.h> + +#include <android_native_app_glue.h> + +#include "../Application.h" +#include "Timer.h" + +static double now_ms() { return SkTime::GetNSecs() * 1e-6; } + +using sk_app::Application; + +/** + * This is the main entry point of a native application that is using + * android_native_app_glue. It runs in its own thread, with its own + * event loop for receiving input events and doing other things. + */ +void android_main(struct android_app* state) { + // Make sure glue isn't stripped. + app_dummy(); + + static const char* gCmdLine[] = { + "viewer", + "--skps", + "/data/local/tmp/skp", + }; + + std::unique_ptr<Application> vkApp(Application::Create(SK_ARRAY_COUNT(gCmdLine), + const_cast<char**>(gCmdLine), + state)); + + double currentTime = 0.0; + double previousTime = 0.0; + + // loop waiting for stuff to do. + while (1) { + // Read all pending events. + int ident; + int events; + struct android_poll_source* source; + + // block forever waiting for events. + while ((ident=ALooper_pollAll(-1, NULL, &events, + (void**)&source)) >= 0) { + + // Process this event. + if (source != NULL) { + source->process(state, source); + } + + // Check if we are exiting. + if (state->destroyRequested != 0) { + return; + } + + previousTime = currentTime; + currentTime = now_ms(); + vkApp->onIdle(currentTime - previousTime); + } + } +} +//END_INCLUDE(all) diff --git a/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp b/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp new file mode 100644 index 0000000000..05f3bddd31 --- /dev/null +++ b/tools/viewer/sk_app/win/VulkanWindowContext_win.cpp @@ -0,0 +1,62 @@ + +/* + * 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 "VulkanWindowContext_win.h" + +#include "vk/GrVkInterface.h" +#include "vk/GrVkUtil.h" + +namespace sk_app { + +// Platform dependant call +VkSurfaceKHR VulkanWindowContext::createVkSurface(VkInstance instance, void* platformData) { + static PFN_vkCreateWin32SurfaceKHR createWin32SurfaceKHR = nullptr; + if (!createWin32SurfaceKHR) { + createWin32SurfaceKHR = (PFN_vkCreateWin32SurfaceKHR) vkGetInstanceProcAddr(instance, + "vkCreateWin32SurfaceKHR"); + } + + if (!platformData) { + return VK_NULL_HANDLE; + } + 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 = createWin32SurfaceKHR(instance, &surfaceCreateInfo, nullptr, &surface); + if (VK_SUCCESS != res) { + return VK_NULL_HANDLE; + } + + return surface; +} + +// Platform dependant call +bool VulkanWindowContext::canPresent(VkInstance instance, VkPhysicalDevice physDev, + uint32_t queueFamilyIndex) { + static PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR + getPhysicalDeviceWin32PresentationSupportKHR = nullptr; + if (!getPhysicalDeviceWin32PresentationSupportKHR) { + getPhysicalDeviceWin32PresentationSupportKHR = + (PFN_vkGetPhysicalDeviceWin32PresentationSupportKHR) vkGetInstanceProcAddr(instance, + "vkGetPhysicalDeviceWin32PresentationSupportKHR"); + } + + VkBool32 check = getPhysicalDeviceWin32PresentationSupportKHR(physDev, queueFamilyIndex); + return (VK_FALSE != check); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/win/VulkanWindowContext_win.h b/tools/viewer/sk_app/win/VulkanWindowContext_win.h new file mode 100644 index 0000000000..e0b5a156f9 --- /dev/null +++ b/tools/viewer/sk_app/win/VulkanWindowContext_win.h @@ -0,0 +1,28 @@ + +/* + * 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 "../VulkanWindowContext.h" + +namespace sk_app { + +// for Windows +struct ContextPlatformData_win { + HINSTANCE fHInstance; + HWND fHWnd; +}; + +} + +#endif // SK_VULKAN + +#endif diff --git a/tools/viewer/sk_app/win/Window_win.cpp b/tools/viewer/sk_app/win/Window_win.cpp new file mode 100644 index 0000000000..1dd07a6c9c --- /dev/null +++ b/tools/viewer/sk_app/win/Window_win.cpp @@ -0,0 +1,285 @@ +/* +* 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 "SkUtils.h" +#include "VulkanWindowContext_win.h" + +namespace sk_app { + +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; +} + +static Window::Key get_key(WPARAM vk) { + static const struct { + WPARAM fVK; + Window::Key fKey; + } gPair[] = { + { VK_BACK, Window::kBack_Key }, + { VK_CLEAR, Window::kBack_Key }, + { VK_RETURN, Window::kOK_Key }, + { VK_UP, Window::kUp_Key }, + { VK_DOWN, Window::kDown_Key }, + { VK_LEFT, Window::kLeft_Key }, + { VK_RIGHT, Window::kRight_Key } + }; + for (size_t i = 0; i < SK_ARRAY_COUNT(gPair); i++) { + if (gPair[i].fVK == vk) { + return gPair[i].fKey; + } + } + return Window::kNONE_Key; +} + +static uint32_t get_modifiers(UINT message, WPARAM wParam, LPARAM lParam) { + uint32_t modifiers = 0; + + switch (message) { + case WM_UNICHAR: + case WM_CHAR: + if (0 == (lParam & (1 << 30))) { + modifiers |= Window::kFirstPress_ModifierKey; + } + if (lParam & (1 << 29)) { + modifiers |= Window::kOption_ModifierKey; + } + break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + if (0 == (lParam & (1 << 30))) { + modifiers |= Window::kFirstPress_ModifierKey; + } + if (lParam & (1 << 29)) { + modifiers |= Window::kOption_ModifierKey; + } + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + if (lParam & (1 << 29)) { + modifiers |= Window::kOption_ModifierKey; + } + break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: + case WM_MOUSEMOVE: + if (wParam & MK_CONTROL) { + modifiers |= Window::kControl_ModifierKey; + } + if (wParam & MK_SHIFT) { + modifiers |= Window::kShift_ModifierKey; + } + } + + return modifiers; +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + PAINTSTRUCT ps; + HDC hdc; + + Window_win* window = (Window_win*) GetWindowLongPtr(hWnd, GWLP_USERDATA); + + bool eventHandled = false; + + switch (message) { + case WM_PAINT: + hdc = BeginPaint(hWnd, &ps); + window->onPaint(); + EndPaint(hWnd, &ps); + eventHandled = true; + break; + + case WM_CLOSE: + case WM_DESTROY: + PostQuitMessage(0); + eventHandled = true; + break; + + case WM_ACTIVATE: + // disable/enable rendering here, depending on wParam != WA_INACTIVE + break; + + case WM_SIZE: + window->onResize(LOWORD(lParam), HIWORD(lParam)); + eventHandled = true; + break; + + case WM_UNICHAR: + eventHandled = window->onChar((SkUnichar)wParam, + get_modifiers(message, wParam, lParam)); + break; + + case WM_CHAR: { + const uint16_t* c = reinterpret_cast<uint16_t*>(&wParam); + eventHandled = window->onChar(SkUTF16_NextUnichar(&c), + get_modifiers(message, wParam, lParam)); + } break; + + case WM_KEYDOWN: + case WM_SYSKEYDOWN: + eventHandled = window->onKey(get_key(wParam), Window::kDown_InputState, + get_modifiers(message, wParam, lParam)); + break; + + case WM_KEYUP: + case WM_SYSKEYUP: + eventHandled = window->onKey(get_key(wParam), Window::kUp_InputState, + get_modifiers(message, wParam, lParam)); + break; + + case WM_LBUTTONDOWN: + case WM_LBUTTONUP: { + 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; + //} + + Window::InputState istate = ((wParam & MK_LBUTTON) != 0) ? Window::kDown_InputState + : Window::kUp_InputState; + + eventHandled = window->onMouse(xPos, yPos, istate, + get_modifiers(message, wParam, lParam)); + } break; + + case WM_MOUSEMOVE: + // only track if left button is down + if ((wParam & MK_LBUTTON) != 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; + //} + + eventHandled = window->onMouse(xPos, yPos, Window::kMove_InputState, + get_modifiers(message, wParam, lParam)); + } + break; + + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + + return eventHandled ? 0 : 1; +} + +void Window_win::setTitle(const char* title) { + SetWindowTextA(fHWnd, title); +} + +void Window_win::show() { + ShowWindow(fHWnd, SW_SHOW); +} + + +bool Window_win::attach(BackEndType attachType, int msaaSampleCount) { + if (kVulkan_BackendType != attachType) { + return false; + } + + ContextPlatformData_win platformData; + platformData.fHInstance = fHInstance; + platformData.fHWnd = fHWnd; + + fWindowContext = VulkanWindowContext::Create((void*)&platformData, msaaSampleCount); + + return (SkToBool(fWindowContext)); +} + +void Window_win::inval() { + InvalidateRect(fHWnd, nullptr, false); +} + +} // namespace sk_app diff --git a/tools/viewer/sk_app/win/Window_win.h b/tools/viewer/sk_app/win/Window_win.h new file mode 100644 index 0000000000..e295212f31 --- /dev/null +++ b/tools/viewer/sk_app/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" + +namespace sk_app { + +class Window_win : public Window { +public: + Window_win() : Window() {} + ~Window_win() override {} + + bool init(HINSTANCE instance); + + void setTitle(const char*) override; + void show() override; + + bool attach(BackEndType attachType, int msaaSampleCount) override; + + void inval() override; + +private: + HINSTANCE fHInstance; + HWND fHWnd; +}; + +} // namespace sk_app + +#endif diff --git a/tools/viewer/sk_app/win/main_win.cpp b/tools/viewer/sk_app/win/main_win.cpp new file mode 100644 index 0000000000..ba1629c837 --- /dev/null +++ b/tools/viewer/sk_app/win/main_win.cpp @@ -0,0 +1,87 @@ +/* +* 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 "Timer.h" +#include "Window_win.h" +#include "../Application.h" + +using sk_app::Application; + +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 +} + +static double now_ms() { return SkTime::GetNSecs() * 1e-6; } + +// 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 }; + + double currentTime = 0.0; + double previousTime = 0.0; + + // Main message loop + while (WM_QUIT != msg.message) { + if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } else { + previousTime = currentTime; + currentTime = now_ms(); + app->onIdle(currentTime - previousTime); + } + } + + delete app; + + return (int)msg.wParam; +} |