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