1 // Copyright (C) 2024 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 express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "VirtioGpuRingBlob.h"
16 
17 #include <string>
18 
19 #include "gfxstream/virtio-gpu-gfxstream-renderer.h"
20 
21 namespace gfxstream {
22 namespace host {
23 
24 using android::base::SharedMemory;
25 
RingBlob(uint32_t id,uint64_t size,uint64_t alignment,std::variant<std::unique_ptr<AlignedMemory>,std::unique_ptr<SharedMemory>> memory)26 RingBlob::RingBlob(uint32_t id,
27                    uint64_t size,
28                    uint64_t alignment,
29                    std::variant<std::unique_ptr<AlignedMemory>, std::unique_ptr<SharedMemory>> memory) :
30     mId(id), mSize(size), mAlignment(alignment), mMemory(std::move(memory)) {}
31 
isExportable() const32 bool RingBlob::isExportable() const {
33     return std::holds_alternative<std::unique_ptr<SharedMemory>>(mMemory);
34 }
35 
releaseHandle()36 android::base::SharedMemory::handle_type RingBlob::releaseHandle() {
37     if (!isExportable()) {
38         return SharedMemory::invalidHandle();
39     }
40     return std::get<std::unique_ptr<SharedMemory>>(mMemory)->releaseHandle();
41 }
42 
map()43 void* RingBlob::map() {
44     if (std::holds_alternative<std::unique_ptr<AlignedMemory>>(mMemory)) {
45         return std::get<std::unique_ptr<AlignedMemory>>(mMemory)->addr;
46     } else {
47         return std::get<std::unique_ptr<SharedMemory>>(mMemory)->get();
48     }
49 }
50 
51 /*static*/
CreateWithShmem(uint32_t id,uint64_t size)52 std::unique_ptr<RingBlob> RingBlob::CreateWithShmem(uint32_t id, uint64_t size) {
53     const std::string name = "gfxstream-ringblob-shmem-" + std::to_string(id);
54 
55     auto shmem = std::make_unique<SharedMemory>(name, size);
56     int ret = shmem->create(0600);
57     if (ret) {
58         stream_renderer_error("Failed to allocate ring blob shared memory.");
59         return nullptr;
60     }
61 
62     return std::unique_ptr<RingBlob>(new RingBlob(id, size, 1, std::move(shmem)));
63 }
64 
65 /*static*/
CreateWithHostMemory(uint32_t id,uint64_t size,uint64_t alignment)66 std::unique_ptr<RingBlob> RingBlob::CreateWithHostMemory(uint32_t id, uint64_t size, uint64_t alignment) {
67     auto memory = std::make_unique<AlignedMemory>(alignment, size);
68     if (memory->addr == nullptr) {
69         stream_renderer_error("Failed to allocate ring blob host memory.");
70         return nullptr;
71     }
72 
73     return std::unique_ptr<RingBlob>(new RingBlob(id, size, alignment, std::move(memory)));
74 }
75 
76 #ifdef GFXSTREAM_BUILD_WITH_SNAPSHOT_FRONTEND_SUPPORT
77 
78 using gfxstream::host::snapshot::VirtioGpuRingBlobSnapshot;
79 
Snapshot()80 std::optional<VirtioGpuRingBlobSnapshot> RingBlob::Snapshot() {
81     VirtioGpuRingBlobSnapshot snapshot;
82 
83     snapshot.set_id(mId);
84     snapshot.set_size(mSize);
85     snapshot.set_alignment(mAlignment);
86     if (std::holds_alternative<std::unique_ptr<SharedMemory>>(mMemory)) {
87         snapshot.set_type(VirtioGpuRingBlobSnapshot::TYPE_SHARED_MEMORY);
88     } else {
89         snapshot.set_type(VirtioGpuRingBlobSnapshot::TYPE_HOST_MEMORY);
90     }
91 
92     void* mapped = map();
93     if (!mapped) {
94         stream_renderer_error("Failed to map ring blob memory for snapshot.");
95         return std::nullopt;
96     }
97     snapshot.set_memory(mapped, mSize);
98 
99     return snapshot;
100 }
101 
Restore(const VirtioGpuRingBlobSnapshot & snapshot)102 /*static*/ std::optional<std::unique_ptr<RingBlob>> RingBlob::Restore(
103         const VirtioGpuRingBlobSnapshot& snapshot) {
104 
105     std::unique_ptr<RingBlob> resource;
106     if (snapshot.type() == VirtioGpuRingBlobSnapshot::TYPE_SHARED_MEMORY) {
107         resource = RingBlob::CreateWithShmem(snapshot.id(), snapshot.size());
108     } else {
109         resource = RingBlob::CreateWithHostMemory(snapshot.id(), snapshot.size(), snapshot.alignment());
110     }
111     if (!resource) {
112         return std::nullopt;
113     }
114 
115     void* mapped = resource->map();
116     if (!mapped) {
117         stream_renderer_error("Failed to map ring blob memory for restore.");
118         return std::nullopt;
119     }
120 
121     std::memcpy(mapped, snapshot.memory().c_str(), snapshot.memory().size());
122 
123     return resource;
124 }
125 
126 #endif
127 
128 }  // namespace host
129 }  // namespace gfxstream