1 // Copyright 2019 The Dawn Authors 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 #ifndef DAWNWIRE_SERVER_OBJECTSTORAGE_H_ 16 #define DAWNWIRE_SERVER_OBJECTSTORAGE_H_ 17 18 #include "dawn_wire/WireCmd_autogen.h" 19 #include "dawn_wire/WireServer.h" 20 21 #include <algorithm> 22 #include <map> 23 #include <unordered_set> 24 25 namespace dawn_wire { namespace server { 26 27 struct DeviceInfo { 28 std::unordered_set<uint64_t> childObjectTypesAndIds; 29 Server* server; 30 ObjectHandle self; 31 }; 32 33 // Whether this object has been allocated, or reserved for async object creation. 34 // Used by the KnownObjects queries 35 enum class AllocationState : uint32_t { 36 Free, 37 Reserved, 38 Allocated, 39 }; 40 41 template <typename T> 42 struct ObjectDataBase { 43 // The backend-provided handle and generation to this object. 44 T handle; 45 uint32_t generation = 0; 46 47 AllocationState state; 48 49 // This points to an allocation that is owned by the device. 50 DeviceInfo* deviceInfo = nullptr; 51 }; 52 53 // Stores what the backend knows about the type. 54 template <typename T> 55 struct ObjectData : public ObjectDataBase<T> {}; 56 57 enum class BufferMapWriteState { Unmapped, Mapped, MapError }; 58 59 template <> 60 struct ObjectData<WGPUBuffer> : public ObjectDataBase<WGPUBuffer> { 61 // TODO(enga): Use a tagged pointer to save space. 62 std::unique_ptr<MemoryTransferService::ReadHandle> readHandle; 63 std::unique_ptr<MemoryTransferService::WriteHandle> writeHandle; 64 BufferMapWriteState mapWriteState = BufferMapWriteState::Unmapped; 65 WGPUBufferUsageFlags usage = WGPUBufferUsage_None; 66 // Indicate if writeHandle needs to be destroyed on unmap 67 bool mappedAtCreation = false; 68 }; 69 70 // Pack the ObjectType and ObjectId as a single value for storage in 71 // an std::unordered_set. This lets us avoid providing our own hash and 72 // equality comparison operators. 73 inline uint64_t PackObjectTypeAndId(ObjectType type, ObjectId id) { 74 static_assert(sizeof(ObjectType) * 8 <= 32, ""); 75 static_assert(sizeof(ObjectId) * 8 <= 32, ""); 76 return (static_cast<uint64_t>(type) << 32) + id; 77 } 78 79 inline std::pair<ObjectType, ObjectId> UnpackObjectTypeAndId(uint64_t payload) { 80 ObjectType type = static_cast<ObjectType>(payload >> 32); 81 ObjectId id = payload & 0xFFFFFFFF; 82 return std::make_pair(type, id); 83 } 84 85 template <> 86 struct ObjectData<WGPUDevice> : public ObjectDataBase<WGPUDevice> { 87 // Store |info| as a separate allocation so that its address does not move. 88 // The pointer to |info| is stored in device child objects. 89 std::unique_ptr<DeviceInfo> info = std::make_unique<DeviceInfo>(); 90 }; 91 92 // Keeps track of the mapping between client IDs and backend objects. 93 template <typename T> 94 class KnownObjects { 95 public: 96 using Data = ObjectData<T>; 97 98 KnownObjects() { 99 // Reserve ID 0 so that it can be used to represent nullptr for optional object values 100 // in the wire format. However don't tag it as allocated so that it is an error to ask 101 // KnownObjects for ID 0. 102 Data reservation; 103 reservation.handle = nullptr; 104 reservation.state = AllocationState::Free; 105 mKnown.push_back(std::move(reservation)); 106 } 107 108 // Get a backend objects for a given client ID. 109 // Returns nullptr if the ID hasn't previously been allocated. 110 const Data* Get(uint32_t id, AllocationState expected = AllocationState::Allocated) const { 111 if (id >= mKnown.size()) { 112 return nullptr; 113 } 114 115 const Data* data = &mKnown[id]; 116 117 if (data->state != expected) { 118 return nullptr; 119 } 120 121 return data; 122 } 123 Data* Get(uint32_t id, AllocationState expected = AllocationState::Allocated) { 124 if (id >= mKnown.size()) { 125 return nullptr; 126 } 127 128 Data* data = &mKnown[id]; 129 130 if (data->state != expected) { 131 return nullptr; 132 } 133 134 return data; 135 } 136 137 // Allocates the data for a given ID and returns it. 138 // Returns nullptr if the ID is already allocated, or too far ahead, or if ID is 0 (ID 0 is 139 // reserved for nullptr). Invalidates all the Data* 140 Data* Allocate(uint32_t id, AllocationState state = AllocationState::Allocated) { 141 if (id == 0 || id > mKnown.size()) { 142 return nullptr; 143 } 144 145 Data data; 146 data.state = state; 147 data.handle = nullptr; 148 149 if (id >= mKnown.size()) { 150 mKnown.push_back(std::move(data)); 151 return &mKnown.back(); 152 } 153 154 if (mKnown[id].state != AllocationState::Free) { 155 return nullptr; 156 } 157 158 mKnown[id] = std::move(data); 159 return &mKnown[id]; 160 } 161 162 // Marks an ID as deallocated 163 void Free(uint32_t id) { 164 ASSERT(id < mKnown.size()); 165 mKnown[id].state = AllocationState::Free; 166 } 167 168 std::vector<T> AcquireAllHandles() { 169 std::vector<T> objects; 170 for (Data& data : mKnown) { 171 if (data.state == AllocationState::Allocated && data.handle != nullptr) { 172 objects.push_back(data.handle); 173 data.state = AllocationState::Free; 174 data.handle = nullptr; 175 } 176 } 177 178 return objects; 179 } 180 181 std::vector<T> GetAllHandles() { 182 std::vector<T> objects; 183 for (Data& data : mKnown) { 184 if (data.state == AllocationState::Allocated && data.handle != nullptr) { 185 objects.push_back(data.handle); 186 } 187 } 188 189 return objects; 190 } 191 192 private: 193 std::vector<Data> mKnown; 194 }; 195 196 // ObjectIds are lost in deserialization. Store the ids of deserialized 197 // objects here so they can be used in command handlers. This is useful 198 // for creating ReturnWireCmds which contain client ids 199 template <typename T> 200 class ObjectIdLookupTable { 201 public: 202 void Store(T key, ObjectId id) { 203 mTable[key] = id; 204 } 205 206 // Return the cached ObjectId, or 0 (null handle) 207 ObjectId Get(T key) const { 208 const auto it = mTable.find(key); 209 if (it != mTable.end()) { 210 return it->second; 211 } 212 return 0; 213 } 214 215 void Remove(T key) { 216 auto it = mTable.find(key); 217 if (it != mTable.end()) { 218 mTable.erase(it); 219 } 220 } 221 222 private: 223 std::map<T, ObjectId> mTable; 224 }; 225 226 }} // namespace dawn_wire::server 227 228 #endif // DAWNWIRE_SERVER_OBJECTSTORAGE_H_ 229