1 /*
2 * Copyright (C) 2021 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 #include "CommandBufferStagingStream.h"
17
18 #include <errno.h>
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23
24 #include <atomic>
25 #include <vector>
26
27 #include "util/log.h"
28
29 static const size_t kReadSize = 512 * 1024;
30 static const size_t kWriteOffset = kReadSize;
31
32 namespace gfxstream {
33 namespace vk {
34
CommandBufferStagingStream()35 CommandBufferStagingStream::CommandBufferStagingStream()
36 : IOStream(1048576), m_size(0), m_writePos(0) {
37 // use default allocators
38 m_alloc = [](size_t size) -> Memory {
39 return {
40 .deviceMemory = VK_NULL_HANDLE, // no device memory for malloc
41 .ptr = malloc(size),
42 };
43 };
44 m_free = [](const Memory& mem) { free(mem.ptr); };
45 m_realloc = [](const Memory& mem, size_t size) -> Memory {
46 return {.deviceMemory = VK_NULL_HANDLE, .ptr = realloc(mem.ptr, size)};
47 };
48 }
49
CommandBufferStagingStream(const Alloc & allocFn,const Free & freeFn)50 CommandBufferStagingStream::CommandBufferStagingStream(const Alloc& allocFn, const Free& freeFn)
51 : CommandBufferStagingStream() {
52 m_usingCustomAlloc = true;
53 // for custom allocation, allocate metadata memory at the beginning.
54 // m_alloc, m_free and m_realloc wraps sync data logic
55
56 // \param size to allocate
57 // \return ptr starting at data
58 m_alloc = [&allocFn](size_t size) -> Memory {
59 // allocation requested size + sync data size
60
61 // <---sync bytes--><----Data--->
62 // |———————————————|————————————|
63 // |0|1|2|3|4|5|6|7|............|
64 // |———————————————|————————————|
65 // ꜛ ꜛ
66 // allocated ptr ptr to data [dataPtr]
67
68 Memory memory;
69 if (!allocFn) {
70 mesa_loge("Custom allocation (%zu bytes) failed\n", size);
71 return memory;
72 }
73
74 // custom allocation/free requires metadata for sync between host/guest
75 const size_t totalSize = size + kSyncDataSize;
76 memory = allocFn(totalSize);
77 if (!memory.ptr) {
78 mesa_loge("Custom allocation (%zu bytes) failed\n", size);
79 return memory;
80 }
81
82 // set sync data to read complete
83 uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(memory.ptr);
84 __atomic_store_n(syncDWordPtr, kSyncDataReadComplete, __ATOMIC_RELEASE);
85 return memory;
86 };
87
88 m_free = [&freeFn](const Memory& mem) {
89 if (!freeFn) {
90 mesa_loge("Custom free for memory(%p) failed\n", mem.ptr);
91 return;
92 }
93 freeFn(mem);
94 };
95
96 // \param ptr is the data pointer currently allocated
97 // \return dataPtr starting at data
98 m_realloc = [this](const Memory& mem, size_t size) -> Memory {
99 // realloc requires freeing previously allocated memory
100 // read sync DWORD to ensure host is done reading this memory
101 // before releasing it.
102
103 size_t hostWaits = 0;
104
105 uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(mem.ptr);
106 while (__atomic_load_n(syncDWordPtr, __ATOMIC_ACQUIRE) != kSyncDataReadComplete) {
107 hostWaits++;
108 usleep(10);
109 if (hostWaits > 1000) {
110 mesa_logd("%s: warning, stalled on host decoding on this command buffer stream\n",
111 __func__);
112 }
113 }
114
115 // for custom allocation/free, memory holding metadata must be copied
116 // along with stream data
117 // <---sync bytes--><----Data--->
118 // |———————————————|————————————|
119 // |0|1|2|3|4|5|6|7|............|
120 // |———————————————|————————————|
121 // ꜛ ꜛ
122 // [copyLocation] ptr to data [ptr]
123
124 const size_t toCopySize = m_writePos + kSyncDataSize;
125 unsigned char* copyLocation = static_cast<unsigned char*>(mem.ptr);
126 std::vector<uint8_t> tmp(copyLocation, copyLocation + toCopySize);
127 m_free(mem);
128
129 // get new buffer and copy previous stream data to it
130 Memory newMemory = m_alloc(size);
131 unsigned char* newBuf = static_cast<unsigned char*>(newMemory.ptr);
132 if (!newBuf) {
133 mesa_loge("Custom allocation (%zu bytes) failed\n", size);
134 return newMemory;
135 }
136 // copy previous data
137 memcpy(newBuf, tmp.data(), toCopySize);
138
139 return newMemory;
140 };
141 }
142
~CommandBufferStagingStream()143 CommandBufferStagingStream::~CommandBufferStagingStream() {
144 flush();
145 if (m_mem.ptr) m_free(m_mem);
146 }
147
getDataPtr()148 unsigned char* CommandBufferStagingStream::getDataPtr() {
149 if (!m_mem.ptr) return nullptr;
150 const size_t metadataSize = m_usingCustomAlloc ? kSyncDataSize : 0;
151 return static_cast<unsigned char*>(m_mem.ptr) + metadataSize;
152 }
153
markFlushing()154 void CommandBufferStagingStream::markFlushing() {
155 if (!m_usingCustomAlloc) {
156 return;
157 }
158 uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(m_mem.ptr);
159 __atomic_store_n(syncDWordPtr, kSyncDataReadPending, __ATOMIC_RELEASE);
160 }
161
idealAllocSize(size_t len)162 size_t CommandBufferStagingStream::idealAllocSize(size_t len) {
163 if (len > 1048576) return len;
164 return 1048576;
165 }
166
allocBuffer(size_t minSize)167 void* CommandBufferStagingStream::allocBuffer(size_t minSize) {
168 size_t allocSize = (1048576 < minSize ? minSize : 1048576);
169 // Initial case: blank
170 if (!m_mem.ptr) {
171 m_mem = m_alloc(allocSize);
172 m_size = allocSize;
173 return getDataPtr();
174 }
175
176 // Calculate remaining
177 size_t remaining = m_size - m_writePos;
178 // check if there is at least minSize bytes left in buffer
179 // if not, reallocate a buffer of big enough size
180 if (remaining < minSize) {
181 size_t newAllocSize = m_size * 2 + allocSize;
182 m_mem = m_realloc(m_mem, newAllocSize);
183 m_size = newAllocSize;
184
185 return (void*)(getDataPtr() + m_writePos);
186 }
187
188 // for custom allocations, host should have finished reading
189 // data from command buffer since command buffers are flushed
190 // on queue submit.
191 // allocBuffer should not be called on command buffers that are currently
192 // being read by the host
193 if (m_usingCustomAlloc) {
194 uint32_t* syncDWordPtr = reinterpret_cast<uint32_t*>(m_mem.ptr);
195 if (__atomic_load_n(syncDWordPtr, __ATOMIC_ACQUIRE) != kSyncDataReadComplete) {
196 mesa_loge("FATAL: allocBuffer() called but previous read not complete");
197 abort();
198 }
199 }
200
201 return (void*)(getDataPtr() + m_writePos);
202 }
203
commitBuffer(size_t size)204 int CommandBufferStagingStream::commitBuffer(size_t size) {
205 m_writePos += size;
206 return 0;
207 }
208
readFully(void *,size_t)209 const unsigned char* CommandBufferStagingStream::readFully(void*, size_t) {
210 // Not supported
211 mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
212 abort();
213 return nullptr;
214 }
215
read(void *,size_t *)216 const unsigned char* CommandBufferStagingStream::read(void*, size_t*) {
217 // Not supported
218 mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
219 abort();
220 return nullptr;
221 }
222
writeFully(const void *,size_t)223 int CommandBufferStagingStream::writeFully(const void*, size_t) {
224 // Not supported
225 mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
226 abort();
227 return 0;
228 }
229
commitBufferAndReadFully(size_t,void *,size_t)230 const unsigned char* CommandBufferStagingStream::commitBufferAndReadFully(size_t, void*, size_t) {
231 // Not supported
232 mesa_loge("CommandBufferStagingStream::%s: Fatal: not supported\n", __func__);
233 abort();
234 return nullptr;
235 }
236
getWritten(unsigned char ** bufOut,size_t * sizeOut)237 void CommandBufferStagingStream::getWritten(unsigned char** bufOut, size_t* sizeOut) {
238 *bufOut = getDataPtr();
239 *sizeOut = m_writePos;
240 }
241
reset()242 void CommandBufferStagingStream::reset() {
243 m_writePos = 0;
244 IOStream::rewind();
245 }
246
getDeviceMemory()247 VkDeviceMemory CommandBufferStagingStream::getDeviceMemory() { return m_mem.deviceMemory; }
248
249 } // namespace vk
250 } // namespace gfxstream
251