1 /*
2 * Copyright (C) 2024 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 #include <stdio.h>
18 #include <string.h>
19
20 #include <unistd.h>
21
22 #include <android-base/logging.h>
23
24 #include "ring_buffer_manager.h"
25
26 namespace cuttlefish {
27
28 namespace {
29 constexpr int kNumberOfRingBufferFrames = 3;
RingBufferMemorySize(int w,int h)30 inline int RingBufferMemorySize(int w, int h) {
31 return sizeof(DisplayRingBufferHeader) +
32 ((w * h * 4) * kNumberOfRingBufferFrames);
33 }
34 } // namespace
35
GetAddress()36 void* DisplayRingBuffer::GetAddress() { return addr_; };
37
Create(const std::string & name,int size)38 Result<std::unique_ptr<DisplayRingBuffer>> DisplayRingBuffer::Create(
39 const std::string& name, int size) {
40 void* addr = nullptr;
41
42 SharedFD sfd =
43 SharedFD::ShmOpen(name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR);
44
45 CF_EXPECTF(sfd->IsOpen(), "Display buffer create failed {}", sfd->StrError());
46
47 sfd->Truncate(size);
48
49 ScopedMMap smm = sfd->MMap(NULL, size, PROT_WRITE, MAP_SHARED, 0);
50 addr = smm.get();
51
52 return std::unique_ptr<DisplayRingBuffer>(
53 new DisplayRingBuffer(addr, name, true, std::move(smm)));
54 }
55
~DisplayRingBuffer()56 DisplayRingBuffer::~DisplayRingBuffer() {
57 // Only unlink if we are the owner of the buffer.
58 if (owned_) {
59 shm_unlink(name_.c_str());
60 }
61 }
62
63 // Allowing optional in the case the buffer doesn't yet exist.
ShmemGet(const std::string & name,int size)64 std::optional<std::unique_ptr<DisplayRingBuffer>> DisplayRingBuffer::ShmemGet(
65 const std::string& name, int size) {
66 void* addr = nullptr;
67 SharedFD sfd = SharedFD::ShmOpen(name, O_RDWR, S_IRUSR | S_IWUSR);
68 if (!sfd->IsOpen()) {
69 return std::nullopt;
70 }
71 ScopedMMap smm = sfd->MMap(NULL, size, PROT_WRITE, MAP_SHARED, 0);
72 addr = smm.get();
73
74 if (!addr) {
75 return std::nullopt;
76 }
77
78 return std::unique_ptr<DisplayRingBuffer>(
79 new DisplayRingBuffer(addr, name, false, std::move(smm)));
80 }
81
DisplayRingBuffer(void * addr,std::string name,bool owned,ScopedMMap shm)82 DisplayRingBuffer::DisplayRingBuffer(void* addr, std::string name, bool owned,
83 ScopedMMap shm)
84 : addr_(addr), name_(std::move(name)), owned_(owned), shm_(std::move(shm)) {
85 header_ = (DisplayRingBufferHeader*)addr;
86 }
87
WriteNextFrame(std::uint8_t * frame_data,int size)88 std::uint8_t* DisplayRingBuffer::WriteNextFrame(std::uint8_t* frame_data,
89 int size) {
90 int new_frame_index =
91 (header_->last_valid_frame_index_ + 1) % kNumberOfRingBufferFrames;
92
93 std::uint8_t* frame_memory_address =
94 ComputeFrameAddressForIndex(new_frame_index);
95 memcpy(frame_memory_address, frame_data, size);
96
97 header_->last_valid_frame_index_ = new_frame_index;
98 return frame_memory_address;
99 }
100
CurrentFrame()101 std::uint8_t* DisplayRingBuffer::CurrentFrame() {
102 return ComputeFrameAddressForIndex(header_->last_valid_frame_index_);
103 }
104
ComputeFrameAddressForIndex(std::uint32_t index)105 std::uint8_t* DisplayRingBuffer::ComputeFrameAddressForIndex(
106 std::uint32_t index) {
107 int frame_memory_index = (index * (header_->display_width_ *
108 header_->display_height_ * header_->bpp_));
109 return ((std::uint8_t*)addr_) + sizeof(DisplayRingBufferHeader) +
110 frame_memory_index;
111 }
112
set(std::uint32_t w,std::uint32_t h,std::uint32_t bpp,std::uint32_t index)113 void DisplayRingBufferHeader::set(std::uint32_t w, std::uint32_t h,
114 std::uint32_t bpp, std::uint32_t index) {
115 display_width_ = w;
116 display_height_ = h;
117 bpp_ = bpp;
118 last_valid_frame_index_.store(index);
119 }
120
DisplayRingBufferManager(int vm_index,std::string group_uuid)121 DisplayRingBufferManager::DisplayRingBufferManager(int vm_index,
122 std::string group_uuid)
123 : local_group_index_(vm_index), group_uuid_(group_uuid) {}
124
CreateLocalDisplayBuffer(int vm_index,int display_index,int display_width,int display_height)125 Result<void> DisplayRingBufferManager::CreateLocalDisplayBuffer(
126 int vm_index, int display_index, int display_width, int display_height) {
127 auto buffer_key = std::make_pair(vm_index, display_index);
128
129 if (!display_buffer_cache_.contains(buffer_key)) {
130 std::string shmem_name = MakeLayerName(display_index);
131
132 auto shm_buffer = CF_EXPECT(DisplayRingBuffer::Create(
133 shmem_name, RingBufferMemorySize(display_width, display_height)));
134 std::uint8_t* shmem_local_display = (std::uint8_t*)shm_buffer->GetAddress();
135
136 // Here we coerce the IPC buffer into having a header with metadata
137 // containing DisplayRingBufferHeader struct. Then copy the values over
138 // so that the metadata is initialized correctly. This allows any process
139 // to remotely understand the ringbuffer state properly, to obtain the size
140 // and compute valid frame addresses for reading / writing frame data.
141 DisplayRingBufferHeader* dbi =
142 (DisplayRingBufferHeader*)shmem_local_display;
143 dbi->set(display_width, display_height, 4, 0);
144
145 display_buffer_cache_[buffer_key] = std::move(shm_buffer);
146 }
147 return {};
148 }
149
WriteFrame(int vm_index,int display_index,std::uint8_t * frame_data,int size)150 std::uint8_t* DisplayRingBufferManager::WriteFrame(int vm_index,
151 int display_index,
152 std::uint8_t* frame_data,
153 int size) {
154 auto buffer_key = std::make_pair(vm_index, display_index);
155 if (display_buffer_cache_.contains(buffer_key)) {
156 return display_buffer_cache_[buffer_key]->WriteNextFrame(frame_data, size);
157 }
158 // It's possible to request a write to buffer that doesn't yet exist.
159 return nullptr;
160 }
161
ReadFrame(int vm_index,int display_index,int frame_width,int frame_height)162 std::uint8_t* DisplayRingBufferManager::ReadFrame(int vm_index,
163 int display_index,
164 int frame_width,
165 int frame_height) {
166 auto buffer_key = std::make_pair(vm_index, display_index);
167
168 // If this buffer was read successfully in the past, that valid pointer is
169 // returned from the cache
170 if (!display_buffer_cache_.contains(buffer_key)) {
171 // Since no cache found, next step is to request from OS to map a new IPC
172 // buffer. It may not yet exist so we want this method to only cache if it
173 // is a non-null pointer, to retrigger this logic continually every request.
174 // Once the buffer exists the pointer would become non-null
175
176 std::string shmem_name = MakeLayerName(display_index, vm_index);
177 std::optional<std::unique_ptr<DisplayRingBuffer>> shmem_buffer =
178 DisplayRingBuffer::ShmemGet(
179 shmem_name.c_str(),
180 RingBufferMemorySize(frame_width, frame_height));
181
182 if (shmem_buffer.has_value() && shmem_buffer.value()->GetAddress()) {
183 display_buffer_cache_[buffer_key] = std::move(shmem_buffer.value());
184 } else {
185 return nullptr;
186 }
187 }
188
189 return display_buffer_cache_[buffer_key]->CurrentFrame();
190 }
191
MakeLayerName(int display_index,int vm_index)192 std::string DisplayRingBufferManager::MakeLayerName(int display_index,
193 int vm_index) {
194 if (vm_index == -1) {
195 vm_index = local_group_index_;
196 }
197 return std::format("/cf_shmem_display_{}_{}_{}", vm_index, display_index,
198 group_uuid_);
199 }
200
201 } // end namespace cuttlefish