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