• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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