xref: /aosp_15_r20/external/drm_hwcomposer/drm/DrmFbImporter.cpp (revision 0a9764fe0a15e71ebbeb85e87e10990c23aab47f)
1 /*
2  * Copyright (C) 2021 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 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
18 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
19 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
20 #define LOG_TAG "drmhwc"
21 
22 #include "DrmFbImporter.h"
23 
24 #include <hardware/gralloc.h>
25 #include <utils/Trace.h>
26 #include <xf86drm.h>
27 #include <xf86drmMode.h>
28 
29 #include <cinttypes>
30 #include <system_error>
31 
32 #include "utils/log.h"
33 #include "utils/properties.h"
34 
35 namespace android {
36 
CreateInstance(BufferInfo * bo,GemHandle first_gem_handle,DrmDevice & drm)37 auto DrmFbIdHandle::CreateInstance(BufferInfo *bo, GemHandle first_gem_handle,
38                                    DrmDevice &drm)
39     -> std::shared_ptr<DrmFbIdHandle> {
40   // NOLINTNEXTLINE(misc-const-correctness)
41   ATRACE_NAME("Import dmabufs and register FB");
42 
43   // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): priv. constructor usage
44   std::shared_ptr<DrmFbIdHandle> local(new DrmFbIdHandle(drm));
45 
46   local->gem_handles_[0] = first_gem_handle;
47   int32_t err = 0;
48 
49   /* Framebuffer object creation require gem handle for every used plane */
50   for (size_t i = 1; i < local->gem_handles_.size(); i++) {
51     if (bo->prime_fds[i] > 0) {
52       if (bo->prime_fds[i] != bo->prime_fds[0]) {
53         err = drmPrimeFDToHandle(*drm.GetFd(), bo->prime_fds[i],
54                                  &local->gem_handles_.at(i));
55         if (err != 0) {
56           ALOGE("failed to import prime fd %d errno=%d", bo->prime_fds[i],
57                 errno);
58         }
59       } else {
60         local->gem_handles_.at(i) = local->gem_handles_[0];
61       }
62     }
63   }
64 
65   auto has_modifiers = bo->modifiers[0] != DRM_FORMAT_MOD_NONE &&
66                        bo->modifiers[0] != DRM_FORMAT_MOD_INVALID;
67 
68   if (!drm.HasAddFb2ModifiersSupport() && has_modifiers) {
69     ALOGE("No ADDFB2 with modifier support. Can't import modifier %" PRIu64,
70           bo->modifiers[0]);
71     local.reset();
72     return local;
73   }
74 
75   /* Create framebuffer object */
76   if (!has_modifiers) {
77     err = drmModeAddFB2(*drm.GetFd(), bo->width, bo->height, bo->format,
78                         local->gem_handles_.data(), &bo->pitches[0],
79                         &bo->offsets[0], &local->fb_id_, 0);
80   } else {
81     err = drmModeAddFB2WithModifiers(*drm.GetFd(), bo->width, bo->height,
82                                      bo->format, local->gem_handles_.data(),
83                                      &bo->pitches[0], &bo->offsets[0],
84                                      &bo->modifiers[0], &local->fb_id_,
85                                      DRM_MODE_FB_MODIFIERS);
86   }
87   if (err != 0) {
88     ALOGE("could not create drm fb %d", err);
89     local.reset();
90   }
91 
92   return local;
93 }
94 
~DrmFbIdHandle()95 DrmFbIdHandle::~DrmFbIdHandle() {
96   // NOLINTNEXTLINE(misc-const-correctness)
97   ATRACE_NAME("Close FB and dmabufs");
98 
99   /* Destroy framebuffer object */
100   if (drmModeRmFB(*drm_fd_, fb_id_) != 0) {
101     ALOGE("Failed to rm fb");
102   }
103 
104   /* Close GEM handles.
105    *
106    * WARNING: TODO(nobody):
107    * From Linux side libweston relies on libgbm to get KMS handle and never
108    * closes it (handle is closed by libgbm on buffer destruction)
109    * Probably we should offer similar approach to users (at least on user
110    * request via system properties)
111    */
112   struct drm_gem_close gem_close {};
113   for (size_t i = 0; i < gem_handles_.size(); i++) {
114     /* Don't close invalid handle. Close handle only once in cases
115      * where several YUV planes located in the single buffer. */
116     if (gem_handles_[i] == 0 ||
117         (i != 0 && gem_handles_[i] == gem_handles_[0])) {
118       continue;
119     }
120     gem_close.handle = gem_handles_[i];
121     auto err = drmIoctl(*drm_fd_, DRM_IOCTL_GEM_CLOSE, &gem_close);
122     if (err != 0) {
123       ALOGE("Failed to close gem handle %d, errno: %d", gem_handles_[i], errno);
124     }
125   }
126 }
127 
GetOrCreateFbId(BufferInfo * bo)128 auto DrmFbImporter::GetOrCreateFbId(BufferInfo *bo)
129     -> std::shared_ptr<DrmFbIdHandle> {
130   /* TODO: Clean up DrmDevices and DrmFbImporter inter-dependency.
131    *
132    * DrmFbImporter can outlive DrmDevice which will cause issues when resources
133    * are released. Addressing this would require a restructure on how
134    * ResourceManager stores DrmDevices and DrmFbImporter to make sure they
135    * depend on each other. For now, just acquire the DRM fd from the DrmDevice
136    * to make sure it is not closed.
137    */
138   if (drm_fd_ == nullptr) {
139     drm_fd_ = drm_->GetFd();
140   }
141 
142   /* Lookup DrmFbIdHandle in cache first. First handle serves as a cache key. */
143   GemHandle first_handle = 0;
144   auto err = drmPrimeFDToHandle(*drm_fd_, bo->prime_fds[0], &first_handle);
145 
146   if (err != 0) {
147     ALOGE("Failed to import prime fd %d ret=%d", bo->prime_fds[0], err);
148     return {};
149   }
150 
151   auto drm_fb_id_cached = drm_fb_id_handle_cache_.find(first_handle);
152 
153   if (drm_fb_id_cached != drm_fb_id_handle_cache_.end()) {
154     if (auto drm_fb_id_handle_shared = drm_fb_id_cached->second.lock()) {
155       return drm_fb_id_handle_shared;
156     }
157     drm_fb_id_handle_cache_.erase(drm_fb_id_cached);
158   }
159 
160   /* Cleanup cached empty weak pointers */
161   const int minimal_cleanup_size = 128;
162   if (drm_fb_id_handle_cache_.size() > minimal_cleanup_size) {
163     CleanupEmptyCacheElements();
164   }
165 
166   /* No DrmFbIdHandle found in cache, create framebuffer object */
167   auto fb_id_handle = DrmFbIdHandle::CreateInstance(bo, first_handle, *drm_);
168   if (fb_id_handle) {
169     drm_fb_id_handle_cache_[first_handle] = fb_id_handle;
170   }
171 
172   return fb_id_handle;
173 }
174 
175 }  // namespace android
176