/* * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "GraphiteVkRenderEngine.h" #undef LOG_TAG #define LOG_TAG "RenderEngine" #include #include #include #include #include #include #include #include namespace android::renderengine::skia { std::unique_ptr GraphiteVkRenderEngine::create( const RenderEngineCreationArgs& args) { std::unique_ptr engine(new GraphiteVkRenderEngine(args)); engine->ensureContextsCreated(); if (getVulkanInterface(false).isInitialized()) { ALOGD("GraphiteVkRenderEngine::%s: successfully initialized GraphiteVkRenderEngine", __func__); return engine; } else { ALOGE("GraphiteVkRenderEngine::%s: could not create GraphiteVkRenderEngine. " "Likely insufficient Vulkan support", __func__); return {}; } } // Graphite-specific function signature for fFinishedProc callback. static void unref_semaphore(void* semaphore, skgpu::CallbackResult result) { if (result != skgpu::CallbackResult::kSuccess) { ALOGE("Graphite submission of work to GPU failed, check for Skia errors"); } SkiaVkRenderEngine::DestroySemaphoreInfo* info = reinterpret_cast(semaphore); info->unref(); } std::unique_ptr GraphiteVkRenderEngine::createContext( VulkanInterface& vulkanInterface) { return SkiaGpuContext::MakeVulkan_Graphite(vulkanInterface.getGraphiteBackendContext()); } void GraphiteVkRenderEngine::waitFence(SkiaGpuContext*, base::borrowed_fd fenceFd) { if (fenceFd.get() < 0) return; int dupedFd = dup(fenceFd.get()); if (dupedFd < 0) { ALOGE("failed to create duplicate fence fd: %d", dupedFd); sync_wait(fenceFd.get(), -1); return; } base::unique_fd fenceDup(dupedFd); VkSemaphore waitSemaphore = getVulkanInterface(isProtected()).importSemaphoreFromSyncFd(fenceDup.release()); graphite::BackendSemaphore beSemaphore(waitSemaphore); mStagedWaitSemaphores.push_back(beSemaphore); } base::unique_fd GraphiteVkRenderEngine::flushAndSubmit(SkiaGpuContext* context, sk_sp) { // Minimal Recording setup. Required even if there are no incoming semaphores to wait on, and if // creating the outgoing signaling semaphore fails. std::unique_ptr recording = context->graphiteRecorder()->snap(); graphite::InsertRecordingInfo insertInfo; insertInfo.fRecording = recording.get(); VulkanInterface& vulkanInterface = getVulkanInterface(isProtected()); // This "signal" semaphore is called after rendering, but it is cleaned up in the same mechanism // as "wait" semaphores from waitFence. VkSemaphore vkSignalSemaphore = vulkanInterface.createExportableSemaphore(); graphite::BackendSemaphore backendSignalSemaphore(vkSignalSemaphore); // Collect all Vk semaphores that DestroySemaphoreInfo needs to own and delete after GPU work. std::vector vkSemaphoresToCleanUp; if (vkSignalSemaphore != VK_NULL_HANDLE) { vkSemaphoresToCleanUp.push_back(vkSignalSemaphore); } for (auto backendWaitSemaphore : mStagedWaitSemaphores) { vkSemaphoresToCleanUp.push_back(backendWaitSemaphore.getVkSemaphore()); } DestroySemaphoreInfo* destroySemaphoreInfo = nullptr; if (vkSemaphoresToCleanUp.size() > 0) { destroySemaphoreInfo = new DestroySemaphoreInfo(vulkanInterface, std::move(vkSemaphoresToCleanUp)); insertInfo.fNumWaitSemaphores = mStagedWaitSemaphores.size(); insertInfo.fWaitSemaphores = mStagedWaitSemaphores.data(); insertInfo.fNumSignalSemaphores = 1; insertInfo.fSignalSemaphores = &backendSignalSemaphore; insertInfo.fFinishedProc = unref_semaphore; insertInfo.fFinishedContext = destroySemaphoreInfo; } const bool inserted = context->graphiteContext()->insertRecording(insertInfo); LOG_ALWAYS_FATAL_IF(!inserted, "graphite::Context::insertRecording(...) failed, check for Skia errors"); const bool submitted = context->graphiteContext()->submit(graphite::SyncToCpu::kNo); LOG_ALWAYS_FATAL_IF(!submitted, "graphite::Context::submit(...) failed, check for Skia errors"); // Skia's "backend" semaphores can be deleted immediately after inserting the recording; only // the underlying VK semaphores need to be kept until GPU work is complete. mStagedWaitSemaphores.clear(); base::unique_fd drawFenceFd(-1); if (vkSignalSemaphore != VK_NULL_HANDLE) { drawFenceFd.reset(vulkanInterface.exportSemaphoreSyncFd(vkSignalSemaphore)); } // Now that drawFenceFd has been created, we can delete RE's reference to this semaphore, as // another reference is still held until fFinishedProc is called after completion of GPU work. if (destroySemaphoreInfo) { destroySemaphoreInfo->unref(); } return drawFenceFd; } } // namespace android::renderengine::skia