xref: /aosp_15_r20/external/skia/example/VulkanBasic.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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