1 /*
2 * Copyright 2022 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 // #define LOG_NDEBUG 0
18 #undef LOG_TAG
19 #define LOG_TAG "RenderEngine"
20 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
21
22 #include "SkiaVkRenderEngine.h"
23
24 #include "GaneshVkRenderEngine.h"
25 #include "compat/SkiaGpuContext.h"
26
27 #include <include/gpu/ganesh/GrBackendSemaphore.h>
28 #include <include/gpu/ganesh/GrContextOptions.h>
29 #include <include/gpu/ganesh/GrDirectContext.h>
30 #include <include/gpu/ganesh/vk/GrVkBackendSemaphore.h>
31 #include <include/gpu/ganesh/vk/GrVkDirectContext.h>
32 #include <include/gpu/ganesh/vk/GrVkTypes.h>
33
34 #include <android-base/stringprintf.h>
35 #include <common/trace.h>
36 #include <sync/sync.h>
37
38 #include <memory>
39 #include <string>
40
41 #include <vulkan/vulkan.h>
42 #include "log/log_main.h"
43
44 namespace android {
45 namespace renderengine {
46
47 static skia::VulkanInterface sVulkanInterface;
48 static skia::VulkanInterface sProtectedContentVulkanInterface;
49
sSetupVulkanInterface()50 static void sSetupVulkanInterface() {
51 if (!sVulkanInterface.isInitialized()) {
52 sVulkanInterface.init(false /* no protected content */);
53 // We will have to abort if non-protected VkDevice creation fails (then nothing works).
54 LOG_ALWAYS_FATAL_IF(!sVulkanInterface.isInitialized(),
55 "Could not initialize Vulkan RenderEngine!");
56 }
57 if (!sProtectedContentVulkanInterface.isInitialized()) {
58 sProtectedContentVulkanInterface.init(true /* protected content */);
59 if (!sProtectedContentVulkanInterface.isInitialized()) {
60 ALOGE("Could not initialize protected content Vulkan RenderEngine.");
61 }
62 }
63 }
64
canSupport(GraphicsApi graphicsApi)65 bool RenderEngine::canSupport(GraphicsApi graphicsApi) {
66 switch (graphicsApi) {
67 case GraphicsApi::GL:
68 return true;
69 case GraphicsApi::VK: {
70 // Static local variables are initialized once, on first invocation of the function.
71 static const bool canSupportVulkan = []() {
72 if (!sVulkanInterface.isInitialized()) {
73 sVulkanInterface.init(false /* no protected content */);
74 ALOGD("%s: initialized == %s.", __func__,
75 sVulkanInterface.isInitialized() ? "true" : "false");
76 if (!sVulkanInterface.isInitialized()) {
77 sVulkanInterface.teardown();
78 return false;
79 }
80 }
81 return true;
82 }();
83 return canSupportVulkan;
84 }
85 }
86 }
87
teardown(GraphicsApi graphicsApi)88 void RenderEngine::teardown(GraphicsApi graphicsApi) {
89 switch (graphicsApi) {
90 case GraphicsApi::GL:
91 break;
92 case GraphicsApi::VK: {
93 if (sVulkanInterface.isInitialized()) {
94 sVulkanInterface.teardown();
95 ALOGD("Tearing down the unprotected VulkanInterface.");
96 }
97 if (sProtectedContentVulkanInterface.isInitialized()) {
98 sProtectedContentVulkanInterface.teardown();
99 ALOGD("Tearing down the protected VulkanInterface.");
100 }
101 break;
102 }
103 }
104 }
105
106 namespace skia {
107
108 using base::StringAppendF;
109
SkiaVkRenderEngine(const RenderEngineCreationArgs & args)110 SkiaVkRenderEngine::SkiaVkRenderEngine(const RenderEngineCreationArgs& args)
111 : SkiaRenderEngine(args.threaded, static_cast<PixelFormat>(args.pixelFormat),
112 args.blurAlgorithm) {}
113
~SkiaVkRenderEngine()114 SkiaVkRenderEngine::~SkiaVkRenderEngine() {
115 finishRenderingAndAbandonContexts();
116 // Teardown VulkanInterfaces after Skia contexts have been abandoned
117 teardown(GraphicsApi::VK);
118 }
119
createContexts()120 SkiaRenderEngine::Contexts SkiaVkRenderEngine::createContexts() {
121 sSetupVulkanInterface();
122 // More work would need to be done in order to have multiple RenderEngine instances. In
123 // particular, they would not be able to share the same VulkanInterface(s).
124 LOG_ALWAYS_FATAL_IF(!sVulkanInterface.takeOwnership(),
125 "SkiaVkRenderEngine couldn't take ownership of existing unprotected "
126 "VulkanInterface! Only one SkiaVkRenderEngine instance may exist at a "
127 "time.");
128 if (sProtectedContentVulkanInterface.isInitialized()) {
129 // takeOwnership fails on an uninitialized VulkanInterface, but protected content support is
130 // optional.
131 LOG_ALWAYS_FATAL_IF(!sProtectedContentVulkanInterface.takeOwnership(),
132 "SkiaVkRenderEngine couldn't take ownership of existing protected "
133 "VulkanInterface! Only one SkiaVkRenderEngine instance may exist at a "
134 "time.");
135 }
136
137 SkiaRenderEngine::Contexts contexts;
138 contexts.first = createContext(sVulkanInterface);
139 if (supportsProtectedContentImpl()) {
140 contexts.second = createContext(sProtectedContentVulkanInterface);
141 }
142
143 return contexts;
144 }
145
supportsProtectedContentImpl() const146 bool SkiaVkRenderEngine::supportsProtectedContentImpl() const {
147 return sProtectedContentVulkanInterface.isInitialized();
148 }
149
useProtectedContextImpl(GrProtected)150 bool SkiaVkRenderEngine::useProtectedContextImpl(GrProtected) {
151 return true;
152 }
153
getVulkanInterface(bool protectedContext)154 VulkanInterface& SkiaVkRenderEngine::getVulkanInterface(bool protectedContext) {
155 if (protectedContext) {
156 return sProtectedContentVulkanInterface;
157 }
158 return sVulkanInterface;
159 }
160
getContextPriority()161 int SkiaVkRenderEngine::getContextPriority() {
162 // EGL_CONTEXT_PRIORITY_REALTIME_NV
163 constexpr int kRealtimePriority = 0x3357;
164 if (getVulkanInterface(isProtected()).isRealtimePriority()) {
165 return kRealtimePriority;
166 } else {
167 return 0;
168 }
169 }
170
appendBackendSpecificInfoToDump(std::string & result)171 void SkiaVkRenderEngine::appendBackendSpecificInfoToDump(std::string& result) {
172 // Subclasses will prepend a backend-specific name / section header
173 StringAppendF(&result, "Vulkan device initialized: %d\n", sVulkanInterface.isInitialized());
174 StringAppendF(&result, "Vulkan protected device initialized: %d\n",
175 sProtectedContentVulkanInterface.isInitialized());
176
177 if (!sVulkanInterface.isInitialized()) {
178 return;
179 }
180
181 StringAppendF(&result, "Instance extensions: [\n");
182 for (const auto& name : sVulkanInterface.getInstanceExtensionNames()) {
183 StringAppendF(&result, " %s\n", name.c_str());
184 }
185 StringAppendF(&result, "]\n");
186
187 StringAppendF(&result, "Device extensions: [\n");
188 for (const auto& name : sVulkanInterface.getDeviceExtensionNames()) {
189 StringAppendF(&result, " %s\n", name.c_str());
190 }
191 StringAppendF(&result, "]\n");
192 }
193
194 } // namespace skia
195 } // namespace renderengine
196 } // namespace android
197