Support AHARDWAREBUFFER_FORMAT_BLOB buffers
by mapping BLOBs to `Buffer`s instead of `ColorBuffer`s and by
adding the needed plumbing for reads and updates.
Note: this is not a fully complete solution as technically a
guest not running with ANGLE could still have native Vulkan
users which import the BLOB AHBs. This has not yet been observed
so this might be okay for now.
Bug: b/234513607
Test: launch_cvd --gpu_mode=gfxstream (with WIP ANGLE)
Test: cts -m CtsNativeHardwareTestCases
Test: ANGLE's `ExternalBufferTestES31.*/ES3_1_Vulkan` tests
Change-Id: I782559e5a57cadc9568d46b2e0949924a6493810
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index 077a490..b4f6fd0 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -1599,12 +1599,34 @@
return handle;
}
+void FrameBuffer::createBufferWithHandle(uint64_t size, HandleType handle) {
+ {
+ AutoLock mutex(m_lock);
+ AutoLock colorBufferMapLock(m_colorBufferMapLock);
+
+ // Check for handle collision
+ if (m_buffers.count(handle) != 0) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Buffer already exists with handle " << handle;
+ }
+
+ handle = createBufferWithHandleLocked(size, handle);
+ if (!handle) {
+ return;
+ }
+ }
+
+ if (m_displayVk || m_guestUsesAngle) {
+ goldfish_vk::setupVkBuffer(handle, /* vulkanOnly */ true,
+ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT);
+ }
+}
+
HandleType FrameBuffer::createBufferWithHandleLocked(int p_size,
HandleType handle) {
if (m_buffers.count(handle) != 0) {
- // emugl::emugl_crash_reporter(
- // "FATAL: buffer with handle %u already exists", handle);
- // abort();
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Buffer already exists with handle " << handle;
}
BufferPtr buffer(Buffer::create(p_size, handle, m_colorBufferHelper));
@@ -2201,6 +2223,23 @@
return true;
}
+void FrameBuffer::readBuffer(HandleType handle, uint64_t offset, uint64_t size, void* bytes) {
+ if (m_guestUsesAngle) {
+ goldfish_vk::readBufferToBytes(handle, offset, size, bytes);
+ return;
+ }
+
+ AutoLock mutex(m_lock);
+
+ BufferPtr buffer = findBuffer(handle);
+ if (!buffer) {
+ ERR("Failed to read buffer: buffer %d not found.", handle);
+ return;
+ }
+
+ buffer->read(offset, size, bytes);
+}
+
void FrameBuffer::readColorBuffer(HandleType p_colorbuffer,
int x,
int y,
@@ -2336,6 +2375,24 @@
nullptr);
}
+bool FrameBuffer::updateBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* bytes) {
+ if (m_guestUsesAngle) {
+ return goldfish_vk::updateBufferFromBytes(p_buffer, offset, size, bytes);
+ }
+
+ AutoLock mutex(m_lock);
+
+ BufferPtr buffer = findBuffer(p_buffer);
+ if (!buffer) {
+ ERR("Failed to update buffer: buffer %d not found.", p_buffer);
+ return false;
+ }
+
+ buffer->subUpdate(offset, size, bytes);
+
+ return true;
+}
+
bool FrameBuffer::updateColorBuffer(HandleType p_colorbuffer,
int x,
int y,
@@ -3441,6 +3498,16 @@
}
}
+BufferPtr FrameBuffer::findBuffer(HandleType p_buffer) {
+ AutoLock colorBufferMapLock(m_colorBufferMapLock);
+ BufferMap::iterator b(m_buffers.find(p_buffer));
+ if (b == m_buffers.end()) {
+ return nullptr;
+ } else {
+ return b->second.buffer;
+ }
+}
+
void FrameBuffer::registerProcessCleanupCallback(void* key, std::function<void()> cb) {
AutoLock mutex(m_lock);
RenderThreadInfo* tInfo = RenderThreadInfo::get();
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index 3b46ede..7a94bb9 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -234,10 +234,8 @@
// Variant of createColorBuffer except with a particular
// handle already assigned. This is for use with
// virtio-gpu's RESOURCE_CREATE ioctl.
- void createColorBufferWithHandle(int p_width, int p_height,
- GLenum p_internalFormat,
- FrameworkFormat p_frameworkFormat,
- HandleType handle);
+ void createColorBufferWithHandle(int p_width, int p_height, GLenum p_internalFormat,
+ FrameworkFormat p_frameworkFormat, HandleType handle);
// Create a new data Buffer instance from this display instance.
// The buffer will be backed by a VkBuffer and VkDeviceMemory (if Vulkan
@@ -247,6 +245,11 @@
// memory.
HandleType createBuffer(uint64_t size, uint32_t memoryProperty);
+ // Variant of createBuffer except with a particular handle already
+ // assigned and using device local memory. This is for use with
+ // virtio-gpu's RESOURCE_CREATE ioctl for BLOB resources.
+ void createBufferWithHandle(uint64_t size, HandleType handle);
+
// Call this function when a render thread terminates to destroy all
// the remaining contexts it created. Necessary to avoid leaking host
// contexts when a guest application crashes, for example.
@@ -331,6 +334,14 @@
// Returns true on success, false on failure.
bool bindColorBufferToRenderbuffer(HandleType p_colorbuffer);
+ // Read the content of a given Buffer into client memory.
+ // |p_buffer| is the Buffer's handle value.
+ // |offset| and |size| are the position and size of a slice of the buffer
+ // that will be read.
+ // |bytes| is the address of a caller-provided buffer that will be filled
+ // with the buffer data.
+ void readBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* bytes);
+
// Read the content of a given ColorBuffer into client memory.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation.
@@ -367,6 +378,14 @@
uint32_t texture_type,
uint32_t* textures);
+ // Update the content of a given Buffer from client data.
+ // |p_buffer| is the Buffer's handle value.
+ // |offset| and |size| are the position and size of a slice of the buffer
+ // that will be updated.
+ // |bytes| is the address of a caller-provided buffer containing the new
+ // buffer data.
+ bool updateBuffer(HandleType p_buffer, uint64_t offset, uint64_t size, void* pixels);
+
// Update the content of a given ColorBuffer from client data.
// |p_colorbuffer| is the ColorBuffer's handle value. Similar
// to glReadPixels(), this can be a slow operation.
@@ -544,6 +563,7 @@
void onLastColorBufferRef(uint32_t handle);
ContextHelper* getColorBufferHelper() { return m_colorBufferHelper; }
ColorBufferPtr findColorBuffer(HandleType p_colorbuffer);
+ BufferPtr findBuffer(HandleType p_buffer);
void registerProcessCleanupCallback(void* key,
std::function<void()> callback);
diff --git a/stream-servers/RendererImpl.cpp b/stream-servers/RendererImpl.cpp
index ee23f75..bfb5ea4 100644
--- a/stream-servers/RendererImpl.cpp
+++ b/stream-servers/RendererImpl.cpp
@@ -13,20 +13,18 @@
// limitations under the License.
#include "RendererImpl.h"
-#include "RenderChannelImpl.h"
-#include "RenderThread.h"
-
-#include "base/System.h"
-#include "snapshot/common.h"
-#include "host-common/logging.h"
-
-#include "FenceSync.h"
-#include "FrameBuffer.h"
+#include <assert.h>
#include <algorithm>
#include <utility>
-#include <assert.h>
+#include "FenceSync.h"
+#include "FrameBuffer.h"
+#include "RenderChannelImpl.h"
+#include "RenderThread.h"
+#include "base/System.h"
+#include "host-common/logging.h"
+#include "snapshot/common.h"
namespace emugl {
@@ -491,6 +489,10 @@
}
static struct AndroidVirtioGpuOps sVirtioGpuOps = {
+ .create_buffer_with_handle =
+ [](uint64_t size, uint32_t handle) {
+ FrameBuffer::getFB()->createBufferWithHandle(size, handle);
+ },
.create_color_buffer_with_handle =
[](uint32_t width,
uint32_t height,
@@ -505,10 +507,22 @@
[](uint32_t handle) {
FrameBuffer::getFB()->openColorBuffer(handle);
},
+ .close_buffer =
+ [](uint32_t handle) {
+ FrameBuffer::getFB()->closeBuffer(handle);
+ },
.close_color_buffer =
[](uint32_t handle) {
FrameBuffer::getFB()->closeColorBuffer(handle);
},
+ .update_buffer =
+ [](uint32_t handle,
+ uint64_t offset,
+ uint64_t size,
+ void* bytes) {
+ FrameBuffer::getFB()->updateBuffer(
+ handle, offset, size, bytes);
+ },
.update_color_buffer =
[](uint32_t handle,
int x,
@@ -521,6 +535,14 @@
FrameBuffer::getFB()->updateColorBuffer(
handle, x, y, width, height, format, type, pixels);
},
+ .read_buffer =
+ [](uint32_t handle,
+ uint64_t offset,
+ uint64_t size,
+ void* bytes) {
+ FrameBuffer::getFB()->readBuffer(
+ handle, offset, size, bytes);
+ },
.read_color_buffer =
[](uint32_t handle,
int x,
diff --git a/stream-servers/virgl_hw.h b/stream-servers/virgl_hw.h
index 22fd0c4..d30e810 100644
--- a/stream-servers/virgl_hw.h
+++ b/stream-servers/virgl_hw.h
@@ -340,6 +340,8 @@
*/
#define VIRGL_BIND_STAGING (1 << 19)
#define VIRGL_BIND_SHARED (1 << 20)
+#define VIRGL_BIND_PREFER_EMULATED_BGRA (1 << 21)
+#define VIRGL_BIND_LINEAR (1 << 22)
#define VIRGL_RESOURCE_Y_0_TOP (1 << 0)
#endif
diff --git a/stream-servers/virtio-gpu-gfxstream-renderer.cpp b/stream-servers/virtio-gpu-gfxstream-renderer.cpp
index e3f435c..d023cb0 100644
--- a/stream-servers/virtio-gpu-gfxstream-renderer.cpp
+++ b/stream-servers/virtio-gpu-gfxstream-renderer.cpp
@@ -167,6 +167,16 @@
bool hasAddressSpaceHandle;
};
+enum class ResType {
+ // Used as a communication channel between the guest and the host
+ // which does not need an allocation on the host GPU.
+ PIPE,
+ // Used as a GPU data buffer.
+ BUFFER,
+ // Used as a GPU texture.
+ COLOR_BUFFER,
+};
+
struct PipeResEntry {
virgl_renderer_resource_create_args args;
iovec* iov;
@@ -180,6 +190,7 @@
uint64_t blobId;
uint32_t hvSlot;
uint32_t caching;
+ ResType type;
};
static inline uint32_t align_up(uint32_t n, uint32_t a) {
@@ -948,26 +959,48 @@
#define PIPE_BIND_COMMAND_ARGS_BUFFER (1 << 17) /* pipe_draw_info.indirect */
#define PIPE_BIND_QUERY_BUFFER (1 << 18) /* get_query_result_resource */
-
- void handleCreateResourceGraphicsUsage(
- struct virgl_renderer_resource_create_args *args,
- struct iovec *iov, uint32_t num_iovs) {
-
- if (args->target == PIPE_BUFFER) {
- // Nothing to handle; this is generic pipe usage.
- return;
+ ResType getResourceType(const struct virgl_renderer_resource_create_args& args) const {
+ if (args.target == PIPE_BUFFER) {
+ return ResType::PIPE;
}
+ if (args.format != VIRGL_FORMAT_R8_UNORM) {
+ return ResType::COLOR_BUFFER;
+ }
+ if (args.bind & VIRGL_BIND_SAMPLER_VIEW) {
+ return ResType::COLOR_BUFFER;
+ }
+ if (args.bind & VIRGL_BIND_RENDER_TARGET) {
+ return ResType::COLOR_BUFFER;
+ }
+ if (args.bind & VIRGL_BIND_SCANOUT) {
+ return ResType::COLOR_BUFFER;
+ }
+ if (args.bind & VIRGL_BIND_CURSOR) {
+ return ResType::COLOR_BUFFER;
+ }
+ if (!(args.bind & VIRGL_BIND_LINEAR)) {
+ return ResType::COLOR_BUFFER;
+ }
+
+ return ResType::BUFFER;
+ }
+
+ void handleCreateResourceBuffer(struct virgl_renderer_resource_create_args* args) {
+ mVirtioGpuOps->create_buffer_with_handle(args->width * args->height, args->handle);
+ }
+
+ void handleCreateResourceColorBuffer(struct virgl_renderer_resource_create_args* args) {
// corresponds to allocation of gralloc buffer in minigbm
VGPLOG("w h %u %u resid %u -> rcCreateColorBufferWithHandle",
args->width, args->height, args->handle);
- uint32_t glformat = virgl_format_to_gl(args->format);
- uint32_t fwkformat = virgl_format_to_fwk_format(args->format);
- mVirtioGpuOps->create_color_buffer_with_handle(
- args->width, args->height, glformat, fwkformat, args->handle);
+
+ const uint32_t glformat = virgl_format_to_gl(args->format);
+ const uint32_t fwkformat = virgl_format_to_fwk_format(args->format);
+ mVirtioGpuOps->create_color_buffer_with_handle(args->width, args->height, glformat,
+ fwkformat, args->handle);
mVirtioGpuOps->set_guest_managed_color_buffer_lifetime(true /* guest manages lifetime */);
- mVirtioGpuOps->open_color_buffer(
- args->handle);
+ mVirtioGpuOps->open_color_buffer(args->handle);
}
int createResource(
@@ -976,7 +1009,17 @@
VGPLOG("handle: %u. num iovs: %u", args->handle, num_iovs);
- handleCreateResourceGraphicsUsage(args, iov, num_iovs);
+ const auto resType = getResourceType(*args);
+ switch (resType) {
+ case ResType::PIPE:
+ break;
+ case ResType::BUFFER:
+ handleCreateResourceBuffer(args);
+ break;
+ case ResType::COLOR_BUFFER:
+ handleCreateResourceColorBuffer(args);
+ break;
+ }
PipeResEntry e;
e.args = *args;
@@ -986,6 +1029,7 @@
e.hvaSize = 0;
e.blobId = 0;
e.hvSlot = 0;
+ e.type = resType;
allocResource(e, iov, num_iovs);
AutoLock lock(mLock);
@@ -993,11 +1037,6 @@
return 0;
}
- void handleUnrefResourceGraphicsUsage(PipeResEntry* res, uint32_t resId) {
- if (res->args.target == PIPE_BUFFER) return;
- mVirtioGpuOps->close_color_buffer(resId);
- }
-
void unrefResource(uint32_t toUnrefId) {
AutoLock lock(mLock);
VGPLOG("handle: %u", toUnrefId);
@@ -1015,8 +1054,16 @@
}
auto& entry = it->second;
-
- handleUnrefResourceGraphicsUsage(&entry, toUnrefId);
+ switch (entry.type) {
+ case ResType::PIPE:
+ break;
+ case ResType::BUFFER:
+ mVirtioGpuOps->close_buffer(toUnrefId);
+ break;
+ case ResType::COLOR_BUFFER:
+ mVirtioGpuOps->close_color_buffer(toUnrefId);
+ break;
+ }
if (entry.linear) {
free(entry.linear);
@@ -1081,56 +1128,145 @@
VGPLOG("done");
}
- bool handleTransferReadGraphicsUsage(
- PipeResEntry* res, uint64_t offset, virgl_box* box) {
- // PIPE_BUFFER: Generic pipe usage
- if (res->args.target == PIPE_BUFFER) return true;
+ int handleTransferReadPipe(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::PIPE) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Resource " << res->args.handle << " is not a PIPE resource.";
+ return -1;
+ }
- // Others: Gralloc transfer read operation
+ // Do the pipe service op here, if there is an associated hostpipe.
+ auto hostPipe = res->hostPipe;
+ if (!hostPipe) return -1;
+
+ auto ops = ensureAndGetServiceOps();
+
+ size_t readBytes = 0;
+ size_t wantedBytes = readBytes + (size_t)box->w;
+
+ while (readBytes < wantedBytes) {
+ GoldfishPipeBuffer buf = {
+ ((char*)res->linear) + box->x + readBytes,
+ wantedBytes - readBytes,
+ };
+ auto status = ops->guest_recv(hostPipe, &buf, 1);
+
+ if (status > 0) {
+ readBytes += status;
+ } else if (status != kPipeTryAgain) {
+ return EIO;
+ }
+ }
+
+ return 0;
+ }
+
+ int handleTransferWritePipe(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::PIPE) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Resource " << res->args.handle << " is not a PIPE resource.";
+ return -1;
+ }
+
+ // Do the pipe service op here, if there is an associated hostpipe.
+ auto hostPipe = res->hostPipe;
+ if (!hostPipe) {
+ VGPLOG("No hostPipe");
+ return -1;
+ }
+
+ VGPLOG("resid: %d offset: 0x%llx hostpipe: %p", res->args.handle,
+ (unsigned long long)offset, hostPipe);
+
+ auto ops = ensureAndGetServiceOps();
+
+ size_t writtenBytes = 0;
+ size_t wantedBytes = (size_t)box->w;
+
+ while (writtenBytes < wantedBytes) {
+ GoldfishPipeBuffer buf = {
+ ((char*)res->linear) + box->x + writtenBytes,
+ wantedBytes - writtenBytes,
+ };
+
+ // guest_send can now reallocate the pipe.
+ void* hostPipeBefore = hostPipe;
+ auto status = ops->guest_send(&hostPipe, &buf, 1);
+ if (hostPipe != hostPipeBefore) {
+ resetPipe((GoldfishHwPipe*)(uintptr_t)(res->ctxId), hostPipe);
+ auto it = mResources.find(res->args.handle);
+ res = &it->second;
+ }
+
+ if (status > 0) {
+ writtenBytes += status;
+ } else if (status != kPipeTryAgain) {
+ return EIO;
+ }
+ }
+
+ return 0;
+ }
+
+ int handleTransferReadBuffer(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::BUFFER) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Resource " << res->args.handle << " is not a BUFFER resource.";
+ return -1;
+ }
+
+ mVirtioGpuOps->read_buffer(res->args.handle, 0, res->args.width * res->args.height,
+ res->linear);
+ return 0;
+ }
+
+ int handleTransferWriteBuffer(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::BUFFER) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << res->args.handle << " is not a BUFFER resource.";
+ return -1;
+ }
+
+ mVirtioGpuOps->update_buffer(res->args.handle, 0, res->args.width * res->args.height,
+ res->linear);
+ return 0;
+ }
+
+ void handleTransferReadColorBuffer(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::COLOR_BUFFER) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Resource " << res->args.handle << " is not a COLOR_BUFFER resource.";
+ return;
+ }
+
auto glformat = virgl_format_to_gl(res->args.format);
auto gltype = gl_format_to_natural_type(glformat);
// We always xfer the whole thing again from GL
// since it's fiddly to calc / copy-out subregions
if (virgl_format_is_yuv(res->args.format)) {
- mVirtioGpuOps->read_color_buffer_yuv(
- res->args.handle,
- 0, 0,
- res->args.width, res->args.height,
- res->linear, res->linearSize);
+ mVirtioGpuOps->read_color_buffer_yuv(res->args.handle, 0, 0, res->args.width,
+ res->args.height, res->linear, res->linearSize);
} else {
- mVirtioGpuOps->read_color_buffer(
- res->args.handle,
- 0, 0,
- res->args.width, res->args.height,
- glformat,
- gltype,
- res->linear);
+ mVirtioGpuOps->read_color_buffer(res->args.handle, 0, 0, res->args.width,
+ res->args.height, glformat, gltype, res->linear);
}
-
- return false;
}
- bool handleTransferWriteGraphicsUsage(
- PipeResEntry* res, uint64_t offset, virgl_box* box) {
- // PIPE_BUFFER: Generic pipe usage
- if (res->args.target == PIPE_BUFFER) return true;
+ void handleTransferWriteColorBuffer(PipeResEntry* res, uint64_t offset, virgl_box* box) {
+ if (res->type != ResType::COLOR_BUFFER) {
+ GFXSTREAM_ABORT(FatalError(ABORT_REASON_OTHER))
+ << "Resource " << res->args.handle << " is not a COLOR_BUFFER resource.";
+ return;
+ }
- // Others: Gralloc transfer read operation
auto glformat = virgl_format_to_gl(res->args.format);
auto gltype = gl_format_to_natural_type(glformat);
// We always xfer the whole thing again to GL
// since it's fiddly to calc / copy-out subregions
- mVirtioGpuOps->update_color_buffer(
- res->args.handle,
- 0, 0,
- res->args.width, res->args.height,
- glformat,
- gltype,
- res->linear);
-
- return false;
+ mVirtioGpuOps->update_color_buffer(res->args.handle, 0, 0, res->args.width,
+ res->args.height, glformat, gltype, res->linear);
}
int transferReadIov(int resId, uint64_t offset, virgl_box* box, struct iovec* iov, int iovec_cnt) {
@@ -1146,38 +1282,27 @@
auto it = mResources.find(resId);
if (it == mResources.end()) return EINVAL;
+ int ret = 0;
+
auto& entry = it->second;
+ switch (entry.type) {
+ case ResType::PIPE:
+ ret = handleTransferReadPipe(&entry, offset, box);
+ break;
+ case ResType::BUFFER:
+ ret = handleTransferReadBuffer(&entry, offset, box);
+ break;
+ case ResType::COLOR_BUFFER:
+ handleTransferReadColorBuffer(&entry, offset, box);
+ break;
+ }
- if (handleTransferReadGraphicsUsage(
- &entry, offset, box)) {
- // Do the pipe service op here, if there is an associated hostpipe.
- auto hostPipe = entry.hostPipe;
- if (!hostPipe) return -1;
-
- auto ops = ensureAndGetServiceOps();
-
- size_t readBytes = 0;
- size_t wantedBytes = readBytes + (size_t)box->w;
-
- while (readBytes < wantedBytes) {
- GoldfishPipeBuffer buf = {
- ((char*)entry.linear) + box->x + readBytes,
- wantedBytes - readBytes,
- };
- auto status = ops->guest_recv(hostPipe, &buf, 1);
-
- if (status > 0) {
- readBytes += status;
- } else if (status != kPipeTryAgain) {
- return EIO;
- }
- }
+ if (ret != 0) {
+ return ret;
}
VGPLOG("Linear first word: %d", *(int*)(entry.linear));
- int syncRes;
-
if (iovec_cnt) {
PipeResEntry e = {
entry.args,
@@ -1186,16 +1311,13 @@
entry.linear,
entry.linearSize,
};
- syncRes =
- sync_iov(&e, offset, box, LINEAR_TO_IOV);
+ ret = sync_iov(&e, offset, box, LINEAR_TO_IOV);
} else {
- syncRes =
- sync_iov(&entry, offset, box, LINEAR_TO_IOV);
+ ret = sync_iov(&entry, offset, box, LINEAR_TO_IOV);
}
VGPLOG("done");
-
- return syncRes;
+ return ret;
}
int transferWriteIov(int resId, uint64_t offset, virgl_box* box, struct iovec* iov, int iovec_cnt) {
@@ -1206,8 +1328,8 @@
if (it == mResources.end()) return EINVAL;
auto& entry = it->second;
- int syncRes;
+ int ret = 0;
if (iovec_cnt) {
PipeResEntry e = {
entry.args,
@@ -1216,52 +1338,29 @@
entry.linear,
entry.linearSize,
};
- syncRes = sync_iov(&e, offset, box, IOV_TO_LINEAR);
+ ret = sync_iov(&e, offset, box, IOV_TO_LINEAR);
} else {
- syncRes = sync_iov(&entry, offset, box, IOV_TO_LINEAR);
+ ret = sync_iov(&entry, offset, box, IOV_TO_LINEAR);
}
- if (handleTransferWriteGraphicsUsage(&entry, offset, box)) {
- // Do the pipe service op here, if there is an associated hostpipe.
- auto hostPipe = entry.hostPipe;
- if (!hostPipe) {
- VGPLOG("No hostPipe");
- return syncRes;
- }
+ if (ret != 0) {
+ return ret;
+ }
- VGPLOG("resid: %d offset: 0x%llx hostpipe: %p", resId,
- (unsigned long long)offset, hostPipe);
-
- auto ops = ensureAndGetServiceOps();
-
- size_t writtenBytes = 0;
- size_t wantedBytes = (size_t)box->w;
-
- while (writtenBytes < wantedBytes) {
- GoldfishPipeBuffer buf = {
- ((char*)entry.linear) + box->x + writtenBytes,
- wantedBytes - writtenBytes,
- };
-
- // guest_send can now reallocate the pipe.
- void* hostPipeBefore = hostPipe;
- auto status = ops->guest_send(&hostPipe, &buf, 1);
- if (hostPipe != hostPipeBefore) {
- resetPipe((GoldfishHwPipe*)(uintptr_t)(entry.ctxId), hostPipe);
- it = mResources.find(resId);
- entry = it->second;
- }
-
- if (status > 0) {
- writtenBytes += status;
- } else if (status != kPipeTryAgain) {
- return EIO;
- }
- }
+ switch (entry.type) {
+ case ResType::PIPE:
+ ret = handleTransferWritePipe(&entry, offset, box);
+ break;
+ case ResType::BUFFER:
+ ret = handleTransferWriteBuffer(&entry, offset, box);
+ break;
+ case ResType::COLOR_BUFFER:
+ handleTransferWriteColorBuffer(&entry, offset, box);
+ break;
}
VGPLOG("done");
- return syncRes;
+ return ret;
}
void attachResource(uint32_t ctxId, uint32_t resId) {
diff --git a/stream-servers/virtio_gpu_ops.h b/stream-servers/virtio_gpu_ops.h
index 448b7c8..4aa2cff 100644
--- a/stream-servers/virtio_gpu_ops.h
+++ b/stream-servers/virtio_gpu_ops.h
@@ -15,6 +15,10 @@
#include <functional>
+/* virtio-gpu interface for buffers
+ * (triggered by minigbm/egl calling virtio-gpu ioctls) */
+typedef void (*create_buffer_with_handle_t)(uint64_t size, uint32_t handle);
+
/* virtio-gpu interface for color buffers
* (triggered by minigbm/egl calling virtio-gpu ioctls) */
typedef void (*create_color_buffer_with_handle_t)(
@@ -66,10 +70,13 @@
uint32_t* textures);
typedef void (*open_color_buffer_t)(uint32_t handle);
+typedef void (*close_buffer_t)(uint32_t handle);
typedef void (*close_color_buffer_t)(uint32_t handle);
+typedef void (*update_buffer_t)(uint32_t handle, uint64_t offset, uint64_t sizeToRead, void* bytes);
typedef void (*update_color_buffer_t)(
uint32_t handle, int x, int y, int width, int height,
uint32_t format, uint32_t type, void* pixels);
+typedef void (*read_buffer_t)(uint32_t handle, uint64_t offset, uint64_t sizeToRead, void* bytes);
typedef void (*read_color_buffer_t)(
uint32_t handle, int x, int y, int width, int height,
uint32_t format, uint32_t type, void* pixels);
@@ -109,10 +116,14 @@
typedef bool (*platform_destroy_shared_egl_context_t)(void* context);
struct AndroidVirtioGpuOps {
+ create_buffer_with_handle_t create_buffer_with_handle;
create_color_buffer_with_handle_t create_color_buffer_with_handle;
open_color_buffer_t open_color_buffer;
+ close_buffer_t close_buffer;
close_color_buffer_t close_color_buffer;
+ update_buffer_t update_buffer;
update_color_buffer_t update_color_buffer;
+ read_buffer_t read_buffer;
read_color_buffer_t read_color_buffer;
read_color_buffer_yuv_t read_color_buffer_yuv;
post_color_buffer_t post_color_buffer;
diff --git a/stream-servers/vulkan/VkCommonOperations.cpp b/stream-servers/vulkan/VkCommonOperations.cpp
index 4035271..9788941 100644
--- a/stream-servers/vulkan/VkCommonOperations.cpp
+++ b/stream-servers/vulkan/VkCommonOperations.cpp
@@ -1431,6 +1431,7 @@
static VkFormat glFormat2VkFormat(GLint internalformat) {
switch (internalformat) {
+ case GL_R8:
case GL_LUMINANCE:
return VK_FORMAT_R8_UNORM;
case GL_RGB:
@@ -1457,8 +1458,9 @@
case GL_BGRA_EXT:
case GL_BGRA8_EXT:
return VK_FORMAT_B8G8R8A8_UNORM;
- ;
default:
+ VK_COMMON_ERROR("Unhandled format %d, falling back to VK_FORMAT_R8G8B8A8_UNORM",
+ internalformat);
return VK_FORMAT_R8G8B8A8_UNORM;
}
};
@@ -2537,6 +2539,177 @@
return infoPtr->memory.exportedHandle;
}
+bool readBufferToBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* outBytes) {
+ if (!sVkEmulation || !sVkEmulation->live) {
+ VK_COMMON_ERROR("VkEmulation not available.");
+ return false;
+ }
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto bufferInfo = android::base::find(sVkEmulation->buffers, bufferHandle);
+ if (!bufferInfo) {
+ VK_COMMON_ERROR("Failed to read from Buffer:%d, not found.", bufferHandle);
+ return false;
+ }
+
+ const auto& stagingBufferInfo = sVkEmulation->staging;
+ if (size > stagingBufferInfo.size) {
+ VK_COMMON_ERROR("Failed to read from Buffer:%d, staging buffer too small.", bufferHandle);
+ return false;
+ }
+
+ const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .pNext = nullptr,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+
+ VkCommandBuffer commandBuffer = sVkEmulation->commandBuffer;
+
+ VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
+
+ const VkBufferCopy bufferCopy = {
+ .srcOffset = offset,
+ .dstOffset = 0,
+ .size = size,
+ };
+ vk->vkCmdCopyBuffer(commandBuffer, bufferInfo->buffer, stagingBufferInfo.buffer, 1,
+ &bufferCopy);
+
+ VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
+
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreCount = 0,
+ .pWaitSemaphores = nullptr,
+ .pWaitDstStageMask = nullptr,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &commandBuffer,
+ .signalSemaphoreCount = 0,
+ .pSignalSemaphores = nullptr,
+ };
+
+ {
+ android::base::AutoLock lock(*sVkEmulation->queueLock);
+ VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo,
+ sVkEmulation->commandBufferFence));
+ }
+
+ static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL;
+
+ VK_CHECK(vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence,
+ VK_TRUE, ANB_MAX_WAIT_NS));
+
+ VK_CHECK(vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence));
+
+ const VkMappedMemoryRange toInvalidate = {
+ .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
+ .pNext = nullptr,
+ .memory = stagingBufferInfo.memory.memory,
+ .offset = 0,
+ .size = size,
+ };
+
+ VK_CHECK(vk->vkInvalidateMappedMemoryRanges(sVkEmulation->device, 1, &toInvalidate));
+
+ const void* srcPtr = reinterpret_cast<const void*>(
+ reinterpret_cast<const char*>(stagingBufferInfo.memory.mappedPtr));
+ void* dstPtr = outBytes;
+ void* dstPtrOffset = reinterpret_cast<void*>(reinterpret_cast<char*>(dstPtr) + offset);
+ std::memcpy(dstPtrOffset, srcPtr, size);
+
+ return true;
+}
+
+bool updateBufferFromBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* bytes) {
+ if (!sVkEmulation || !sVkEmulation->live) {
+ VK_COMMON_ERROR("VkEmulation not available.");
+ return false;
+ }
+
+ auto vk = sVkEmulation->dvk;
+
+ AutoLock lock(sVkEmulationLock);
+
+ auto bufferInfo = android::base::find(sVkEmulation->buffers, bufferHandle);
+ if (!bufferInfo) {
+ VK_COMMON_ERROR("Failed to update Buffer:%d, not found.", bufferHandle);
+ return false;
+ }
+
+ const auto& stagingBufferInfo = sVkEmulation->staging;
+ if (size > stagingBufferInfo.size) {
+ VK_COMMON_ERROR("Failed to update Buffer:%d, staging buffer too small.", bufferHandle);
+ return false;
+ }
+
+ const void* srcPtr = bytes;
+ const void* srcPtrOffset =
+ reinterpret_cast<const void*>(reinterpret_cast<const char*>(srcPtr) + offset);
+ void* dstPtr = stagingBufferInfo.memory.mappedPtr;
+ std::memcpy(dstPtr, srcPtrOffset, size);
+
+ const VkMappedMemoryRange toFlush = {
+ .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
+ .pNext = nullptr,
+ .memory = stagingBufferInfo.memory.memory,
+ .offset = 0,
+ .size = size,
+ };
+ VK_CHECK(vk->vkFlushMappedMemoryRanges(sVkEmulation->device, 1, &toFlush));
+
+ const VkCommandBufferBeginInfo beginInfo = {
+ .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+ .pNext = nullptr,
+ .flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT,
+ };
+
+ VkCommandBuffer commandBuffer = sVkEmulation->commandBuffer;
+
+ VK_CHECK(vk->vkBeginCommandBuffer(commandBuffer, &beginInfo));
+
+ const VkBufferCopy bufferCopy = {
+ .srcOffset = 0,
+ .dstOffset = offset,
+ .size = size,
+ };
+ vk->vkCmdCopyBuffer(commandBuffer, stagingBufferInfo.buffer, bufferInfo->buffer, 1,
+ &bufferCopy);
+
+ VK_CHECK(vk->vkEndCommandBuffer(commandBuffer));
+
+ const VkSubmitInfo submitInfo = {
+ .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
+ .pNext = nullptr,
+ .waitSemaphoreCount = 0,
+ .pWaitSemaphores = nullptr,
+ .pWaitDstStageMask = nullptr,
+ .commandBufferCount = 1,
+ .pCommandBuffers = &commandBuffer,
+ .signalSemaphoreCount = 0,
+ .pSignalSemaphores = nullptr,
+ };
+
+ {
+ android::base::AutoLock lock(*sVkEmulation->queueLock);
+ VK_CHECK(vk->vkQueueSubmit(sVkEmulation->queue, 1, &submitInfo,
+ sVkEmulation->commandBufferFence));
+ }
+
+ static constexpr uint64_t ANB_MAX_WAIT_NS = 5ULL * 1000ULL * 1000ULL * 1000ULL;
+
+ VK_CHECK(vk->vkWaitForFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence,
+ VK_TRUE, ANB_MAX_WAIT_NS));
+
+ VK_CHECK(vk->vkResetFences(sVkEmulation->device, 1, &sVkEmulation->commandBufferFence));
+
+ return true;
+}
+
VkExternalMemoryHandleTypeFlags transformExternalMemoryHandleTypeFlags_tohost(
VkExternalMemoryHandleTypeFlags bits) {
VkExternalMemoryHandleTypeFlags res = bits;
diff --git a/stream-servers/vulkan/VkCommonOperations.h b/stream-servers/vulkan/VkCommonOperations.h
index 709fd1d..4580463 100644
--- a/stream-servers/vulkan/VkCommonOperations.h
+++ b/stream-servers/vulkan/VkCommonOperations.h
@@ -409,6 +409,9 @@
bool teardownVkBuffer(uint32_t bufferHandle);
VK_EXT_MEMORY_HANDLE getBufferExtMemoryHandle(uint32_t bufferHandle);
+bool readBufferToBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* outBytes);
+bool updateBufferFromBytes(uint32_t bufferHandle, uint64_t offset, uint64_t size, void* bytes);
+
VkExternalMemoryHandleTypeFlags transformExternalMemoryHandleTypeFlags_tohost(
VkExternalMemoryHandleTypeFlags bits);