1 /*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkAlphaType.h"
9 #include "include/core/SkCanvas.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorType.h"
12 #include "include/core/SkImageInfo.h"
13 #include "include/core/SkRefCnt.h"
14 #include "include/core/SkSurface.h"
15 #include "include/core/SkTypes.h"
16 #include "include/gpu/GrDirectContext.h"
17 #include "include/gpu/vk/GrVkBackendContext.h"
18 #include "include/gpu/vk/VulkanExtensions.h"
19 #include "tools/gpu/vk/VkTestUtils.h"
20
21 #include <string.h>
22 #include <vulkan/vulkan_core.h>
23 #include <functional>
24 #include <memory>
25
26 #define ACQUIRE_INST_VK_PROC(name) \
27 do { \
28 fVk##name = reinterpret_cast<PFN_vk##name>(getProc("vk" #name, backendContext.fInstance, \
29 VK_NULL_HANDLE)); \
30 if (fVk##name == nullptr) { \
31 SkDebugf("Function ptr for vk%s could not be acquired\n", #name); \
32 return 1; \
33 } \
34 } while(false)
35
main(int argc,char ** argv)36 int main(int argc, char** argv) {
37 GrVkBackendContext backendContext;
38 VkDebugReportCallbackEXT debugCallback;
39 std::unique_ptr<skgpu::VulkanExtensions> extensions(new skgpu::VulkanExtensions());
40 std::unique_ptr<VkPhysicalDeviceFeatures2> features(new VkPhysicalDeviceFeatures2);
41
42 // First we need to create a GrVkBackendContext so that we can make a Vulkan GrDirectContext.
43 // The vast majority of this chunk of code is setting up the VkInstance and VkDevice objects.
44 // Normally a client will have their own way of creating these objects. This example uses Skia's
45 // test helper sk_gpu_test::CreateVkBackendContext to aid in this. Clients can look at this
46 // function as a guide on things to consider when setting up Vulkan for themselves, but they
47 // should not depend on that function. We may arbitrarily change it as it is meant only for Skia
48 // internal testing. Additionally it may do some odd things that a normal Vulkan user wouldn't
49 // do because it is againt meant for Skia testing.
50 {
51 PFN_vkGetInstanceProcAddr instProc;
52 if (!sk_gpu_test::LoadVkLibraryAndGetProcAddrFuncs(&instProc)) {
53 return 1;
54 }
55
56 memset(features.get(), 0, sizeof(VkPhysicalDeviceFeatures2));
57 features->sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
58 features->pNext = nullptr;
59 // Fill in features you want to enable here
60
61 backendContext.fInstance = VK_NULL_HANDLE;
62 backendContext.fDevice = VK_NULL_HANDLE;
63
64 if (!sk_gpu_test::CreateVkBackendContext(instProc, &backendContext, extensions.get(),
65 features.get(), &debugCallback)) {
66 return 1;
67 }
68 }
69
70 auto getProc = backendContext.fGetProc;
71 PFN_vkDestroyInstance fVkDestroyInstance;
72 PFN_vkDestroyDebugReportCallbackEXT fVkDestroyDebugReportCallbackEXT = nullptr;
73 PFN_vkDestroyDevice fVkDestroyDevice;
74 ACQUIRE_INST_VK_PROC(DestroyInstance);
75 if (debugCallback != VK_NULL_HANDLE) {
76 ACQUIRE_INST_VK_PROC(DestroyDebugReportCallbackEXT);
77 }
78 ACQUIRE_INST_VK_PROC(DestroyDevice);
79
80 // Create a GrDirectContext with our GrVkBackendContext
81 sk_sp<GrDirectContext> context = GrDirectContext::MakeVulkan(backendContext);
82 if (!context) {
83 fVkDestroyDevice(backendContext.fDevice, nullptr);
84 if (debugCallback != VK_NULL_HANDLE) {
85 fVkDestroyDebugReportCallbackEXT(backendContext.fInstance, debugCallback, nullptr);
86 }
87 fVkDestroyInstance(backendContext.fInstance, nullptr);
88 return 1;
89 }
90
91 SkImageInfo imageInfo = SkImageInfo::Make(16, 16, kRGBA_8888_SkColorType, kPremul_SkAlphaType);
92
93 // Create an SkSurface backed by a Vulkan VkImage. Often clients will be getting VkImages from
94 // swapchains. In those cases they should use SkSurface::MakeFromBackendTexture or
95 // SkSurface::MakeFromBackendRenderTarget to wrap those premade VkImages in Skia. See the
96 // HelloWorld example app to see how this is done.
97 sk_sp<SkSurface> surface =
98 SkSurface::MakeRenderTarget(context.get(), skgpu::Budgeted::kYes, imageInfo);
99 if (!surface) {
100 context.reset();
101 fVkDestroyDevice(backendContext.fDevice, nullptr);
102 if (debugCallback != VK_NULL_HANDLE) {
103 fVkDestroyDebugReportCallbackEXT(backendContext.fInstance, debugCallback, nullptr);
104 } fVkDestroyInstance(backendContext.fInstance, nullptr);
105 return 1;
106 }
107
108 surface->getCanvas()->clear(SK_ColorRED);
109
110 // After drawing to our surface, we must first flush the recorded work (i.e. convert all our
111 // recorded SkCanvas calls into a VkCommandBuffer). Then we call submit to submit our
112 // VkCommandBuffers to the gpu queue.
113 surface->flush();
114 context->submit();
115
116 surface.reset();
117 context.reset();
118
119 // Skia doesn't own the VkDevice or VkInstance so the client must manage their lifetime. The
120 // client must not delete these objects until cleaning up all Skia objects that may have used
121 // them first.
122 fVkDestroyDevice(backendContext.fDevice, nullptr);
123 if (debugCallback != VK_NULL_HANDLE) {
124 fVkDestroyDebugReportCallbackEXT(backendContext.fInstance, debugCallback, nullptr);
125 } fVkDestroyInstance(backendContext.fInstance, nullptr);
126 return 0;
127 }
128