• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2023 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expresso or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "GrallocMinigbm.h"
16 
17 #include <cinttypes>
18 #include <cstring>
19 #include <stdlib.h>
20 
21 #include <cros_gralloc/cros_gralloc_handle.h>
22 #include <errno.h>
23 #include <log/log.h>
24 #include <sys/user.h>
25 #include <xf86drm.h>
26 #include <vndk/hardware_buffer.h>
27 
28 #include "virtgpu_drm.h"
29 
30 #if defined(PAGE_SIZE)
31 constexpr size_t kPageSize = PAGE_SIZE;
32 #else
33 #include <unistd.h>
34 static const size_t kPageSize = getpagesize();
35 #endif
36 
37 
38 namespace gfxstream {
39 namespace {
40 
align_up(uint32_t n,uint32_t a)41 static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; }
42 
getVirtioGpuResourceInfo(int fd,native_handle_t const * handle,struct drm_virtgpu_resource_info * info)43 bool getVirtioGpuResourceInfo(int fd, native_handle_t const* handle,
44                               struct drm_virtgpu_resource_info* info) {
45     memset(info, 0x0, sizeof(*info));
46     if (fd < 0) {
47         ALOGE("%s: Error, rendernode fd missing\n", __func__);
48         return false;
49     }
50 
51     struct drm_gem_close gem_close;
52     memset(&gem_close, 0x0, sizeof(gem_close));
53 
54     cros_gralloc_handle const* cros_handle = reinterpret_cast<cros_gralloc_handle const*>(handle);
55 
56     uint32_t prime_handle;
57     int ret = drmPrimeFDToHandle(fd, cros_handle->fds[0], &prime_handle);
58     if (ret) {
59         ALOGE("%s: DRM_IOCTL_PRIME_FD_TO_HANDLE failed: %s (errno %d)\n", __func__, strerror(errno),
60               errno);
61         return false;
62     }
63     struct ManagedDrmGem {
64         ManagedDrmGem(int fd, uint32_t handle)
65             : m_fd(fd), m_prime_handle(handle) {}
66         ManagedDrmGem(const ManagedDrmGem&) = delete;
67         ~ManagedDrmGem() {
68             struct drm_gem_close gem_close {
69                 .handle = m_prime_handle, .pad = 0,
70             };
71             int ret = drmIoctl(m_fd, DRM_IOCTL_GEM_CLOSE, &gem_close);
72             if (ret) {
73                 ALOGE("%s: DRM_IOCTL_GEM_CLOSE failed on handle %" PRIu32 ": %s(%d).", __func__,
74                       m_prime_handle, strerror(errno), errno);
75             }
76         }
77 
78         int m_fd;
79         uint32_t m_prime_handle;
80     } managed_prime_handle(fd, prime_handle);
81 
82     info->bo_handle = managed_prime_handle.m_prime_handle;
83 
84     struct drm_virtgpu_3d_wait virtgpuWait {
85         .handle = managed_prime_handle.m_prime_handle, .flags = 0,
86     };
87     // This only works for host resources by VIRTGPU_RESOURCE_CREATE ioctl.
88     // We need to use a different mechanism to synchronize with the host if
89     // the minigbm gralloc swiches to virtio-gpu blobs or cross-domain
90     // backend.
91     int retry = 0;
92     do {
93         if (retry > 10) {
94             ALOGE("%s DRM_IOCTL_VIRTGPU_WAIT failed with EBUSY %d times.", __func__, retry);
95             return false;
96         }
97         ret = drmIoctl(fd, DRM_IOCTL_VIRTGPU_WAIT, &virtgpuWait);
98         ++retry;
99     } while (ret < 0 && errno == EBUSY);
100     if (ret) {
101         ALOGE("%s: DRM_IOCTL_VIRTGPU_WAIT failed: %s(%d)", __func__, strerror(errno), errno);
102         return false;
103     }
104 
105     ret = drmIoctl(fd, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, info);
106     if (ret) {
107         ALOGE("%s: DRM_IOCTL_VIRTGPU_RESOURCE_INFO failed: %s (errno %d)\n", __func__,
108               strerror(errno), errno);
109         return false;
110     }
111 
112     return true;
113 }
114 
115 }  // namespace
116 
createColorBuffer(void *,int width,int height,uint32_t glformat)117 uint32_t MinigbmGralloc::createColorBuffer(void*, int width, int height,
118                                            uint32_t glformat) {
119     // Only supported format for pbuffers in gfxstream should be RGBA8
120     const uint32_t kVirglFormatRGBA = 67;  // VIRGL_FORMAT_R8G8B8A8_UNORM;
121     uint32_t virtgpu_format = 0;
122     uint32_t bpp = 0;
123     switch (glformat) {
124         case kGlRGB:
125             ALOGV("Note: egl wanted GL_RGB, still using RGBA");
126             virtgpu_format = kVirglFormatRGBA;
127             bpp = 4;
128             break;
129         case kGlRGBA:
130             virtgpu_format = kVirglFormatRGBA;
131             bpp = 4;
132             break;
133         default:
134             ALOGV("Note: egl wanted 0x%x, still using RGBA", glformat);
135             virtgpu_format = kVirglFormatRGBA;
136             bpp = 4;
137             break;
138     }
139     const uint32_t kPipeTexture2D = 2;          // PIPE_TEXTURE_2D
140     const uint32_t kBindRenderTarget = 1 << 1;  // VIRGL_BIND_RENDER_TARGET
141     struct drm_virtgpu_resource_create res_create;
142     memset(&res_create, 0, sizeof(res_create));
143     res_create.target = kPipeTexture2D;
144     res_create.format = virtgpu_format;
145     res_create.bind = kBindRenderTarget;
146     res_create.width = width;
147     res_create.height = height;
148     res_create.depth = 1;
149     res_create.array_size = 1;
150     res_create.last_level = 0;
151     res_create.nr_samples = 0;
152     res_create.stride = bpp * width;
153     res_create.size = align_up(bpp * width * height, kPageSize);
154 
155     int ret = drmIoctl(m_fd, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE, &res_create);
156     if (ret) {
157         ALOGE("%s: DRM_IOCTL_VIRTGPU_RESOURCE_CREATE failed with %s (%d)\n", __func__,
158               strerror(errno), errno);
159         abort();
160     }
161 
162     return res_create.res_handle;
163 }
164 
allocate(uint32_t width,uint32_t height,uint32_t format,uint64_t usage,AHardwareBuffer ** outputAhb)165 int MinigbmGralloc::allocate(uint32_t width,
166                              uint32_t height,
167                              uint32_t format,
168                              uint64_t usage,
169                              AHardwareBuffer** outputAhb) {
170 
171     struct AHardwareBuffer_Desc desc = {
172         .width = width,
173         .height = height,
174         .layers = 1,
175         .format = format,
176         .usage = usage,
177     };
178 
179     return AHardwareBuffer_allocate(&desc, outputAhb);
180 }
181 
acquire(AHardwareBuffer * ahb)182 void MinigbmGralloc::acquire(AHardwareBuffer* ahb) {
183     AHardwareBuffer_acquire(ahb);
184 }
185 
release(AHardwareBuffer * ahb)186 void MinigbmGralloc::release(AHardwareBuffer* ahb) {
187     AHardwareBuffer_release(ahb);
188 }
189 
getHostHandle(const native_handle_t * handle)190 uint32_t MinigbmGralloc::getHostHandle(const native_handle_t* handle) {
191     struct drm_virtgpu_resource_info info;
192     if (!getVirtioGpuResourceInfo(m_fd, handle, &info)) {
193         ALOGE("%s: failed to get resource info\n", __func__);
194         return 0;
195     }
196 
197     return info.res_handle;
198 }
199 
getHostHandle(const AHardwareBuffer * ahb)200 uint32_t MinigbmGralloc::getHostHandle(const AHardwareBuffer* ahb) {
201     const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
202     return getHostHandle(handle);
203 }
204 
getFormat(const native_handle_t * handle)205 int MinigbmGralloc::getFormat(const native_handle_t* handle) {
206     return ((cros_gralloc_handle*)handle)->droid_format;
207 }
208 
getFormat(const AHardwareBuffer * ahb)209 int MinigbmGralloc::getFormat(const AHardwareBuffer* ahb) {
210     const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
211 
212     return ((cros_gralloc_handle*)handle)->droid_format;
213 }
214 
getFormatDrmFourcc(const native_handle_t * handle)215 uint32_t MinigbmGralloc::getFormatDrmFourcc(const native_handle_t* handle) {
216     return ((cros_gralloc_handle*)handle)->format;
217 }
218 
getFormatDrmFourcc(const AHardwareBuffer * ahb)219 uint32_t MinigbmGralloc::getFormatDrmFourcc(const AHardwareBuffer* ahb) {
220     const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
221     return getFormatDrmFourcc(handle);
222 }
223 
getAllocatedSize(const native_handle_t * handle)224 size_t MinigbmGralloc::getAllocatedSize(const native_handle_t* handle) {
225     struct drm_virtgpu_resource_info info;
226     if (!getVirtioGpuResourceInfo(m_fd, handle, &info)) {
227         ALOGE("%s: failed to get resource info\n", __func__);
228         return 0;
229     }
230     return info.size;
231 }
232 
getAllocatedSize(const AHardwareBuffer * ahb)233 size_t MinigbmGralloc::getAllocatedSize(const AHardwareBuffer* ahb) {
234     const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
235     return getAllocatedSize(handle);
236 }
237 
238 }  // namespace gfxstream
239