This commit is contained in:
elwrcl committed 2026-06-06 13:25:15 +03:00
1 parent 04d4dc8b09
commit 818cab85dc
12511 files changed
+6156641

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
# Copyright © 2024 Google
# SPDX-License-Identifier: MIT
BasedOnStyle: Google
IndentWidth: 4
ColumnLimit: 100
IncludeCategories:
- Regex: '^(<(gtest|gmock))'
Priority: -1
FixNamespaceComments: true
PointerAlignment: Left
DerivePointerAlignment: false
# Ensure proper formatting of macros such as GOLDFISH_VK_LIST_DISPATCHABLE_HANDLE_TYPES
StatementMacros: ["f"]
+219
View File
@@ -0,0 +1,219 @@
/*
* Copyright 2019 Google
* SPDX-License-Identifier: MIT
*/
#include "Stream.h"
#include <assert.h>
#include <string.h>
namespace gfxstream {
namespace aemu {
void Stream::putByte(uint8_t value) { write(&value, 1U); }
uint8_t Stream::getByte() {
uint8_t value[1] = {0};
read(value, 1U);
return value[0];
}
void Stream::putBe16(uint16_t value) {
uint8_t b[2] = {(uint8_t)(value >> 8), (uint8_t)value};
write(b, 2U);
}
uint16_t Stream::getBe16() {
uint8_t b[2] = {0, 0};
read(b, 2U);
return ((uint16_t)b[0] << 8) | (uint16_t)b[1];
}
void Stream::putBe32(uint32_t value) {
uint8_t b[4] = {(uint8_t)(value >> 24), (uint8_t)(value >> 16), (uint8_t)(value >> 8),
(uint8_t)value};
write(b, 4U);
}
uint32_t Stream::getBe32() {
uint8_t b[4] = {0, 0, 0, 0};
read(b, 4U);
return ((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | (uint32_t)b[3];
}
void Stream::putBe64(uint64_t value) {
uint8_t b[8] = {(uint8_t)(value >> 56), (uint8_t)(value >> 48), (uint8_t)(value >> 40),
(uint8_t)(value >> 32), (uint8_t)(value >> 24), (uint8_t)(value >> 16),
(uint8_t)(value >> 8), (uint8_t)value};
write(b, 8U);
}
uint64_t Stream::getBe64() {
uint8_t b[8] = {0, 0, 0, 0, 0, 0, 0, 0};
read(b, 8U);
return ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) | ((uint64_t)b[2] << 40) |
((uint64_t)b[3] << 32) | ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | (uint64_t)b[7];
}
void Stream::putFloat(float v) {
union {
float f;
uint8_t bytes[sizeof(float)];
} u;
u.f
= v;
this->write(u.bytes, sizeof(u.bytes));
}
float Stream::getFloat() {
union {
float f;
uint8_t bytes[sizeof(float)];
} u;
this->read(u.bytes, sizeof(u.bytes));
return u.f;
}
void Stream::putString(const char* str) { putString(str, strlen(str)); }
void Stream::putString(const char* str, size_t len) {
this->putBe32(len);
this->write(str, len);
}
void Stream::putString(const std::string& str) { putString(str.c_str()); }
std::string Stream::getString() {
std::string result;
size_t len = this->getBe32();
if (len > 0) {
result.resize(len);
if (this->read(&result[0], len) != static_cast<ssize_t>(len)) {
result.clear();
}
}
#ifdef _WIN32
else {
// std::string in GCC's STL still uses copy on write implementation
// with a single shared buffer for an empty string. Its dtor has
// a check for that shared buffer, and it deallocates memory only if
// the current string's instance address != shared empty string address
// Unfortunately, in Windows DLLs each DLL has its own copy of this
// empty string (that's just the way Windows DLLs work), so if this
// code creates an empty string and passes it over into another module,
// that module's std::string::~string() will compare address with its
// empty string object, find that they are different and will try to
// free() a static object.
// To mitigate it we make sure the string allocates something, so it
// isn't empty internally and dtor is OK to delete the storage.
result.reserve(1);
}
#endif
return result;
}
void Stream::putPackedNum(uint64_t num) {
do {
auto byte = uint8_t(num & 0x7f);
num >>= 7;
if (num) {
byte |= 0x80;
}
putByte(byte);
} while (num != 0);
}
uint64_t Stream::getPackedNum() {
uint64_t res = 0;
uint8_t byte;
int i = 0;
do {
byte = getByte();
res |= uint64_t(byte & 0x7f) << (i++ * 7);
} while (byte & 0x80 && i < 10);
return res;
}
void Stream::putPackedSignedNum(int64_t num) {
if (num >= 0) {
assert((uint64_t(num) & (1ULL << 63)) == 0);
putPackedNum(uint64_t(num) << 1);
} else {
assert((uint64_t(-num) & (1ULL << 63)) == 0);
putPackedNum((uint64_t(-num) << 1) | 1);
}
}
int64_t Stream::getPackedSignedNum() {
auto num = getPackedNum();
auto sign = num & 1;
return sign ? -int64_t(num >> 1) : (num >> 1);
}
// Static big-endian conversions
// the |v| pointer is unlikely to be aligned---use memcpy throughout
void Stream::toByte(uint8_t*) {} // no conversion
void Stream::toBe16(uint8_t* v) {
uint16_t value;
memcpy(&value, v, sizeof(uint16_t));
uint8_t b[2] = {(uint8_t)(value >> 8), (uint8_t)value};
memcpy(v, b, sizeof(uint16_t));
}
void Stream::toBe32(uint8_t* v) {
uint32_t value;
memcpy(&value, v, sizeof(uint32_t));
uint8_t b[4] = {(uint8_t)(value >> 24), (uint8_t)(value >> 16), (uint8_t)(value >> 8),
(uint8_t)value};
memcpy(v, b, sizeof(uint32_t));
}
void Stream::toBe64(uint8_t* v) {
uint64_t value;
memcpy(&value, v, sizeof(uint64_t));
uint8_t b[8] = {(uint8_t)(value >> 56), (uint8_t)(value >> 48), (uint8_t)(value >> 40),
(uint8_t)(value >> 32), (uint8_t)(value >> 24), (uint8_t)(value >> 16),
(uint8_t)(value >> 8), (uint8_t)value};
memcpy(v, b, sizeof(uint64_t));
}
void Stream::fromByte(uint8_t*) {} // no conversion
void Stream::fromBe16(uint8_t* v) {
uint8_t b[2];
memcpy(b, v, sizeof(uint16_t));
uint16_t value = ((uint16_t)b[0] << 8) | (uint16_t)b[1];
memcpy(v, &value, sizeof(uint16_t));
}
void Stream::fromBe32(uint8_t* v) {
uint8_t b[4];
memcpy(b, v, sizeof(uint32_t));
uint32_t value =
((uint32_t)b[0] << 24) | ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | (uint32_t)b[3];
memcpy(v, &value, sizeof(uint32_t));
}
void Stream::fromBe64(uint8_t* v) {
uint8_t b[8];
memcpy(b, v, sizeof(uint64_t));
uint64_t value = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) | ((uint64_t)b[2] << 40) |
((uint64_t)b[3] << 32) | ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | (uint64_t)b[7];
memcpy(v, &value, sizeof(uint64_t));
}
void saveStringArray(Stream* stream, const char* const* strings, uint32_t count) {
stream->putBe32(count);
for (uint32_t i = 0; i < count; ++i) {
stream->putString(strings[i]);
}
}
} // namespace aemu
} // namespace gfxstream
+150
View File
@@ -0,0 +1,150 @@
/*
* Copyright 2018 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdio.h>
#include <algorithm>
#include <atomic>
#include <cinttypes>
#include <cstdlib>
#include <cstring>
#include <type_traits>
#include <vector>
#ifdef _WIN32
#include <malloc.h>
#endif
namespace gfxstream {
/**
* Do not abuse this by using any complicated T. Use it for POD or primitives
*/
template <class T, size_t align>
class AlignedBuf {
constexpr static bool triviallyCopyable() {
#if (defined(__GNUC__) && !defined(__clang__) && __GNUC__ <= 4) || defined(__OLD_STD_VERSION__)
// Older g++ doesn't support std::is_trivially_copyable.
constexpr bool triviallyCopyable = std::has_trivial_copy_constructor<T>::value;
#else
constexpr bool triviallyCopyable = std::is_trivially_copyable<T>::value;
#endif
return triviallyCopyable;
}
static_assert(triviallyCopyable() && std::is_standard_layout<T>::value &&
std::is_trivially_default_constructible<T>::value);
public:
explicit AlignedBuf(size_t size) {
static_assert(align && ((align & (align - 1)) == 0),
"AlignedBuf only supports power-of-2 aligments.");
resizeImpl(size);
}
AlignedBuf(const AlignedBuf& other) : AlignedBuf(other.mSize) {
if (other.mBuffer) { // could have got moved out
std::copy(other.mBuffer, other.mBuffer + other.mSize, mBuffer);
}
}
AlignedBuf& operator=(const AlignedBuf& other) {
if (this != &other) {
AlignedBuf tmp(other);
*this = std::move(tmp);
}
return *this;
}
AlignedBuf(AlignedBuf&& other) { *this = std::move(other); }
AlignedBuf& operator=(AlignedBuf&& other) {
mBuffer = other.mBuffer;
mSize = other.mSize;
other.mBuffer = nullptr;
other.mSize = 0;
return *this;
}
~AlignedBuf() {
if (mBuffer) freeImpl(mBuffer);
} // account for getting moved out
void resize(size_t newSize) { resizeImpl(newSize); }
size_t size() const { return mSize; }
T* data() { return mBuffer; }
T& operator[](size_t index) { return mBuffer[index]; }
const T& operator[](size_t index) const { return mBuffer[index]; }
bool operator==(const AlignedBuf& other) const {
return 0 == std::memcmp(mBuffer, other.mBuffer, sizeof(T) * std::min(mSize, other.mSize));
}
private:
T* getNewBuffer(size_t newSize) {
if (newSize == 0) {
return nullptr;
}
size_t pad = std::max(align, sizeof(T));
size_t newSizeBytes = ((align - 1 + newSize * sizeof(T) + pad) / align) * align;
return static_cast<T*>(reallocImpl(nullptr, newSizeBytes));
}
void resizeImpl(size_t newSize) {
T* new_buffer = getNewBuffer(newSize);
if (new_buffer && mBuffer) {
size_t keepSize = std::min(newSize, mSize);
std::copy(mBuffer, mBuffer + keepSize, new_buffer);
}
if (mBuffer) {
freeImpl(mBuffer);
}
mBuffer = new_buffer;
mSize = (new_buffer ? newSize : 0);
}
void* reallocImpl(void* oldPtr, size_t sizeBytes) {
if (oldPtr) {
freeImpl(oldPtr);
}
// Platform aligned malloc might not behave right
// if we give it an alignment value smaller than sizeof(void*).
size_t actualAlign = std::max(align, sizeof(void*));
#ifdef _WIN32
return _aligned_malloc(sizeBytes, actualAlign);
#else
void* res;
if (posix_memalign(&res, actualAlign, sizeBytes)) {
fprintf(stderr, "%s: failed to alloc aligned memory\n", __func__);
abort();
}
return res;
#endif
}
void freeImpl(void* ptr) {
#ifdef _WIN32
_aligned_free(ptr);
#else
free(ptr);
#endif
}
T* mBuffer = nullptr;
size_t mSize = 0;
};
// Convenience function for aligned malloc across platforms
void* aligned_buf_alloc(size_t align, size_t size);
void aligned_buf_free(void* buf);
} // namespace gfxstream
+59
View File
@@ -0,0 +1,59 @@
/*
* Copyright 2021 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <inttypes.h>
#include <stddef.h>
#include <string.h>
namespace gfxstream {
namespace aemu {
// A generic memory allocator interface which could be used to allocate
// a certain size of memory region, or memory region for arrays / strings.
// How the memory are recycled / freed is up to derived classes.
class Allocator {
public:
Allocator() = default;
virtual ~Allocator() = default;
virtual void* alloc(size_t wantedSize) = 0;
// Convenience function to allocate an array
// of objects of type T.
template <class T>
T* allocArray(size_t count) {
size_t bytes = sizeof(T) * count;
void* res = alloc(bytes);
return (T*)res;
}
char* strDup(const char* toCopy) {
size_t bytes = strlen(toCopy) + 1;
void* res = alloc(bytes);
memset(res, 0x0, bytes);
memcpy(res, toCopy, bytes);
return (char*)res;
}
char** strDupArray(const char* const* arrayToCopy, size_t count) {
char** res = allocArray<char*>(count);
for (size_t i = 0; i < count; i++) {
res[i] = strDup(arrayToCopy[i]);
}
return res;
}
void* dupArray(const void* buf, size_t bytes) {
void* res = alloc(bytes);
memcpy(res, buf, bytes);
return res;
}
};
} // namespace aemu
} // namespace gfxstream
+69
View File
@@ -0,0 +1,69 @@
/*
* Copyright 2019 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <inttypes.h>
#include <unordered_set>
#include <vector>
#include "AlignedBuf.h"
#include "Allocator.h"
namespace gfxstream {
namespace aemu {
// Class to make it easier to set up memory regions where it is fast
// to allocate buffers AND we don't care about freeing individual pieces,
// BUT it's necessary to preserve previous pointer values in between the first
// alloc() after a freeAll(), and the freeAll() itself, allowing some sloppy use of
// malloc in the first pass while we find out how much data was needed.
class BumpPool : public Allocator {
public:
BumpPool(size_t startingBytes = 4096) : mStorage(startingBytes / sizeof(uint64_t)) {}
// All memory allocated by this pool
// is automatically deleted when the pool
// is deconstructed.
~BumpPool() { freeAll(); }
void* alloc(size_t wantedSize) override {
size_t wantedSizeRoundedUp =
sizeof(uint64_t) * ((wantedSize + sizeof(uint64_t) - 1) / (sizeof(uint64_t)));
mTotalWantedThisGeneration += wantedSizeRoundedUp;
if (mAllocPos + wantedSizeRoundedUp > mStorage.size() * sizeof(uint64_t)) {
mNeedRealloc = true;
void* fallbackPtr = malloc(wantedSizeRoundedUp);
mFallbackPtrs.insert(fallbackPtr);
return fallbackPtr;
}
void* allocPtr = (void*)(((unsigned char*)mStorage.data()) + mAllocPos);
mAllocPos += wantedSizeRoundedUp;
return allocPtr;
}
void freeAll() {
mAllocPos = 0;
if (mNeedRealloc) {
mStorage.resize((mTotalWantedThisGeneration * 2) / sizeof(uint64_t));
mNeedRealloc = false;
for (auto ptr : mFallbackPtrs) {
free(ptr);
}
mFallbackPtrs.clear();
}
mTotalWantedThisGeneration = 0;
}
private:
AlignedBuf<uint64_t, 8> mStorage;
std::unordered_set<void*> mFallbackPtrs;
size_t mAllocPos = 0;
size_t mTotalWantedThisGeneration = 0;
bool mNeedRealloc = false;
};
} // namespace aemu
} // namespace gfxstream
+113
View File
@@ -0,0 +1,113 @@
/*
* Copyright 2019 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <inttypes.h>
#include <sys/types.h>
#include <string>
namespace gfxstream {
namespace aemu {
// Abstract interface to byte streams of all kind.
// This is mainly used to implement disk serialization.
class Stream {
public:
// Default constructor.
Stream() = default;
// Destructor.
virtual ~Stream() = default;
// Read up to |size| bytes and copy them to |buffer|. Return the number
// of bytes that were actually transferred, or -errno value on error.
virtual ssize_t read(void* buffer, size_t size) = 0;
// Write up to |size| bytes from |buffer| into the stream. Return the
// number of bytes that were actually transferred, or -errno value on
// error.
virtual ssize_t write(const void* buffer, size_t size) = 0;
virtual void* getProtobuf() { return nullptr; }
// Write a single byte |value| into the stream. Ignore errors.
void putByte(uint8_t value);
// Write a 16-bit |value| as big-endian into the stream. Ignore errors.
void putBe16(uint16_t value);
// Write a 32-bit |value| as big-endian into the stream. Ignore errors.
void putBe32(uint32_t value);
// Write a 64-bit |value| as big-endian into the stream. Ignore errors.
void putBe64(uint64_t value);
// Read a single byte from the stream. Return 0 on error.
uint8_t getByte();
// Read a single big-endian 16-bit value from the stream.
// Return 0 on error.
uint16_t getBe16();
// Read a single big-endian 32-bit value from the stream.
// Return 0 on error.
uint32_t getBe32();
// Read a single big-endian 64-bit value from the stream.
// Return 0 on error.
uint64_t getBe64();
// Write a 32-bit float |value| to the stream.
void putFloat(float value);
// Read a single 32-bit float value from the stream.
float getFloat();
// Write a 0-terminated C string |str| into the stream. Ignore error.
void putString(const char* str);
void putString(const std::string& str);
// Write a string |str| of |strlen| bytes into the stream.
// Ignore errors.
void putString(const char* str, size_t strlen);
// Read a string from the stream. Return a new string instance,
// which will be empty on error. Note that this can only be used
// to read strings that were written with putString().
std::string getString();
// Put/gen an integer number into the stream, making it use as little space
// there as possible.
// It uses a simple byte-by-byte encoding scheme, putting 7 bits of the
// number with the 8th bit set when there's more data to read, until the
// whole number is read.
// The compression is efficient if the number range is small, but it starts
// wasting space when values approach 14 bits for int16 (16K), 28 bits for
// int32 (268M) or 56 bits for int64 (still a lot).
void putPackedNum(uint64_t num);
uint64_t getPackedNum();
// Same thing, but encode negative numbers efficiently as well (single sign
// bit + packed unsigned representation)
void putPackedSignedNum(int64_t num);
int64_t getPackedSignedNum();
// Static big-endian conversions
static void toByte(uint8_t*);
static void toBe16(uint8_t*);
static void toBe32(uint8_t*);
static void toBe64(uint8_t*);
static void fromByte(uint8_t*);
static void fromBe16(uint8_t*);
static void fromBe32(uint8_t*);
static void fromBe64(uint8_t*);
};
void saveStringArray(Stream* stream, const char* const* strings, uint32_t count);
} // namespace aemu
} // namespace gfxstream
+179
View File
@@ -0,0 +1,179 @@
/*
* Copyright 2018 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdbool.h>
#include <stdint.h>
#define RING_BUFFER_SHIFT 11
#define RING_BUFFER_SIZE (1 << RING_BUFFER_SHIFT)
#define NUM_CONFIG_FIELDS 32
// Single producer/consumer ring buffer struct that can be shared
// between host and guest as-is.
struct ring_buffer {
uint32_t host_version;
uint32_t guest_version;
uint32_t write_pos; // Atomically updated for the consumer
uint32_t unused0[13]; // Separate cache line
uint32_t read_pos; // Atomically updated for the producer
uint32_t read_live_count;
uint32_t read_yield_count;
uint32_t read_sleep_us_count;
uint32_t unused1[12]; // Separate cache line
uint8_t buf[RING_BUFFER_SIZE];
uint32_t state; // An atomically updated variable from both
// producer and consumer for other forms of
// coordination.
uint32_t config[NUM_CONFIG_FIELDS];
};
void ring_buffer_init(struct ring_buffer* r);
// Writes or reads step_size at a time. Sets errno=EAGAIN if full or empty.
// Returns the number of step_size steps read.
long ring_buffer_write(struct ring_buffer* r, const void* data, uint32_t step_size, uint32_t steps);
long ring_buffer_read(struct ring_buffer* r, void* data, uint32_t step_size, uint32_t steps);
// Like ring_buffer_write / ring_buffer_read, but merely advances the counters
// without reading or writing anything. Returns the number of step_size steps
// advanced.
long ring_buffer_advance_write(struct ring_buffer* r, uint32_t step_size, uint32_t steps);
long ring_buffer_advance_read(struct ring_buffer* r, uint32_t step_size, uint32_t steps);
// If we want to work with dynamically allocated buffers, a separate struct is
// needed; the host and guest are in different address spaces and thus have
// different views of the same memory, with the host and guest having different
// copies of this struct.
struct ring_buffer_view {
uint8_t* buf;
uint32_t size;
uint32_t mask;
};
// Convenience struct that holds a pointer to a ring along with a view. It's a
// common pattern for the ring and the buffer of the view to be shared between
// two entities (in this case, usually guest and host).
struct ring_buffer_with_view {
struct ring_buffer* ring;
struct ring_buffer_view view;
};
// Calculates the highest power of 2 so that
// (1 << shift) <= size.
uint32_t ring_buffer_calc_shift(uint32_t size);
// Initializes ring buffer with view using |buf|. If |size| is not a power of
// two, then the buffer will assume a size equal to the greater power of two
// less than |size|.
void ring_buffer_view_init(struct ring_buffer* r, struct ring_buffer_view* v, uint8_t* buf,
uint32_t size);
void ring_buffer_init_view_only(struct ring_buffer_view* v, uint8_t* buf, uint32_t size);
// Read/write functions with the view.
long ring_buffer_view_write(struct ring_buffer* r, struct ring_buffer_view* v, const void* data,
uint32_t step_size, uint32_t steps);
long ring_buffer_view_read(struct ring_buffer* r, struct ring_buffer_view* v, void* data,
uint32_t step_size, uint32_t steps);
// Usage of ring_buffer as a waitable object.
// These functions will back off if spinning too long.
//
// if |v| is null, it is assumed that the statically allocated ring buffer is
// used.
//
// Returns true if ring buffer became available, false if timed out.
bool ring_buffer_wait_write(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes, uint64_t timeout_us);
bool ring_buffer_wait_read(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes, uint64_t timeout_us);
// read/write fully, blocking if there is nothing to read/write.
void ring_buffer_write_fully(struct ring_buffer* r, struct ring_buffer_view* v, const void* data,
uint32_t bytes);
void ring_buffer_read_fully(struct ring_buffer* r, struct ring_buffer_view* v, void* data,
uint32_t bytes);
// Like read/write fully, but with an abort value. The value is read from
// |abortPtr| each time. If |abortPtr| is null, then behaves the same
// as ring_buffer_(read|write)_fully.
// Returns the actual number of bytes sent or received.
uint32_t ring_buffer_write_fully_with_abort(struct ring_buffer* r, struct ring_buffer_view* v,
const void* data, uint32_t bytes, uint32_t abort_value,
const volatile uint32_t* abort_ptr);
uint32_t ring_buffer_read_fully_with_abort(struct ring_buffer* r, struct ring_buffer_view* v,
void* data, uint32_t bytes, uint32_t abort_value,
const volatile uint32_t* abort_ptr);
uint32_t ring_buffer_view_get_ring_pos(const struct ring_buffer_view* v, uint32_t index);
bool ring_buffer_can_write(const struct ring_buffer* r, uint32_t bytes);
bool ring_buffer_can_read(const struct ring_buffer* r, uint32_t bytes);
bool ring_buffer_view_can_write(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes);
bool ring_buffer_view_can_read(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes);
uint32_t ring_buffer_available_read(const struct ring_buffer* r, const struct ring_buffer_view* v);
uint32_t ring_buffer_available_write(const struct ring_buffer* r, const struct ring_buffer_view* v);
// Copies out contents from the consumer side of
// ring buffer/view |r,v|.
// If there is less available read than |wanted_bytes|,
// returns -1.
// On success, returns 0.
int ring_buffer_copy_contents(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t wanted_bytes, uint8_t* res);
// Lockless synchronization where the consumer is allowed to hang up and go to
// sleep. This can be considered a sort of asymmetric lock for two threads,
// where the consumer can be more sleepy. It captures the pattern we usually use
// for emulator devices; the guest asks the host for something, and some host
// thread services the request and goes back to sleep.
enum ring_buffer_sync_state {
RING_BUFFER_SYNC_PRODUCER_IDLE = 0,
RING_BUFFER_SYNC_PRODUCER_ACTIVE = 1,
RING_BUFFER_SYNC_CONSUMER_HANGING_UP = 2,
RING_BUFFER_SYNC_CONSUMER_HUNG_UP = 3,
};
// Sync state is RING_BUFFER_SYNC_PRODUCER_IDLE.
void ring_buffer_sync_init(struct ring_buffer* r);
// Tries to acquire the channel for sending.
// Returns false if the consumer was in the middle of hanging up,
// true if the producer successfully acquired the channel
// (put it in the RING_BUFFER_SYNC_PRODUCER_ACTIVE state).
bool ring_buffer_producer_acquire(struct ring_buffer* r);
// Same as above, but acquires from RING_BUFFER_SYNC_CONSUMER_HUNG_UP.
bool ring_buffer_producer_acquire_from_hangup(struct ring_buffer* r);
// Waits until the consumer hangs up.
void ring_buffer_producer_wait_hangup(struct ring_buffer* r);
// Sets the state back to RING_BUFFER_SYNC_PRODUCER_IDLE.
void ring_buffer_producer_idle(struct ring_buffer* r);
// There is no symmetric consumer acquire because the consumer can consume with
// the ring buffer being in any state (albeit with long waiting if the producer
// does not send anything)
// Tries to acquire the channel on the consumer side for
// hanging up. Returns false if the producer is in the middle of sending,
// true if the consumer successfully hung up the channel
// (put it in the RING_BUFFER_SYNC_CONSUMER_HUNG_UP state).
bool ring_buffer_consumer_hangup(struct ring_buffer* r);
// Waits until the producer has set the state to
// RING_BUFFER_SYNC_PRODUCER_IDLE.
void ring_buffer_consumer_wait_producer_idle(struct ring_buffer* r);
// Sets the state to hung up.
void ring_buffer_consumer_hung_up(struct ring_buffer* r);
// Convenient function to reschedule thread
void ring_buffer_yield();
#ifdef __cplusplus
}
#endif
+8
View File
@@ -0,0 +1,8 @@
# Copyright 2024 Google LLC
# SPDX-License-Identifier: MIT
inc_aemu = include_directories('include')
files_libaemu = files('Stream.cpp', 'ring_buffer.cpp')
libaemu = static_library('aemu', files_libaemu, include_directories: [inc_aemu])
+545
View File
@@ -0,0 +1,545 @@
/*
* Copyright 2018 Google
* SPDX-License-Identifier: MIT
*/
#include "ring_buffer.h"
#include <errno.h>
#include <string.h>
#ifdef _MSC_VER
#include "aemu/base/msvc.h"
#else
#include <sys/time.h>
#endif
#ifdef __x86_64__
#include <emmintrin.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <sched.h>
#include <unistd.h>
#endif
#define RING_BUFFER_MASK (RING_BUFFER_SIZE - 1)
#define RING_BUFFER_VERSION 1
void ring_buffer_init(struct ring_buffer* r) {
r->host_version = 1;
r->write_pos = 0;
r->read_pos = 0;
r->read_live_count = 0;
r->read_yield_count = 0;
r->read_sleep_us_count = 0;
r->state = 0;
}
static uint32_t get_ring_pos(uint32_t index) { return index & RING_BUFFER_MASK; }
bool ring_buffer_can_write(const struct ring_buffer* r, uint32_t bytes) {
uint32_t read_view;
__atomic_load(&r->read_pos, &read_view, __ATOMIC_SEQ_CST);
return get_ring_pos(read_view - r->write_pos - 1) >= bytes;
}
bool ring_buffer_can_read(const struct ring_buffer* r, uint32_t bytes) {
uint32_t write_view;
__atomic_load(&r->write_pos, &write_view, __ATOMIC_SEQ_CST);
return get_ring_pos(write_view - r->read_pos) >= bytes;
}
long ring_buffer_write(struct ring_buffer* r, const void* data, uint32_t step_size,
uint32_t steps) {
const uint8_t* data_bytes = (const uint8_t*)data;
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_can_write(r, step_size)) {
errno = -EAGAIN;
return (long)i;
}
// Needs to be split up into 2 writes for the edge case.
uint32_t available_at_end = RING_BUFFER_SIZE - get_ring_pos(r->write_pos);
if (step_size > available_at_end) {
uint32_t remaining = step_size - available_at_end;
memcpy(&r->buf[get_ring_pos(r->write_pos)], data_bytes + i * step_size,
available_at_end);
memcpy(&r->buf[get_ring_pos(r->write_pos + available_at_end)],
data_bytes + i * step_size + available_at_end, remaining);
} else {
memcpy(&r->buf[get_ring_pos(r->write_pos)], data_bytes + i * step_size, step_size);
}
__atomic_add_fetch(&r->write_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
long ring_buffer_read(struct ring_buffer* r, void* data, uint32_t step_size, uint32_t steps) {
uint8_t* data_bytes = (uint8_t*)data;
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_can_read(r, step_size)) {
errno = -EAGAIN;
return (long)i;
}
// Needs to be split up into 2 reads for the edge case.
uint32_t available_at_end = RING_BUFFER_SIZE - get_ring_pos(r->read_pos);
if (step_size > available_at_end) {
uint32_t remaining = step_size - available_at_end;
memcpy(data_bytes + i * step_size, &r->buf[get_ring_pos(r->read_pos)],
available_at_end);
memcpy(data_bytes + i * step_size + available_at_end,
&r->buf[get_ring_pos(r->read_pos + available_at_end)], remaining);
} else {
memcpy(data_bytes + i * step_size, &r->buf[get_ring_pos(r->read_pos)], step_size);
}
__atomic_add_fetch(&r->read_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
long ring_buffer_advance_write(struct ring_buffer* r, uint32_t step_size, uint32_t steps) {
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_can_write(r, step_size)) {
errno = -EAGAIN;
return (long)i;
}
__atomic_add_fetch(&r->write_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
long ring_buffer_advance_read(struct ring_buffer* r, uint32_t step_size, uint32_t steps) {
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_can_read(r, step_size)) {
errno = -EAGAIN;
return (long)i;
}
__atomic_add_fetch(&r->read_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
uint32_t ring_buffer_calc_shift(uint32_t size) {
uint32_t shift = 0;
while ((1u << shift) < size) {
++shift;
}
// if size is not a power of 2,
if ((1u << shift) > size) {
--shift;
}
return shift;
}
void ring_buffer_view_init(struct ring_buffer* r, struct ring_buffer_view* v, uint8_t* buf,
uint32_t size) {
uint32_t shift = ring_buffer_calc_shift(size);
ring_buffer_init(r);
v->buf = buf;
v->size = (1u << shift);
v->mask = (1u << shift) - 1;
}
void ring_buffer_init_view_only(struct ring_buffer_view* v, uint8_t* buf, uint32_t size) {
uint32_t shift = ring_buffer_calc_shift(size);
v->buf = buf;
v->size = (1u << shift);
v->mask = (1u << shift) - 1;
}
uint32_t ring_buffer_view_get_ring_pos(const struct ring_buffer_view* v, uint32_t index) {
return index & v->mask;
}
bool ring_buffer_view_can_write(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes) {
uint32_t read_view;
__atomic_load(&r->read_pos, &read_view, __ATOMIC_SEQ_CST);
return ring_buffer_view_get_ring_pos(v, read_view - r->write_pos - 1) >= bytes;
}
bool ring_buffer_view_can_read(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes) {
uint32_t write_view;
__atomic_load(&r->write_pos, &write_view, __ATOMIC_SEQ_CST);
return ring_buffer_view_get_ring_pos(v, write_view - r->read_pos) >= bytes;
}
uint32_t ring_buffer_available_read(const struct ring_buffer* r, const struct ring_buffer_view* v) {
uint32_t write_view;
__atomic_load(&r->write_pos, &write_view, __ATOMIC_SEQ_CST);
if (v) {
return ring_buffer_view_get_ring_pos(v, write_view - r->read_pos);
} else {
return get_ring_pos(write_view - r->read_pos);
}
}
uint32_t ring_buffer_available_write(const struct ring_buffer* r,
const struct ring_buffer_view* v) {
uint32_t read_view;
__atomic_load(&r->read_pos, &read_view, __ATOMIC_SEQ_CST);
if (v) {
return ring_buffer_view_get_ring_pos(v, read_view - r->write_pos - 1);
} else {
return get_ring_pos(read_view - r->write_pos - 1);
}
}
int ring_buffer_copy_contents(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t wanted_bytes, uint8_t* res) {
uint32_t total_available = ring_buffer_available_read(r, v);
uint32_t available_at_end = 0;
if (v) {
available_at_end = v->size - ring_buffer_view_get_ring_pos(v, r->read_pos);
} else {
available_at_end = RING_BUFFER_SIZE - get_ring_pos(r->write_pos);
}
if (total_available < wanted_bytes) {
return -1;
}
if (v) {
if (wanted_bytes > available_at_end) {
uint32_t remaining = wanted_bytes - available_at_end;
memcpy(res, &v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos)], available_at_end);
memcpy(res + available_at_end,
&v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos + available_at_end)],
remaining);
} else {
memcpy(res, &v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos)], wanted_bytes);
}
} else {
if (wanted_bytes > available_at_end) {
uint32_t remaining = wanted_bytes - available_at_end;
memcpy(res, &r->buf[get_ring_pos(r->read_pos)], available_at_end);
memcpy(res + available_at_end, &r->buf[get_ring_pos(r->read_pos + available_at_end)],
remaining);
} else {
memcpy(res, &r->buf[get_ring_pos(r->read_pos)], wanted_bytes);
}
}
return 0;
}
long ring_buffer_view_write(struct ring_buffer* r, struct ring_buffer_view* v, const void* data,
uint32_t step_size, uint32_t steps) {
uint8_t* data_bytes = (uint8_t*)data;
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_view_can_write(r, v, step_size)) {
errno = -EAGAIN;
return (long)i;
}
// Needs to be split up into 2 writes for the edge case.
uint32_t available_at_end = v->size - ring_buffer_view_get_ring_pos(v, r->write_pos);
if (step_size > available_at_end) {
uint32_t remaining = step_size - available_at_end;
memcpy(&v->buf[ring_buffer_view_get_ring_pos(v, r->write_pos)],
data_bytes + i * step_size, available_at_end);
memcpy(&v->buf[ring_buffer_view_get_ring_pos(v, r->write_pos + available_at_end)],
data_bytes + i * step_size + available_at_end, remaining);
} else {
memcpy(&v->buf[ring_buffer_view_get_ring_pos(v, r->write_pos)],
data_bytes + i * step_size, step_size);
}
__atomic_add_fetch(&r->write_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
long ring_buffer_view_read(struct ring_buffer* r, struct ring_buffer_view* v, void* data,
uint32_t step_size, uint32_t steps) {
uint8_t* data_bytes = (uint8_t*)data;
uint32_t i;
for (i = 0; i < steps; ++i) {
if (!ring_buffer_view_can_read(r, v, step_size)) {
errno = -EAGAIN;
return (long)i;
}
// Needs to be split up into 2 reads for the edge case.
uint32_t available_at_end = v->size - ring_buffer_view_get_ring_pos(v, r->read_pos);
if (step_size > available_at_end) {
uint32_t remaining = step_size - available_at_end;
memcpy(data_bytes + i * step_size,
&v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos)], available_at_end);
memcpy(data_bytes + i * step_size + available_at_end,
&v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos + available_at_end)],
remaining);
} else {
memcpy(data_bytes + i * step_size,
&v->buf[ring_buffer_view_get_ring_pos(v, r->read_pos)], step_size);
}
__atomic_add_fetch(&r->read_pos, step_size, __ATOMIC_SEQ_CST);
}
errno = 0;
return (long)steps;
}
void ring_buffer_yield() {
#ifdef _WIN32
_mm_pause();
#else
sched_yield();
#endif
}
static void ring_buffer_sleep() {
#ifdef _WIN32
Sleep(2);
#else
usleep(2000);
#endif
}
static uint64_t ring_buffer_curr_us() {
uint64_t res;
struct timeval tv;
gettimeofday(&tv, NULL);
res = tv.tv_sec * 1000000ULL + tv.tv_usec;
return res;
}
static const uint32_t yield_backoff_us = 1000;
static const uint32_t sleep_backoff_us = 2000;
bool ring_buffer_wait_write(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes, uint64_t timeout_us) {
uint64_t start_us = ring_buffer_curr_us();
uint64_t curr_wait_us;
bool can_write = v ? ring_buffer_view_can_write(r, v, bytes) : ring_buffer_can_write(r, bytes);
while (!can_write) {
#ifdef __x86_64
_mm_pause();
#endif
curr_wait_us = ring_buffer_curr_us() - start_us;
if (curr_wait_us > yield_backoff_us) {
ring_buffer_yield();
}
if (curr_wait_us > sleep_backoff_us) {
ring_buffer_sleep();
}
if (curr_wait_us > timeout_us) {
return false;
}
can_write = v ? ring_buffer_view_can_write(r, v, bytes) : ring_buffer_can_write(r, bytes);
}
return true;
}
bool ring_buffer_wait_read(const struct ring_buffer* r, const struct ring_buffer_view* v,
uint32_t bytes, uint64_t timeout_us) {
uint64_t start_us = ring_buffer_curr_us();
uint64_t curr_wait_us;
bool can_read = v ? ring_buffer_view_can_read(r, v, bytes) : ring_buffer_can_read(r, bytes);
while (!can_read) {
// TODO(bohu): find aarch64 equivalent
#ifdef __x86_64
_mm_pause();
#endif
curr_wait_us = ring_buffer_curr_us() - start_us;
if (curr_wait_us > yield_backoff_us) {
ring_buffer_yield();
((struct ring_buffer*)r)->read_yield_count++;
}
if (curr_wait_us > sleep_backoff_us) {
ring_buffer_sleep();
((struct ring_buffer*)r)->read_sleep_us_count += 2000;
}
if (curr_wait_us > timeout_us) {
return false;
}
can_read = v ? ring_buffer_view_can_read(r, v, bytes) : ring_buffer_can_read(r, bytes);
}
((struct ring_buffer*)r)->read_live_count++;
return true;
}
static uint32_t get_step_size(struct ring_buffer* r, struct ring_buffer_view* v, uint32_t bytes) {
uint32_t available = v ? (v->size >> 1) : (RING_BUFFER_SIZE >> 1);
uint32_t res = available < bytes ? available : bytes;
return res;
}
void ring_buffer_write_fully(struct ring_buffer* r, struct ring_buffer_view* v, const void* data,
uint32_t bytes) {
ring_buffer_write_fully_with_abort(r, v, data, bytes, 0, 0);
}
void ring_buffer_read_fully(struct ring_buffer* r, struct ring_buffer_view* v, void* data,
uint32_t bytes) {
ring_buffer_read_fully_with_abort(r, v, data, bytes, 0, 0);
}
uint32_t ring_buffer_write_fully_with_abort(struct ring_buffer* r, struct ring_buffer_view* v,
const void* data, uint32_t bytes, uint32_t abort_value,
const volatile uint32_t* abort_ptr) {
uint32_t candidate_step = get_step_size(r, v, bytes);
uint32_t processed = 0;
uint8_t* dst = (uint8_t*)data;
while (processed < bytes) {
if (bytes - processed < candidate_step) {
candidate_step = bytes - processed;
}
long processed_here = 0;
ring_buffer_wait_write(r, v, candidate_step, (uint64_t)(-1));
if (v) {
processed_here = ring_buffer_view_write(r, v, dst + processed, candidate_step, 1);
} else {
processed_here = ring_buffer_write(r, dst + processed, candidate_step, 1);
}
processed += processed_here ? candidate_step : 0;
if (abort_ptr && (abort_value == *abort_ptr)) {
return processed;
}
}
return processed;
}
uint32_t ring_buffer_read_fully_with_abort(struct ring_buffer* r, struct ring_buffer_view* v,
void* data, uint32_t bytes, uint32_t abort_value,
const volatile uint32_t* abort_ptr) {
uint32_t candidate_step = get_step_size(r, v, bytes);
uint32_t processed = 0;
uint8_t* dst = (uint8_t*)data;
while (processed < bytes) {
#ifdef __x86_64
_mm_pause();
#endif
if (bytes - processed < candidate_step) {
candidate_step = bytes - processed;
}
long processed_here = 0;
ring_buffer_wait_read(r, v, candidate_step, (uint64_t)(-1));
if (v) {
processed_here = ring_buffer_view_read(r, v, dst + processed, candidate_step, 1);
} else {
processed_here = ring_buffer_read(r, dst + processed, candidate_step, 1);
}
processed += processed_here ? candidate_step : 0;
if (abort_ptr && (abort_value == *abort_ptr)) {
return processed;
}
}
return processed;
}
void ring_buffer_sync_init(struct ring_buffer* r) {
__atomic_store_n(&r->state, RING_BUFFER_SYNC_PRODUCER_IDLE, __ATOMIC_SEQ_CST);
}
bool ring_buffer_producer_acquire(struct ring_buffer* r) {
uint32_t expected_idle = RING_BUFFER_SYNC_PRODUCER_IDLE;
bool success =
__atomic_compare_exchange_n(&r->state, &expected_idle, RING_BUFFER_SYNC_PRODUCER_ACTIVE,
false /* strong */, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return success;
}
bool ring_buffer_producer_acquire_from_hangup(struct ring_buffer* r) {
uint32_t expected_hangup = RING_BUFFER_SYNC_CONSUMER_HUNG_UP;
bool success =
__atomic_compare_exchange_n(&r->state, &expected_hangup, RING_BUFFER_SYNC_PRODUCER_ACTIVE,
false /* strong */, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return success;
}
void ring_buffer_producer_wait_hangup(struct ring_buffer* r) {
while (__atomic_load_n(&r->state, __ATOMIC_SEQ_CST) != RING_BUFFER_SYNC_CONSUMER_HUNG_UP) {
ring_buffer_yield();
}
}
void ring_buffer_producer_idle(struct ring_buffer* r) {
__atomic_store_n(&r->state, RING_BUFFER_SYNC_PRODUCER_IDLE, __ATOMIC_SEQ_CST);
}
bool ring_buffer_consumer_hangup(struct ring_buffer* r) {
uint32_t expected_idle = RING_BUFFER_SYNC_PRODUCER_IDLE;
bool success =
__atomic_compare_exchange_n(&r->state, &expected_idle, RING_BUFFER_SYNC_CONSUMER_HANGING_UP,
false /* strong */, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST);
return success;
}
void ring_buffer_consumer_wait_producer_idle(struct ring_buffer* r) {
while (__atomic_load_n(&r->state, __ATOMIC_SEQ_CST) != RING_BUFFER_SYNC_PRODUCER_IDLE) {
ring_buffer_yield();
}
}
void ring_buffer_consumer_hung_up(struct ring_buffer* r) {
__atomic_store_n(&r->state, RING_BUFFER_SYNC_CONSUMER_HUNG_UP, __ATOMIC_SEQ_CST);
}
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
# Copyright 2022 Android Open Source Project
# SPDX-License-Identifier: MIT
# Please run this from the Mesa codegen directory
# >> cd ${mesa_dir}/src/gfxstream/codegen
# >> ./generate-gfxstream-vulkan.sh ${gfxstream_dir}
#
# Note in AOSP, ${gfxstream_dir} is optional, and the script autodetects the
# path to gfxstream.
export MESA_DIR="$PWD/../../.."
if [ -z "$1" ];
then
export GFXSTREAM_DIR="$MESA_DIR/../../hardware/google/gfxstream"
else
export GFXSTREAM_DIR="$1"
fi
export PREFIX_DIR="src/gfxstream"
# We should use just use one vk.xml eventually..
export VK_MESA_XML="$MESA_DIR/src/vulkan/registry/vk.xml"
export VK_XML="$GFXSTREAM_DIR/third_party/vulkan_docs/xml/vk.xml"
export GFXSTREAM_GUEST_ENCODER_DIR="/tmp/"
export GFXSTREAM_HOST_DECODER_DIR="$GFXSTREAM_DIR/host/vulkan"
export GFXSTREAM_OUTPUT_DIR="$GFXSTREAM_HOST_DECODER_DIR/cereal"
export GFXSTREAM_SCRIPTS_DIR="$GFXSTREAM_DIR/scripts"
export GEN_VK="$MESA_DIR/$PREFIX_DIR/codegen/scripts/genvk.py"
export CUSTOM_XML="$MESA_DIR/$PREFIX_DIR/codegen/xml/vk_gfxstream.xml"
python3 "$GEN_VK" -registry "$VK_XML" -registryGfxstream "$CUSTOM_XML" cereal -o "$GFXSTREAM_OUTPUT_DIR"
export CEREAL_VARIANT=guest
export GFXSTREAM_GUEST_ENCODER_DIR="$MESA_DIR/src/gfxstream/guest/vulkan_enc"
python3 "$GEN_VK" -registry "$VK_MESA_XML" -registryGfxstream "$CUSTOM_XML" cereal -o /tmp/
# Should have a unified headers dir here:
python3 "$GEN_VK" -registry "$CUSTOM_XML" vulkan_gfxstream.h -o "$GFXSTREAM_GUEST_ENCODER_DIR"
python3 "$GEN_VK" -registry "$CUSTOM_XML" vulkan_gfxstream.h -o "$GFXSTREAM_HOST_DECODER_DIR"
View File
Whitespace-only changes.
@@ -0,0 +1,20 @@
from .common import *
from .decoder import *
from .encoder import *
from .extensionstructs import *
from .frontend import *
from .functable import *
from .marshaling import *
from .reservedmarshaling import *
from .counting import *
from .testing import *
from .transform import *
from .deepcopy import *
from .handlemap import *
from .dispatch import *
from .unbox import *
from .decodersnapshot import *
from .subdecode import *
from .api_log_decoder import *
from .vkextensionstructuretype import VulkanGfxstreamStructureType, \
VulkanAndroidNativeBufferStructureType
@@ -0,0 +1,344 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: MIT
import os
from typing import List, Set, Dict, Optional
from . import VulkanType, VulkanCompoundType
from .wrapperdefs import VulkanWrapperGenerator
class ApiLogDecoder(VulkanWrapperGenerator):
"""
This class generates decoding logic for the graphics API logs captured by
[GfxApiLogger](http://source/play-internal/battlestar/aosp/device/generic/vulkan-cereal/base/GfxApiLogger.h)
This allows developers to see a pretty-printed version of the API log data when using
print_gfx_logs.py
"""
# List of Vulkan APIs that we will generate decoding logic for
generated_apis = [
"vkAcquireImageANDROID",
"vkAllocateMemory",
"vkBeginCommandBufferAsyncGOOGLE",
"vkBindBufferMemory",
"vkBindImageMemory",
"vkCmdBeginRenderPass",
"vkCmdBindDescriptorSets",
"vkCmdBindIndexBuffer",
"vkCmdBindPipeline",
"vkCmdBindVertexBuffers",
"vkCmdClearAttachments",
"vkCmdClearColorImage",
"vkCmdCopyBufferToImage",
"vkCmdCopyImageToBuffer",
"vkCmdDraw",
"vkCmdDrawIndexed",
"vkCmdEndRenderPass",
"vkCmdPipelineBarrier",
"vkCmdPipelineBarrier2",
"vkCmdSetScissor",
"vkCmdSetViewport",
"vkCollectDescriptorPoolIdsGOOGLE",
"vkCreateBufferWithRequirementsGOOGLE",
"vkCreateDescriptorPool",
"vkCreateDescriptorSetLayout",
"vkCreateFence",
"vkCreateFramebuffer",
"vkCreateGraphicsPipelines",
"vkCreateImageView",
"vkCreateImageWithRequirementsGOOGLE",
"vkCreatePipelineCache",
"vkCreatePipelineLayout",
"vkCreateRenderPass",
"vkCreateSampler",
"vkCreateSemaphore",
"vkCreateShaderModule",
"vkDestroyBuffer",
"vkDestroyCommandPool",
"vkDestroyDescriptorPool",
"vkDestroyDescriptorSetLayout",
"vkDestroyDevice",
"vkDestroyFence",
"vkDestroyFramebuffer",
"vkDestroyImage",
"vkDestroyImageView",
"vkDestroyInstance",
"vkDestroyPipeline",
"vkDestroyPipelineCache",
"vkDestroyPipelineLayout",
"vkDestroyRenderPass",
"vkDestroySemaphore",
"vkDestroyShaderModule",
"vkEndCommandBufferAsyncGOOGLE",
"vkFreeCommandBuffers",
"vkFreeMemory",
"vkFreeMemorySyncGOOGLE",
"vkGetFenceStatus",
"vkGetMemoryHostAddressInfoGOOGLE",
"vkGetBlobGOOGLE",
"vkGetPhysicalDeviceFormatProperties",
"vkGetPhysicalDeviceProperties2KHR",
"vkGetPipelineCacheData",
"vkGetSemaphoreGOOGLE",
"vkGetSwapchainGrallocUsageANDROID",
"vkQueueCommitDescriptorSetUpdatesGOOGLE",
"vkQueueFlushCommandsGOOGLE",
"vkQueueSignalReleaseImageANDROIDAsyncGOOGLE",
"vkQueueSubmitAsyncGOOGLE",
"vkQueueWaitIdle",
"vkResetFences",
"vkWaitForFences",
]
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.typeInfo = typeInfo
# Set of Vulkan structs that we need to write decoding logic for
self.structs: Set[str] = set()
# Maps enum group names to the list of enums in the group, for all enum groups in the spec
# E.g.: "VkResult": ["VK_SUCCESS", "VK_NOT_READY", "VK_TIMEOUT", etc...]
self.all_enums: Dict[str, List[str]] = {}
# Set of Vulkan enums that we need to write decoding logic for
self.needed_enums: Set[str] = {"VkStructureType"}
def onBegin(self):
self.module.append("""
#####################################################################################################
# Pretty-printer functions for Vulkan data structures
# THIS FILE IS AUTO-GENERATED - DO NOT EDIT
#
# To re-generate this file, run generate-vulkan-sources.sh
#####################################################################################################
""".lstrip())
def onGenGroup(self, groupinfo, groupName, alias=None):
"""Called for each enum group in the spec"""
for enum in groupinfo.elem.findall("enum"):
self.all_enums[groupName] = self.all_enums.get(groupName, []) + [enum.get('name')]
def onEnd(self):
for api_name in sorted(self.generated_apis):
self.process_api(api_name)
self.process_structs()
self.process_enums()
def process_api(self, api_name):
"""Main entry point to generate decoding logic for each Vulkan API"""
api = self.typeInfo.apis[api_name]
self.module.append('def OP_{}(printer, indent: int):\n'.format(api_name))
# Decode the sequence number. All commands have sequence numbers, except those handled
# by VkSubdecoder.cpp. The logic here is a bit of a hack since it's based on the command
# name. Ideally, we would detect whether a particular command is part of a subdecode block
# in the decoding script.
if not api_name.startswith("vkCmd") and api_name != "vkBeginCommandBufferAsyncGOOGLE":
self.module.append(' printer.write_int("seqno: ", 4, indent)\n')
for param in api.parameters:
# Add any structs that this API uses to the list of structs to write decoding logic for
if self.typeInfo.isCompoundType(param.typeName):
self.structs.add(param.typeName)
# Don't try to print the pData field of vkQueueFlushCommandsGOOGLE, those are the
# commands processed as part of the subdecode pass
if api.name == "vkQueueFlushCommandsGOOGLE" and param.paramName == "pData":
continue
# Write out decoding logic for that parameter
self.process_type(param)
# Finally, add a return statement. This is needed in case the API has no parameters.
self.module.append(' return\n\n')
def process_structs(self):
"""Writes decoding logic for all the structs that we use"""
# self.structs now contains all the structs used directly by the Vulkan APIs we use.
# Recursively expand this set to add all the structs used by these structs.
copy = self.structs.copy()
self.structs.clear()
for struct_name in copy:
self.expand_needed_structs(struct_name)
# Now we have the full list of structs that we need to write decoding logic for.
# Write a decoder for each of them
for struct_name in sorted(self.structs):
struct = self.typeInfo.structs[struct_name]
self.module.append('def struct_{}(printer, indent: int):\n'.format(struct_name))
for member in self.get_members(struct):
self.process_type(member)
self.module.append('\n')
def expand_needed_structs(self, struct_name: str):
"""
Recursively adds all the structs used by a given struct to the list of structs to process
"""
if struct_name in self.structs:
return
self.structs.add(struct_name)
struct = self.typeInfo.structs[struct_name]
for member in self.get_members(struct):
if self.typeInfo.isCompoundType(member.typeName):
self.expand_needed_structs(member.typeName)
def get_members(self, struct: VulkanCompoundType):
"""
Returns the members of a struct/union that we need to process.
For structs, returns the list of all members
For unions, returns a list with just the first member.
"""
return struct.members[0:1] if struct.isUnion else struct.members
def process_type(self, type: VulkanType):
"""
Writes decoding logic for a single Vulkan type. This could be the parameter in a Vulkan API,
or a struct member.
"""
if type.typeName == "VkStructureType":
self.module.append(
' printer.write_stype_and_pnext("{}", indent)\n'.format(
type.parent.structEnumExpr))
return
if type.isNextPointer():
return
if type.paramName == "commandBuffer":
if type.parent.name != "vkQueueFlushCommandsGOOGLE":
return
# Enums
if type.isEnum(self.typeInfo):
self.needed_enums.add(type.typeName)
self.module.append(
' printer.write_enum("{}", {}, indent)\n'.format(
type.paramName, type.typeName))
return
# Bitmasks
if type.isBitmask(self.typeInfo):
enum_type = self.typeInfo.bitmasks.get(type.typeName)
if enum_type:
self.needed_enums.add(enum_type)
self.module.append(
' printer.write_flags("{}", {}, indent)\n'.format(
type.paramName, enum_type))
return
# else, fall through and let the primitive type logic handle it
# Structs or unions
if self.typeInfo.isCompoundType(type.typeName):
self.module.append(
' printer.write_struct("{name}", struct_{type}, {optional}, {count}, indent)\n'
.format(name=type.paramName,
type=type.typeName,
optional=type.isOptionalPointer(),
count=self.get_length_expression(type)))
return
# Null-terminated strings
if type.isString():
self.module.append(' printer.write_string("{}", None, indent)\n'.format(
type.paramName))
return
# Arrays of primitive types
if type.staticArrExpr and type.primitiveEncodingSize and type.primitiveEncodingSize <= 8:
# Array sizes are specified either as a number, or as an enum value
array_size = int(type.staticArrExpr) if type.staticArrExpr.isdigit() \
else self.typeInfo.enumValues.get(type.staticArrExpr)
assert array_size is not None, type.staticArrExpr
if type.typeName == "char":
self.module.append(
' printer.write_string("{}", {}, indent)\n'.format(
type.paramName, array_size))
elif type.typeName == "float":
self.module.append(
' printer.write_float("{}", indent, count={})\n'
.format(type.paramName, array_size))
else:
self.module.append(
' printer.write_int("{name}", {int_size}, indent, signed={signed}, count={array_size})\n'
.format(name=type.paramName,
array_size=array_size,
int_size=type.primitiveEncodingSize,
signed=type.isSigned()))
return
# Pointers
if type.pointerIndirectionLevels > 0:
# Assume that all uint32* are always serialized directly rather than passed by pointers.
# This is probably not always true (e.g. out params) - fix this as needed.
size = 4 if type.primitiveEncodingSize == 4 else 8
self.module.append(
' {name} = printer.write_int("{name}", {size}, indent, optional={opt}, count={count}, big_endian={big_endian})\n'
.format(name=type.paramName,
size=size,
opt=type.isOptionalPointer(),
count=self.get_length_expression(type),
big_endian=self.using_big_endian(type)))
return
# Primitive types (ints, floats)
if type.isSimpleValueType(self.typeInfo) and type.primitiveEncodingSize:
if type.typeName == "float":
self.module.append(
' printer.write_float("{name}", indent)\n'.format(name=type.paramName))
else:
self.module.append(
' {name} = printer.write_int("{name}", {size}, indent, signed={signed}, big_endian={big_endian})\n'.format(
name=type.paramName,
size=type.primitiveEncodingSize,
signed=type.isSigned(),
big_endian=self.using_big_endian(type))
)
return
raise NotImplementedError(
"No decoding logic for {} {}".format(type.typeName, type.paramName))
def using_big_endian(self, type: VulkanType):
"""For some reason gfxstream serializes some types as big endian"""
return type.typeName == "size_t"
def get_length_expression(self, type: VulkanType) -> Optional[str]:
"""Returns the length expression for a given type"""
if type.lenExpr is None:
return None
if type.lenExpr.isalpha():
return type.lenExpr
# There are a couple of instances in the spec where we use a math expression to express the
# length (e.g. VkPipelineMultisampleStateCreateInfo). CodeGen().generalLengthAccess() has
# logic o parse these expressions correctly, but for now,we just use a simple lookup table.
known_expressions = {
r"latexmath:[\lceil{\mathit{rasterizationSamples} \over 32}\rceil]":
"int(rasterizationSamples / 32)",
r"latexmath:[\textrm{codeSize} \over 4]": "int(codeSize / 4)",
r"null-terminated": None
}
if type.lenExpr in known_expressions:
return known_expressions[type.lenExpr]
raise NotImplementedError("Unknown length expression: " + type.lenExpr)
def process_enums(self):
"""
For each Vulkan enum that we use, write out a python dictionary mapping the enum values back
to the enum name as a string
"""
for enum_name in sorted(self.needed_enums):
self.module.append('{} = {{\n'.format(enum_name))
for identifier in self.all_enums[enum_name]:
value = self.typeInfo.enumValues.get(identifier)
if value is not None and isinstance(value, int):
self.module.append(' {}: "{}",\n'.format(value, identifier))
self.module.append('}\n\n')
@@ -0,0 +1,4 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: MIT
from .vulkantypes import *
from .codegen import *
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,681 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: MIT
from copy import copy
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator, Atom, FuncExpr, FuncExprVal, FuncLambda
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import ROOT_TYPE_VAR_NAME, ROOT_TYPE_PARAM
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME
class VulkanCountingCodegen(VulkanTypeIterator):
def __init__(self, cgen, featureBitsVar, toCountVar, countVar, rootTypeVar, prefix, forApiOutput=False, mapHandles=True, handleMapOverwrites=False, doFiltering=True):
self.cgen = cgen
self.featureBitsVar = featureBitsVar
self.toCountVar = toCountVar
self.rootTypeVar = rootTypeVar
self.countVar = countVar
self.prefix = prefix
self.forApiOutput = forApiOutput
self.exprAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = True)
self.exprValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = False)
self.exprPrimitiveValueAccessor = lambda t: self.cgen.generalAccess(t, parentVarName = self.toCountVar, asPtr = False)
self.lenAccessor = lambda t: self.cgen.generalLengthAccess(t, parentVarName = self.toCountVar)
self.lenAccessorGuard = lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName = self.toCountVar)
self.filterVarAccessor = lambda t: self.cgen.filterVarAccess(t, parentVarName = self.toCountVar)
self.checked = False
self.mapHandles = mapHandles
self.handleMapOverwrites = handleMapOverwrites
self.doFiltering = doFiltering
def getTypeForStreaming(self, vulkanType):
res = copy(vulkanType)
if not vulkanType.accessibleAsPointer():
res = res.getForAddressAccess()
if vulkanType.staticArrExpr:
res = res.getForAddressAccess()
return res
def makeCastExpr(self, vulkanType):
return "(%s)" % (
self.cgen.makeCTypeDecl(vulkanType, useParamName=False))
def genCount(self, sizeExpr):
self.cgen.stmt("*%s += %s" % (self.countVar, sizeExpr))
def genPrimitiveStreamCall(self, vulkanType):
self.genCount(str(self.cgen.countPrimitive(
self.typeInfo,
vulkanType)))
def genHandleMappingCall(self, vulkanType, access, lenAccess):
if lenAccess is None:
lenAccess = "1"
handle64Bytes = "8"
else:
handle64Bytes = "%s * 8" % lenAccess
handle64Var = self.cgen.var()
if lenAccess != "1":
self.cgen.beginIf(lenAccess)
# self.cgen.stmt("uint64_t* %s" % handle64Var)
# self.cgen.stmt(
# "%s->alloc((void**)&%s, %s * 8)" % \
# (self.streamVarName, handle64Var, lenAccess))
handle64VarAccess = handle64Var
handle64VarType = \
makeVulkanTypeSimple(False, "uint64_t", 1, paramName=handle64Var)
else:
self.cgen.stmt("uint64_t %s" % handle64Var)
self.cgen.stmt("(void)%s" % handle64Var)
handle64VarAccess = "&%s" % handle64Var
handle64VarType = \
makeVulkanTypeSimple(False, "uint64_t", 0, paramName=handle64Var)
if self.handleMapOverwrites:
# self.cgen.stmt(
# "static_assert(8 == sizeof(%s), \"handle map overwrite requres %s to be 8 bytes long\")" % \
# (vulkanType.typeName, vulkanType.typeName))
# self.cgen.stmt(
# "%s->handleMapping()->mapHandles_%s((%s*)%s, %s)" %
# (self.streamVarName, vulkanType.typeName, vulkanType.typeName,
# access, lenAccess))
self.genCount("8 * %s" % lenAccess)
else:
# self.cgen.stmt(
# "%s->handleMapping()->mapHandles_%s_u64(%s, %s, %s)" %
# (self.streamVarName, vulkanType.typeName,
# access,
# handle64VarAccess, lenAccess))
self.genCount(handle64Bytes)
if lenAccess != "1":
self.cgen.endIf()
def doAllocSpace(self, vulkanType):
pass
def getOptionalStringFeatureExpr(self, vulkanType):
feature = vulkanType.getProtectStreamFeature()
if feature is None:
return None
return "%s & %s" % (self.featureBitsVar, feature)
def onCheck(self, vulkanType):
if self.forApiOutput:
return
featureExpr = self.getOptionalStringFeatureExpr(vulkanType);
self.checked = True
access = self.exprAccessor(vulkanType)
needConsistencyCheck = False
self.cgen.line("// WARNING PTR CHECK")
checkAccess = self.exprAccessor(vulkanType)
addrExpr = "&" + checkAccess
sizeExpr = self.cgen.sizeofExpr(vulkanType)
if featureExpr is not None:
self.cgen.beginIf(featureExpr)
self.genPrimitiveStreamCall(
vulkanType)
if featureExpr is not None:
self.cgen.endIf()
if featureExpr is not None:
self.cgen.beginIf("(!(%s) || %s)" % (featureExpr, access))
else:
self.cgen.beginIf(access)
if needConsistencyCheck and featureExpr is None:
self.cgen.beginIf("!(%s)" % checkName)
self.cgen.stmt(
"fprintf(stderr, \"fatal: %s inconsistent between guest and host\\n\")" % (access))
self.cgen.endIf()
def onCheckWithNullOptionalStringFeature(self, vulkanType):
self.cgen.beginIf("%s & VULKAN_STREAM_FEATURE_NULL_OPTIONAL_STRINGS_BIT" % self.featureBitsVar)
self.onCheck(vulkanType)
def endCheckWithNullOptionalStringFeature(self, vulkanType):
self.endCheck(vulkanType)
self.cgen.endIf()
self.cgen.beginElse()
def finalCheckWithNullOptionalStringFeature(self, vulkanType):
self.cgen.endElse()
def endCheck(self, vulkanType):
if self.checked:
self.cgen.endIf()
self.checked = False
def genFilterFunc(self, filterfunc, env):
def loop(expr, lambdaEnv={}):
def do_func(expr):
fnamestr = expr.name.name
if "not" == fnamestr:
return "!(%s)" % (loop(expr.args[0], lambdaEnv))
if "eq" == fnamestr:
return "(%s == %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv))
if "and" == fnamestr:
return "(%s && %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv))
if "or" == fnamestr:
return "(%s || %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv))
if "bitwise_and" == fnamestr:
return "(%s & %s)" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv))
if "getfield" == fnamestr:
ptrlevels = get_ptrlevels(expr.args[0].val.name)
if ptrlevels == 0:
return "%s.%s" % (loop(expr.args[0], lambdaEnv), expr.args[1].val)
else:
return "(%s(%s)).%s" % ("*" * ptrlevels, loop(expr.args[0], lambdaEnv), expr.args[1].val)
if "if" == fnamestr:
return "((%s) ? (%s) : (%s))" % (loop(expr.args[0], lambdaEnv), loop(expr.args[1], lambdaEnv), loop(expr.args[2], lambdaEnv))
return "%s(%s)" % (fnamestr, ", ".join(map(lambda e: loop(e, lambdaEnv), expr.args)))
def do_expratom(atomname, lambdaEnv= {}):
if lambdaEnv.get(atomname, None) is not None:
return atomname
enventry = env.get(atomname, None)
if None != enventry:
return self.getEnvAccessExpr(atomname)
return atomname
def get_ptrlevels(atomname, lambdaEnv= {}):
if lambdaEnv.get(atomname, None) is not None:
return 0
enventry = env.get(atomname, None)
if None != enventry:
return self.getPointerIndirectionLevels(atomname)
return 0
def do_exprval(expr, lambdaEnv= {}):
expratom = expr.val
if Atom == type(expratom):
return do_expratom(expratom.name, lambdaEnv)
return "%s" % expratom
def do_lambda(expr, lambdaEnv= {}):
params = expr.vs
body = expr.body
newEnv = {}
for (k, v) in lambdaEnv.items():
newEnv[k] = v
for p in params:
newEnv[p.name] = p.typ
return "[](%s) { return %s; }" % (", ".join(list(map(lambda p: "%s %s" % (p.typ, p.name), params))), loop(body, lambdaEnv=newEnv))
if FuncExpr == type(expr):
return do_func(expr)
if FuncLambda == type(expr):
return do_lambda(expr)
elif FuncExprVal == type(expr):
return do_exprval(expr)
return loop(filterfunc)
def beginFilterGuard(self, vulkanType):
if vulkanType.filterVar == None:
return
if self.doFiltering == False:
return
filterVarAccess = self.getEnvAccessExpr(vulkanType.filterVar)
filterValsExpr = None
filterFuncExpr = None
filterExpr = None
filterFeature = "%s & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % self.featureBitsVar
if None != vulkanType.filterVals:
filterValsExpr = " || ".join(map(lambda filterval: "(%s == %s)" % (filterval, filterVarAccess), vulkanType.filterVals))
if None != vulkanType.filterFunc:
filterFuncExpr = self.genFilterFunc(vulkanType.filterFunc, self.currentStructInfo.environment)
if None != filterValsExpr and None != filterFuncExpr:
filterExpr = "%s || %s" % (filterValsExpr, filterFuncExpr)
elif None == filterValsExpr and None == filterFuncExpr:
# Assume is bool
self.cgen.beginIf(filterVarAccess)
elif None != filterValsExpr:
self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterValsExpr))
elif None != filterFuncExpr:
self.cgen.beginIf("(!(%s) || (%s))" % (filterFeature, filterFuncExpr))
def endFilterGuard(self, vulkanType, cleanupExpr=None):
if vulkanType.filterVar == None:
return
if self.doFiltering == False:
return
if cleanupExpr == None:
self.cgen.endIf()
else:
self.cgen.endIf()
self.cgen.beginElse()
self.cgen.stmt(cleanupExpr)
self.cgen.endElse()
def getEnvAccessExpr(self, varName):
parentEnvEntry = self.currentStructInfo.environment.get(varName, None)
if parentEnvEntry != None:
isParentMember = parentEnvEntry["structmember"]
if isParentMember:
envAccess = self.exprValueAccessor(list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0])
else:
envAccess = varName
return envAccess
return None
def getPointerIndirectionLevels(self, varName):
parentEnvEntry = self.currentStructInfo.environment.get(varName, None)
if parentEnvEntry != None:
isParentMember = parentEnvEntry["structmember"]
if isParentMember:
return list(filter(lambda member: member.paramName == varName, self.currentStructInfo.members))[0].pointerIndirectionLevels
else:
return 0
return 0
return 0
def onCompoundType(self, vulkanType):
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
self.beginFilterGuard(vulkanType)
if vulkanType.pointerIndirectionLevels > 0:
self.doAllocSpace(vulkanType)
if lenAccess is not None:
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
loopVar = "i"
access = "%s + %s" % (access, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess)
forIncr = "++%s" % loopVar
self.cgen.beginFor(forInit, forCond, forIncr)
accessWithCast = "%s(%s)" % (self.makeCastExpr(
self.getTypeForStreaming(vulkanType)), access)
callParams = [self.featureBitsVar,
self.rootTypeVar, accessWithCast, self.countVar]
for (bindName, localName) in vulkanType.binds.items():
callParams.append(self.getEnvAccessExpr(localName))
self.cgen.funcCall(None, self.prefix + vulkanType.typeName,
callParams)
if lenAccess is not None:
self.cgen.endFor()
if lenAccessGuard is not None:
self.cgen.endIf()
self.endFilterGuard(vulkanType)
def onString(self, vulkanType):
access = self.exprAccessor(vulkanType)
self.genCount("sizeof(uint32_t) + (%s ? strlen(%s) : 0)" % (access, access))
def onStringArray(self, vulkanType):
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
self.genCount("sizeof(uint32_t)")
if lenAccess is not None:
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
self.cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i")
self.genCount("sizeof(uint32_t) + (%s[i] ? strlen(%s[i]) : 0)" % (access, access))
self.cgen.endFor()
if lenAccessGuard is not None:
self.cgen.endIf()
def onStaticArr(self, vulkanType):
access = self.exprValueAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
if lenAccess is not None:
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
finalLenExpr = "%s * %s" % (lenAccess, self.cgen.sizeofExpr(vulkanType))
if lenAccessGuard is not None:
self.cgen.endIf()
self.genCount(finalLenExpr)
def onStructExtension(self, vulkanType):
sTypeParam = copy(vulkanType)
sTypeParam.paramName = "sType"
access = self.exprAccessor(vulkanType)
sizeVar = "%s_size" % vulkanType.paramName
castedAccessExpr = access
sTypeAccess = self.exprAccessor(sTypeParam)
self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" %
self.rootTypeVar)
self.cgen.stmt("%s = %s" % (self.rootTypeVar, sTypeAccess))
self.cgen.endIf()
self.cgen.funcCall(None, self.prefix + "extension_struct",
[self.featureBitsVar, self.rootTypeVar, castedAccessExpr, self.countVar])
def onPointer(self, vulkanType):
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
self.beginFilterGuard(vulkanType)
self.doAllocSpace(vulkanType)
if vulkanType.isHandleType() and self.mapHandles:
self.genHandleMappingCall(vulkanType, access, lenAccess)
else:
if self.typeInfo.isNonAbiPortableType(vulkanType.typeName):
if lenAccess is not None:
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
self.cgen.beginFor("uint32_t i = 0", "i < (uint32_t)%s" % lenAccess, "++i")
self.genPrimitiveStreamCall(vulkanType.getForValueAccess())
self.cgen.endFor()
if lenAccessGuard is not None:
self.cgen.endIf()
else:
self.genPrimitiveStreamCall(vulkanType.getForValueAccess())
else:
if lenAccess is not None:
needLenAccessGuard = True
finalLenExpr = "%s * %s" % (
lenAccess, self.cgen.sizeofExpr(vulkanType.getForValueAccess()))
else:
needLenAccessGuard = False
finalLenExpr = "%s" % (
self.cgen.sizeofExpr(vulkanType.getForValueAccess()))
if needLenAccessGuard and lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
self.genCount(finalLenExpr)
if needLenAccessGuard and lenAccessGuard is not None:
self.cgen.endIf()
self.endFilterGuard(vulkanType)
def onValue(self, vulkanType):
self.beginFilterGuard(vulkanType)
if vulkanType.isHandleType() and self.mapHandles:
access = self.exprAccessor(vulkanType)
self.genHandleMappingCall(
vulkanType.getForAddressAccess(), access, "1")
elif self.typeInfo.isNonAbiPortableType(vulkanType.typeName):
access = self.exprPrimitiveValueAccessor(vulkanType)
self.genPrimitiveStreamCall(vulkanType)
else:
access = self.exprAccessor(vulkanType)
self.genCount(self.cgen.sizeofExpr(vulkanType))
self.endFilterGuard(vulkanType)
def streamLetParameter(self, structInfo, letParamInfo):
filterFeature = "%s & VULKAN_STREAM_FEATURE_IGNORED_HANDLES_BIT" % (self.featureBitsVar)
self.cgen.stmt("%s %s = 1" % (letParamInfo.typeName, letParamInfo.paramName))
self.cgen.beginIf(filterFeature)
bodyExpr = self.currentStructInfo.environment[letParamInfo.paramName]["body"]
self.cgen.stmt("%s = %s" % (letParamInfo.paramName, self.genFilterFunc(bodyExpr, self.currentStructInfo.environment)))
self.genPrimitiveStreamCall(letParamInfo)
self.cgen.endIf()
class VulkanCounting(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.featureBitsVar = "featureBits"
self.featureBitsVarType = makeVulkanTypeSimple(False, "uint32_t", 0, self.featureBitsVar)
self.countingPrefix = "count_"
self.countVars = ["toCount", "count"]
self.countVarType = makeVulkanTypeSimple(False, "size_t", 1, self.countVars[1])
self.voidType = makeVulkanTypeSimple(False, "void", 0)
self.rootTypeVar = ROOT_TYPE_VAR_NAME
self.countingCodegen = \
VulkanCountingCodegen(
self.codegen,
self.featureBitsVar,
self.countVars[0],
self.countVars[1],
self.rootTypeVar,
self.countingPrefix)
self.knownDefs = {}
self.extensionCountingPrototype = \
VulkanAPI(self.countingPrefix + "extension_struct",
self.voidType,
[self.featureBitsVarType,
ROOT_TYPE_PARAM,
STRUCT_EXTENSION_PARAM,
self.countVarType])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
self.module.appendImpl(self.codegen.makeFuncDecl(
self.extensionCountingPrototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownDefs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
# TODO(liyl): might not work if freeParams != []
self.module.appendHeader(
self.codegen.makeFuncAlias(self.countingPrefix + name,
self.countingPrefix + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
freeParams = []
letParams = []
for (envname, bindingInfo) in list(sorted(structInfo.environment.items(), key = lambda kv: kv[0])):
if None == bindingInfo["binding"]:
freeParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname))
else:
if not bindingInfo["structmember"]:
letParams.append(makeVulkanTypeSimple(True, bindingInfo["type"], 0, envname))
typeFromName = \
lambda varname: \
makeVulkanTypeSimple(True, name, 1, varname)
countingParams = \
[makeVulkanTypeSimple(False, "uint32_t", 0, self.featureBitsVar),
ROOT_TYPE_PARAM,
typeFromName(self.countVars[0]),
makeVulkanTypeSimple(False, "size_t", 1, self.countVars[1])]
countingPrototype = \
VulkanAPI(self.countingPrefix + name,
self.voidType,
countingParams + freeParams)
countingPrototypeNoFilter = \
VulkanAPI(self.countingPrefix + name,
self.voidType,
countingParams)
def structCountingDef(cgen):
self.countingCodegen.cgen = cgen
self.countingCodegen.currentStructInfo = structInfo
cgen.stmt("(void)%s" % self.featureBitsVar)
cgen.stmt("(void)%s" % self.rootTypeVar)
cgen.stmt("(void)%s" % self.countVars[0])
cgen.stmt("(void)%s" % self.countVars[1])
if category == "struct":
# marshal 'let' parameters first
for letp in letParams:
self.countingCodegen.streamLetParameter(self.typeInfo, letp)
for member in structInfo.members:
iterateVulkanType(self.typeInfo, member, self.countingCodegen)
if category == "union":
iterateVulkanType(self.typeInfo, structInfo.members[0], self.countingCodegen)
def structCountingDefNoFilter(cgen):
self.countingCodegen.cgen = cgen
self.countingCodegen.currentStructInfo = structInfo
self.countingCodegen.doFiltering = False
cgen.stmt("(void)%s" % self.featureBitsVar)
cgen.stmt("(void)%s" % self.rootTypeVar)
cgen.stmt("(void)%s" % self.countVars[0])
cgen.stmt("(void)%s" % self.countVars[1])
if category == "struct":
# marshal 'let' parameters first
for letp in letParams:
self.countingCodegen.streamLetParameter(self.typeInfo, letp)
for member in structInfo.members:
iterateVulkanType(self.typeInfo, member, self.countingCodegen)
if category == "union":
iterateVulkanType(self.typeInfo, structInfo.members[0], self.countingCodegen)
self.countingCodegen.doFiltering = True
self.module.appendHeader(
self.codegen.makeFuncDecl(countingPrototype))
self.module.appendImpl(
self.codegen.makeFuncImpl(countingPrototype, structCountingDef))
if freeParams != []:
self.module.appendHeader(
self.cgenHeader.makeFuncDecl(countingPrototypeNoFilter))
self.module.appendImpl(
self.cgenImpl.makeFuncImpl(
countingPrototypeNoFilter, structCountingDefNoFilter))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def doExtensionStructCountCodegen(self, cgen, extParam, forEach, funcproto):
accessVar = "structAccess"
sizeVar = "currExtSize"
cgen.stmt("VkInstanceCreateInfo* %s = (VkInstanceCreateInfo*)(%s)" % (accessVar, extParam.paramName))
cgen.stmt("size_t %s = %s(%s, %s, %s)" % (sizeVar, EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME,
self.featureBitsVar, ROOT_TYPE_VAR_NAME, extParam.paramName))
cgen.beginIf("!%s && %s" % (sizeVar, extParam.paramName))
cgen.line("// unknown struct extension; skip and call on its pNext field");
cgen.funcCall(None, funcproto.name, [
self.featureBitsVar, ROOT_TYPE_VAR_NAME, "(void*)%s->pNext" % accessVar, self.countVars[1]])
cgen.stmt("return")
cgen.endIf()
cgen.beginElse()
cgen.line("// known or null extension struct")
cgen.stmt("*%s += sizeof(uint32_t)" % self.countVars[1])
cgen.beginIf("!%s" % (sizeVar))
cgen.line("// exit if this was a null extension struct (size == 0 in this branch)")
cgen.stmt("return")
cgen.endIf()
cgen.endIf()
cgen.stmt("*%s += sizeof(VkStructureType)" % self.countVars[1])
def fatalDefault(cgen):
cgen.line("// fatal; the switch is only taken if the extension struct is known");
cgen.stmt("abort()")
pass
self.emitForEachStructExtension(
cgen,
makeVulkanTypeSimple(False, "void", 0, "void"),
extParam,
forEach,
defaultEmit=fatalDefault,
rootTypeVar=ROOT_TYPE_PARAM)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
def forEachExtensionCounting(ext, castedAccess, cgen):
cgen.funcCall(None, self.countingPrefix + ext.name,
[self.featureBitsVar, self.rootTypeVar, castedAccess, self.countVars[1]])
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionCountingPrototype,
lambda cgen: self.doExtensionStructCountCodegen(
cgen,
STRUCT_EXTENSION_PARAM,
forEachExtensionCounting,
self.extensionCountingPrototype)))
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,630 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen, VulkanWrapperGenerator, VulkanAPIWrapper
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, DISPATCHABLE_HANDLE_TYPES, NON_DISPATCHABLE_HANDLE_TYPES
from .transform import TransformCodegen, genTransformsForVulkanType
from .wrapperdefs import API_PREFIX_MARSHAL
from .wrapperdefs import API_PREFIX_UNMARSHAL
from .wrapperdefs import VULKAN_STREAM_TYPE
from copy import copy
from dataclasses import dataclass
decoder_snapshot_decl_preamble = """
namespace gfxstream {
class Stream;
} // namespace gfxstream
namespace gfxstream {
namespace base {
class BumpPool;
} // namespace base
} // namespace gfxstream
namespace gfxstream {
namespace host {
namespace vk {
class VkDecoderSnapshot {
public:
VkDecoderSnapshot();
~VkDecoderSnapshot();
void clear();
void saveReplayBuffers(gfxstream::Stream* stream);
static void loadReplayBuffers(gfxstream::Stream* stream, std::vector<uint64_t>* outHandleBuffer, std::vector<uint8_t>* outDecoderBuffer);
VkSnapshotApiCallHandle createApiCallInfo();
void destroyApiCallInfoIfUnused(VkSnapshotApiCallHandle handle);
// Performs bookkeeping to track that a given api call created the given VkObject handles.
// This is a public function so that `VkDecoderGlobalState` can inform snapshot of any
// additional handles created while emulating features.
void addOrderedBoxedHandlesCreatedByCall(VkSnapshotApiCallHandle apiCallHandle, VkObjectHandle* boxedHandles, uint32_t boxedHandlesCount);
"""
decoder_snapshot_decl_postamble = """
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};
} // namespace vk
} // namespace host
} // namespace gfxstream
"""
decoder_snapshot_impl_preamble ="""
namespace gfxstream {
namespace host {
namespace vk {
class VkDecoderSnapshot::Impl {
public:
Impl() { }
void clear() {
std::lock_guard<std::mutex> lock(mReconstructionMutex);
mReconstruction.clear();
}
void saveReplayBuffers(gfxstream::Stream* stream) {
std::lock_guard<std::mutex> lock(mReconstructionMutex);
mReconstruction.saveReplayBuffers(stream);
}
static void loadReplayBuffers(gfxstream::Stream* stream, std::vector<uint64_t>* outHandleBuffer, std::vector<uint8_t>* outDecoderBuffer) {
VkReconstruction::loadReplayBuffers(stream, outHandleBuffer, outDecoderBuffer);
}
VkSnapshotApiCallHandle createApiCallInfo() {
std::lock_guard<std::mutex> lock(mReconstructionMutex);
return mReconstruction.createApiCallInfo();
}
void destroyApiCallInfoIfUnused(VkSnapshotApiCallHandle apiCallHandle) {
std::lock_guard<std::mutex> lock(mReconstructionMutex);
return mReconstruction.destroyApiCallInfoIfUnused(apiCallHandle);
}
void addOrderedBoxedHandlesCreatedByCall(VkSnapshotApiCallHandle apiCallHandle, VkObjectHandle* boxedHandles, uint32_t boxedHandlesCount) {
std::lock_guard<std::mutex> lock(mReconstructionMutex);
return mReconstruction.addOrderedBoxedHandlesCreatedByCall(apiCallHandle, boxedHandles, boxedHandlesCount);
}
"""
decoder_snapshot_impl_postamble = """
private:
std::mutex mReconstructionMutex;
VkReconstruction mReconstruction GUARDED_BY(mReconstructionMutex);
};
VkDecoderSnapshot::VkDecoderSnapshot() :
mImpl(new VkDecoderSnapshot::Impl()) { }
void VkDecoderSnapshot::clear() {
mImpl->clear();
}
void VkDecoderSnapshot::saveReplayBuffers(gfxstream::Stream* stream) {
mImpl->saveReplayBuffers(stream);
}
/*static*/
void VkDecoderSnapshot::loadReplayBuffers(gfxstream::Stream* stream, std::vector<uint64_t>* outHandleBuffer, std::vector<uint8_t>* outDecoderBuffer) {
VkDecoderSnapshot::Impl::loadReplayBuffers(stream, outHandleBuffer, outDecoderBuffer);
}
VkSnapshotApiCallHandle VkDecoderSnapshot::createApiCallInfo() {
return mImpl->createApiCallInfo();
}
void VkDecoderSnapshot::destroyApiCallInfoIfUnused(VkSnapshotApiCallHandle handle) {
mImpl->destroyApiCallInfoIfUnused(handle);
}
void VkDecoderSnapshot::addOrderedBoxedHandlesCreatedByCall(VkSnapshotApiCallHandle apiCallHandle, VkObjectHandle* boxedHandles, uint32_t boxedHandlesCount) {
mImpl->addOrderedBoxedHandlesCreatedByCall(apiCallHandle, boxedHandles, boxedHandlesCount);
}
VkDecoderSnapshot::~VkDecoderSnapshot() = default;
"""
decoder_snapshot_namespace_postamble = """
} // namespace vk
} // namespace host
} // namespace gfxstream
"""
AUXILIARY_SNAPSHOT_API_BASE_PARAM_COUNT = 3
AUXILIARY_SNAPSHOT_API_PARAM_NAMES = [
"input_result",
]
# Vulkan handle dependencies.
# (a, b): a depends on b
SNAPSHOT_HANDLE_DEPENDENCIES = [
# Dispatchable handle types
("VkCommandBuffer", "VkCommandPool"),
("VkCommandPool", "VkDevice"),
("VkQueue", "VkDevice"),
("VkDevice", "VkPhysicalDevice"),
("VkPhysicalDevice", "VkInstance")] + \
list(map(lambda handleType : (handleType, "VkDevice"), NON_DISPATCHABLE_HANDLE_TYPES))
handleDependenciesDict = dict(SNAPSHOT_HANDLE_DEPENDENCIES)
def extract_deps_vkAllocateMemory(param, access, lenExpr, api, cgen):
cgen.stmt("const VkMemoryDedicatedAllocateInfo* dedicatedAllocateInfo = vk_find_struct<VkMemoryDedicatedAllocateInfo>(pAllocateInfo)");
cgen.beginIf("dedicatedAllocateInfo");
cgen.beginIf("dedicatedAllocateInfo->image")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkImage(dedicatedAllocateInfo->image)"))
cgen.endIf()
cgen.beginIf("dedicatedAllocateInfo->buffer")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkBuffer(dedicatedAllocateInfo->buffer)"))
cgen.endIf()
cgen.endIf()
def extract_deps_vkAllocateCommandBuffers(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkCommandPool(pAllocateInfo->commandPool)"))
def extract_deps_vkAllocateDescriptorSets(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkDescriptorPool(pAllocateInfo->descriptorPool)"))
def extract_deps_vkUpdateDescriptorSets(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)device)")
cgen.beginFor("uint32_t i = 0", "i < descriptorWriteCount", "++i")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkDescriptorSet( pDescriptorWrites[i].dstSet))")
cgen.beginFor("uint32_t j = 0", "j < pDescriptorWrites[i].descriptorCount", "++j")
cgen.beginIf("(pDescriptorWrites[i].pImageInfo)")
cgen.beginIf("pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkSampler( pDescriptorWrites[i].pImageInfo[j].sampler))")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImageView( pDescriptorWrites[i].pImageInfo[j].imageView))")
cgen.endIf()
cgen.beginIf("pDescriptorWrites[i].pImageInfo[j].imageView")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImageView( pDescriptorWrites[i].pImageInfo[j].imageView))")
cgen.endIf()
cgen.beginIf("pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_SAMPLER")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkSampler( pDescriptorWrites[i].pImageInfo[j].sampler))")
cgen.endIf()
cgen.endIf()
cgen.beginIf("pDescriptorWrites[i].pBufferInfo");
cgen.beginIf("pDescriptorWrites[i].descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER");
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer( pDescriptorWrites[i].pBufferInfo[j].buffer))")
cgen.endIf()
cgen.endIf()
cgen.endFor()
cgen.endFor()
def extract_deps_vkCreateImageView(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkImage(pCreateInfo->image)"))
def extract_deps_vkCreateBufferView(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkBuffer(pCreateInfo->buffer)"))
def extract_deps_vkCreateGraphicsPipelines(param, access, lenExpr, api, cgen):
cgen.beginFor("uint32_t i = 0", "i < createInfoCount", "++i")
cgen.beginFor("uint32_t j = 0", "j < pCreateInfos[i].stageCount", "++j")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(%s + i), %s, (uint64_t)(uintptr_t)%s)" % \
(access, 1, "unboxed_to_boxed_non_dispatchable_VkShaderModule(pCreateInfos[i].pStages[j].module)"))
cgen.endFor()
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(%s + i), %s, (uint64_t)(uintptr_t)%s)" % \
(access, 1, "unboxed_to_boxed_non_dispatchable_VkRenderPass(pCreateInfos[i].renderPass)"))
cgen.endFor()
def extract_deps_vkCreateFramebuffer(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkRenderPass(pCreateInfo->renderPass)"))
cgen.beginIf("(pCreateInfo->flags & VK_FRAMEBUFFER_CREATE_IMAGELESS_BIT) == 0")
cgen.beginFor("uint32_t i = 0", "i < pCreateInfo->attachmentCount" , "++i")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkImageView(pCreateInfo->pAttachments[i])"))
cgen.endFor()
cgen.endIf();
def extract_deps_vkBindImageMemory(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s, VkReconstruction::BOUND_MEMORY)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkDeviceMemory(memory)"))
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)((%s)[0]), VkReconstruction::BOUND_MEMORY)" % \
(access, lenExpr, access))
def extract_deps_vkBindBufferMemory(param, access, lenExpr, api, cgen):
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s, VkReconstruction::BOUND_MEMORY)" % \
(access, lenExpr, "unboxed_to_boxed_non_dispatchable_VkDeviceMemory(memory)"))
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)((%s)[0]), VkReconstruction::BOUND_MEMORY)" % \
(access, lenExpr, access))
specialCaseDependencyExtractors = {
"vkAllocateCommandBuffers" : extract_deps_vkAllocateCommandBuffers,
"vkAllocateDescriptorSets" : extract_deps_vkAllocateDescriptorSets,
"vkAllocateMemory" : extract_deps_vkAllocateMemory,
"vkCreateImageView" : extract_deps_vkCreateImageView,
"vkCreateBufferView" : extract_deps_vkCreateBufferView,
"vkCreateGraphicsPipelines" : extract_deps_vkCreateGraphicsPipelines,
"vkCreateFramebuffer" : extract_deps_vkCreateFramebuffer,
"vkUpdateDescriptorSets" : extract_deps_vkUpdateDescriptorSets,
}
apiSequences = {
"vkAllocateMemory" : ["vkAllocateMemory", "vkMapMemoryIntoAddressSpaceGOOGLE"]
}
@dataclass(frozen=True)
class VkObjectState:
vk_object : str
state : str = "VkReconstruction::CREATED"
# TODO: add vkBindImageMemory2 and vkBindBufferMemory2 into this list
apiChangeState = {
}
def api_special_implementation_common(api, cgen, tag_vk):
cgen.line("// Note: special implementation");
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("VkDecoderGlobalState* m_state = VkDecoderGlobalState::get()")
cgen.stmt("uint64_t handle = m_state->newGlobalVkGenericHandle(%s)" % tag_vk)
cgen.stmt("mReconstruction.addHandles((const uint64_t*)(&handle), 1)")
cgen.stmt("mReconstruction.forEachHandleAddApi((const uint64_t*)(&handle), 1, apiCallHandle, VkReconstruction::CREATED)")
cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiCallHandle, (const uint64_t*)(&handle), 1)")
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
def api_special_implementation_vkCmdPipelineBarrier(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.beginFor("uint32_t i = 0", "i < bufferMemoryBarrierCount", "++i")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer( pBufferMemoryBarriers[i].buffer))")
cgen.endFor()
cgen.beginFor("uint32_t i = 0", "i < imageMemoryBarrierCount", "++i")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImage( pImageMemoryBarriers[i].image))")
cgen.endFor()
def api_special_implementation_vkUpdateDescriptorSetWithTemplateSizedGOOGLE(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("VkDecoderGlobalState* m_state = VkDecoderGlobalState::get()")
cgen.beginIf("m_state->batchedDescriptorSetUpdateEnabled()")
cgen.stmt("return")
cgen.endIf();
cgen.stmt("uint64_t handle = m_state->newGlobalVkGenericHandle(Tag_VkUpdateDescriptorSets)")
cgen.stmt("mReconstruction.addHandles((const uint64_t*)(&handle), 1)")
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
cgen.stmt("mReconstruction.addHandleDependency( (const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)device)")
cgen.stmt("mReconstruction.forEachHandleAddApi((const uint64_t*)(&handle), 1, apiCallHandle, VkReconstruction::CREATED)")
cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiCallHandle, (const uint64_t*)(&handle), 1)")
def api_special_implementation_vkCmdBeginRenderPass(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkFramebuffer( pRenderPassBegin->framebuffer))")
def api_special_implementation_vkMapMemoryIntoAddressSpaceGOOGLE(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("VkDecoderGlobalState* m_state = VkDecoderGlobalState::get()")
cgen.stmt("uint64_t handle = m_state->newGlobalVkGenericHandle(Tag_VkMapMemory)")
cgen.stmt("mReconstruction.addHandles((const uint64_t*)(&handle), 1)")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkDeviceMemory(memory))")
cgen.stmt("mReconstruction.forEachHandleAddApi((const uint64_t*)(&handle), 1, apiCallHandle, VkReconstruction::CREATED)")
cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiCallHandle, (const uint64_t*)(&handle), 1)")
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
def api_special_implementation_vkCmdBeginRenderPass(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkFramebuffer( pRenderPassBegin->framebuffer))")
def api_special_implementation_vkCmdCopyBufferToImage(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer(srcBuffer))")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImage(dstImage))")
def api_special_implementation_vkCmdCopyImageToBuffer(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImage(srcImage))")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer(dstBuffer))")
def api_special_implementation_vkCmdCopyBuffer(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer(srcBuffer))")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer(dstBuffer))")
def api_special_implementation_vkCmdBindVertexBuffers(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.beginFor("uint32_t i = 0", "i < bindingCount", "++i")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkBuffer(pBuffers[i]))")
cgen.endFor()
def api_special_implementation_vkCmdBindPipeline(api, cgen):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.addApiCallDependencyOnVkObject(apiCallHandle, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkPipeline(pipeline))")
def api_special_implementation_vkResetCommandPool(api, cgen):
cgen.line("// Note: special implementation");
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.removeGrandChildren((uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkCommandPool(commandPool))")
def api_special_implementation_vkResetCommandBuffer(api, cgen):
cgen.line("// Note: special implementation");
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.stmt("mReconstruction.removeDescendantsOfHandle((uint64_t)(uintptr_t)commandBuffer)")
def api_special_implementation_vkBindBufferMemory(api, cgen):
api_special_implementation_common(api, cgen, "Tag_VkBindMemory")
cgen.stmt("mReconstruction.addHandleDependency( (const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkDeviceMemory(memory))")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)(unboxed_to_boxed_non_dispatchable_VkBuffer(buffer)))")
def api_special_implementation_vkBindImageMemory(api, cgen):
api_special_implementation_common(api, cgen, "Tag_VkBindMemory")
cgen.stmt("mReconstruction.addHandleDependency( (const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkDeviceMemory(memory))")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)(&handle), 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImage(image))")
def api_special_implementation_vkBindImageMemory2(api, cgen):
api_special_implementation_common(api, cgen, "Tag_VkBindMemory")
cgen.beginFor("uint32_t i = 0", "i < bindInfoCount", "++i")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)&handle, 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkDeviceMemory(pBindInfos[i].memory))")
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)&handle, 1, (uint64_t)(uintptr_t)unboxed_to_boxed_non_dispatchable_VkImage(pBindInfos[i].image))")
cgen.endFor()
apiSpecialImplementation = {
"vkBindBufferMemory": api_special_implementation_vkBindBufferMemory,
"vkBindImageMemory": api_special_implementation_vkBindImageMemory,
"vkBindImageMemory2": api_special_implementation_vkBindImageMemory2,
"vkBindImageMemory2KHR": api_special_implementation_vkBindImageMemory2,
"vkMapMemoryIntoAddressSpaceGOOGLE": api_special_implementation_vkMapMemoryIntoAddressSpaceGOOGLE,
"vkGetBlobGOOGLE": api_special_implementation_vkMapMemoryIntoAddressSpaceGOOGLE,
"vkResetCommandBuffer": api_special_implementation_vkResetCommandBuffer,
"vkResetCommandPool": api_special_implementation_vkResetCommandPool,
"vkCmdBindVertexBuffers": api_special_implementation_vkCmdBindVertexBuffers,
"vkCmdBindPipeline": api_special_implementation_vkCmdBindPipeline,
"vkCmdCopyBufferToImage": api_special_implementation_vkCmdCopyBufferToImage,
"vkCmdCopyImageToBuffer": api_special_implementation_vkCmdCopyImageToBuffer,
"vkCmdCopyBuffer": api_special_implementation_vkCmdCopyBuffer,
"vkCmdPipelineBarrier": api_special_implementation_vkCmdPipelineBarrier,
"vkCmdBeginRenderPass": api_special_implementation_vkCmdBeginRenderPass,
"vkCmdBeginRenderPass2": api_special_implementation_vkCmdBeginRenderPass,
"vkUpdateDescriptorSetWithTemplateSizedGOOGLE": api_special_implementation_vkUpdateDescriptorSetWithTemplateSizedGOOGLE,
"vkUpdateDescriptorSetWithTemplateSized2GOOGLE": api_special_implementation_vkUpdateDescriptorSetWithTemplateSizedGOOGLE,
}
apiModifies = {
"vkBeginCommandBuffer" : ["commandBuffer"],
"vkEndCommandBuffer" : ["commandBuffer"],
}
apiActions = {
}
apiActionsTag = {
}
apiClearModifiers = {
}
delayedDestroys = [
"vkDestroyShaderModule",
]
# The following types are created and cached by other commands.
# Thus we should not snapshot their "create" commands.
skipCreatorSnapshotTypes = [
"VkQueue", # created by vkCreateDevice
]
def is_state_change_operation(api, param):
if param.isCreatedBy(api) and param.typeName not in skipCreatorSnapshotTypes:
return True
if api.name in apiChangeState:
if param.paramName == apiChangeState[api.name].vk_object:
return True
return False
def get_target_state(api, param):
if param.isCreatedBy(api):
return "VkReconstruction::CREATED"
if api.name in apiActions:
return "VkReconstruction::CREATED"
if api.name in apiChangeState:
if param.paramName == apiChangeState[api.name].vk_object:
return apiChangeState[api.name].state
return None
def is_action_operation(api, param):
if api.name in apiActions:
if param.paramName in apiActions[api.name]:
return True
return False
def is_modify_operation(api, param):
if api.name in apiModifies:
if param.paramName in apiModifies[api.name]:
return True
return False
def emit_impl(typeInfo, api, cgen):
if api.name in apiSpecialImplementation:
apiSpecialImplementation[api.name](api, cgen)
for p in api.parameters:
if not (p.isHandleType):
continue
lenExpr = cgen.generalLengthAccess(p)
lenAccessGuard = cgen.generalLengthAccessGuard(p)
if lenExpr is None:
lenExpr = "1"
# Note that in vkCreate*, the last parameter (the output) is boxed. But all input parameters are unboxed.
if p.pointerIndirectionLevels > 0:
access = p.paramName
else:
access = "(&%s)" % p.paramName
if is_state_change_operation(api, p):
if p.isCreatedBy(api):
boxed_access = access
else:
cgen.stmt("%s boxed_%s = unboxed_to_boxed_non_dispatchable_%s(%s[0])" % (p.typeName, p.typeName, p.typeName, access))
boxed_access = "&boxed_%s" % p.typeName
if p.pointerIndirectionLevels > 0:
cgen.stmt("if (!%s) return" % access)
cgen.stmt("if (input_result != VK_SUCCESS) return")
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.line("// %s create" % p.paramName)
if p.isCreatedBy(api):
cgen.stmt("mReconstruction.addHandles((const uint64_t*)%s, %s)" % (boxed_access, lenExpr));
if p.isCreatedBy(api) and p.typeName in handleDependenciesDict:
dependsOnType = handleDependenciesDict[p.typeName];
for p2 in api.parameters:
if p2.typeName == dependsOnType:
cgen.stmt("mReconstruction.addHandleDependency((const uint64_t*)%s, %s, (uint64_t)(uintptr_t)%s)" % (boxed_access, lenExpr, p2.paramName))
if api.name in specialCaseDependencyExtractors:
specialCaseDependencyExtractors[api.name](p, boxed_access, lenExpr, api, cgen)
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
if lenAccessGuard is not None:
cgen.beginIf(lenAccessGuard)
cgen.stmt(f"mReconstruction.forEachHandleAddApi((const uint64_t*){boxed_access}, {lenExpr}, apiCallHandle, {get_target_state(api, p)})")
if p.isCreatedBy(api):
cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiCallHandle, (const uint64_t*)%s, %s)" % (boxed_access, lenExpr))
if lenAccessGuard is not None:
cgen.endIf()
if p.isDestroyedBy(api):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.line("// %s destroy" % p.paramName)
if lenAccessGuard is not None:
cgen.beginIf(lenAccessGuard)
shouldRecursiveDestroy = "false" if api.name in delayedDestroys else "true"
cgen.stmt("mReconstruction.removeHandles((const uint64_t*)%s, %s, %s)" % (access, lenExpr, shouldRecursiveDestroy));
if lenAccessGuard is not None:
cgen.endIf()
if is_action_operation(api, p):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.line("// %s action" % p.paramName)
cgen.stmt("VkDecoderGlobalState* m_state = VkDecoderGlobalState::get()")
cgen.beginIf("m_state->batchedDescriptorSetUpdateEnabled()")
cgen.stmt("return")
cgen.endIf();
cgen.stmt("uint64_t handle = m_state->newGlobalVkGenericHandle(%s)" % apiActionsTag[api.name])
cgen.stmt("mReconstruction.addHandles((const uint64_t*)(&handle), 1)");
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
if api.name in specialCaseDependencyExtractors:
specialCaseDependencyExtractors[api.name](p, None, None, api, cgen)
cgen.stmt(f"mReconstruction.forEachHandleAddApi((const uint64_t*)(&handle), 1, apiCallHandle, {get_target_state(api, p)})")
cgen.stmt("mReconstruction.setCreatedHandlesForApi(apiCallHandle, (const uint64_t*)(&handle), 1)")
elif is_modify_operation(api, p):
cgen.stmt("std::lock_guard<std::mutex> lock(mReconstructionMutex)")
cgen.line("// %s modify" % p.paramName)
cgen.stmt("mReconstruction.setApiTrace(apiCallHandle, apiCallPacket, apiCallPacketSize)")
if lenAccessGuard is not None:
cgen.beginIf(lenAccessGuard)
cgen.beginFor("uint32_t i = 0", "i < %s" % lenExpr, "++i")
if p.isNonDispatchableHandleType():
cgen.stmt("%s boxed = unboxed_to_boxed_non_dispatchable_%s(%s[i])" % (p.typeName, p.typeName, access))
else:
cgen.line("// %s is already boxed, no need to box again" % p.paramName)
cgen.stmt("%s boxed = %s(%s[i])" % (p.typeName, p.typeName, access))
if is_modify_operation(api, p):
cgen.stmt("mReconstruction.forEachHandleAddModifyApi((const uint64_t*)(&boxed), 1, apiCallHandle)")
else: # is clear modifier operation
cgen.stmt("mReconstruction.forEachHandleClearModifyApi((const uint64_t*)(&boxed), 1)")
cgen.endFor()
if lenAccessGuard is not None:
cgen.endIf()
def emit_passthrough_to_impl(typeInfo, api, cgen):
cgen.vkApiCall(api, customPrefix = "mImpl->")
class VulkanDecoderSnapshot(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.typeInfo = typeInfo
self.cgenHeader = CodeGen()
self.cgenHeader.incrIndent()
self.cgenImpl = CodeGen()
self.currentFeature = None
self.feature_apis = []
def onBegin(self,):
self.module.appendHeader(decoder_snapshot_decl_preamble)
self.module.appendImpl(decoder_snapshot_impl_preamble)
def onBeginFeature(self, featureName, featureType):
VulkanWrapperGenerator.onBeginFeature(self, featureName, featureType)
self.currentFeature = featureName
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
api = self.typeInfo.apis[name]
additionalParams = [ \
makeVulkanTypeSimple(False, "gfxstream::base::BumpPool", 1, "pool"),
makeVulkanTypeSimple(False, "VkSnapshotApiCallHandle", 0, "apiCallHandle"),
makeVulkanTypeSimple(True, "uint8_t", 1, "apiCallPacket"),
makeVulkanTypeSimple(False, "size_t", 0, "apiCallPacketSize"),
]
if api.retType.typeName != "void":
additionalParams.append( \
makeVulkanTypeSimple(False, api.retType.typeName, 0, "input_result"))
apiForSnapshot = \
api.withCustomParameters( \
additionalParams + \
api.parameters).withCustomReturnType( \
makeVulkanTypeSimple(False, "void", 0, "void"))
self.feature_apis.append((self.currentFeature, apiForSnapshot))
self.cgenHeader.stmt(self.cgenHeader.makeFuncProto(apiForSnapshot))
self.module.appendHeader(self.cgenHeader.swapCode())
self.cgenImpl.emitFuncImpl( \
apiForSnapshot, lambda cgen: emit_impl(self.typeInfo, apiForSnapshot, cgen))
self.module.appendImpl(self.cgenImpl.swapCode())
def onEnd(self,):
self.module.appendHeader(decoder_snapshot_decl_postamble)
self.module.appendImpl(decoder_snapshot_impl_postamble)
self.cgenHeader.decrIndent()
for feature, api in self.feature_apis:
if feature is not None:
self.cgenImpl.line("#ifdef %s" % feature)
apiImplShell = \
api.withModifiedName("VkDecoderSnapshot::" + api.name)
self.cgenImpl.emitFuncImpl( \
apiImplShell, lambda cgen: emit_passthrough_to_impl(self.typeInfo, api, cgen))
if feature is not None:
self.cgenImpl.line("#endif")
self.module.appendImpl(self.cgenImpl.swapCode())
self.module.appendImpl(decoder_snapshot_namespace_postamble)
@@ -0,0 +1,372 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE, EXTENSION_SIZE_API_NAME
class DeepcopyCodegen(VulkanTypeIterator):
def __init__(self, cgen, inputVars, poolVarName, rootVarName, prefix, skipValues=False):
self.cgen = cgen
self.inputVars = inputVars
self.prefix = prefix
self.poolVarName = poolVarName
self.rootVarName = rootVarName
self.skipValues = skipValues
def makeAccess(varName, asPtr = True):
return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr)
def makeLengthAccess(varName):
return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName)
def makeLengthAccessGuard(varName):
return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName)
self.exprAccessorLhs = makeAccess(self.inputVars[0])
self.exprAccessorRhs = makeAccess(self.inputVars[1])
self.exprAccessorValueLhs = makeAccess(self.inputVars[0], asPtr = False)
self.exprAccessorValueRhs = makeAccess(self.inputVars[1], asPtr = False)
self.lenAccessorLhs = makeLengthAccess(self.inputVars[0])
self.lenAccessorRhs = makeLengthAccess(self.inputVars[1])
self.lenAccessorGuardLhs = makeLengthAccessGuard(self.inputVars[0])
self.lenAccessorGuardRhs = makeLengthAccessGuard(self.inputVars[1])
self.checked = False
def needSkip(self, vulkanType):
return False
def makeCastExpr(self, vulkanType):
return "(%s)" % (
self.cgen.makeCTypeDecl(vulkanType, useParamName=False))
def makeNonConstCastForCopy(self, access, vulkanType):
if vulkanType.staticArrExpr:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
elif vulkanType.accessibleAsPointer():
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access)
else:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
return casted
def makeAllocBytesExpr(self, lenAccess, vulkanType):
sizeof = self.cgen.sizeofExpr( \
vulkanType.getForValueAccess())
if lenAccess:
bytesExpr = "%s * %s" % (lenAccess, sizeof)
else:
bytesExpr = sizeof
return bytesExpr
def onCheck(self, vulkanType):
pass
def endCheck(self, vulkanType):
pass
def onCompoundType(self, vulkanType):
if self.needSkip(vulkanType):
self.cgen.line("// TODO: Unsupported : %s" %
self.cgen.makeCTypeDecl(vulkanType))
return
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
lenAccessRhs = self.lenAccessorRhs(vulkanType)
lenAccessorGuardLhs = self.lenAccessorGuardLhs(vulkanType)
lenAccessorGuardRhs = self.lenAccessorGuardRhs(vulkanType)
isPtr = vulkanType.pointerIndirectionLevels > 0
if lenAccessorGuardLhs is not None:
self.cgen.beginIf(lenAccessorGuardLhs)
if isPtr:
self.cgen.stmt("%s = nullptr" % accessRhs)
self.cgen.beginIf(accessLhs)
self.cgen.stmt( \
"%s = %s%s->alloc(%s)" % \
(accessRhs, self.makeCastExpr(vulkanType.getForNonConstAccess()),
self.poolVarName, self.makeAllocBytesExpr(lenAccessLhs, vulkanType)))
if lenAccessLhs is not None:
loopVar = "i"
accessLhs = "%s + %s" % (accessLhs, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs)
forIncr = "++%s" % loopVar
if isPtr:
# Avoid generating a self-assign.
if lenAccessRhs != lenAccessLhs:
self.cgen.stmt("%s = %s" % (lenAccessRhs, lenAccessLhs))
accessRhs = "%s + %s" % (accessRhs, loopVar)
self.cgen.beginFor(forInit, forCond, forIncr)
accessRhsCasted = self.makeNonConstCastForCopy(accessRhs, vulkanType)
self.cgen.funcCall(None, self.prefix + vulkanType.typeName,
[self.poolVarName, self.rootVarName, accessLhs, accessRhsCasted])
if lenAccessLhs is not None:
self.cgen.endFor()
if isPtr:
self.cgen.endIf()
if lenAccessorGuardLhs is not None:
self.cgen.endIf()
def onString(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
self.cgen.stmt("%s = nullptr" % accessRhs)
self.cgen.beginIf(accessLhs)
self.cgen.stmt( \
"%s = %s->strDup(%s)" % \
(accessRhs,
self.poolVarName,
accessLhs))
self.cgen.endIf()
def onStringArray(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
self.cgen.stmt("%s = nullptr" % accessRhs)
if lenAccessLhs is not None:
self.cgen.beginIf("%s && %s" % (accessLhs, lenAccessLhs))
self.cgen.stmt( \
"%s = %s->strDupArray(%s, %s)" % \
(accessRhs,
self.poolVarName,
accessLhs,
lenAccessLhs))
self.cgen.endIf()
def onStaticArr(self, vulkanType):
accessLhs = self.exprAccessorValueLhs(vulkanType)
accessRhs = self.exprAccessorValueRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
bytesExpr = self.makeAllocBytesExpr(lenAccessLhs, vulkanType)
self.cgen.stmt("memcpy(%s, %s, %s)" % (accessRhs, accessLhs, bytesExpr))
def onStructExtension(self, vulkanType):
lhs = self.exprAccessorLhs(vulkanType)
rhs = self.exprAccessorRhs(vulkanType)
rhsExpr = "(%s)(%s)" % ("void*", rhs)
nextVar = "from_%s" % vulkanType.paramName
sizeVar = "%s_size" % vulkanType.paramName
self.cgen.beginIf("%s == VK_STRUCTURE_TYPE_MAX_ENUM" %
self.rootVarName)
self.cgen.stmt("%s = from->sType" % self.rootVarName)
self.cgen.endIf()
self.cgen.stmt("const void* %s = %s" % (nextVar, self.inputVars[0]))
self.cgen.stmt("size_t %s = 0u" % sizeVar)
self.cgen.beginWhile("!%s && %s" % (sizeVar, nextVar))
self.cgen.stmt("%s = static_cast<const VkBaseOutStructure*>(%s)->%s" % (
nextVar, nextVar, vulkanType.paramName
))
self.cgen.stmt("%s = %s(%s, %s)" % (
sizeVar, EXTENSION_SIZE_API_NAME, self.rootVarName, nextVar))
self.cgen.endWhile()
self.cgen.stmt("%s = nullptr" % rhs)
self.cgen.beginIf(sizeVar)
self.cgen.stmt( \
"%s = %s%s->alloc(%s)" % \
(rhs, self.makeCastExpr(vulkanType.getForNonConstAccess()), self.poolVarName, sizeVar))
self.cgen.funcCall(None, self.prefix + "extension_struct",
[self.poolVarName, self.rootVarName, nextVar, rhsExpr])
self.cgen.endIf()
def onPointer(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
if self.needSkip(vulkanType):
self.cgen.stmt("%s = nullptr" % accessRhs)
return
lenAccessLhs = self.lenAccessorLhs(vulkanType)
self.cgen.stmt("%s = nullptr" % accessRhs)
self.cgen.beginIf(accessLhs)
bytesExpr = self.makeAllocBytesExpr(lenAccessLhs, vulkanType)
self.cgen.stmt( \
"%s = %s%s->dupArray(%s, %s)" % \
(accessRhs,
self.makeCastExpr(vulkanType.getForNonConstAccess()),
self.poolVarName,
accessLhs,
bytesExpr))
self.cgen.endIf()
def onValue(self, vulkanType):
if self.skipValues:
return
accessLhs = self.exprAccessorValueLhs(vulkanType)
accessRhs = self.exprAccessorValueRhs(vulkanType)
self.cgen.stmt("%s = %s" % (accessRhs, accessLhs))
class VulkanDeepcopy(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.deepcopyPrefix = "deepcopy_"
self.deepcopyVars = ["from", "to"]
self.deepcopyAllocatorVarName = "alloc"
self.deepcopyAllocatorParam = \
makeVulkanTypeSimple(False, "Allocator", 1,
self.deepcopyAllocatorVarName)
self.deepcopyRootVarName = "rootType"
self.deepcopyRootParam = \
makeVulkanTypeSimple(False, "VkStructureType",
0, self.deepcopyRootVarName)
self.voidType = makeVulkanTypeSimple(False, "void", 0)
self.deepcopyCodegen = \
DeepcopyCodegen(
None,
self.deepcopyVars,
self.deepcopyAllocatorVarName,
self.deepcopyRootVarName,
self.deepcopyPrefix,
skipValues=True)
self.knownDefs = {}
self.extensionDeepcopyPrototype = \
VulkanAPI(self.deepcopyPrefix + "extension_struct",
self.voidType,
[self.deepcopyAllocatorParam,
self.deepcopyRootParam,
STRUCT_EXTENSION_PARAM,
STRUCT_EXTENSION_PARAM_FOR_WRITE])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
self.module.appendImpl(self.codegen.makeFuncDecl(
self.extensionDeepcopyPrototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownDefs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
self.module.appendHeader(
self.codegen.makeFuncAlias(self.deepcopyPrefix + name,
self.deepcopyPrefix + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
typeFromName = \
lambda varname: \
makeVulkanTypeSimple(varname == "from", name, 1, varname)
deepcopyParams = \
[self.deepcopyAllocatorParam, self.deepcopyRootParam] + \
list(map(typeFromName, self.deepcopyVars))
deepcopyPrototype = \
VulkanAPI(self.deepcopyPrefix + name,
self.voidType,
deepcopyParams)
def structDeepcopyDef(cgen):
self.deepcopyCodegen.cgen = cgen
canSimplyAssign = True
for member in structInfo.members:
if not member.isSimpleValueType(self.typeInfo):
canSimplyAssign = False
cgen.stmt("(void)%s" % self.deepcopyAllocatorVarName)
cgen.stmt("(void)%s" % self.deepcopyRootVarName)
cgen.stmt("*to = *from")
if canSimplyAssign:
pass
else:
for member in structInfo.members:
iterateVulkanType(self.typeInfo, member,
self.deepcopyCodegen)
self.module.appendHeader(
self.codegen.makeFuncDecl(deepcopyPrototype))
self.module.appendImpl(
self.codegen.makeFuncImpl(deepcopyPrototype, structDeepcopyDef))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
def deepcopyDstExpr(cgen, typeName):
return cgen.makeReinterpretCast( \
STRUCT_EXTENSION_PARAM_FOR_WRITE.paramName,
typeName, const=False)
def forEachExtensionDeepcopy(ext, castedAccess, cgen):
cgen.funcCall(None, self.deepcopyPrefix + ext.name,
[self.deepcopyAllocatorVarName,
self.deepcopyRootVarName,
castedAccess, deepcopyDstExpr(cgen, ext.name)])
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionDeepcopyPrototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
self.voidType,
STRUCT_EXTENSION_PARAM,
forEachExtensionDeepcopy,
rootTypeVar=self.deepcopyRootParam)))
@@ -0,0 +1,482 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
from .wrapperdefs import VulkanWrapperGenerator
# No real good way to automatically infer the most important Vulkan API
# functions as it relates to which getProcAddress function to use, plus
# we might want to control which function to use depending on our
# performance needs.
# This is based on the minimum set of functions needed to be directly
# queried with dlsym and not returning null.
getProcAddrFuncs = [
"vkGetInstanceProcAddr",
"vkDestroyInstance",
"vkEnumeratePhysicalDevices",
"vkGetPhysicalDeviceFeatures",
"vkGetPhysicalDeviceFormatProperties",
"vkGetPhysicalDeviceImageFormatProperties",
"vkGetPhysicalDeviceProperties",
"vkGetPhysicalDeviceQueueFamilyProperties",
"vkGetPhysicalDeviceMemoryProperties",
"vkCreateDevice",
"vkDestroyDevice",
"vkEnumerateDeviceExtensionProperties",
"vkEnumerateDeviceLayerProperties",
]
# Some methods can only be found using dlsym() while we cannot get the function
# address using vkGetInstProcAddr() or vkGetDeviceProcAddr(). These function
# pointers should only be initialized when setting up the dispatch from system
# loader.
getProcAddrOnlyFuncs = []
getInstanceProcAddrNoInstanceFuncs = [
"vkCreateInstance",
"vkEnumerateInstanceExtensionProperties",
"vkEnumerateInstanceLayerProperties",
]
getInstanceProcAddrFuncs = [
"vkGetDeviceProcAddr",
"vkCreateSwapchainKHR",
"vkDestroySwapchainKHR",
"vkGetSwapchainImagesKHR",
"vkAcquireNextImageKHR",
"vkQueuePresentKHR",
"vkCreateMacOSSurfaceMVK",
"vkCreateWin32SurfaceKHR",
"vkGetPhysicalDeviceWin32PresentationSupportKHR",
"vkCreateXlibSurfaceKHR",
"vkGetPhysicalDeviceXlibPresentationSupportKHR",
"vkCreateXcbSurfaceKHR",
"vkGetPhysicalDeviceXcbPresentationSupportKHR",
"vkGetPhysicalDeviceSparseImageFormatProperties",
"vkEnumerateInstanceVersion",
"vkEnumeratePhysicalDeviceGroups",
"vkGetPhysicalDeviceFeatures2",
"vkGetPhysicalDeviceProperties2",
"vkGetPhysicalDeviceFormatProperties2",
"vkGetPhysicalDeviceImageFormatProperties2",
"vkGetPhysicalDeviceQueueFamilyProperties2",
"vkGetPhysicalDeviceMemoryProperties2",
"vkGetPhysicalDeviceSparseImageFormatProperties2",
"vkGetPhysicalDeviceExternalBufferProperties",
"vkGetPhysicalDeviceExternalFenceProperties",
"vkGetPhysicalDeviceExternalSemaphoreProperties",
]
# Implicitly, everything else is going to be obtained
# with vkGetDeviceProcAddr,
# unless it has instance in the arg.
def isGetProcAddressAPI(vulkanApi):
return vulkanApi.name in getProcAddrFuncs
def isGetProcAddressOnlyAPI(vulkanApi):
return vulkanApi.name in getProcAddrOnlyFuncs
def isGetInstanceProcAddressNoInstanceAPI(vulkanApi):
return vulkanApi.name in getInstanceProcAddrNoInstanceFuncs
def isGetInstanceProcAddressAPI(vulkanApi):
if vulkanApi.name in getInstanceProcAddrFuncs:
return True
if vulkanApi.parameters[0].typeName == "VkInstance":
return True
return False
def isGetDeviceProcAddressAPI(vulkanApi):
if isGetProcAddressAPI(vulkanApi):
return False
if isGetProcAddressOnlyAPI(vulkanApi):
return False
if isGetInstanceProcAddressAPI(vulkanApi):
return False
return True
def inferProcAddressFuncType(vulkanApi):
if isGetProcAddressAPI(vulkanApi):
return "global"
if isGetProcAddressOnlyAPI(vulkanApi):
return "global-only"
if isGetInstanceProcAddressNoInstanceAPI(vulkanApi):
return "global-instance"
if isGetInstanceProcAddressAPI(vulkanApi):
return "instance"
return "device"
# VulkanDispatch defines a struct, VulkanDispatch,
# that is populated by function pointers from the Vulkan
# loader. No attempt is made to do something different
# for instance vs device functions.
class VulkanDispatch(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.apisToGet = {}
self.cgenHeader = CodeGen()
self.cgenImpl = CodeGen()
self.typeInfo = typeInfo
self.currentFeature = ""
self.featureForCodegen = ""
def onBegin(self):
# The first way is to use just the loader to get symbols. This doesn't
# necessarily work with extensions because at that point the dispatch
# table needs to be specific to a particular Vulkan instance or device.
self.cgenHeader.line("""
void init_vulkan_dispatch_from_system_loader(
DlOpenFunc dlOpenFunc,
DlSymFunc dlSymFunc,
VulkanDispatch* dispatch_out);
""")
# The second way is to initialize the table from a given Vulkan
# instance or device. Provided the instance or device was created with
# the right extensions, we can obtain function pointers to extension
# functions this way.
self.cgenHeader.line("""
void init_vulkan_dispatch_from_instance(
VulkanDispatch* vk,
VkInstance instance,
VulkanDispatch* dispatch_out);
""")
self.cgenHeader.line("""
void init_vulkan_dispatch_from_device(
VulkanDispatch* vk,
VkDevice device,
VulkanDispatch* dispatch_out);
""")
# After populating a VulkanDispatch with the above methods,
# it can be useful to check whether the Vulkan 1.0 or 1.1 methods
# are all there.
def emit_feature_check_decl(cgen, tag, featureToCheck):
cgen.line("""
bool vulkan_dispatch_check_%s_%s(
const VulkanDispatch* vk);
""" % (tag, featureToCheck))
emit_feature_check_decl(self.cgenHeader, "instance", "VK_BASE_VERSION_1_0")
emit_feature_check_decl(self.cgenHeader, "instance", "VK_BASE_VERSION_1_1")
emit_feature_check_decl(self.cgenHeader, "device", "VK_BASE_VERSION_1_0")
emit_feature_check_decl(self.cgenHeader, "device", "VK_BASE_VERSION_1_1")
self.cgenHeader.line("struct VulkanDispatch {")
self.module.appendHeader(self.cgenHeader.swapCode())
def syncFeatureQuiet(self, cgen, feature):
if self.featureForCodegen != feature:
if feature == "":
self.featureForCodegen = feature
return
self.featureForCodegen = feature
def syncFeature(self, cgen, feature):
if self.featureForCodegen != feature:
if feature == "":
cgen.leftline("#endif")
self.featureForCodegen = feature
return
if self.featureForCodegen != "":
cgen.leftline("#endif")
cgen.leftline("#ifdef %s" % feature)
self.featureForCodegen = feature
def makeDlsymCall(self, cgen, apiname, typedecl):
cgen.stmt( \
"out->%s = (%s)dlSymFunc(lib, \"%s\")" % \
(apiname, typedecl, apiname))
def makeGetInstanceProcAddrCall(self, cgen, dispatch, instance, apiname, typedecl):
cgen.stmt( \
"out->%s = (%s)%s->vkGetInstanceProcAddr(%s, \"%s\")" % \
(apiname, typedecl, dispatch, instance, apiname))
def makeGetDeviceProcAddrCall(self, cgen, dispatch, device, apiname, typedecl):
cgen.stmt( \
"out->%s = (%s)%s->vkGetDeviceProcAddr(%s, \"%s\")" % \
(apiname, typedecl, dispatch, device, apiname))
def onEnd(self):
self.cgenHeader.line("};")
self.module.appendHeader(self.cgenHeader.swapCode())
# Getting dispatch tables from the loader
self.cgenImpl.line("""
void init_vulkan_dispatch_from_system_loader(
DlOpenFunc dlOpenFunc,
DlSymFunc dlSymFunc,
VulkanDispatch* out)""")
self.cgenImpl.beginBlock()
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
self.cgenImpl.stmt("void* lib = dlOpenFunc()")
self.cgenImpl.stmt("if (!lib) return")
apis = \
self.apisToGet["global"] + \
self.apisToGet["global-instance"] + \
self.apisToGet["instance"] + \
self.apisToGet["device"]
if "global-only" in self.apisToGet:
apis = apis + self.apisToGet["global-only"]
for vulkanApi, typeDecl, feature in apis:
self.syncFeature(self.cgenImpl, feature)
self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl)
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.endBlock()
# Getting instance dispatch tables
self.cgenImpl.line("""
void init_vulkan_dispatch_from_instance(
VulkanDispatch* vk,
VkInstance instance,
VulkanDispatch* out)""")
self.cgenImpl.beginBlock()
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
apis = \
self.apisToGet["global"] + \
self.apisToGet["global-instance"] + \
self.apisToGet["instance"] + \
self.apisToGet["device"]
for vulkanApi, typeDecl, feature in apis:
self.syncFeature(self.cgenImpl, feature)
self.makeGetInstanceProcAddrCall(
self.cgenImpl, "vk", "instance", vulkanApi.name, typeDecl)
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.endBlock()
# Getting device dispatch tables
self.cgenImpl.line("""
void init_vulkan_dispatch_from_device(
VulkanDispatch* vk,
VkDevice device,
VulkanDispatch* out)""")
self.cgenImpl.beginBlock()
self.cgenImpl.stmt("memset(out, 0x0, sizeof(VulkanDispatch))")
apis = \
self.apisToGet["global"] + \
self.apisToGet["global-instance"] + \
self.apisToGet["instance"] + \
self.apisToGet["device"]
for vulkanApi, typeDecl, feature in apis:
self.syncFeature(self.cgenImpl, feature)
self.makeGetDeviceProcAddrCall(
self.cgenImpl, "vk", "device", vulkanApi.name, typeDecl)
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.endBlock()
# Check Vulkan 1.0 / 1.1 functions
def emit_check_impl(cgen, dispatchVar, feature, featureToCheck, apiName):
if feature == featureToCheck:
cgen.beginIf("!%s->%s" % (dispatchVar, apiName))
cgen.stmt("fprintf(stderr, \"%s check failed: %s not found\\n\")" % (featureToCheck, apiName))
cgen.stmt("good = false")
cgen.endIf()
def emit_feature_check_impl(context, cgen, tag, featureToCheck, apis):
cgen.line("""
bool vulkan_dispatch_check_%s_%s(
const VulkanDispatch* vk)
""" % (tag, featureToCheck))
cgen.beginBlock()
cgen.stmt("bool good = true")
for vulkanApi, typeDecl, feature in apis:
context.syncFeatureQuiet(self.cgenImpl, feature)
emit_check_impl(cgen, "vk", feature, featureToCheck, vulkanApi.name)
context.syncFeatureQuiet(self.cgenImpl, "")
cgen.stmt("return good")
cgen.endBlock()
instanceApis = self.apisToGet["global-instance"] + self.apisToGet["instance"]
emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_BASE_VERSION_1_0", instanceApis)
emit_feature_check_impl(self, self.cgenImpl, "instance", "VK_BASE_VERSION_1_1", instanceApis)
emit_feature_check_impl(self, self.cgenImpl, "device", "VK_BASE_VERSION_1_0", self.apisToGet["device"])
emit_feature_check_impl(self, self.cgenImpl, "device", "VK_BASE_VERSION_1_1", self.apisToGet["device"])
self.module.appendImpl(self.cgenImpl.swapCode())
def onBeginFeature(self, featureName, featureType):
self.currentFeature = featureName
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
vulkanApi = self.typeInfo.apis[name]
typeDecl = "PFN_%s" % name
procAddressType = inferProcAddressFuncType(vulkanApi)
self.cgenHeader.stmt("%s %s" % (typeDecl, name));
self.module.appendHeader(self.cgenHeader.swapCode())
current = self.apisToGet.get(procAddressType, [])
if current == []:
self.apisToGet[procAddressType] = current
current.append((vulkanApi, typeDecl, self.currentFeature))
# VulkanDispatchFast allows one to get the optimal function pointers
# for a given Vulkan API call, in order to improve performance.
#
# We can optionally query VkDevices to get function pointers that are
# closer to the ICD and have fewer levels of indirection from the loader
# to get there.
# See
# https://github.com/KhronosGroup/Vulkan-Loader/blob/master/loader/LoaderAndLayerInterface.md
# for more info.
#
# This requires the calling C++ code to provide functions to
# generate the desired instances and devices, otherwise we won't know
# which instance or device to pass to vkGet(Instance|Device)ProcAddr,
# so it does push more complexity to the user.
class VulkanDispatchFast(VulkanDispatch):
def __init__(self, module, typeInfo):
VulkanDispatch.__init__(self, module, typeInfo)
def onBegin(self):
self.cgenHeader.line("""
void init_vulkan_dispatch_from_system_loader(
DlOpenFunc dlOpenFunc,
DlSymFunc dlSymFunc,
InstanceGetter instanceGetter,
DeviceGetter deviceGetter,
VulkanDispatch* dispatch_out);
""")
self.cgenHeader.line("struct VulkanDispatch {")
self.cgenHeader.line("VkInstance instance;")
self.cgenHeader.line("VkPhysicalDevice physicalDevice;")
self.cgenHeader.line("uint32_t physicalDeviceQueueFamilyInfoCount;")
self.cgenHeader.line("VkQueueFamilyProperties* physicalDeviceQueueFamilyInfos;")
self.cgenHeader.line("VkDevice device;")
self.cgenHeader.line("bool presentCapable;")
self.module.appendHeader(self.cgenHeader.swapCode())
def makeGetProcAddr(self, cgen, dispatchLevel, dispatch, apiname, typedecl):
if dispatchLevel == "instance":
funcname = "vkGetInstanceProcAddr"
elif dispatchLevel == "device":
funcname = "vkGetDeviceProcAddr"
else:
raise
cgen.stmt( \
"out->%s = (%s)out->%s(%s, \"%s\")" % \
(apiname, typedecl, funcname, dispatch, apiname))
def onEnd(self):
self.cgenHeader.line("};")
self.module.appendHeader(self.cgenHeader.swapCode())
self.cgenImpl.line("""
void init_vulkan_dispatch_from_system_loader(
DlOpenFunc dlOpenFunc,
DlSymFunc dlSymFunc,
InstanceGetter instanceGetter,
DeviceGetter deviceGetter,
VulkanDispatch* out)""")
self.cgenImpl.beginBlock()
self.cgenImpl.stmt("out->instance = nullptr")
self.cgenImpl.stmt("out->physicalDevice = nullptr")
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfoCount = 0")
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = nullptr")
self.cgenImpl.stmt("out->device = nullptr")
self.cgenImpl.stmt("out->presentCapable = false")
self.cgenImpl.stmt("void* lib = dlOpenFunc()")
self.cgenImpl.stmt("if (!lib) return")
for vulkanApi, typeDecl, feature in self.apisToGet["global"]:
self.syncFeature(self.cgenImpl, feature)
self.makeDlsymCall(self.cgenImpl, vulkanApi.name, typeDecl)
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.stmt("if (!out->vkGetInstanceProcAddr) return")
for vulkanApi, typeDecl, feature in self.apisToGet["global-instance"]:
self.syncFeature(self.cgenImpl, feature)
self.makeGetProcAddr( \
self.cgenImpl, "instance", "nullptr", vulkanApi.name, typeDecl);
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.stmt("if (!instanceGetter(out, &out->instance)) return")
for vulkanApi, typeDecl, feature in self.apisToGet["instance"]:
self.syncFeature(self.cgenImpl, feature)
self.makeGetProcAddr( \
self.cgenImpl, "instance", "out->instance", vulkanApi.name, typeDecl);
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, nullptr, &out->device, &out->presentCapable)) return")
self.cgenImpl.stmt("out->physicalDeviceQueueFamilyInfos = (VkQueueFamilyProperties*)malloc(out->physicalDeviceQueueFamilyInfoCount * sizeof(VkQueueFamilyProperties))");
self.cgenImpl.stmt("if (!deviceGetter(out, out->instance, &out->physicalDevice, &out->physicalDeviceQueueFamilyInfoCount, out->physicalDeviceQueueFamilyInfos, &out->device, &out->presentCapable)) return")
for vulkanApi, typeDecl, feature in self.apisToGet["device"]:
self.syncFeature(self.cgenImpl, feature)
self.makeGetProcAddr( \
self.cgenImpl, "device", "out->device", vulkanApi.name, typeDecl);
self.syncFeature(self.cgenImpl, "")
self.cgenImpl.endBlock()
self.module.appendImpl(self.cgenImpl.swapCode())
def onBeginFeature(self, featureName, featureType):
VulkanDispatch.onBeginFeature(self, featureName, featureType);
def onGenType(self, typeXml, name, alias):
VulkanDispatch.onGenType(self, typeXml, name, alias);
def onGenCmd(self, cmdinfo, name, alias):
VulkanDispatch.onGenCmd(self, cmdinfo, name, alias);
@@ -0,0 +1,677 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
import copy
from .common.codegen import CodeGen, VulkanWrapperGenerator
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
from .marshaling import VulkanMarshalingCodegen
from .reservedmarshaling import VulkanReservedMarshalingCodegen
from .counting import VulkanCountingCodegen
from .handlemap import HandleMapCodegen
from .deepcopy import DeepcopyCodegen
from .transform import TransformCodegen, genTransformsForVulkanType
from .wrapperdefs import API_PREFIX_RESERVEDMARSHAL
from .wrapperdefs import API_PREFIX_MARSHAL
from .wrapperdefs import API_PREFIX_UNMARSHAL
from .wrapperdefs import ROOT_TYPE_DEFAULT_VALUE
from .wrapperdefs import VULKAN_STREAM_TYPE_GUEST
encoder_decl_preamble = """
class VkEncoder {
public:
VkEncoder(gfxstream::guest::IOStream* stream);
~VkEncoder();
#include "VkEncoder.h.inl"
"""
encoder_decl_postamble = """
private:
class Impl;
std::unique_ptr<Impl> mImpl;
};
"""
encoder_impl_preamble ="""
using namespace gfxstream::vk;
using gfxstream::aemu::BumpPool;
#include "VkEncoder.cpp.inl"
#define VALIDATE_RET(retType, success, validate) \\
retType goldfish_vk_validateResult = validate; \\
if (goldfish_vk_validateResult != success) return goldfish_vk_validateResult; \\
#define VALIDATE_VOID(validate) \\
VkResult goldfish_vk_validateResult = validate; \\
if (goldfish_vk_validateResult != VK_SUCCESS) return; \\
"""
STREAM = "stream"
RESOURCES = "sResourceTracker"
POOL = "pool"
ENCODER_PREVALIDATED_APIS = [
"vkFlushMappedMemoryRanges",
"vkInvalidateMappedMemoryRanges",
]
ENCODER_CUSTOM_RESOURCE_PREPROCESS = [
"vkMapMemoryIntoAddressSpaceGOOGLE",
"vkDestroyDevice",
]
ENCODER_CUSTOM_RESOURCE_POSTPROCESS = [
"vkCreateInstance",
"vkCreateDevice",
"vkMapMemoryIntoAddressSpaceGOOGLE",
"vkGetPhysicalDeviceFeatures2",
"vkCreateDescriptorUpdateTemplate",
"vkGetPhysicalDeviceExternalSemaphoreProperties",
]
ENCODER_EXPLICIT_FLUSHED_APIS = [
"vkEndCommandBufferAsyncGOOGLE",
"vkQueueSubmitAsyncGOOGLE",
"vkQueueBindSparseAsyncGOOGLE",
"vkQueueWaitIdleAsyncGOOGLE",
"vkQueueSignalReleaseImageANDROID",
"vkDestroyDevice",
]
SUCCESS_RET_TYPES = {
"VkResult" : "VK_SUCCESS",
"void" : None,
# TODO: Put up success results for other return types here.
}
ENCODER_THIS_PARAM = makeVulkanTypeSimple(False, "VkEncoder", 1, "this")
# Common components of encoding a Vulkan API call
def make_event_handler_call(
handler_access,
api,
context_param,
input_result_param,
cgen,
suffix=""):
extraParams = [context_param.paramName]
if input_result_param:
extraParams.append(input_result_param)
return cgen.makeCallExpr( \
"%s->on_%s%s" % (handler_access, api.name, suffix),
extraParams + \
[p.paramName for p in api.parameters[:-1]])
def emit_custom_pre_validate(typeInfo, api, cgen):
if api.name in ENCODER_PREVALIDATED_APIS:
callExpr = \
make_event_handler_call( \
"mImpl->validation()", api,
ENCODER_THIS_PARAM,
SUCCESS_RET_TYPES[api.getRetTypeExpr()],
cgen)
if api.getRetTypeExpr() == "void":
cgen.stmt("VALIDATE_VOID(%s)" % callExpr)
else:
cgen.stmt("VALIDATE_RET(%s, %s, %s)" % \
(api.getRetTypeExpr(),
SUCCESS_RET_TYPES[api.getRetTypeExpr()],
callExpr))
def emit_custom_resource_preprocess(typeInfo, api, cgen):
if api.name in ENCODER_CUSTOM_RESOURCE_PREPROCESS:
cgen.stmt( \
make_event_handler_call( \
"sResourceTracker", api,
ENCODER_THIS_PARAM,
SUCCESS_RET_TYPES[api.getRetTypeExpr()],
cgen, suffix="_pre"))
def emit_custom_resource_postprocess(typeInfo, api, cgen):
if api.name in ENCODER_CUSTOM_RESOURCE_POSTPROCESS:
cgen.stmt(make_event_handler_call( \
"sResourceTracker",
api,
ENCODER_THIS_PARAM,
api.getRetVarExpr(),
cgen))
def emit_count_marshal(typeInfo, param, cgen):
res = \
iterateVulkanType(
typeInfo, param,
VulkanCountingCodegen( \
cgen, "sFeatureBits", param.paramName, "countPtr", ROOT_TYPE_DEFAULT_VALUE,
"count_"))
if not res:
cgen.stmt("(void)%s" % param.paramName)
def emit_marshal(typeInfo, param, cgen):
forOutput = param.isHandleType() and ("out" in param.inout)
if forOutput:
cgen.stmt("/* is handle, possibly out */")
res = \
iterateVulkanType(
typeInfo, param,
VulkanReservedMarshalingCodegen( \
cgen, "guest", STREAM, ROOT_TYPE_DEFAULT_VALUE, param.paramName, "streamPtrPtr",
API_PREFIX_RESERVEDMARSHAL,
"" if forOutput else "get_host_u64_",
direction="write"))
if not res:
cgen.stmt("(void)%s" % param.paramName)
if forOutput:
cgen.stmt("/* is handle, possibly out */")
def emit_unmarshal(typeInfo, param, cgen):
iterateVulkanType(
typeInfo, param,
VulkanMarshalingCodegen( \
cgen, STREAM, ROOT_TYPE_DEFAULT_VALUE, param.paramName,
API_PREFIX_UNMARSHAL, direction="read"))
def emit_deepcopy(typeInfo, param, cgen):
res = \
iterateVulkanType(typeInfo, param, DeepcopyCodegen(
cgen, [param.paramName, "local_" + param.paramName], "pool", ROOT_TYPE_DEFAULT_VALUE, "deepcopy_"))
if not res:
cgen.stmt("(void)%s" % param.paramName)
def emit_transform(typeInfo, param, cgen, variant="tohost"):
res = \
iterateVulkanType(typeInfo, param, TransformCodegen( \
cgen, param.paramName, "sResourceTracker", "transform_%s_" % variant, variant))
if not res:
cgen.stmt("(void)%s" % param.paramName)
def emit_handlemap_create(typeInfo, param, cgen):
iterateVulkanType(typeInfo, param, HandleMapCodegen(
cgen, None, "sResourceTracker", "handlemap_",
lambda vtype: typeInfo.isHandleType(vtype.typeName)
))
def custom_encoder_args(api):
params = ["this"]
if api.getRetVarExpr() is not None:
params.append(api.getRetVarExpr())
return params
def emit_handlemap_destroy(typeInfo, param, cgen):
iterateVulkanType(typeInfo, param, HandleMapCodegen(
cgen, None, "sResourceTracker->destroyMapping()", "handlemap_",
lambda vtype: typeInfo.isHandleType(vtype.typeName)
))
class EncodingParameters(object):
def __init__(self, api):
self.localCopied = []
self.toWrite = []
self.toRead = []
self.toCreate = []
self.toDestroy = []
for param in api.parameters:
param.action = None
param.inout = "in"
if param.paramName == "doLock":
continue
if param.possiblyOutput():
param.inout += "out"
self.toWrite.append(param)
self.toRead.append(param)
if param.isCreatedBy(api):
self.toCreate.append(param)
param.action = "create"
else:
if param.paramName == "doLock":
continue
if param.isDestroyedBy(api):
self.toDestroy.append(param)
param.action = "destroy"
localCopyParam = \
param.getForNonConstAccess().withModifiedName( \
"local_" + param.paramName)
self.localCopied.append((param, localCopyParam))
self.toWrite.append(localCopyParam)
def emit_parameter_encode_preamble_write(typeInfo, api, cgen):
emit_custom_pre_validate(typeInfo, api, cgen);
emit_custom_resource_preprocess(typeInfo, api, cgen);
cgen.stmt("auto %s = mImpl->stream()" % STREAM)
cgen.stmt("auto %s = mImpl->pool()" % POOL)
# cgen.stmt("%s->setHandleMapping(%s->unwrapMapping())" % (STREAM, RESOURCES))
encodingParams = EncodingParameters(api)
for (_, localCopyParam) in encodingParams.localCopied:
cgen.stmt(cgen.makeRichCTypeDecl(localCopyParam))
def emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen, customUnwrap=None):
encodingParams = EncodingParameters(api)
for (origParam, localCopyParam) in encodingParams.localCopied:
shouldCustomCopy = \
customUnwrap and \
origParam.paramName in customUnwrap and \
"copyOp" in customUnwrap[origParam.paramName]
shouldCustomMap = \
customUnwrap and \
origParam.paramName in customUnwrap and \
"mapOp" in customUnwrap[origParam.paramName]
if shouldCustomCopy:
customUnwrap[origParam.paramName]["copyOp"](cgen, origParam, localCopyParam)
else:
# if this is a pointer type and we don't do custom copy nor unwrap,
# and the transform doesn't end up doing anything,
# don't deepcopy, just cast it.
avoidDeepcopy = False
if origParam.pointerIndirectionLevels > 0:
testCgen = CodeGen()
genTransformsForVulkanType("sResourceTracker", origParam, lambda p: testCgen.generalAccess(p, parentVarName = None, asPtr = True), lambda p: testCgen.generalLengthAccess(p, parentVarName = None), testCgen)
emit_transform(typeInfo, origParam, testCgen, variant="tohost")
if "" == testCgen.swapCode():
avoidDeepcopy = True
if avoidDeepcopy:
cgen.line("// Avoiding deepcopy for %s" % origParam.paramName)
cgen.stmt("%s = (%s%s)%s" % (localCopyParam.paramName, localCopyParam.typeName, "*" * origParam.pointerIndirectionLevels, origParam.paramName))
else:
emit_deepcopy(typeInfo, origParam, cgen)
for (origParam, localCopyParam) in encodingParams.localCopied:
shouldCustomMap = \
customUnwrap and \
origParam.paramName in customUnwrap and \
"mapOp" in customUnwrap[origParam.paramName]
if shouldCustomMap:
customUnwrap[origParam.paramName]["mapOp"](cgen, origParam, localCopyParam)
else:
if localCopyParam.typeName == "VkAllocationCallbacks":
cgen.stmt("%s = nullptr" % localCopyParam.paramName)
apiForTransform = \
api.withCustomParameters( \
map(lambda p: p[1], \
encodingParams.localCopied))
# Apply transforms if applicable.
# Apply transform to API itself:
genTransformsForVulkanType(
"sResourceTracker",
apiForTransform,
lambda p: cgen.generalAccess(p, parentVarName = None, asPtr = True),
lambda p: cgen.generalLengthAccess(p, parentVarName = None),
cgen)
# For all local copied parameters, run the transforms
for localParam in apiForTransform.parameters:
if "doLock" in localParam.paramName:
continue
emit_transform(typeInfo, localParam, cgen, variant="tohost")
cgen.stmt("size_t count = 0")
cgen.stmt("size_t* countPtr = &count")
cgen.beginBlock()
# Use counting stream to calculate the packet size.
for p in encodingParams.toWrite:
emit_count_marshal(typeInfo, p, cgen)
cgen.endBlock()
def is_cmdbuf_dispatch(api):
return "VkCommandBuffer" == api.parameters[0].typeName
def emit_parameter_encode_write_packet_info(typeInfo, api, cgen):
# Seqno and skipping dispatch serialize are for use with VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT
doSeqno = True
doDispatchSerialize = True
if is_cmdbuf_dispatch(api):
doSeqno = False
doDispatchSerialize = False
if doSeqno:
cgen.stmt("uint32_t packetSize_%s = 4 + 4 + (queueSubmitWithCommandsEnabled ? 4 : 0) + count" % (api.name))
else:
cgen.stmt("uint32_t packetSize_%s = 4 + 4 + count" % (api.name))
if not doDispatchSerialize:
cgen.stmt("if (queueSubmitWithCommandsEnabled) packetSize_%s -= 8" % api.name)
cgen.stmt("uint8_t* streamPtr = %s->reserve(packetSize_%s)" % (STREAM, api.name))
cgen.stmt("uint8_t** streamPtrPtr = &streamPtr")
cgen.stmt("uint32_t opcode_%s = OP_%s" % (api.name, api.name))
if doSeqno:
cgen.stmt("uint32_t seqno; if (queueSubmitWithCommandsEnabled) seqno = ResourceTracker::nextSeqno()")
cgen.stmt("memcpy(streamPtr, &opcode_%s, sizeof(uint32_t)); streamPtr += sizeof(uint32_t)" % api.name)
cgen.stmt("memcpy(streamPtr, &packetSize_%s, sizeof(uint32_t)); streamPtr += sizeof(uint32_t)" % api.name)
if doSeqno:
cgen.line("if (queueSubmitWithCommandsEnabled) { memcpy(streamPtr, &seqno, sizeof(uint32_t)); streamPtr += sizeof(uint32_t); }")
def emit_parameter_encode_do_parameter_write(typeInfo, api, cgen):
encodingParams = EncodingParameters(api)
dispatchDone = False
for p in encodingParams.toWrite:
if is_cmdbuf_dispatch(api) and not dispatchDone:
cgen.beginIf("!queueSubmitWithCommandsEnabled")
emit_marshal(typeInfo, p, cgen)
cgen.endIf()
else:
emit_marshal(typeInfo, p, cgen)
dispatchDone = True
def emit_parameter_encode_read(typeInfo, api, cgen):
encodingParams = EncodingParameters(api)
for p in encodingParams.toRead:
if p.action == "create":
cgen.stmt(
"%s->setHandleMapping(%s->createMapping())" % \
(STREAM, RESOURCES))
emit_unmarshal(typeInfo, p, cgen)
if p.action == "create":
cgen.stmt(
"%s->unsetHandleMapping()" % STREAM)
emit_transform(typeInfo, p, cgen, variant="fromhost")
def emit_post(typeInfo, api, cgen):
encodingParams = EncodingParameters(api)
emit_custom_resource_postprocess(typeInfo, api, cgen)
for p in encodingParams.toDestroy:
emit_handlemap_destroy(typeInfo, p, cgen)
doSeqno = True
if is_cmdbuf_dispatch(api):
doSeqno = False
retType = api.getRetTypeExpr()
if api.name in ENCODER_EXPLICIT_FLUSHED_APIS:
cgen.stmt("stream->flush()");
return
if doSeqno:
if retType == "void":
encodingParams = EncodingParameters(api)
if 0 == len(encodingParams.toRead):
cgen.stmt("stream->flush()");
def emit_pool_free(cgen):
cgen.stmt("++encodeCount")
cgen.beginIf("0 == encodeCount % POOL_CLEAR_INTERVAL")
cgen.stmt("pool->freeAll()")
cgen.stmt("%s->clearPool()" % STREAM)
cgen.endIf()
def emit_return_unmarshal(typeInfo, api, cgen):
retType = api.getRetTypeExpr()
if retType == "void":
return
retVar = api.getRetVarExpr()
cgen.stmt("%s %s = (%s)0" % (retType, retVar, retType))
cgen.stmt("%s->read(&%s, %s)" % \
(STREAM, retVar, cgen.sizeofExpr(api.retType)))
def emit_return(typeInfo, api, cgen):
if api.getRetTypeExpr() == "void":
return
retVar = api.getRetVarExpr()
cgen.stmt("return %s" % retVar)
def emit_lock(cgen):
cgen.stmt("(void)doLock");
cgen.stmt("bool queueSubmitWithCommandsEnabled = sFeatureBits & VULKAN_STREAM_FEATURE_QUEUE_SUBMIT_WITH_COMMANDS_BIT")
cgen.stmt("if (!queueSubmitWithCommandsEnabled && doLock) this->lock()")
def emit_unlock(cgen):
cgen.stmt("if (!queueSubmitWithCommandsEnabled && doLock) this->unlock()")
def emit_debug_log(typeInfo, api, cgen):
logFormat = []
logVargs = []
for param in api.parameters:
if param.paramName == "doLock":
continue
paramFormatSpecifier = param.getPrintFormatSpecifier()
if not paramFormatSpecifier:
continue
logFormat.append(param.paramName + ":" + paramFormatSpecifier)
logVargs.append(param.paramName)
logFormatStr = ", ".join(logFormat)
logVargsStr = ", ".join(logVargs)
def emit_default_encoding(typeInfo, api, cgen):
emit_debug_log(typeInfo, api, cgen)
emit_lock(cgen)
emit_parameter_encode_preamble_write(typeInfo, api, cgen)
emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen)
emit_parameter_encode_write_packet_info(typeInfo, api, cgen)
emit_parameter_encode_do_parameter_write(typeInfo, api, cgen)
emit_parameter_encode_read(typeInfo, api, cgen)
emit_return_unmarshal(typeInfo, api, cgen)
emit_post(typeInfo, api, cgen)
emit_pool_free(cgen)
emit_unlock(cgen)
emit_return(typeInfo, api, cgen)
## Custom encoding definitions##################################################
def emit_only_goldfish_custom(typeInfo, api, cgen):
emit_lock(cgen)
cgen.vkApiCall( \
api,
customPrefix="sResourceTracker->on_",
customParameters=custom_encoder_args(api) + \
[p.paramName for p in api.parameters[:-1]])
emit_unlock(cgen)
emit_return(typeInfo, api, cgen)
def emit_only_resource_event(typeInfo, api, cgen):
cgen.stmt("(void)doLock");
input_result = None
retExpr = api.getRetVarExpr()
if retExpr:
retType = api.getRetTypeExpr()
input_result = SUCCESS_RET_TYPES[retType]
cgen.stmt("%s %s = (%s)0" % (retType, retExpr, retType))
cgen.stmt(
(("%s = " % retExpr) if retExpr else "") +
make_event_handler_call(
"sResourceTracker",
api,
ENCODER_THIS_PARAM,
input_result, cgen))
if retExpr:
emit_return(typeInfo, api, cgen)
def emit_with_custom_unwrap(custom):
def call(typeInfo, api, cgen):
emit_lock(cgen)
emit_parameter_encode_preamble_write(typeInfo, api, cgen)
emit_parameter_encode_copy_unwrap_count(
typeInfo, api, cgen, customUnwrap=custom)
emit_parameter_encode_write_packet_info(typeInfo, api, cgen)
emit_parameter_encode_do_parameter_write(typeInfo, api, cgen)
emit_parameter_encode_read(typeInfo, api, cgen)
emit_return_unmarshal(typeInfo, api, cgen)
emit_pool_free(cgen)
emit_unlock(cgen)
emit_return(typeInfo, api, cgen)
return call
def encode_vkFlushMappedMemoryRanges(typeInfo, api, cgen):
emit_lock(cgen)
emit_parameter_encode_preamble_write(typeInfo, api, cgen)
emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen)
def emit_flush_ranges(streamVar):
cgen.beginIf("!sResourceTracker->usingDirectMapping()")
cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i")
cgen.stmt("auto range = pMemoryRanges[i]")
cgen.stmt("auto memory = range.memory")
cgen.stmt("auto size = range.size")
cgen.stmt("auto offset = range.offset")
cgen.stmt("uint64_t streamSize = 0")
cgen.stmt("if (!memory) { %s->write(&streamSize, sizeof(uint64_t)); continue; }" % streamVar)
cgen.stmt("auto hostPtr = sResourceTracker->getMappedPointer(memory)")
cgen.stmt("auto actualSize = size == VK_WHOLE_SIZE ? sResourceTracker->getMappedSize(memory) : size")
cgen.stmt("if (!hostPtr) { %s->write(&streamSize, sizeof(uint64_t)); continue; }" % streamVar)
cgen.stmt("streamSize = actualSize")
cgen.stmt("%s->write(&streamSize, sizeof(uint64_t))" % streamVar)
cgen.stmt("uint8_t* targetRange = hostPtr + offset")
cgen.stmt("%s->write(targetRange, actualSize)" % streamVar)
cgen.endFor()
cgen.endIf()
emit_parameter_encode_write_packet_info(typeInfo, api, cgen)
emit_parameter_encode_do_parameter_write(typeInfo, api, cgen)
emit_flush_ranges(STREAM)
emit_parameter_encode_read(typeInfo, api, cgen)
emit_return_unmarshal(typeInfo, api, cgen)
emit_pool_free(cgen)
emit_unlock(cgen)
emit_return(typeInfo, api, cgen)
def encode_vkInvalidateMappedMemoryRanges(typeInfo, api, cgen):
emit_lock(cgen)
emit_parameter_encode_preamble_write(typeInfo, api, cgen)
emit_parameter_encode_copy_unwrap_count(typeInfo, api, cgen)
emit_parameter_encode_write_packet_info(typeInfo, api, cgen)
emit_parameter_encode_do_parameter_write(typeInfo, api, cgen)
emit_parameter_encode_read(typeInfo, api, cgen)
emit_return_unmarshal(typeInfo, api, cgen)
def emit_invalidate_ranges(streamVar):
cgen.beginIf("!sResourceTracker->usingDirectMapping()")
cgen.beginFor("uint32_t i = 0", "i < memoryRangeCount", "++i")
cgen.stmt("auto range = pMemoryRanges[i]")
cgen.stmt("auto memory = range.memory")
cgen.stmt("auto size = range.size")
cgen.stmt("auto offset = range.offset")
cgen.stmt("uint64_t streamSize = 0")
cgen.stmt("if (!memory) { %s->read(&streamSize, sizeof(uint64_t)); continue; }" % streamVar)
cgen.stmt("auto hostPtr = sResourceTracker->getMappedPointer(memory)")
cgen.stmt("auto actualSize = size == VK_WHOLE_SIZE ? sResourceTracker->getMappedSize(memory) : size")
cgen.stmt("if (!hostPtr) { %s->read(&streamSize, sizeof(uint64_t)); continue; }" % streamVar)
cgen.stmt("streamSize = actualSize")
cgen.stmt("%s->read(&streamSize, sizeof(uint64_t))" % streamVar)
cgen.stmt("uint8_t* targetRange = hostPtr + offset")
cgen.stmt("%s->read(targetRange, actualSize)" % streamVar)
cgen.endFor()
cgen.endIf()
emit_invalidate_ranges(STREAM)
emit_pool_free(cgen)
emit_unlock(cgen)
emit_return(typeInfo, api, cgen)
def emit_manual_inline(typeInfo, api, cgen):
cgen.line("#include \"%s_encode_impl.cpp.inl\"" % api.name)
def unwrap_vkCreateImage_pCreateInfo():
def mapOp(cgen, orig, local):
cgen.stmt("sResourceTracker->unwrap_vkCreateImage_pCreateInfo(%s, %s)" %
(orig.paramName, local.paramName))
return { "pCreateInfo" : { "mapOp" : mapOp } }
def unwrap_vkBindImageMemory2_pBindInfos():
def mapOp(cgen, orig, local):
cgen.stmt("sResourceTracker->unwrap_VkBindImageMemory2_pBindInfos(bindInfoCount, %s, %s)" %
(orig.paramName, local.paramName))
return { "pBindInfos" : { "mapOp" : mapOp } }
def unwrap_vkAcquireImageANDROID_nativeFenceFd():
def mapOp(cgen, orig, local):
cgen.stmt("sResourceTracker->unwrap_vkAcquireImageANDROID_nativeFenceFd(%s, &%s)" %
(orig.paramName, local.paramName))
return { "nativeFenceFd" : { "mapOp" : mapOp } }
custom_encodes = {
"vkMapMemory" : emit_only_resource_event,
"vkUnmapMemory" : emit_only_resource_event,
"vkFlushMappedMemoryRanges" : encode_vkFlushMappedMemoryRanges,
"vkInvalidateMappedMemoryRanges" : encode_vkInvalidateMappedMemoryRanges,
"vkCreateImage" : emit_with_custom_unwrap(unwrap_vkCreateImage_pCreateInfo()),
"vkCreateImageWithRequirementsGOOGLE" : emit_with_custom_unwrap(unwrap_vkCreateImage_pCreateInfo()),
"vkBindImageMemory2": emit_with_custom_unwrap(unwrap_vkBindImageMemory2_pBindInfos()),
"vkAcquireImageANDROID" : emit_with_custom_unwrap(unwrap_vkAcquireImageANDROID_nativeFenceFd()),
"vkQueueFlushCommandsGOOGLE" : emit_manual_inline,
}
class VulkanEncoder(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.typeInfo = typeInfo
self.cgenHeader = CodeGen()
self.cgenHeader.incrIndent()
self.cgenImpl = CodeGen()
def onBegin(self,):
self.module.appendHeader(encoder_decl_preamble)
self.module.appendImpl(encoder_impl_preamble)
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
api = copy.deepcopy(self.typeInfo.apis[name])
api.parameters.append(makeVulkanTypeSimple(False, "uint32_t", 0, "doLock"))
self.cgenHeader.stmt(self.cgenHeader.makeFuncProto(api))
apiImpl = api.withModifiedName("VkEncoder::" + api.name)
self.module.appendHeader(self.cgenHeader.swapCode())
def emit_function_impl(cgen):
if api.name in custom_encodes.keys():
custom_encodes[api.name](self.typeInfo, api, cgen)
else:
emit_default_encoding(self.typeInfo, api, cgen)
self.module.appendImpl(self.cgenImpl.makeFuncImpl(apiImpl, emit_function_impl))
def onEnd(self,):
self.module.appendHeader(encoder_decl_postamble)
self.cgenHeader.decrIndent()
@@ -0,0 +1,125 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import STRUCT_EXTENSION_PARAM
from .wrapperdefs import STRUCT_EXTENSION_PARAM_FOR_WRITE
from .wrapperdefs import EXTENSION_SIZE_API_NAME
from .wrapperdefs import EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME
from .wrapperdefs import STRUCT_TYPE_API_NAME
class VulkanExtensionStructs(VulkanWrapperGenerator):
def __init__(self, module, typeInfo, variant="host"):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.variant = variant
self.structTypeRetType = \
makeVulkanTypeSimple(False, "uint32_t", 0)
self.rootTypeVarName = "rootType"
self.rootTypeParam = \
makeVulkanTypeSimple(False, "VkStructureType",
0, self.rootTypeVarName)
self.structTypePrototype = \
VulkanAPI(STRUCT_TYPE_API_NAME,
self.structTypeRetType,
[STRUCT_EXTENSION_PARAM])
self.extensionStructSizeRetType = \
makeVulkanTypeSimple(False, "size_t", 0)
self.extensionStructSizePrototype = \
VulkanAPI(EXTENSION_SIZE_API_NAME,
self.extensionStructSizeRetType,
[self.rootTypeParam, STRUCT_EXTENSION_PARAM])
self.streamFeaturesType = makeVulkanTypeSimple(False, "uint32_t", 0, "streamFeatures")
self.extensionStructSizeWithStreamFeaturesPrototype = \
VulkanAPI(EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME,
self.extensionStructSizeRetType,
[self.streamFeaturesType, self.rootTypeParam, STRUCT_EXTENSION_PARAM])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
self.module.appendHeader(self.codegen.makeFuncDecl(
self.structTypePrototype))
self.module.appendHeader(self.codegen.makeFuncDecl(
self.extensionStructSizePrototype))
self.module.appendHeader(self.codegen.makeFuncDecl(
self.extensionStructSizeWithStreamFeaturesPrototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
def castAsStruct(varName, typeName, const=True):
return "reinterpret_cast<%s%s*>(%s)" % \
("const " if const else "", typeName, varName)
def structTypeImpl(cgen):
cgen.stmt(
"const uint32_t asStructType = *(%s)" %
(castAsStruct(STRUCT_EXTENSION_PARAM.paramName, "uint32_t")))
cgen.stmt("return asStructType")
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.structTypePrototype, structTypeImpl))
def forEachExtensionReturnSize(ext, _, cgen):
cgen.stmt("return sizeof(%s)" % ext.name)
def forEachExtensionReturnSizeProtectedByFeature(ext, _, cgen):
streamFeature = ext.getProtectStreamFeature()
if streamFeature is None:
cgen.stmt("return sizeof(%s)" % ext.name)
return
cgen.beginIf("%s & %s" % ("streamFeatures", streamFeature))
cgen.stmt("return sizeof(%s)" % ext.name)
cgen.endIf()
cgen.beginElse()
cgen.stmt("return 0")
cgen.endIf()
def defaultAbortEmit(cgen):
# The 'structType' name and return behavior are defined in
# emitForEachStructExtension and not accessible here. Consequently,
# this is a copy-paste from there and must be updated accordingly.
# NOTE: No need for %% if no substitution is made.
cgen.stmt("const std::string structTypeString = string_VkStructureType(VkStructureType(structType))")
cgen.stmt("GFXSTREAM_FATAL(\"Unhandled Vulkan structure type %s [%d], aborting.\", structTypeString.c_str(), structType)")
cgen.stmt("return static_cast<%s>(0)" % self.extensionStructSizeRetType.typeName)
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionStructSizePrototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
self.extensionStructSizeRetType,
STRUCT_EXTENSION_PARAM,
forEachExtensionReturnSize, autoBreak=False,
defaultEmit=(defaultAbortEmit if self.variant == "host" else None),
rootTypeVar=self.rootTypeParam)))
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionStructSizeWithStreamFeaturesPrototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
self.extensionStructSizeRetType,
STRUCT_EXTENSION_PARAM,
forEachExtensionReturnSizeProtectedByFeature, autoBreak=False,
defaultEmit=(defaultAbortEmit if self.variant == "host" else None),
rootTypeVar=self.rootTypeParam)))
@@ -0,0 +1,90 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen, VulkanAPIWrapper
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import API_PREFIX_VALIDATE
from .wrapperdefs import PARAMETERS_VALIDATE
from .wrapperdefs import VOID_TYPE
from .wrapperdefs import VALIDATE_RESULT_TYPE
from .wrapperdefs import VALIDATE_VAR_NAME
from .wrapperdefs import VALIDATE_GOOD_RESULT
from .wrapperdefs import VULKAN_STREAM_TYPE
from .wrapperdefs import VULKAN_STREAM_VAR_NAME
from .wrapperdefs import API_PREFIX_MARSHAL
from .wrapperdefs import API_PREFIX_FRONTEND
# Frontend
class VulkanFrontend(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
def validateDefFunc(_codegen, _api):
# TODO
pass
self.validateWrapper = \
VulkanAPIWrapper(
API_PREFIX_VALIDATE,
PARAMETERS_VALIDATE,
VOID_TYPE,
validateDefFunc)
def frontendDefFunc(codegen, api):
retTypeName = api.retType.typeName
codegen.stmt(
"%s %s = %s" % (VALIDATE_RESULT_TYPE, VALIDATE_VAR_NAME,
VALIDATE_GOOD_RESULT))
codegen.funcCall(None, API_PREFIX_VALIDATE + api.origName,
["&%s" % VALIDATE_VAR_NAME] + list(
map(lambda p: p.paramName, api.parameters)))
codegen.beginIf(
"%s != %s" % (VALIDATE_VAR_NAME, VALIDATE_GOOD_RESULT))
if retTypeName == VALIDATE_RESULT_TYPE:
codegen.stmt("return %s" % VALIDATE_VAR_NAME)
elif retTypeName != "void":
codegen.stmt("return (%s)0" % retTypeName)
else:
codegen.stmt("return")
codegen.endIf()
codegen.stmt("// VULKAN_STREAM_GET()")
codegen.stmt("%s* %s = nullptr" % (VULKAN_STREAM_TYPE,
VULKAN_STREAM_VAR_NAME))
retLhs = None
if retTypeName != "void":
retLhs = retTypeName + " res"
codegen.funcCall(retLhs, API_PREFIX_MARSHAL + api.origName,
[VULKAN_STREAM_VAR_NAME] + list(
map(lambda p: p.paramName, api.parameters)))
if retTypeName != "void":
codegen.stmt("return res")
self.frontendWrapper = \
VulkanAPIWrapper(
API_PREFIX_FRONTEND,
[],
None,
frontendDefFunc)
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
self.module.appendHeader(
self.frontendWrapper.makeDecl(self.typeInfo, name))
self.module.appendImpl(
self.validateWrapper.makeDefinition(
self.typeInfo, name, isStatic=True))
self.module.appendImpl(
self.frontendWrapper.makeDefinition(self.typeInfo, name))
@@ -0,0 +1,604 @@
from .common.codegen import CodeGen, VulkanWrapperGenerator
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType
from .common.vulkantypes import EXCLUDED_APIS
from .common.vulkantypes import HANDLE_TYPES
import copy
import re
RESOURCE_TRACKER_ENTRIES = [
"vkEnumerateInstanceExtensionProperties",
"vkEnumerateDeviceExtensionProperties",
"vkEnumeratePhysicalDevices",
"vkAllocateMemory",
"vkFreeMemory",
"vkCreateImage",
"vkDestroyImage",
"vkGetImageMemoryRequirements",
"vkGetImageMemoryRequirements2",
"vkGetImageDrmFormatModifierPropertiesEXT",
"vkBindImageMemory",
"vkBindImageMemory2",
"vkCreateBuffer",
"vkDestroyBuffer",
"vkGetBufferMemoryRequirements",
"vkGetBufferMemoryRequirements2",
"vkGetPhysicalDeviceProperties2",
"vkGetPhysicalDeviceProperties2KHR",
"vkCreateSemaphore",
"vkDestroySemaphore",
"vkQueueSubmit",
"vkQueueSubmit2",
"vkQueueWaitIdle",
"vkImportSemaphoreFdKHR",
"vkGetSemaphoreFdKHR",
# Warning: These need to be defined in vk.xml (currently no-op) {
"vkGetMemoryFuchsiaHandleKHR",
"vkGetMemoryFuchsiaHandlePropertiesKHR",
"vkGetSemaphoreFuchsiaHandleKHR",
"vkImportSemaphoreFuchsiaHandleKHR",
# } end Warning: These need to be defined in vk.xml (currently no-op)
"vkGetAndroidHardwareBufferPropertiesANDROID",
"vkGetMemoryAndroidHardwareBufferANDROID",
"vkGetMemoryFdKHR",
"vkGetMemoryFdPropertiesKHR",
"vkCreateSamplerYcbcrConversion",
"vkDestroySamplerYcbcrConversion",
"vkUpdateDescriptorSetWithTemplate",
"vkGetPhysicalDeviceFormatProperties2",
"vkGetPhysicalDeviceImageFormatProperties2",
"vkBeginCommandBuffer",
"vkEndCommandBuffer",
"vkResetCommandBuffer",
"vkCreateImageView",
"vkCreateSampler",
"vkGetPhysicalDeviceExternalFenceProperties",
"vkGetPhysicalDeviceExternalBufferProperties",
"vkCreateFence",
"vkResetFences",
"vkImportFenceFdKHR",
"vkGetFenceFdKHR",
"vkGetFenceStatus",
"vkWaitForFences",
"vkCreateDescriptorPool",
"vkDestroyDescriptorPool",
"vkResetDescriptorPool",
"vkAllocateDescriptorSets",
"vkFreeDescriptorSets",
"vkCreateDescriptorSetLayout",
"vkCmdExecuteCommands",
"vkCmdBindDescriptorSets",
"vkDestroyDescriptorSetLayout",
"vkAllocateCommandBuffers",
"vkQueueSignalReleaseImageANDROID",
"vkCmdPipelineBarrier",
"vkCreateGraphicsPipelines",
"vkCmdClearColorImage",
# Fuchsia
"vkGetMemoryZirconHandleFUCHSIA",
"vkGetMemoryZirconHandlePropertiesFUCHSIA",
"vkGetSemaphoreZirconHandleFUCHSIA",
"vkImportSemaphoreZirconHandleFUCHSIA",
"vkCreateBufferCollectionFUCHSIA",
"vkDestroyBufferCollectionFUCHSIA",
"vkSetBufferCollectionImageConstraintsFUCHSIA",
"vkSetBufferCollectionBufferConstraintsFUCHSIA",
"vkGetBufferCollectionPropertiesFUCHSIA",
"vkSetPrivateData",
"vkSetPrivateDataKHR",
"vkGetPrivateData",
"vkGetPrivateDataKHR",
"vkCreatePrivateDataSlot",
"vkCreatePrivateDataSlotEXT",
"vkDestroyPrivateDataSlot",
"vkDestroyPrivateDataSlotEXT",
]
SUCCESS_VAL = {
"VkResult" : ["VK_SUCCESS"],
}
# These could be entrypoints that are custom-written for gfxstream, or ones that
# are meant fall back to the vk_common_* entrypoints
NON_AUTOGEN_ENTRYPOINTS = [
# Instance/device/physical-device special-handling, dispatch tables, etc..
"vkCreateInstance",
"vkDestroyInstance",
"vkGetInstanceProcAddr",
"vkEnumerateInstanceVersion",
"vkEnumerateInstanceLayerProperties",
"vkEnumerateInstanceExtensionProperties",
"vkEnumerateDeviceExtensionProperties",
"vkGetDeviceProcAddr",
"vkEnumeratePhysicalDevices",
"vkEnumeratePhysicalDeviceGroups",
"vkCreateDevice",
"vkDestroyDevice",
# Manual alloc/free + vk_*_init/free() call w/ special params
# Command pool/buffer handling
"vkCreateCommandPool",
"vkDestroyCommandPool",
"vkAllocateCommandBuffers",
"vkResetCommandPool",
"vkFreeCommandBuffers",
"vkResetCommandPool",
# Transform feedback
"vkCmdBeginTransformFeedbackEXT",
# Special cases to handle struct translations in the pNext chain
# TODO: Make a codegen module (use deepcopy as reference) to make this more robust
"vkAllocateMemory",
"vkUpdateDescriptorSets",
# Use vk_common_* entrypoints; usually just dispatches to the "vk*2()" API variant
"vkGetDeviceQueue",
"vkGetDeviceQueue2",
"vkGetPhysicalDeviceProperties",
# Custom gfxstream functions
"vkMapMemoryIntoAddressSpaceGOOGLE",
"vkUpdateDescriptorSetWithTemplateSizedGOOGLE",
"vkBeginCommandBufferAsyncGOOGLE",
"vkEndCommandBufferAsyncGOOGLE",
"vkResetCommandBufferAsyncGOOGLE",
"vkCommandBufferHostSyncGOOGLE",
"vkCreateImageWithRequirementsGOOGLE",
"vkCreateBufferWithRequirementsGOOGLE",
"vkGetMemoryHostAddressInfoGOOGLE",
"vkGetPhysicalDeviceProperties2",
"vkFreeMemorySyncGOOGLE",
"vkQueueHostSyncGOOGLE",
"vkQueueSubmitAsyncGOOGLE",
"vkQueueWaitIdleAsyncGOOGLE",
"vkQueueBindSparseAsyncGOOGLE",
"vkGetLinearImageLayoutGOOGLE",
"vkGetLinearImageLayout2GOOGLE",
"vkQueueFlushCommandsGOOGLE",
"vkQueueCommitDescriptorSetUpdatesGOOGLE",
"vkCollectDescriptorPoolIdsGOOGLE",
"vkQueueSignalReleaseImageANDROIDAsyncGOOGLE",
"vkQueueFlushCommandsFromAuxMemoryGOOGLE",
"vkGetBlobGOOGLE",
"vkUpdateDescriptorSetWithTemplateSized2GOOGLE",
"vkQueueSubmitAsync2GOOGLE",
"vkGetSemaphoreGOOGLE",
"vkTraceAsyncGOOGLE",
"vkSetDebugMetadataAsyncGOOGLE",
]
# Handles that need to be translated to/from their corresponding gfxstream object types
HANDLES_TRANSLATE = {
"VkInstance",
"VkPhysicalDevice",
"VkDevice",
"VkQueue",
"VkCommandPool",
"VkCommandBuffer",
"VkFence",
"VkSemaphore",
# TODO: Still need this translation to avoid descriptorSets crash
"VkBuffer",
}
# Types that have a corresponding method for transforming
# an input list to its internal counterpart
TYPES_TRANSFORM_LIST_METHOD = {
"VkFence",
"VkSemaphore",
"VkSemaphoreSubmitInfo",
}
def is_cmdbuf_dispatch(api):
return "VkCommandBuffer" == api.parameters[0].typeName
def is_queue_dispatch(api):
return "VkQueue" == api.parameters[0].typeName
def getCreateParam(api):
for param in api.parameters:
if param.isCreatedBy(api):
return param
return None
def getDestroyParam(api):
for param in api.parameters:
if param.isDestroyedBy(api):
return param
return None
# i.e. VkQueryPool --> vk_query_pool
def typeNameToMesaType(typeName):
vkTypeNameRegex = "(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])"
words = re.split(vkTypeNameRegex, typeName)
outputType = "vk"
for word in words[1:]:
outputType += "_"
outputType += word.lower()
return outputType
def typeNameToBaseName(typeName):
return typeNameToMesaType(typeName)[len("vk_"):]
def paramNameToObjectName(paramName):
return "gfxstream_%s" % paramName
def typeNameToVkObjectType(typeName):
return "VK_OBJECT_TYPE_%s" % typeNameToBaseName(typeName).upper()
def typeNameToObjectType(typeName):
return "gfxstream_vk_%s" % typeNameToBaseName(typeName)
def transformListFuncName(typeName):
return "transform%sList" % (typeName)
def isAllocatorParam(param):
ALLOCATOR_TYPE_NAME = "VkAllocationCallbacks"
return (param.pointerIndirectionLevels == 1
and param.isConst
and param.typeName == ALLOCATOR_TYPE_NAME)
def isArrayParam(param):
return (1 == param.pointerIndirectionLevels
and param.isConst
and "len" in param.attribs)
INTERNAL_OBJECT_NAME = "internal_object"
class VulkanFuncTable(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.typeInfo = typeInfo
self.cgen = CodeGen()
self.entries = []
self.entryFeatures = []
self.cmdToFeatureType = {}
self.feature = None
self.featureType = None
def onBegin(self,):
cgen = self.cgen
self.module.appendImpl(cgen.swapCode())
pass
def onBeginFeature(self, featureName, featureType):
self.feature = featureName
self.featureType = featureType
def onEndFeature(self):
self.feature = None
self.featureType = None
def onFeatureNewCmd(self, name):
self.cmdToFeatureType[name] = self.featureType
def onGenCmd(self, cmdinfo, name, alias):
typeInfo = self.typeInfo
cgen = self.cgen
api = typeInfo.apis[name]
self.entries.append(api)
self.entryFeatures.append(self.feature)
self.loopVars = ["i", "j", "k", "l", "m", "n"]
self.loopVarIndex = 0
def getNextLoopVar():
if self.loopVarIndex >= len(self.loopVars):
raise
loopVar = self.loopVars[self.loopVarIndex]
self.loopVarIndex += 1
return loopVar
def isCompoundType(typeName):
return typeInfo.isCompoundType(typeName)
def handleTranslationRequired(typeName):
return typeName in HANDLE_TYPES and typeName in HANDLES_TRANSLATE
def translationRequired(typeName):
if isCompoundType(typeName):
struct = typeInfo.structs[typeName]
for member in struct.members:
if translationRequired(member.typeName):
return True
return False
else:
return handleTranslationRequired(typeName)
def genDestroyGfxstreamObjects():
destroyParam = getDestroyParam(api)
if not destroyParam:
return
if not translationRequired(destroyParam.typeName):
return
objectName = paramNameToObjectName(destroyParam.paramName)
allocatorParam = "NULL"
for p in api.parameters:
if isAllocatorParam(p):
allocatorParam = p.paramName
deviceParam = api.parameters[0]
if "VkDevice" != deviceParam.typeName:
print("ERROR: Unhandled non-VkDevice parameters[0]: %s (for API: %s)" %(deviceParam.typeName, api.name))
raise
# call vk_object_free() directly
mesaObjectDestroy = "(void *)%s" % objectName
cgen.funcCall(
None,
"vk_object_free",
["&%s->vk" % paramNameToObjectName(deviceParam.paramName), allocatorParam, mesaObjectDestroy]
)
def genMesaObjectAlloc(allocCallLhs):
deviceParam = api.parameters[0]
if "VkDevice" != deviceParam.typeName:
print("ERROR: Unhandled non-VkDevice parameters[0]: %s (for API: %s)" %(deviceParam.typeName, api.name))
raise
allocatorParam = "NULL"
for p in api.parameters:
if isAllocatorParam(p):
allocatorParam = p.paramName
createParam = getCreateParam(api)
objectType = typeNameToObjectType(createParam.typeName)
# Call vk_object_zalloc directly
cgen.funcCall(
allocCallLhs,
"(%s *)vk_object_zalloc" % objectType,
["&%s->vk" % paramNameToObjectName(deviceParam.paramName), allocatorParam, ("sizeof(%s)" % objectType), typeNameToVkObjectType(createParam.typeName)]
)
# Alloc/create gfxstream_vk_* object
def genCreateGfxstreamObjects():
createParam = getCreateParam(api)
if not createParam:
return False
if not handleTranslationRequired(createParam.typeName):
return False
objectType = "struct %s" % typeNameToObjectType(createParam.typeName)
callLhs = "%s *%s" % (objectType, paramNameToObjectName(createParam.paramName))
genMesaObjectAlloc(callLhs)
retVar = api.getRetVarExpr()
if retVar:
retTypeName = api.getRetTypeExpr()
# ex: vkCreateBuffer_VkResult_return = gfxstream_buffer ? VK_SUCCESS : VK_ERROR_OUT_OF_HOST_MEMORY;
cgen.stmt("%s = %s ? %s : %s" %
(retVar, paramNameToObjectName(createParam.paramName), SUCCESS_VAL[retTypeName][0], "VK_ERROR_OUT_OF_HOST_MEMORY"))
return True
def genVkFromHandle(param, fromName):
objectName = paramNameToObjectName(param.paramName)
cgen.stmt("VK_FROM_HANDLE(%s, %s, %s)" %
(typeNameToObjectType(param.typeName), objectName, fromName))
return objectName
def genGetGfxstreamHandles():
createParam = getCreateParam(api)
for param in api.parameters:
if not handleTranslationRequired(param.typeName):
continue
elif isArrayParam(param):
continue
elif param != createParam:
if param.pointerIndirectionLevels > 0:
print("ERROR: Unhandled pointerIndirectionLevels > 1 for API %s (param %s)" % (api.name, param.paramName))
raise
genVkFromHandle(param, param.paramName)
def internalNestedParamName(param):
parentName = ""
if param.parent:
parentName = "_%s" % param.parent.typeName
return "internal%s_%s" % (parentName, param.paramName)
def genInternalArrayDeclarations(param, countParamName, nestLevel=0):
internalArray = None
if 0 == nestLevel:
internalArray = "internal_%s" % param.paramName
cgen.stmt("std::vector<%s> %s(%s)" % (param.typeName, internalArray, countParamName))
elif 1 == nestLevel or 2 == nestLevel:
internalArray = internalNestedParamName(param)
if isArrayParam(param):
cgen.stmt("std::vector<std::vector<%s>> %s" % (param.typeName, internalArray))
else:
cgen.stmt("std::vector<%s> %s" % (param.typeName, internalArray))
else:
print("ERROR: nestLevel > 2 not verified.")
raise
if isCompoundType(param.typeName):
for member in typeInfo.structs[param.typeName].members:
if translationRequired(member.typeName):
if handleTranslationRequired(member.typeName) and not isArrayParam(member):
# No declarations for non-array handleType
continue
genInternalArrayDeclarations(member, countParamName, nestLevel + 1)
return internalArray
def genInternalCompoundType(param, outName, inName, currLoopVar):
nextLoopVar = None
cgen.stmt("%s = %s" % (outName, inName))
for member in typeInfo.structs[param.typeName].members:
if not translationRequired(member.typeName):
continue
cgen.line("/* %s::%s */" % (param.typeName, member.paramName))
nestedOutName = ("%s[%s]" % (internalNestedParamName(member), currLoopVar))
if isArrayParam(member):
countParamName = "%s.%s" % (outName, member.attribs["len"])
inArrayName = "%s.%s" % (outName, member.paramName)
cgen.stmt("%s.push_back(std::vector<%s>())" % (internalNestedParamName(member), member.typeName))
if member.typeName in TYPES_TRANSFORM_LIST_METHOD:
# Use the corresponding transformList call
cgen.funcCall(nestedOutName, transformListFuncName(member.typeName), [inArrayName, countParamName])
cgen.stmt("%s = %s.data()" % (inArrayName, nestedOutName))
cgen.stmt("%s = %s.size()" % (countParamName, nestedOutName))
else:
# Standard translation
cgen.stmt("%s.resize(%s)" % (nestedOutName, countParamName))
if not nextLoopVar:
nextLoopVar = getNextLoopVar()
internalArray = genInternalArray(member, countParamName, nestedOutName, inArrayName, nextLoopVar)
cgen.stmt("%s = %s" %(inArrayName, internalArray))
elif isCompoundType(member.typeName):
memberFullName = "%s.%s" % (outName, member.paramName)
if 1 == member.pointerIndirectionLevels:
cgen.beginIf(memberFullName)
inParamName = "%s[0]" % memberFullName
genInternalCompoundType(member, nestedOutName, inParamName, currLoopVar)
cgen.stmt("%s.%s = &%s" % (outName, member.paramName, nestedOutName))
else:
cgen.beginBlock()
genInternalCompoundType(member, nestedOutName, memberFullName, currLoopVar)
cgen.stmt("%s.%s = %s" % (outName, member.paramName, nestedOutName))
cgen.endBlock()
else:
# Replace member with internal object
replaceName = "%s.%s" % (outName, member.paramName)
if member.isOptional:
cgen.beginIf(replaceName)
gfxstreamObject = genVkFromHandle(member, replaceName)
cgen.stmt("%s = %s->%s" % (replaceName, gfxstreamObject, INTERNAL_OBJECT_NAME))
if member.isOptional:
cgen.endIf()
def genInternalArray(param, countParamName, outArrayName, inArrayName, loopVar):
if param.typeName in TYPES_TRANSFORM_LIST_METHOD:
# Use the corresponding transformList call
cgen.funcCall(outArrayName, transformListFuncName(param.typeName), [inArrayName, countParamName])
cgen.stmt("%s = %s.data()" % (inArrayName, outArrayName))
cgen.stmt("%s = %s.size()" % (countParamName, outArrayName))
else:
cgen.beginFor("uint32_t %s = 0" % loopVar, "%s < %s" % (loopVar, countParamName), "++%s" % loopVar)
if param.isOptional:
cgen.beginIf(inArrayName)
if isCompoundType(param.typeName):
genInternalCompoundType(param, ("%s[%s]" % (outArrayName, loopVar)), "%s[%s]" % (inArrayName, loopVar), loopVar)
else:
gfxstreamObject = genVkFromHandle(param, "%s[%s]" % (inArrayName, loopVar))
cgen.stmt("%s[%s] = %s->%s" % (outArrayName, loopVar, gfxstreamObject, INTERNAL_OBJECT_NAME))
if param.isOptional:
cgen.endIf()
cgen.endFor()
return "%s.data()" % outArrayName
# Translate params into params needed for gfxstream-internal
# encoder/resource-tracker calls
def getEncoderOrResourceTrackerParams():
createParam = getCreateParam(api)
outParams = copy.deepcopy(api.parameters)
nextLoopVar = getNextLoopVar()
for param in outParams:
if not translationRequired(param.typeName):
continue
elif isArrayParam(param) or isCompoundType(param.typeName):
if param.possiblyOutput():
print("ERROR: Unhandled CompoundType / Array output for API %s (param %s)" % (api.name, param.paramName))
raise
if 1 != param.pointerIndirectionLevels or not param.isConst:
print("ERROR: Compound type / array input is not 'const <type>*' (API: %s, paramName: %s)" % (api.name, param.paramName))
raise
countParamName = "1"
if "len" in param.attribs:
countParamName = param.attribs["len"]
internalArrayName = genInternalArrayDeclarations(param, countParamName)
param.paramName = genInternalArray(param, countParamName, internalArrayName, param.paramName, nextLoopVar)
elif 0 == param.pointerIndirectionLevels:
if param.isOptional:
param.paramName = ("%s ? %s->%s : VK_NULL_HANDLE" % (paramNameToObjectName(param.paramName), paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
else:
param.paramName = ("%s->%s" % (paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
elif createParam and param.paramName == createParam.paramName:
param.paramName = ("&%s->%s" % (paramNameToObjectName(param.paramName), INTERNAL_OBJECT_NAME))
else:
print("ERROR: Unknown handling for param: %s (API: %s)" % (param, api.name))
raise
return outParams
def genEncoderOrResourceTrackerCall(declareResources=True):
if is_cmdbuf_dispatch(api):
cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getCommandBufferEncoder(%s->%s)" % (paramNameToObjectName(api.parameters[0].paramName), INTERNAL_OBJECT_NAME))
elif is_queue_dispatch(api):
cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getQueueEncoder(%s->%s)" % (paramNameToObjectName(api.parameters[0].paramName), INTERNAL_OBJECT_NAME))
else:
cgen.stmt("auto vkEnc = gfxstream::vk::ResourceTracker::getThreadLocalEncoder()")
callLhs = None
retTypeName = api.getRetTypeExpr()
if retTypeName != "void":
callLhs = api.getRetVarExpr()
# Get parameter list modded for gfxstream-internal call
parameters = getEncoderOrResourceTrackerParams()
if name in RESOURCE_TRACKER_ENTRIES:
if declareResources:
cgen.stmt("auto resources = gfxstream::vk::ResourceTracker::get()")
cgen.funcCall(
callLhs, "resources->" + "on_" + api.name,
["vkEnc"] + SUCCESS_VAL.get(retTypeName, []) + \
[p.paramName for p in parameters])
else:
cgen.funcCall(
callLhs, "vkEnc->" + api.name, [p.paramName for p in parameters] + ["true /* do lock */"])
def genReturnExpression():
retTypeName = api.getRetTypeExpr()
# Set the createParam output, if applicable
createParam = getCreateParam(api)
if createParam and handleTranslationRequired(createParam.typeName):
if 1 != createParam.pointerIndirectionLevels:
print("ERROR: Unhandled pointerIndirectionLevels != 1 in return for API %s (createParam %s)" % api.name, createParam.paramName)
raise
# ex: *pBuffer = gfxstream_vk_buffer_to_handle(gfxstream_buffer)
cgen.funcCall(
"*%s" % createParam.paramName,
"%s_to_handle" % typeNameToObjectType(createParam.typeName),
[paramNameToObjectName(createParam.paramName)]
)
if retTypeName != "void":
cgen.stmt("return %s" % api.getRetVarExpr())
def genGfxstreamEntry(declareResources=True):
cgen.stmt("MESA_TRACE_SCOPE(\"%s\")" % api.name)
# declare returnVar
retTypeName = api.getRetTypeExpr()
retVar = api.getRetVarExpr()
if retVar:
cgen.stmt("%s %s = (%s)0" % (retTypeName, retVar, retTypeName))
# Check non-null destroy param for free/destroy calls
destroyParam = getDestroyParam(api)
if destroyParam:
cgen.beginIf("VK_NULL_HANDLE == %s" % destroyParam.paramName)
if api.getRetTypeExpr() != "void":
cgen.stmt("return %s" % api.getRetVarExpr())
else:
cgen.stmt("return")
cgen.endIf()
# Translate handles
genGetGfxstreamHandles()
# Translation/creation of objects
createdObject = genCreateGfxstreamObjects()
# Make encoder/resource-tracker call
if retVar and createdObject:
cgen.beginIf("%s == %s" % (SUCCESS_VAL[retTypeName][0], retVar))
else:
cgen.beginBlock()
genEncoderOrResourceTrackerCall()
cgen.endBlock()
# Destroy gfxstream objects
genDestroyGfxstreamObjects()
# Set output / return variables
genReturnExpression()
api_entry = api.withModifiedName("gfxstream_vk_" + api.name[2:])
if api.name not in NON_AUTOGEN_ENTRYPOINTS:
cgen.line(self.cgen.makeFuncProto(api_entry))
cgen.beginBlock()
genGfxstreamEntry()
cgen.endBlock()
self.module.appendImpl(cgen.swapCode())
def onEnd(self,):
pass
def isDeviceDispatch(self, api):
# TODO(230793667): improve the heuristic and just use "cmdToFeatureType"
return (len(api.parameters) > 0 and
"VkDevice" == api.parameters[0].typeName) or (
"VkCommandBuffer" == api.parameters[0].typeName and
self.cmdToFeatureType.get(api.name, "") == "device")
@@ -0,0 +1,253 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE
class HandleMapCodegen(VulkanTypeIterator):
def __init__(self, cgen, inputVar, handlemapVarName, prefix, isHandleFunc):
self.cgen = cgen
self.inputVar = inputVar
self.prefix = prefix
self.handlemapVarName = handlemapVarName
def makeAccess(varName, asPtr = True):
return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr)
def makeLengthAccess(varName):
return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName)
def makeLengthAccessGuard(varName):
return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName)
self.exprAccessor = makeAccess(self.inputVar)
self.exprAccessorValue = makeAccess(self.inputVar, asPtr = False)
self.lenAccessor = makeLengthAccess(self.inputVar)
self.lenAccessorGuard = makeLengthAccessGuard(self.inputVar)
self.checked = False
self.isHandleFunc = isHandleFunc
def needSkip(self, vulkanType):
return False
def makeCastExpr(self, vulkanType):
return "(%s)" % (
self.cgen.makeCTypeDecl(vulkanType, useParamName=False))
def asNonConstCast(self, access, vulkanType):
if vulkanType.staticArrExpr:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
elif vulkanType.accessibleAsPointer():
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access)
else:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
return casted
def onCheck(self, vulkanType):
pass
def endCheck(self, vulkanType):
pass
def onCompoundType(self, vulkanType):
if self.needSkip(vulkanType):
self.cgen.line("// TODO: Unsupported : %s" %
self.cgen.makeCTypeDecl(vulkanType))
return
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
isPtr = vulkanType.pointerIndirectionLevels > 0
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
if isPtr:
self.cgen.beginIf(access)
if lenAccess is not None:
loopVar = "i"
access = "%s + %s" % (access, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess)
forIncr = "++%s" % loopVar
self.cgen.beginFor(forInit, forCond, forIncr)
accessCasted = self.asNonConstCast(access, vulkanType)
self.cgen.funcCall(None, self.prefix + vulkanType.typeName,
[self.handlemapVarName, accessCasted])
if lenAccess is not None:
self.cgen.endFor()
if isPtr:
self.cgen.endIf()
if lenAccessGuard is not None:
self.cgen.endIf()
def onString(self, vulkanType):
pass
def onStringArray(self, vulkanType):
pass
def onStaticArr(self, vulkanType):
if not self.isHandleFunc(vulkanType):
return
accessLhs = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
if lenAccess is not None:
self.cgen.stmt("%s->mapHandles_%s(%s%s, %s)" % \
(self.handlemapVarName, vulkanType.typeName,
self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()),
accessLhs, lenAccess))
def onStructExtension(self, vulkanType):
access = self.exprAccessor(vulkanType)
castedAccessExpr = "(%s)(%s)" % ("void*", access)
self.cgen.beginIf(access)
self.cgen.funcCall(None, self.prefix + "extension_struct",
[self.handlemapVarName, castedAccessExpr])
self.cgen.endIf()
def onPointer(self, vulkanType):
if self.needSkip(vulkanType):
return
if not self.isHandleFunc(vulkanType):
return
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccess = "1" if lenAccess is None else lenAccess
self.cgen.beginIf(access)
self.cgen.stmt( \
"%s->mapHandles_%s(%s%s, %s)" % \
(self.handlemapVarName,
vulkanType.typeName,
self.makeCastExpr(vulkanType.getForNonConstAccess()),
access,
lenAccess))
self.cgen.endIf()
def onValue(self, vulkanType):
if not self.isHandleFunc(vulkanType):
return
access = self.exprAccessor(vulkanType)
self.cgen.stmt(
"%s->mapHandles_%s(%s%s)" % \
(self.handlemapVarName, vulkanType.typeName,
self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()),
access))
class VulkanHandleMap(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.handlemapPrefix = "handlemap_"
self.toMapVar = "toMap"
self.handlemapVarName = "handlemap"
self.handlemapParam = \
makeVulkanTypeSimple(False, "VulkanHandleMapping", 1,
self.handlemapVarName)
self.voidType = makeVulkanTypeSimple(False, "void", 0)
self.handlemapCodegen = \
HandleMapCodegen(
None,
self.toMapVar,
self.handlemapVarName,
self.handlemapPrefix,
lambda vtype : typeInfo.isHandleType(vtype.typeName))
self.knownDefs = {}
self.extensionHandlemapPrototype = \
VulkanAPI(self.handlemapPrefix + "extension_struct",
self.voidType,
[self.handlemapParam, STRUCT_EXTENSION_PARAM_FOR_WRITE])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
self.module.appendImpl(self.codegen.makeFuncDecl(
self.extensionHandlemapPrototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownDefs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
self.module.appendHeader(
self.codegen.makeFuncAlias(self.handlemapPrefix + name,
self.handlemapPrefix + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
typeFromName = \
lambda varname: \
makeVulkanTypeSimple(varname == "from", name, 1, varname)
handlemapParams = \
[self.handlemapParam] + \
list(map(typeFromName, [self.toMapVar]))
handlemapPrototype = \
VulkanAPI(self.handlemapPrefix + name,
self.voidType,
handlemapParams)
def funcDefGenerator(cgen):
self.handlemapCodegen.cgen = cgen
for p in handlemapParams:
cgen.stmt("(void)%s" % p.paramName)
for member in structInfo.members:
iterateVulkanType(self.typeInfo, member,
self.handlemapCodegen)
self.module.appendHeader(
self.codegen.makeFuncDecl(handlemapPrototype))
self.module.appendImpl(
self.codegen.makeFuncImpl(handlemapPrototype, funcDefGenerator))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
def forEachExtensionHandlemap(ext, castedAccess, cgen):
cgen.funcCall(None, self.handlemapPrefix + ext.name,
[self.handlemapVarName, castedAccess])
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionHandlemapPrototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
self.voidType,
STRUCT_EXTENSION_PARAM_FOR_WRITE,
forEachExtensionHandlemap)))
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,524 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
KNOWN_FUNCTION_OPCODES = {
"vkCreateInstance": 20000,
"vkDestroyInstance": 20001,
"vkEnumeratePhysicalDevices": 20002,
"vkGetPhysicalDeviceFeatures": 20003,
"vkGetPhysicalDeviceFormatProperties": 20004,
"vkGetPhysicalDeviceImageFormatProperties": 20005,
"vkGetPhysicalDeviceProperties": 20006,
"vkGetPhysicalDeviceQueueFamilyProperties": 20007,
"vkGetPhysicalDeviceMemoryProperties": 20008,
"vkGetInstanceProcAddr": 20009,
"vkGetDeviceProcAddr": 20010,
"vkCreateDevice": 20011,
"vkDestroyDevice": 20012,
"vkEnumerateInstanceExtensionProperties": 20013,
"vkEnumerateDeviceExtensionProperties": 20014,
"vkEnumerateInstanceLayerProperties": 20015,
"vkEnumerateDeviceLayerProperties": 20016,
"vkGetDeviceQueue": 20017,
"vkQueueSubmit": 20018,
"vkQueueWaitIdle": 20019,
"vkDeviceWaitIdle": 20020,
"vkAllocateMemory": 20021,
"vkFreeMemory": 20022,
"vkMapMemory": 20023,
"vkUnmapMemory": 20024,
"vkFlushMappedMemoryRanges": 20025,
"vkInvalidateMappedMemoryRanges": 20026,
"vkGetDeviceMemoryCommitment": 20027,
"vkBindBufferMemory": 20028,
"vkBindImageMemory": 20029,
"vkGetBufferMemoryRequirements": 20030,
"vkGetImageMemoryRequirements": 20031,
"vkGetImageSparseMemoryRequirements": 20032,
"vkGetPhysicalDeviceSparseImageFormatProperties": 20033,
"vkQueueBindSparse": 20034,
"vkCreateFence": 20035,
"vkDestroyFence": 20036,
"vkResetFences": 20037,
"vkGetFenceStatus": 20038,
"vkWaitForFences": 20039,
"vkCreateSemaphore": 20040,
"vkDestroySemaphore": 20041,
"vkCreateEvent": 20042,
"vkDestroyEvent": 20043,
"vkGetEventStatus": 20044,
"vkSetEvent": 20045,
"vkResetEvent": 20046,
"vkCreateQueryPool": 20047,
"vkDestroyQueryPool": 20048,
"vkGetQueryPoolResults": 20049,
"vkCreateBuffer": 20050,
"vkDestroyBuffer": 20051,
"vkCreateBufferView": 20052,
"vkDestroyBufferView": 20053,
"vkCreateImage": 20054,
"vkDestroyImage": 20055,
"vkGetImageSubresourceLayout": 20056,
"vkCreateImageView": 20057,
"vkDestroyImageView": 20058,
"vkCreateShaderModule": 20059,
"vkDestroyShaderModule": 20060,
"vkCreatePipelineCache": 20061,
"vkDestroyPipelineCache": 20062,
"vkGetPipelineCacheData": 20063,
"vkMergePipelineCaches": 20064,
"vkCreateGraphicsPipelines": 20065,
"vkCreateComputePipelines": 20066,
"vkDestroyPipeline": 20067,
"vkCreatePipelineLayout": 20068,
"vkDestroyPipelineLayout": 20069,
"vkCreateSampler": 20070,
"vkDestroySampler": 20071,
"vkCreateDescriptorSetLayout": 20072,
"vkDestroyDescriptorSetLayout": 20073,
"vkCreateDescriptorPool": 20074,
"vkDestroyDescriptorPool": 20075,
"vkResetDescriptorPool": 20076,
"vkAllocateDescriptorSets": 20077,
"vkFreeDescriptorSets": 20078,
"vkUpdateDescriptorSets": 20079,
"vkCreateFramebuffer": 20080,
"vkDestroyFramebuffer": 20081,
"vkCreateRenderPass": 20082,
"vkDestroyRenderPass": 20083,
"vkGetRenderAreaGranularity": 20084,
"vkCreateCommandPool": 20085,
"vkDestroyCommandPool": 20086,
"vkResetCommandPool": 20087,
"vkAllocateCommandBuffers": 20088,
"vkFreeCommandBuffers": 20089,
"vkBeginCommandBuffer": 20090,
"vkEndCommandBuffer": 20091,
"vkResetCommandBuffer": 20092,
"vkCmdBindPipeline": 20093,
"vkCmdSetViewport": 20094,
"vkCmdSetScissor": 20095,
"vkCmdSetLineWidth": 20096,
"vkCmdSetDepthBias": 20097,
"vkCmdSetBlendConstants": 20098,
"vkCmdSetDepthBounds": 20099,
"vkCmdSetStencilCompareMask": 20100,
"vkCmdSetStencilWriteMask": 20101,
"vkCmdSetStencilReference": 20102,
"vkCmdBindDescriptorSets": 20103,
"vkCmdBindIndexBuffer": 20104,
"vkCmdBindVertexBuffers": 20105,
"vkCmdDraw": 20106,
"vkCmdDrawIndexed": 20107,
"vkCmdDrawIndirect": 20108,
"vkCmdDrawIndexedIndirect": 20109,
"vkCmdDispatch": 20110,
"vkCmdDispatchIndirect": 20111,
"vkCmdCopyBuffer": 20112,
"vkCmdCopyImage": 20113,
"vkCmdBlitImage": 20114,
"vkCmdCopyBufferToImage": 20115,
"vkCmdCopyImageToBuffer": 20116,
"vkCmdUpdateBuffer": 20117,
"vkCmdFillBuffer": 20118,
"vkCmdClearColorImage": 20119,
"vkCmdClearDepthStencilImage": 20120,
"vkCmdClearAttachments": 20121,
"vkCmdResolveImage": 20122,
"vkCmdSetEvent": 20123,
"vkCmdResetEvent": 20124,
"vkCmdWaitEvents": 20125,
"vkCmdPipelineBarrier": 20126,
"vkCmdBeginQuery": 20127,
"vkCmdEndQuery": 20128,
"vkCmdResetQueryPool": 20129,
"vkCmdWriteTimestamp": 20130,
"vkCmdCopyQueryPoolResults": 20131,
"vkCmdPushConstants": 20132,
"vkCmdBeginRenderPass": 20133,
"vkCmdNextSubpass": 20134,
"vkCmdEndRenderPass": 20135,
"vkCmdExecuteCommands": 20136,
"vkEnumerateInstanceVersion": 20137,
"vkBindBufferMemory2": 20138,
"vkBindImageMemory2": 20139,
"vkGetDeviceGroupPeerMemoryFeatures": 20140,
"vkCmdSetDeviceMask": 20141,
"vkCmdDispatchBase": 20142,
"vkEnumeratePhysicalDeviceGroups": 20143,
"vkGetImageMemoryRequirements2": 20144,
"vkGetBufferMemoryRequirements2": 20145,
"vkGetImageSparseMemoryRequirements2": 20146,
"vkGetPhysicalDeviceFeatures2": 20147,
"vkGetPhysicalDeviceProperties2": 20148,
"vkGetPhysicalDeviceFormatProperties2": 20149,
"vkGetPhysicalDeviceImageFormatProperties2": 20150,
"vkGetPhysicalDeviceQueueFamilyProperties2": 20151,
"vkGetPhysicalDeviceMemoryProperties2": 20152,
"vkGetPhysicalDeviceSparseImageFormatProperties2": 20153,
"vkTrimCommandPool": 20154,
"vkGetDeviceQueue2": 20155,
"vkCreateSamplerYcbcrConversion": 20156,
"vkDestroySamplerYcbcrConversion": 20157,
"vkCreateDescriptorUpdateTemplate": 20158,
"vkDestroyDescriptorUpdateTemplate": 20159,
"vkUpdateDescriptorSetWithTemplate": 20160,
"vkGetPhysicalDeviceExternalBufferProperties": 20161,
"vkGetPhysicalDeviceExternalFenceProperties": 20162,
"vkGetPhysicalDeviceExternalSemaphoreProperties": 20163,
"vkGetDescriptorSetLayoutSupport": 20164,
"vkDestroySurfaceKHR": 20165,
"vkGetPhysicalDeviceSurfaceSupportKHR": 20166,
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR": 20167,
"vkGetPhysicalDeviceSurfaceFormatsKHR": 20168,
"vkGetPhysicalDeviceSurfacePresentModesKHR": 20169,
"vkCreateSwapchainKHR": 20170,
"vkDestroySwapchainKHR": 20171,
"vkGetSwapchainImagesKHR": 20172,
"vkAcquireNextImageKHR": 20173,
"vkQueuePresentKHR": 20174,
"vkGetDeviceGroupPresentCapabilitiesKHR": 20175,
"vkGetDeviceGroupSurfacePresentModesKHR": 20176,
"vkGetPhysicalDevicePresentRectanglesKHR": 20177,
"vkAcquireNextImage2KHR": 20178,
"vkGetPhysicalDeviceDisplayPropertiesKHR": 20179,
"vkGetPhysicalDeviceDisplayPlanePropertiesKHR": 20180,
"vkGetDisplayPlaneSupportedDisplaysKHR": 20181,
"vkGetDisplayModePropertiesKHR": 20182,
"vkCreateDisplayModeKHR": 20183,
"vkGetDisplayPlaneCapabilitiesKHR": 20184,
"vkCreateDisplayPlaneSurfaceKHR": 20185,
"vkCreateSharedSwapchainsKHR": 20186,
"vkCreateXlibSurfaceKHR": 20187,
"vkGetPhysicalDeviceXlibPresentationSupportKHR": 20188,
"vkCreateXcbSurfaceKHR": 20189,
"vkGetPhysicalDeviceXcbPresentationSupportKHR": 20190,
"vkCreateWaylandSurfaceKHR": 20191,
"vkGetPhysicalDeviceWaylandPresentationSupportKHR": 20192,
"vkCreateMirSurfaceKHR": 20193,
"vkGetPhysicalDeviceMirPresentationSupportKHR": 20194,
"vkCreateAndroidSurfaceKHR": 20195,
"vkCreateWin32SurfaceKHR": 20196,
"vkGetPhysicalDeviceWin32PresentationSupportKHR": 20197,
"vkGetPhysicalDeviceFeatures2KHR": 20198,
"vkGetPhysicalDeviceProperties2KHR": 20199,
"vkGetPhysicalDeviceFormatProperties2KHR": 20200,
"vkGetPhysicalDeviceImageFormatProperties2KHR": 20201,
"vkGetPhysicalDeviceQueueFamilyProperties2KHR": 20202,
"vkGetPhysicalDeviceMemoryProperties2KHR": 20203,
"vkGetPhysicalDeviceSparseImageFormatProperties2KHR": 20204,
"vkGetDeviceGroupPeerMemoryFeaturesKHR": 20205,
"vkCmdSetDeviceMaskKHR": 20206,
"vkCmdDispatchBaseKHR": 20207,
"vkTrimCommandPoolKHR": 20208,
"vkEnumeratePhysicalDeviceGroupsKHR": 20209,
"vkGetPhysicalDeviceExternalBufferPropertiesKHR": 20210,
"vkGetMemoryWin32HandleKHR": 20211,
"vkGetMemoryWin32HandlePropertiesKHR": 20212,
"vkGetMemoryFdKHR": 20213,
"vkGetMemoryFdPropertiesKHR": 20214,
"vkGetPhysicalDeviceExternalSemaphorePropertiesKHR": 20215,
"vkImportSemaphoreWin32HandleKHR": 20216,
"vkGetSemaphoreWin32HandleKHR": 20217,
"vkImportSemaphoreFdKHR": 20218,
"vkGetSemaphoreFdKHR": 20219,
"vkCmdPushDescriptorSetKHR": 20220,
"vkCmdPushDescriptorSetWithTemplateKHR": 20221,
"vkCreateDescriptorUpdateTemplateKHR": 20222,
"vkDestroyDescriptorUpdateTemplateKHR": 20223,
"vkUpdateDescriptorSetWithTemplateKHR": 20224,
"vkCreateRenderPass2KHR": 20225,
"vkCmdBeginRenderPass2KHR": 20226,
"vkCmdNextSubpass2KHR": 20227,
"vkCmdEndRenderPass2KHR": 20228,
"vkGetSwapchainStatusKHR": 20229,
"vkGetPhysicalDeviceExternalFencePropertiesKHR": 20230,
"vkImportFenceWin32HandleKHR": 20231,
"vkGetFenceWin32HandleKHR": 20232,
"vkImportFenceFdKHR": 20233,
"vkGetFenceFdKHR": 20234,
"vkGetPhysicalDeviceSurfaceCapabilities2KHR": 20235,
"vkGetPhysicalDeviceSurfaceFormats2KHR": 20236,
"vkGetPhysicalDeviceDisplayProperties2KHR": 20237,
"vkGetPhysicalDeviceDisplayPlaneProperties2KHR": 20238,
"vkGetDisplayModeProperties2KHR": 20239,
"vkGetDisplayPlaneCapabilities2KHR": 20240,
"vkGetImageMemoryRequirements2KHR": 20241,
"vkGetBufferMemoryRequirements2KHR": 20242,
"vkGetImageSparseMemoryRequirements2KHR": 20243,
"vkCreateSamplerYcbcrConversionKHR": 20244,
"vkDestroySamplerYcbcrConversionKHR": 20245,
"vkBindBufferMemory2KHR": 20246,
"vkBindImageMemory2KHR": 20247,
"vkGetDescriptorSetLayoutSupportKHR": 20248,
"vkCmdDrawIndirectCountKHR": 20249,
"vkCmdDrawIndexedIndirectCountKHR": 20250,
"vkGetSwapchainGrallocUsageANDROID": 20251,
"vkAcquireImageANDROID": 20252,
"vkQueueSignalReleaseImageANDROID": 20253,
"vkCreateDebugReportCallbackEXT": 20254,
"vkDestroyDebugReportCallbackEXT": 20255,
"vkDebugReportMessageEXT": 20256,
"vkDebugMarkerSetObjectTagEXT": 20257,
"vkDebugMarkerSetObjectNameEXT": 20258,
"vkCmdDebugMarkerBeginEXT": 20259,
"vkCmdDebugMarkerEndEXT": 20260,
"vkCmdDebugMarkerInsertEXT": 20261,
"vkCmdDrawIndirectCountAMD": 20262,
"vkCmdDrawIndexedIndirectCountAMD": 20263,
"vkGetShaderInfoAMD": 20264,
"vkGetPhysicalDeviceExternalImageFormatPropertiesNV": 20265,
"vkGetMemoryWin32HandleNV": 20266,
"vkCreateViSurfaceNN": 20267,
"vkCmdBeginConditionalRenderingEXT": 20268,
"vkCmdEndConditionalRenderingEXT": 20269,
"vkCmdProcessCommandsNVX": 20270,
"vkCmdReserveSpaceForCommandsNVX": 20271,
"vkCreateIndirectCommandsLayoutNVX": 20272,
"vkDestroyIndirectCommandsLayoutNVX": 20273,
"vkCreateObjectTableNVX": 20274,
"vkDestroyObjectTableNVX": 20275,
"vkRegisterObjectsNVX": 20276,
"vkUnregisterObjectsNVX": 20277,
"vkGetPhysicalDeviceGeneratedCommandsPropertiesNVX": 20278,
"vkCmdSetViewportWScalingNV": 20279,
"vkReleaseDisplayEXT": 20280,
"vkAcquireXlibDisplayEXT": 20281,
"vkGetRandROutputDisplayEXT": 20282,
"vkGetPhysicalDeviceSurfaceCapabilities2EXT": 20283,
"vkDisplayPowerControlEXT": 20284,
"vkRegisterDeviceEventEXT": 20285,
"vkRegisterDisplayEventEXT": 20286,
"vkGetSwapchainCounterEXT": 20287,
"vkGetRefreshCycleDurationGOOGLE": 20288,
"vkGetPastPresentationTimingGOOGLE": 20289,
"vkCmdSetDiscardRectangleEXT": 20290,
"vkSetHdrMetadataEXT": 20291,
"vkCreateIOSSurfaceMVK": 20292,
"vkCreateMacOSSurfaceMVK": 20293,
"vkSetDebugUtilsObjectNameEXT": 20294,
"vkSetDebugUtilsObjectTagEXT": 20295,
"vkQueueBeginDebugUtilsLabelEXT": 20296,
"vkQueueEndDebugUtilsLabelEXT": 20297,
"vkQueueInsertDebugUtilsLabelEXT": 20298,
"vkCmdBeginDebugUtilsLabelEXT": 20299,
"vkCmdEndDebugUtilsLabelEXT": 20300,
"vkCmdInsertDebugUtilsLabelEXT": 20301,
"vkCreateDebugUtilsMessengerEXT": 20302,
"vkDestroyDebugUtilsMessengerEXT": 20303,
"vkSubmitDebugUtilsMessageEXT": 20304,
"vkGetAndroidHardwareBufferPropertiesANDROID": 20305,
"vkGetMemoryAndroidHardwareBufferANDROID": 20306,
"vkCmdSetSampleLocationsEXT": 20307,
"vkGetPhysicalDeviceMultisamplePropertiesEXT": 20308,
"vkCreateValidationCacheEXT": 20309,
"vkDestroyValidationCacheEXT": 20310,
"vkMergeValidationCachesEXT": 20311,
"vkGetValidationCacheDataEXT": 20312,
"vkGetMemoryHostPointerPropertiesEXT": 20313,
"vkCmdWriteBufferMarkerAMD": 20314,
"vkCmdSetCheckpointNV": 20315,
"vkGetQueueCheckpointDataNV": 20316,
"vkMapMemoryIntoAddressSpaceGOOGLE": 20317,
"vkUpdateDescriptorSetWithTemplateSizedGOOGLE": 20320,
"vkBeginCommandBufferAsyncGOOGLE": 20321,
"vkEndCommandBufferAsyncGOOGLE": 20322,
"vkResetCommandBufferAsyncGOOGLE": 20323,
"vkCommandBufferHostSyncGOOGLE": 20324,
"vkCreateImageWithRequirementsGOOGLE": 20325,
"vkCreateBufferWithRequirementsGOOGLE": 20326,
"vkGetMemoryHostAddressInfoGOOGLE": 20327,
"vkFreeMemorySyncGOOGLE": 20328,
"vkQueueHostSyncGOOGLE": 20329,
"vkQueueSubmitAsyncGOOGLE": 20330,
"vkQueueWaitIdleAsyncGOOGLE": 20331,
"vkQueueBindSparseAsyncGOOGLE": 20332,
"vkGetLinearImageLayoutGOOGLE": 20333,
"vkQueueFlushCommandsGOOGLE": 20340,
"vkGetBlobGOOGLE": 20341,
"vkGetSemaphoreGOOGLE": 20342,
}
CUSTOM_MARSHAL_TYPES = {
"VkAccelerationStructureInstanceKHR": {
"common": """
typedef struct VkAccelerationStructureInstanceKHRWithoutBitFields {
VkTransformMatrixKHR transform;
uint32_t dwords[2];
uint64_t accelerationStructureReference;
} VkAccelerationStructureInstanceKHRWithoutBitFields;
""",
"marshaling": """
const VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName});
marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"unmarshaling": """
VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName});
unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"reservedmarshaling": """
(void)vkStream;
const VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName});
reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
"reservedunmarshaling": """
VkAccelerationStructureInstanceKHRWithoutBitFields* {newInputVarName} = (VkAccelerationStructureInstanceKHRWithoutBitFields*)({inputVarName});
reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transform), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
},
"VkAccelerationStructureMatrixMotionInstanceNV": {
"common": """
typedef struct VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields {
VkTransformMatrixKHR transformT0;
VkTransformMatrixKHR transformT1;
uint32_t dwords[2];
uint64_t accelerationStructureReference;
} VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields;
""",
"marshaling": """
const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName});
marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0));
marshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"unmarshaling": """
VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName});
unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0));
unmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"reservedmarshaling": """
(void)vkStream;
const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName});
reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0), ptr);
reservedmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
"reservedunmarshaling": """
VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureMatrixMotionInstanceNVWithoutBitFields*)({inputVarName});
reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT0), ptr);
reservedunmarshal_VkTransformMatrixKHR({streamVarName}, {rootTypeVarName}, (VkTransformMatrixKHR*)(&{newInputVarName}->transformT1), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
},
"VkAccelerationStructureSRTMotionInstanceNV": {
"common": """
typedef struct VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields {
VkSRTDataNV transformT0;
VkSRTDataNV transformT1;
uint32_t dwords[2];
uint64_t accelerationStructureReference;
} VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields;
""",
"marshaling": """
const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName});
marshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0));
marshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->write((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->write((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"unmarshaling": """
VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName});
unmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0));
unmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1));
for (uint32_t i = 0; i < 2; i++) {{
{streamVarName}->read((uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
}}
{streamVarName}->read((uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
""",
"reservedmarshaling": """
(void)vkStream;
const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (const VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName});
reservedmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0), ptr);
reservedmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy(*ptr, (uint32_t*)&({newInputVarName}->dwords[i]), sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy(*ptr, (uint64_t*)&{newInputVarName}->accelerationStructureReference, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
"reservedunmarshaling": """
VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields* {newInputVarName} = (VkAccelerationStructureSRTMotionInstanceNVWithoutBitFields*)({inputVarName});
reservedunmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT0), ptr);
reservedunmarshal_VkSRTDataNV({streamVarName}, {rootTypeVarName}, (VkSRTDataNV*)(&{newInputVarName}->transformT1), ptr);
for (uint32_t i = 0; i < 2; i++) {{
memcpy((uint32_t*)&({newInputVarName}->dwords[i]), *ptr, sizeof(uint32_t));
*ptr += sizeof(uint32_t);
}}
memcpy((uint64_t*)&{newInputVarName}->accelerationStructureReference, *ptr, sizeof(uint64_t));
*ptr += sizeof(uint64_t);
""",
},
"VkDescriptorBufferInfo": {
"common":"",
"reservedunmarshaling": """
uint64_t cgen_var_0;
memcpy((uint64_t*)&cgen_var_0, *ptr, 1 * 8);
*ptr += 1 * 8;
*(VkBuffer*)&forUnmarshaling->buffer = (VkBuffer)try_unbox_VkBuffer((VkBuffer)(*&cgen_var_0));
memcpy((VkDeviceSize*)&forUnmarshaling->offset, *ptr, sizeof(VkDeviceSize));
*ptr += sizeof(VkDeviceSize);
memcpy((VkDeviceSize*)&forUnmarshaling->range, *ptr, sizeof(VkDeviceSize));
*ptr += sizeof(VkDeviceSize);
"""
},
"VkXcbSurfaceCreateInfoKHR": {
"common": """
// This struct should never be marshaled / unmarshaled.
__builtin_trap();
""",
"marshaling": "",
"unmarshaling": "",
"reservedmarshaling": "",
"reservedunmarshaling": "",
},
"VkMetalSurfaceCreateInfoEXT": {
"common": """
// This struct should never be marshaled / unmarshaled.
__builtin_trap();
""",
"marshaling": "",
"unmarshaling": "",
"reservedmarshaling": "",
"reservedunmarshaling": "",
},
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,433 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen, VulkanWrapperGenerator
from .common.vulkantypes import VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeInfo,\
VulkanType
from .reservedmarshaling import VulkanReservedMarshalingCodegen
from .transform import TransformCodegen
from .wrapperdefs import API_PREFIX_RESERVEDUNMARSHAL
from .wrapperdefs import MAX_PACKET_LENGTH
from .wrapperdefs import ROOT_TYPE_DEFAULT_VALUE
decoder_decl_preamble = """
"""
decoder_impl_preamble = """
"""
global_state_prefix = "this->on_"
READ_STREAM = "readStream"
WRITE_STREAM = "vkStream"
SNAPSHOT_API_CALL_HANDLE_VARNAME = "snapshotApiCallHandle"
# Driver workarounds for APIs that don't work well multithreaded
driver_workarounds_global_lock_apis = [
"vkCreatePipelineLayout",
"vkDestroyPipelineLayout",
]
MAX_STACK_ITEMS = "16"
def emit_param_decl_for_reading(param, cgen):
if param.staticArrExpr:
cgen.stmt(
cgen.makeRichCTypeDecl(param.getForNonConstAccess()))
else:
cgen.stmt(
cgen.makeRichCTypeDecl(param))
if param.pointerIndirectionLevels > 0:
lenAccess = cgen.generalLengthAccess(param)
if not lenAccess:
lenAccess = "1"
arrSize = "1" if "1" == lenAccess else "MAX_STACK_ITEMS"
typeHere = "uint8_t*" if "void" == param.typeName else param.typeName
cgen.stmt("%s%s stack_%s[%s]" % (
typeHere, "*" * (param.pointerIndirectionLevels - 1), param.paramName, arrSize))
def emit_unmarshal(typeInfo, param, cgen, output=False, destroy=False, noUnbox=False):
if destroy:
iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen(
cgen,
"host",
READ_STREAM,
ROOT_TYPE_DEFAULT_VALUE,
param.paramName,
"readStreamPtrPtr",
API_PREFIX_RESERVEDUNMARSHAL,
"",
direction="read",
dynAlloc=True))
lenAccess = cgen.generalLengthAccess(param)
lenAccessGuard = cgen.generalLengthAccessGuard(param)
if None == lenAccess or "1" == lenAccess:
cgen.stmt("boxed_%s_preserve = %s" %
(param.paramName, param.paramName))
cgen.stmt("%s = unbox_%s(%s)" %
(param.paramName, param.typeName, param.paramName))
else:
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
cgen.beginFor("uint32_t i = 0", "i < %s" % lenAccess, "++i")
cgen.stmt("boxed_%s_preserve[i] = %s[i]" %
(param.paramName, param.paramName))
cgen.stmt("((%s*)(%s))[i] = unbox_%s(%s[i])" % (param.typeName,
param.paramName, param.typeName, param.paramName))
cgen.endFor()
if lenAccessGuard is not None:
self.cgen.endIf()
else:
if noUnbox:
cgen.line("// No unbox for %s" % (param.paramName))
lenAccess = cgen.generalLengthAccess(param)
if not lenAccess:
lenAccess = "1"
arrSize = "1" if "1" == lenAccess else "MAX_STACK_ITEMS"
iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen(
cgen,
"host",
READ_STREAM,
ROOT_TYPE_DEFAULT_VALUE,
param.paramName,
"readStreamPtrPtr",
API_PREFIX_RESERVEDUNMARSHAL,
"" if (output or noUnbox) else "unbox_",
direction="read",
dynAlloc=True,
stackVar="stack_%s" % param.paramName,
stackArrSize=arrSize))
def emit_dispatch_unmarshal(typeInfo, param, cgen, globalWrapped):
if globalWrapped:
cgen.stmt(
"// Begin global wrapped dispatchable handle unboxing for %s" % param.paramName)
iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen(
cgen,
"host",
READ_STREAM,
ROOT_TYPE_DEFAULT_VALUE,
param.paramName,
"readStreamPtrPtr",
API_PREFIX_RESERVEDUNMARSHAL,
"",
direction="read",
dynAlloc=True))
else:
cgen.stmt(
"// Begin non wrapped dispatchable handle unboxing for %s" % param.paramName)
# cgen.stmt("%s->unsetHandleMapping()" % READ_STREAM)
iterateVulkanType(typeInfo, param, VulkanReservedMarshalingCodegen(
cgen,
"host",
READ_STREAM,
ROOT_TYPE_DEFAULT_VALUE,
param.paramName,
"readStreamPtrPtr",
API_PREFIX_RESERVEDUNMARSHAL,
"",
direction="read",
dynAlloc=True))
cgen.stmt("auto unboxed_%s = unbox_%s(%s)" %
(param.paramName, param.typeName, param.paramName))
cgen.stmt("auto vk = dispatch_%s(%s)" %
(param.typeName, param.paramName))
cgen.stmt("// End manual dispatchable handle unboxing for %s" %
param.paramName)
def emit_transform(typeInfo, param, cgen, variant="tohost"):
res = \
iterateVulkanType(typeInfo, param, TransformCodegen(
cgen, param.paramName, "globalstate", "transform_%s_" % variant, variant))
if not res:
cgen.stmt("(void)%s" % param.paramName)
# Everything here elides the initial arg
class DecodingParameters(object):
def __init__(self, api: VulkanAPI):
self.params: list[VulkanType] = []
self.toRead: list[VulkanType] = []
self.toWrite: list[VulkanType] = []
for i, param in enumerate(api.parameters[1:]):
if i == 0 and param.isDispatchableHandleType():
param.dispatchHandle = True
if param.isNonDispatchableHandleType() and param.isCreatedBy(api):
param.nonDispatchableHandleCreate = True
if param.isNonDispatchableHandleType() and param.isDestroyedBy(api):
param.nonDispatchableHandleDestroy = True
if param.isDispatchableHandleType() and param.isCreatedBy(api):
param.dispatchableHandleCreate = True
if param.isDispatchableHandleType() and param.isDestroyedBy(api):
param.dispatchableHandleDestroy = True
self.toRead.append(param)
if param.possiblyOutput():
self.toWrite.append(param)
self.params.append(param)
def emit_call_log(api, cgen):
decodingParams = DecodingParameters(api)
paramsToRead = decodingParams.toRead
# cgen.beginIf("m_logCalls")
paramLogFormat = "%p"
paramLogArgs = ["(void*)boxed_dispatchHandle"]
for p in paramsToRead:
paramLogFormat += "0x%llx "
for p in paramsToRead:
paramLogArgs.append("(unsigned long long)%s" % (p.paramName))
# cgen.stmt("fprintf(stderr, \"substream %%p: call %s %s\\n\", readStream, %s)" % (api.name, paramLogFormat, ", ".join(paramLogArgs)))
# cgen.endIf()
def emit_decode_parameters(typeInfo, api, cgen, globalWrapped=False):
decodingParams = DecodingParameters(api)
paramsToRead = decodingParams.toRead
for p in paramsToRead:
emit_param_decl_for_reading(p, cgen)
i = 0
for p in paramsToRead:
lenAccess = cgen.generalLengthAccess(p)
if p.dispatchHandle:
emit_dispatch_unmarshal(typeInfo, p, cgen, globalWrapped)
else:
destroy = p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy
noUnbox = False
if p.nonDispatchableHandleDestroy or p.dispatchableHandleDestroy:
destroy = True
cgen.stmt(
"// Begin manual non dispatchable handle destroy unboxing for %s" % p.paramName)
if None == lenAccess or "1" == lenAccess:
cgen.stmt("%s boxed_%s_preserve" %
(p.typeName, p.paramName))
else:
cgen.stmt("%s* boxed_%s_preserve; %s->alloc((void**)&boxed_%s_preserve, %s * sizeof(%s))" %
(p.typeName, p.paramName, READ_STREAM, p.paramName, lenAccess, p.typeName))
if p.possiblyOutput():
cgen.stmt(
"// Begin manual dispatchable handle unboxing for %s" % p.paramName)
cgen.stmt("%s->unsetHandleMapping()" % READ_STREAM)
emit_unmarshal(typeInfo, p, cgen, output=p.possiblyOutput(
), destroy=destroy, noUnbox=noUnbox)
i += 1
for p in paramsToRead:
emit_transform(typeInfo, p, cgen, variant="tohost")
emit_call_log(api, cgen)
def emit_snapshot_call(api, cgen):
apiForSnapshot = \
api.withCustomReturnType(makeVulkanTypeSimple(False, "void", 0, "void"))
customParamsSnapshot = ["pool", SNAPSHOT_API_CALL_HANDLE_VARNAME, "nullptr", "0"]
retTypeName = api.getRetTypeExpr()
if retTypeName != "void":
retVar = api.getRetVarExpr()
customParamsSnapshot.append(retVar)
customParamsSnapshot.append("(VkCommandBuffer)(boxed_dispatchHandle)")
customParamsSnapshot = customParamsSnapshot + list(map(lambda p: p.paramName, api.parameters[1:]))
cgen.beginIf("snapshotsEnabled()")
cgen.vkApiCall(apiForSnapshot, customPrefix="this->snapshot()->", customParameters=customParamsSnapshot)
cgen.endIf()
def emit_dispatch_call(api, cgen):
decodingParams = DecodingParameters(api)
customParams = ["(VkCommandBuffer)dispatchHandle"]
for (i, p) in enumerate(api.parameters[1:]):
customParam = p.paramName
if decodingParams.params[i].dispatchHandle:
customParam = "unboxed_%s" % p.paramName
customParams.append(customParam)
if api.name in driver_workarounds_global_lock_apis:
cgen.stmt("lock()")
cgen.vkApiCall(api, customPrefix="vk->", customParameters=customParams,
checkForDeviceLost=True, globalStatePrefix=global_state_prefix,
checkDispatcher="CC_LIKELY(vk)")
emit_snapshot_call(api, cgen)
if api.name in driver_workarounds_global_lock_apis:
cgen.stmt("unlock()")
def emit_global_state_wrapped_call(api, cgen, context=False):
customParams = ["pool", SNAPSHOT_API_CALL_HANDLE_VARNAME, "(VkCommandBuffer)(boxed_dispatchHandle)"] + \
list(map(lambda p: p.paramName, api.parameters[1:]))
if context:
customParams += ["context"];
cgen.vkApiCall(api, customPrefix=global_state_prefix,
customParameters=customParams, checkForDeviceLost=True,
globalStatePrefix=global_state_prefix, checkDispatcher="CC_LIKELY(vk)")
emit_snapshot_call(api, cgen)
def emit_default_decoding(typeInfo, api, cgen):
emit_decode_parameters(typeInfo, api, cgen)
emit_dispatch_call(api, cgen)
def emit_global_state_wrapped_decoding(typeInfo, api, cgen):
emit_decode_parameters(typeInfo, api, cgen, globalWrapped=True)
emit_global_state_wrapped_call(api, cgen)
def emit_global_state_wrapped_decoding_with_context(typeInfo, api, cgen):
emit_decode_parameters(typeInfo, api, cgen, globalWrapped=True)
emit_global_state_wrapped_call(api, cgen, context=True)
custom_decodes = {
"vkCmdCopyBufferToImage": emit_global_state_wrapped_decoding_with_context,
"vkCmdCopyImage": emit_global_state_wrapped_decoding,
"vkCmdCopyImageToBuffer": emit_global_state_wrapped_decoding,
"vkCmdCopyBufferToImage2": emit_global_state_wrapped_decoding_with_context,
"vkCmdCopyImage2": emit_global_state_wrapped_decoding,
"vkCmdCopyImageToBuffer2": emit_global_state_wrapped_decoding,
"vkCmdCopyBufferToImage2KHR": emit_global_state_wrapped_decoding_with_context,
"vkCmdCopyImage2KHR": emit_global_state_wrapped_decoding,
"vkCmdCopyImageToBuffer2KHR": emit_global_state_wrapped_decoding,
"vkCmdExecuteCommands": emit_global_state_wrapped_decoding,
"vkBeginCommandBuffer": emit_global_state_wrapped_decoding_with_context,
"vkEndCommandBuffer": emit_global_state_wrapped_decoding_with_context,
"vkResetCommandBuffer": emit_global_state_wrapped_decoding,
"vkCmdPipelineBarrier": emit_global_state_wrapped_decoding,
"vkCmdPipelineBarrier2": emit_global_state_wrapped_decoding,
"vkCmdBindPipeline": emit_global_state_wrapped_decoding,
"vkCmdBindDescriptorSets": emit_global_state_wrapped_decoding,
"vkCmdCopyQueryPoolResults": emit_global_state_wrapped_decoding,
"vkBeginCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding_with_context,
"vkEndCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding_with_context,
"vkResetCommandBufferAsyncGOOGLE": emit_global_state_wrapped_decoding,
"vkCommandBufferHostSyncGOOGLE": emit_global_state_wrapped_decoding,
"vkCmdBeginRenderPass" : emit_global_state_wrapped_decoding,
"vkCmdBeginRenderPass2" : emit_global_state_wrapped_decoding,
"vkCmdBeginRenderPass2KHR" : emit_global_state_wrapped_decoding,
"vkCmdSetEvent" : emit_global_state_wrapped_decoding,
"vkCmdResetEvent" : emit_global_state_wrapped_decoding,
}
class VulkanSubDecoder(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.typeInfo = typeInfo
self.cgen = CodeGen()
def onBegin(self,):
self.module.appendImpl(
"#define MAX_STACK_ITEMS %s\n" % MAX_STACK_ITEMS)
self.module.appendImpl(
"#define MAX_PACKET_LENGTH %s\n" % MAX_PACKET_LENGTH)
self.module.appendImpl(
"#define CC_LIKELY(exp) (__builtin_expect( !!(exp), true ))\n")
self.module.appendImpl(
"#define CC_UNLIKELY(exp) (__builtin_expect( !!(exp), false ))\n")
self.module.appendImpl(
"size_t subDecode(VulkanMemReadingStream* readStream, VulkanDispatch* vk, VkSnapshotApiCallHandle %s, void* boxed_dispatchHandle, void* dispatchHandle, VkDeviceSize subDecodeDataSize, const void* pSubDecodeData, const VkDecoderContext& context)\n" % SNAPSHOT_API_CALL_HANDLE_VARNAME)
self.cgen.beginBlock() # function body
self.cgen.stmt("uint32_t count = 0")
self.cgen.stmt("unsigned char *buf = (unsigned char *)pSubDecodeData")
self.cgen.stmt("gfxstream::base::BumpPool* pool = readStream->pool()")
self.cgen.stmt("unsigned char *ptr = (unsigned char *)pSubDecodeData")
self.cgen.stmt(
"const unsigned char* const end = (const unsigned char*)buf + subDecodeDataSize")
self.cgen.stmt(
"VkDecoderGlobalState* globalstate = VkDecoderGlobalState::get()")
self.cgen.line("while (end - ptr >= 8)")
self.cgen.beginBlock() # while loop
self.cgen.stmt("uint32_t opcode = *(uint32_t *)ptr")
self.cgen.stmt("uint32_t packetLen = *(uint32_t *)(ptr + 4)")
self.cgen.line("""
// packetLen should be at least 8 (op code and packet length) and should not be excessively large
if (packetLen < 8 || packetLen > MAX_PACKET_LENGTH) {
GFXSTREAM_WARNING("Bad packet length %d detected, subdecode may fail", packetLen);
}
""")
self.cgen.stmt("if (end - ptr < packetLen) return ptr - (unsigned char*)buf")
self.cgen.stmt("%s->setBuf((uint8_t*)(ptr + 8))" % READ_STREAM)
self.cgen.stmt(
"uint8_t* readStreamPtr = %s->getBuf(); uint8_t** readStreamPtrPtr = &readStreamPtr" % READ_STREAM)
self.cgen.line("switch (opcode)")
self.cgen.beginBlock() # switch stmt
self.module.appendImpl(self.cgen.swapCode())
def onGenCmd(self, cmdinfo, name, alias):
typeInfo = self.typeInfo
cgen = self.cgen
api = typeInfo.apis[name]
if "commandBuffer" != api.parameters[0].paramName:
return
cgen.line("case OP_%s:" % name)
cgen.beginBlock()
cgen.stmt("GFXSTREAM_TRACE_EVENT(GFXSTREAM_TRACE_DECODER_CATEGORY, \"VkSubDecoder %s\")" % name)
if api.name in custom_decodes.keys():
custom_decodes[api.name](typeInfo, api, cgen)
else:
emit_default_decoding(typeInfo, api, cgen)
cgen.stmt("break")
cgen.endBlock()
self.module.appendImpl(self.cgen.swapCode())
def onEnd(self,):
self.cgen.line("default:")
self.cgen.beginBlock()
self.cgen.stmt(
"GFXSTREAM_FATAL(\"Unrecognized opcode %\" PRIu32, opcode)")
self.cgen.endBlock()
self.cgen.endBlock() # switch stmt
self.cgen.stmt("++count; if (count % 1000 == 0) { pool->freeAll(); }")
self.cgen.stmt("ptr += packetLen")
self.cgen.endBlock() # while loop
self.cgen.stmt("pool->freeAll()")
self.cgen.stmt("return ptr - (unsigned char*)buf;")
self.cgen.endBlock() # function body
self.module.appendImpl(self.cgen.swapCode())
@@ -0,0 +1,389 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from copy import copy
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanAPI, makeVulkanTypeSimple, iterateVulkanType, VulkanTypeIterator
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import EQUALITY_VAR_NAMES
from .wrapperdefs import EQUALITY_ON_FAIL_VAR
from .wrapperdefs import EQUALITY_ON_FAIL_VAR_TYPE
from .wrapperdefs import EQUALITY_RET_TYPE
from .wrapperdefs import API_PREFIX_EQUALITY
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM2
class VulkanEqualityCodegen(VulkanTypeIterator):
def __init__(self, cgen, inputVars, onFailCompareVar, prefix):
self.cgen = cgen
self.inputVars = inputVars
self.onFailCompareVar = onFailCompareVar
self.prefix = prefix
def makeAccess(varName, asPtr = True):
return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr)
def makeLengthAccess(varName):
return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName)
def makeLengthAccessGuard(varName):
return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName)
self.exprAccessorLhs = makeAccess(self.inputVars[0])
self.exprAccessorRhs = makeAccess(self.inputVars[1])
self.exprAccessorValueLhs = makeAccess(self.inputVars[0], asPtr = False)
self.exprAccessorValueRhs = makeAccess(self.inputVars[1], asPtr = False)
self.lenAccessorLhs = makeLengthAccess(self.inputVars[0])
self.lenAccessorRhs = makeLengthAccess(self.inputVars[1])
self.lenAccessGuardLhs = makeLengthAccessGuard(self.inputVars[0])
self.lenAccessGuardRhs = makeLengthAccessGuard(self.inputVars[1])
self.checked = False
def getTypeForCompare(self, vulkanType):
res = copy(vulkanType)
if not vulkanType.accessibleAsPointer():
res = res.getForAddressAccess()
if vulkanType.staticArrExpr:
res = res.getForAddressAccess()
return res
def makeCastExpr(self, vulkanType):
return "(%s)" % (
self.cgen.makeCTypeDecl(vulkanType, useParamName=False))
def makeEqualExpr(self, lhs, rhs):
return "(%s) == (%s)" % (lhs, rhs)
def makeEqualBufExpr(self, lhs, rhs, size):
return "(memcmp(%s, %s, %s) == 0)" % (lhs, rhs, size)
def makeEqualStringExpr(self, lhs, rhs):
return "(strcmp(%s, %s) == 0)" % (lhs, rhs)
def makeBothNotNullExpr(self, lhs, rhs):
return "(%s) && (%s)" % (lhs, rhs)
def makeBothNullExpr(self, lhs, rhs):
return "!(%s) && !(%s)" % (lhs, rhs)
def compareWithConsequence(self, compareExpr, vulkanType, errMsg=""):
self.cgen.stmt("if (!(%s)) { %s(\"%s (Error: %s)\"); }" %
(compareExpr, self.onFailCompareVar,
self.exprAccessorValueLhs(vulkanType), errMsg))
def onCheck(self, vulkanType):
self.checked = True
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
bothNull = self.makeBothNullExpr(accessLhs, accessRhs)
bothNotNull = self.makeBothNotNullExpr(accessLhs, accessRhs)
nullMatchExpr = "(%s) || (%s)" % (bothNull, bothNotNull)
self.compareWithConsequence( \
nullMatchExpr,
vulkanType,
"Mismatch in optional field")
skipStreamInternal = vulkanType.typeName == "void"
if skipStreamInternal:
return
self.cgen.beginIf("%s && %s" % (accessLhs, accessRhs))
def endCheck(self, vulkanType):
skipStreamInternal = vulkanType.typeName == "void"
if skipStreamInternal:
return
if self.checked:
self.cgen.endIf()
self.checked = False
def onCompoundType(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
lenAccessRhs = self.lenAccessorRhs(vulkanType)
lenAccessGuardLhs = self.lenAccessGuardLhs(vulkanType)
lenAccessGuardRhs = self.lenAccessGuardRhs(vulkanType)
needNullCheck = vulkanType.pointerIndirectionLevels > 0
if needNullCheck:
bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs)
self.cgen.beginIf(bothNotNullExpr)
if lenAccessLhs is not None:
equalLenExpr = self.makeEqualExpr(lenAccessLhs, lenAccessRhs)
self.compareWithConsequence( \
equalLenExpr,
vulkanType, "Lengths not equal")
loopVar = "i"
accessLhs = "%s + %s" % (accessLhs, loopVar)
accessRhs = "%s + %s" % (accessRhs, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs)
forIncr = "++%s" % loopVar
if needNullCheck:
self.cgen.beginIf(equalLenExpr)
if lenAccessGuardLhs is not None:
self.cgen.beginIf(lenAccessGuardLhs)
self.cgen.beginFor(forInit, forCond, forIncr)
self.cgen.funcCall(None, self.prefix + vulkanType.typeName,
[accessLhs, accessRhs, self.onFailCompareVar])
if lenAccessLhs is not None:
self.cgen.endFor()
if lenAccessGuardLhs is not None:
self.cgen.endIf()
if needNullCheck:
self.cgen.endIf()
if needNullCheck:
self.cgen.endIf()
def onString(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
bothNullExpr = self.makeBothNullExpr(accessLhs, accessRhs)
bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs)
nullMatchExpr = "(%s) || (%s)" % (bothNullExpr, bothNotNullExpr)
self.compareWithConsequence( \
nullMatchExpr,
vulkanType,
"Mismatch in string pointer nullness")
self.cgen.beginIf(bothNotNullExpr)
self.compareWithConsequence(
self.makeEqualStringExpr(accessLhs, accessRhs),
vulkanType, "Unequal strings")
self.cgen.endIf()
def onStringArray(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
lenAccessRhs = self.lenAccessorRhs(vulkanType)
lenAccessGuardLhs = self.lenAccessGuardLhs(vulkanType)
lenAccessGuardRhs = self.lenAccessGuardRhs(vulkanType)
bothNullExpr = self.makeBothNullExpr(accessLhs, accessRhs)
bothNotNullExpr = self.makeBothNotNullExpr(accessLhs, accessRhs)
nullMatchExpr = "(%s) || (%s)" % (bothNullExpr, bothNotNullExpr)
self.compareWithConsequence( \
nullMatchExpr,
vulkanType,
"Mismatch in string array pointer nullness")
if lenAccessLhs is not None and lenAccessRhs is not None:
equalLenExpr = self.makeEqualExpr(lenAccessLhs, lenAccessRhs)
self.compareWithConsequence( \
equalLenExpr,
vulkanType, "Lengths not equal in string array")
self.compareWithConsequence( \
equalLenExpr,
vulkanType, "Lengths not equal in string array")
self.cgen.beginIf("%s && %s" % (equalLenExpr, bothNotNullExpr))
loopVar = "i"
accessLhs = "*(%s + %s)" % (accessLhs, loopVar)
accessRhs = "*(%s + %s)" % (accessRhs, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccessLhs)
forIncr = "++%s" % loopVar
if lenAccessGuardLhs is not None:
self.cgen.beginIf(lenAccessGuardLhs)
self.cgen.beginFor(forInit, forCond, forIncr)
self.compareWithConsequence(
self.makeEqualStringExpr(accessLhs, accessRhs),
vulkanType, "Unequal string in string array")
self.cgen.endFor()
if lenAccessGuardLhs is not None:
self.cgen.endIf()
self.cgen.endIf()
def onStaticArr(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
lenAccessLhs = self.lenAccessorLhs(vulkanType)
if lenAccessLhs is not None:
finalLenExpr = "%s * %s" % (lenAccessLhs,
self.cgen.sizeofExpr(vulkanType))
self.compareWithConsequence(
self.makeEqualBufExpr(accessLhs, accessRhs, finalLenExpr),
vulkanType, "Unequal static array")
def onStructExtension(self, vulkanType):
lhs = self.exprAccessorLhs(vulkanType)
rhs = self.exprAccessorRhs(vulkanType)
self.cgen.beginIf(lhs)
self.cgen.funcCall(None, self.prefix + "extension_struct",
[lhs, rhs, self.onFailCompareVar])
self.cgen.endIf()
def onPointer(self, vulkanType):
accessLhs = self.exprAccessorLhs(vulkanType)
accessRhs = self.exprAccessorRhs(vulkanType)
skipStreamInternal = vulkanType.typeName == "void"
if skipStreamInternal:
return
lenAccessLhs = self.lenAccessorLhs(vulkanType)
lenAccessRhs = self.lenAccessorRhs(vulkanType)
if lenAccessLhs is not None:
self.compareWithConsequence( \
self.makeEqualExpr(lenAccessLhs, lenAccessRhs),
vulkanType, "Lengths not equal")
finalLenExpr = "%s * %s" % (lenAccessLhs,
self.cgen.sizeofExpr(
vulkanType.getForValueAccess()))
else:
finalLenExpr = self.cgen.sizeofExpr(vulkanType.getForValueAccess())
self.compareWithConsequence(
self.makeEqualBufExpr(accessLhs, accessRhs, finalLenExpr),
vulkanType, "Unequal dyn array")
def onValue(self, vulkanType):
accessLhs = self.exprAccessorValueLhs(vulkanType)
accessRhs = self.exprAccessorValueRhs(vulkanType)
self.compareWithConsequence(
self.makeEqualExpr(accessLhs, accessRhs), vulkanType,
"Value not equal")
class VulkanTesting(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.equalityCodegen = \
VulkanEqualityCodegen(
None,
EQUALITY_VAR_NAMES,
EQUALITY_ON_FAIL_VAR,
API_PREFIX_EQUALITY)
self.knownDefs = {}
self.extensionTestingPrototype = \
VulkanAPI(API_PREFIX_EQUALITY + "extension_struct",
EQUALITY_RET_TYPE,
[STRUCT_EXTENSION_PARAM,
STRUCT_EXTENSION_PARAM2,
EQUALITY_ON_FAIL_VAR_TYPE])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
self.module.appendImpl(self.codegen.makeFuncDecl(
self.extensionTestingPrototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownDefs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
self.module.appendHeader(
self.codegen.makeFuncAlias(API_PREFIX_EQUALITY + name,
API_PREFIX_EQUALITY + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
typeFromName = \
lambda varname: makeVulkanTypeSimple(True, name, 1, varname)
compareParams = \
list(map(typeFromName, EQUALITY_VAR_NAMES)) + \
[EQUALITY_ON_FAIL_VAR_TYPE]
comparePrototype = \
VulkanAPI(API_PREFIX_EQUALITY + name,
EQUALITY_RET_TYPE,
compareParams)
def structCompareDef(cgen):
self.equalityCodegen.cgen = cgen
for member in structInfo.members:
iterateVulkanType(self.typeInfo, member,
self.equalityCodegen)
self.module.appendHeader(
self.codegen.makeFuncDecl(comparePrototype))
self.module.appendImpl(
self.codegen.makeFuncImpl(comparePrototype, structCompareDef))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
def forEachExtensionCompare(ext, castedAccess, cgen):
cgen.funcCall(None, API_PREFIX_EQUALITY + ext.name,
[castedAccess,
cgen.makeReinterpretCast(
STRUCT_EXTENSION_PARAM2.paramName, ext.name),
EQUALITY_ON_FAIL_VAR])
self.module.appendImpl(
self.codegen.makeFuncImpl(
self.extensionTestingPrototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
EQUALITY_RET_TYPE,
STRUCT_EXTENSION_PARAM,
forEachExtensionCompare)))
@@ -0,0 +1,336 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanCompoundType, VulkanAPI, makeVulkanTypeSimple, vulkanTypeNeedsTransform, vulkanTypeGetNeededTransformTypes, VulkanTypeIterator, iterateVulkanType, vulkanTypeforEachSubType, TRIVIAL_TRANSFORMED_TYPES, NON_TRIVIAL_TRANSFORMED_TYPES, TRANSFORMED_TYPES
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE
def deviceMemoryTransform(resourceTrackerVarName, structOrApiInfo, getExpr, getLen, cgen, variant="tohost"):
paramIndices = \
structOrApiInfo.deviceMemoryInfoParameterIndices
for _, info in paramIndices.items():
orderedKeys = [
"handle",
"offset",
"size",
"typeIndex",
"typeBits",]
casts = {
"handle" : "VkDeviceMemory*",
"offset" : "VkDeviceSize*",
"size" : "VkDeviceSize*",
"typeIndex" : "uint32_t*",
"typeBits" : "uint32_t*",
}
accesses = {
"handle" : "nullptr",
"offset" : "nullptr",
"size" : "nullptr",
"typeIndex" : "nullptr",
"typeBits" : "nullptr",
}
lenAccesses = {
"handle" : "0",
"offset" : "0",
"size" : "0",
"typeIndex" : "0",
"typeBits" : "0",
}
def doParam(i, vulkanType):
access = getExpr(vulkanType)
lenAccess = getLen(vulkanType)
for k in orderedKeys:
if i == info.__dict__[k]:
accesses[k] = access
if lenAccess is not None:
lenAccesses[k] = lenAccess
else:
lenAccesses[k] = "1"
vulkanTypeforEachSubType(structOrApiInfo, doParam)
callParams = ", ".join( \
["(%s)%s, %s" % (casts[k], accesses[k], lenAccesses[k]) \
for k in orderedKeys])
if variant == "tohost":
cgen.stmt("%s->deviceMemoryTransform_tohost(%s)" % \
(resourceTrackerVarName, callParams))
else:
cgen.stmt("%s->deviceMemoryTransform_fromhost(%s)" % \
(resourceTrackerVarName, callParams))
def directTransform(resourceTrackerVarName, vulkanType, getExpr, getLen, cgen, variant="tohost"):
access = getExpr(vulkanType)
lenAccess = getLen(vulkanType)
if lenAccess:
finalLenAccess = lenAccess
else:
finalLenAccess = "1"
cgen.stmt("%s->transformImpl_%s_%s(%s, %s)" % (resourceTrackerVarName,
vulkanType.typeName, variant, access, finalLenAccess))
def genTransformsForVulkanType(resourceTrackerVarName, structOrApiInfo, getExpr, getLen, cgen, variant="tohost"):
for transform in vulkanTypeGetNeededTransformTypes(structOrApiInfo):
if transform == "devicememory":
deviceMemoryTransform( \
resourceTrackerVarName,
structOrApiInfo,
getExpr, getLen, cgen, variant=variant)
class TransformCodegen(VulkanTypeIterator):
def __init__(self, cgen, inputVar, resourceTrackerVarName, prefix, variant):
self.cgen = cgen
self.inputVar = inputVar
self.prefix = prefix
self.resourceTrackerVarName = resourceTrackerVarName
def makeAccess(varName, asPtr = True):
return lambda t: self.cgen.generalAccess(t, parentVarName = varName, asPtr = asPtr)
def makeLengthAccess(varName):
return lambda t: self.cgen.generalLengthAccess(t, parentVarName = varName)
def makeLengthAccessGuard(varName):
return lambda t: self.cgen.generalLengthAccessGuard(t, parentVarName=varName)
self.exprAccessor = makeAccess(self.inputVar)
self.exprAccessorValue = makeAccess(self.inputVar, asPtr = False)
self.lenAccessor = makeLengthAccess(self.inputVar)
self.lenAccessorGuard = makeLengthAccessGuard(self.inputVar)
self.checked = False
self.variant = variant
def makeCastExpr(self, vulkanType):
return "(%s)" % (
self.cgen.makeCTypeDecl(vulkanType, useParamName=False))
def asNonConstCast(self, access, vulkanType):
if vulkanType.staticArrExpr:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
elif vulkanType.accessibleAsPointer():
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForNonConstAccess()), access)
else:
casted = "%s(%s)" % (self.makeCastExpr(vulkanType.getForAddressAccess().getForNonConstAccess()), access)
return casted
def onCheck(self, vulkanType):
pass
def endCheck(self, vulkanType):
pass
def onCompoundType(self, vulkanType):
access = self.exprAccessor(vulkanType)
lenAccess = self.lenAccessor(vulkanType)
lenAccessGuard = self.lenAccessorGuard(vulkanType)
isPtr = vulkanType.pointerIndirectionLevels > 0
if lenAccessGuard is not None:
self.cgen.beginIf(lenAccessGuard)
if isPtr:
self.cgen.beginIf(access)
if lenAccess is not None:
loopVar = "i"
access = "%s + %s" % (access, loopVar)
forInit = "uint32_t %s = 0" % loopVar
forCond = "%s < (uint32_t)%s" % (loopVar, lenAccess)
forIncr = "++%s" % loopVar
self.cgen.beginFor(forInit, forCond, forIncr)
accessCasted = self.asNonConstCast(access, vulkanType)
if vulkanType.isTransformed:
directTransform(self.resourceTrackerVarName, vulkanType, self.exprAccessor, self.lenAccessor, self.cgen, variant=self.variant)
self.cgen.funcCall(None, self.prefix + vulkanType.typeName,
[self.resourceTrackerVarName, accessCasted])
if lenAccess is not None:
self.cgen.endFor()
if isPtr:
self.cgen.endIf()
if lenAccessGuard is not None:
self.cgen.endIf()
def onString(self, vulkanType):
pass
def onStringArray(self, vulkanType):
pass
def onStaticArr(self, vulkanType):
pass
def onStructExtension(self, vulkanType):
access = self.exprAccessor(vulkanType)
castedAccessExpr = "(%s)(%s)" % ("void*", access)
self.cgen.beginIf(access)
self.cgen.funcCall(None, self.prefix + "extension_struct",
[self.resourceTrackerVarName, castedAccessExpr])
self.cgen.endIf()
def onPointer(self, vulkanType):
pass
def onValue(self, vulkanType):
pass
class VulkanTransform(VulkanWrapperGenerator):
def __init__(self, module, typeInfo, resourceTrackerTypeName="ResourceTracker", resourceTrackerVarName="resourceTracker"):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.transformPrefix = "transform_"
self.tohostpart = "tohost"
self.fromhostpart = "fromhost"
self.variants = [self.tohostpart, self.fromhostpart]
self.toTransformVar = "toTransform"
self.resourceTrackerTypeName = resourceTrackerTypeName
self.resourceTrackerVarName = resourceTrackerVarName
self.transformParam = \
makeVulkanTypeSimple(False, self.resourceTrackerTypeName, 1,
self.resourceTrackerVarName)
self.voidType = makeVulkanTypeSimple(False, "void", 0)
self.extensionTransformPrototypes = []
for variant in self.variants:
self.extensionTransformPrototypes.append( \
VulkanAPI(self.transformPrefix + variant + "_extension_struct",
self.voidType,
[self.transformParam, STRUCT_EXTENSION_PARAM_FOR_WRITE]))
self.knownStructs = {}
self.needsTransform = set([])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
# Set up a convenience macro fro the transformed structs
# and forward-declare the resource tracker class
self.codegen.stmt("class %s" % self.resourceTrackerTypeName)
self.codegen.line("#define LIST_TRIVIAL_TRANSFORMED_TYPES(f) \\")
for name in TRIVIAL_TRANSFORMED_TYPES:
self.codegen.line("f(%s) \\" % name)
self.codegen.line("")
self.codegen.line("#define LIST_NON_TRIVIAL_TRANSFORMED_TYPES(f) \\")
for name in NON_TRIVIAL_TRANSFORMED_TYPES:
self.codegen.line("f(%s) \\" % name)
self.codegen.line("")
self.codegen.line("#define LIST_TRANSFORMED_TYPES(f) \\")
self.codegen.line("LIST_TRIVIAL_TRANSFORMED_TYPES(f) \\")
self.codegen.line("LIST_NON_TRIVIAL_TRANSFORMED_TYPES(f) \\")
self.codegen.line("")
self.module.appendHeader(self.codegen.swapCode())
for prototype in self.extensionTransformPrototypes:
self.module.appendImpl(self.codegen.makeFuncDecl(
prototype))
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownStructs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
for variant in self.variants:
self.module.appendHeader(
self.codegen.makeFuncAlias(self.transformPrefix + variant + "_" + name,
self.transformPrefix + variant + "_" + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
self.knownStructs[name] = structInfo
for variant in self.variants:
api = VulkanAPI( \
self.transformPrefix + variant + "_" + name,
self.voidType,
[self.transformParam] + \
[makeVulkanTypeSimple( \
False, name, 1, self.toTransformVar)])
transformer = TransformCodegen(
None,
self.toTransformVar,
self.resourceTrackerVarName,
self.transformPrefix + variant + "_",
variant)
def funcDefGenerator(cgen):
transformer.cgen = cgen
for p in api.parameters:
cgen.stmt("(void)%s" % p.paramName)
genTransformsForVulkanType(
self.resourceTrackerVarName,
structInfo,
transformer.exprAccessor,
transformer.lenAccessor,
cgen,
variant=variant)
for member in structInfo.members:
iterateVulkanType(
self.typeInfo, member,
transformer)
self.module.appendHeader(
self.codegen.makeFuncDecl(api))
self.module.appendImpl(
self.codegen.makeFuncImpl(api, funcDefGenerator))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
for (variant, prototype) in zip(self.variants, self.extensionTransformPrototypes):
def forEachExtensionTransform(ext, castedAccess, cgen):
if ext.isTransformed:
directTransform(self.resourceTrackerVarName, ext, lambda _ : castedAccess, lambda _ : "1", cgen, variant);
cgen.funcCall(None, self.transformPrefix + variant + "_" + ext.name,
[self.resourceTrackerVarName, castedAccess])
self.module.appendImpl(
self.codegen.makeFuncImpl(
prototype,
lambda cgen: self.emitForEachStructExtension(
cgen,
self.voidType,
STRUCT_EXTENSION_PARAM_FOR_WRITE,
forEachExtensionTransform)))
@@ -0,0 +1,69 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import CodeGen
from .common.vulkantypes import \
VulkanCompoundType, VulkanAPI, makeVulkanTypeSimple, vulkanTypeNeedsTransform, vulkanTypeGetNeededTransformTypes, VulkanTypeIterator, iterateVulkanType, vulkanTypeforEachSubType, TRANSFORMED_TYPES
from .wrapperdefs import VulkanWrapperGenerator
from .wrapperdefs import STRUCT_EXTENSION_PARAM, STRUCT_EXTENSION_PARAM_FOR_WRITE
# This is different from others; it operations solely in terms of deepcopy and handlemap
class VulkanUnbox(VulkanWrapperGenerator):
def __init__(self, module, typeInfo):
VulkanWrapperGenerator.__init__(self, module, typeInfo)
self.codegen = CodeGen()
self.unboxPrefix = "unbox"
self.toUnboxVar = "toUnbox"
self.poolParam = \
makeVulkanTypeSimple(False, "BumpPool", 1, "pool")
self.knownStructs = {}
self.needsTransform = set([])
def onBegin(self,):
VulkanWrapperGenerator.onBegin(self)
def onGenType(self, typeXml, name, alias):
VulkanWrapperGenerator.onGenType(self, typeXml, name, alias)
if name in self.knownStructs:
return
category = self.typeInfo.categoryOf(name)
if category in ["struct", "union"] and alias:
self.module.appendHeader(
self.codegen.makeFuncAlias(self.unboxPrefix + "_" + name,
self.unboxPrefix + "_" + alias))
if category in ["struct", "union"] and not alias:
structInfo = self.typeInfo.structs[name]
self.knownStructs[name] = structInfo
api = VulkanAPI( \
self.unboxPrefix + "_" + name,
makeVulkanTypeSimple(False, name, 1),
[self.poolParam] + \
[makeVulkanTypeSimple( \
True, name, 1, self.toUnboxVar)])
def funcDefGenerator(cgen):
cgen.stmt("BoxedHandleUnwrapMapping unboxMapping")
cgen.stmt("%s* res = (%s*)pool->alloc(sizeof(const %s))" % (name, name, name))
cgen.stmt("deepcopy_%s(pool, %s, %s)" % (name, self.toUnboxVar, "res"))
cgen.stmt("handlemap_%s(%s, %s)" % (name, "&unboxMapping", "res"))
cgen.stmt("return res")
self.module.appendHeader(
self.codegen.makeFuncDecl(api))
self.module.appendImpl(
self.codegen.makeFuncImpl(api, funcDefGenerator))
def onGenCmd(self, cmdinfo, name, alias):
VulkanWrapperGenerator.onGenCmd(self, cmdinfo, name, alias)
def onEnd(self,):
VulkanWrapperGenerator.onEnd(self)
@@ -0,0 +1,34 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .wrapperdefs import VulkanWrapperGenerator
class VulkanExtensionStructureType(VulkanWrapperGenerator):
def __init__(self, extensionName: str, module, typeInfo):
super().__init__(module, typeInfo)
self._extensionName = extensionName
def onGenGroup(self, groupinfo, groupName, alias=None):
super().onGenGroup(groupinfo, groupName, alias)
elem = groupinfo.elem
if (not elem.get('type') == 'enum'):
return
if (not elem.get('name') == 'VkStructureType'):
return
extensionEnumFactoryMacro = f'{self._extensionName.upper()}_ENUM'
for enum in elem.findall(f"enum[@extname='{self._extensionName}']"):
name = enum.get('name')
offset = enum.get('offset')
self.module.appendHeader(
f"#define {name} {extensionEnumFactoryMacro}(VkStructureType, {offset})\n")
class VulkanGfxstreamStructureType(VulkanExtensionStructureType):
def __init__(self, module, typeInfo):
super().__init__('VK_GOOGLE_gfxstream', module, typeInfo)
class VulkanAndroidNativeBufferStructureType(VulkanExtensionStructureType):
def __init__(self, module, typeInfo):
super().__init__('VK_ANDROID_native_buffer', module, typeInfo)
@@ -0,0 +1,96 @@
# Copyright 2018 Google LLC
# SPDX-License-Identifier: MIT
from .common.codegen import VulkanWrapperGenerator
from .common.vulkantypes import makeVulkanTypeSimple
# Contains definitions for various Vulkan API wrappers. This information is
# shared to make it easier for one kind of wrapper to know how to call
# another one.
API_PREFIX_MARSHAL = "marshal_"
API_PREFIX_UNMARSHAL = "unmarshal_"
API_PREFIX_RESERVEDMARSHAL = "reservedmarshal_"
API_PREFIX_RESERVEDUNMARSHAL = "reservedunmarshal_"
MARSHAL_INPUT_VAR_NAME = "forMarshaling"
UNMARSHAL_INPUT_VAR_NAME = "forUnmarshaling"
API_PREFIX_VALIDATE = "validate_"
API_PREFIX_FRONTEND = "goldfish_frontend_"
VULKAN_STREAM_TYPE = "VulkanStream"
VULKAN_STREAM_TYPE_GUEST = "VulkanStreamGuest"
VULKAN_STREAM_VAR_NAME = "vkStream"
VALIDATE_RESULT_TYPE = "VkResult"
VALIDATE_VAR_NAME = "validateResult"
VALIDATE_GOOD_RESULT = "VK_SUCCESS"
ROOT_TYPE_VAR_NAME = "rootType"
ROOT_TYPE_DEFAULT_VALUE = "VK_STRUCTURE_TYPE_MAX_ENUM"
ROOT_TYPE_TYPE = "VkStructureType"
ROOT_TYPE_PARAM = makeVulkanTypeSimple(
False, ROOT_TYPE_TYPE, 0, ROOT_TYPE_VAR_NAME)
PARAMETERS_MARSHALING = [
makeVulkanTypeSimple(False, VULKAN_STREAM_TYPE, 1, VULKAN_STREAM_VAR_NAME),
ROOT_TYPE_PARAM,
]
PARAMETERS_MARSHALING_GUEST = [
makeVulkanTypeSimple(False, VULKAN_STREAM_TYPE_GUEST,
1, VULKAN_STREAM_VAR_NAME),
ROOT_TYPE_PARAM,
]
PARAMETERS_VALIDATE = [
makeVulkanTypeSimple(False, VALIDATE_RESULT_TYPE, 1, VALIDATE_VAR_NAME)
]
PARAMETERS_COUNTING = [
makeVulkanTypeSimple(False, "size_t", 1, VULKAN_STREAM_VAR_NAME)
]
STRUCT_EXTENSION_PARAM = \
makeVulkanTypeSimple(True, "void", 1, "structExtension")
STRUCT_EXTENSION_PARAM2 = \
makeVulkanTypeSimple(True, "void", 1, "structExtension2")
STRUCT_EXTENSION_PARAM_FOR_WRITE = \
makeVulkanTypeSimple(False, "void", 1, "structExtension_out")
STRUCT_TYPE_API_NAME = "goldfish_vk_struct_type"
EXTENSION_SIZE_API_NAME = "goldfish_vk_extension_struct_size"
EXTENSION_SIZE_WITH_STREAM_FEATURES_API_NAME = "goldfish_vk_extension_struct_size_with_stream_features"
VOID_TYPE = makeVulkanTypeSimple(False, "void", 0)
STREAM_RET_TYPE = makeVulkanTypeSimple(False, "void", 0)
API_PREFIX_EQUALITY = "checkEqual_"
EQUALITY_VAR_NAMES = ["a", "b"]
EQUALITY_ON_FAIL_VAR = "onFail"
EQUALITY_ON_FAIL_VAR_TYPE = makeVulkanTypeSimple(False, "OnFailCompareFunc", 0,
EQUALITY_ON_FAIL_VAR)
EQUALITY_RET_TYPE = makeVulkanTypeSimple(False, "void", 0)
RELAXED_APIS = [
"vkWaitForFences",
"vkWaitSemaphores",
"vkWaitSemaphoresKHR",
"vkQueueWaitIdle",
"vkDeviceWaitIdle",
"vkQueueFlushCommandsGOOGLE",
]
STYPE_OVERRIDE = {
"VkPhysicalDeviceFragmentDensityMapFeaturesEXT": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_FEATURES_EXT",
"VkPhysicalDeviceFragmentDensityMapPropertiesEXT": "VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_DENSITY_MAP_PROPERTIES_EXT",
"VkRenderPassFragmentDensityMapCreateInfoEXT": "VK_STRUCTURE_TYPE_RENDER_PASS_FRAGMENT_DENSITY_MAP_CREATE_INFO_EXT",
"VkImportColorBufferGOOGLE": "VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE",
"VkImportBufferGOOGLE": "VK_STRUCTURE_TYPE_IMPORT_BUFFER_GOOGLE",
"VkCreateBlobGOOGLE": "VK_STRUCTURE_TYPE_CREATE_BLOB_GOOGLE",
}
MAX_PACKET_LENGTH = "(400 * 1024 * 1024) // 400MB"
@@ -0,0 +1,954 @@
# Copyright 2023 Google LLC
# SPDX-License-Identifier: MIT
import os, re, sys
from generator import *
from pathlib import Path, PurePosixPath
import cereal
from cereal.wrapperdefs import VULKAN_STREAM_TYPE
from cereal.wrapperdefs import VULKAN_STREAM_TYPE_GUEST
# CerealGenerator - generates set of driver sources
# while being agnostic to the stream implementation
from reg import GroupInfo, TypeInfo, EnumInfo
VK_CEREAL_FLAG_HOST = 1
VK_CEREAL_FLAG_GUEST = 2
VK_CEREAL_FLAG_ALL = VK_CEREAL_FLAG_GUEST | VK_CEREAL_FLAG_HOST
SUPPORTED_FEATURES = [
"VK_VERSION_1_0",
"VK_BASE_VERSION_1_0",
"VK_COMPUTE_VERSION_1_0",
"VK_GRAPHICS_VERSION_1_0",
"VK_VERSION_1_1",
"VK_BASE_VERSION_1_1",
"VK_COMPUTE_VERSION_1_1",
"VK_GRAPHICS_VERSION_1_1",
"VK_VERSION_1_2",
"VK_BASE_VERSION_1_2",
"VK_COMPUTE_VERSION_1_2",
"VK_GRAPHICS_VERSION_1_2",
"VK_VERSION_1_3",
"VK_BASE_VERSION_1_3",
"VK_COMPUTE_VERSION_1_3",
"VK_GRAPHICS_VERSION_1_3",
"VK_VERSION_1_4",
"VK_BASE_VERSION_1_4",
"VK_COMPUTE_VERSION_1_4",
"VK_GRAPHICS_VERSION_1_4",
# Instance extensions
"VK_KHR_get_physical_device_properties2",
"VK_KHR_external_semaphore_capabilities",
"VK_KHR_external_memory_capabilities",
"VK_KHR_external_fence_capabilities",
"VK_EXT_debug_utils",
"VK_EXT_debug_report",
"VK_EXT_validation_features",
# Device extensions
"VK_EXT_external_memory_host",
"VK_KHR_storage_buffer_storage_class",
"VK_KHR_vulkan_memory_model",
"VK_KHR_buffer_device_address",
"VK_KHR_maintenance1",
"VK_KHR_maintenance2",
"VK_KHR_maintenance3",
"VK_KHR_bind_memory2",
"VK_KHR_dedicated_allocation",
"VK_KHR_get_memory_requirements2",
"VK_KHR_sampler_ycbcr_conversion",
"VK_KHR_global_priority",
"VK_KHR_shader_float16_int8",
"VK_AMD_gpu_shader_half_float",
"VK_NV_shader_subgroup_partitioned",
"VK_KHR_shader_subgroup_extended_types",
"VK_EXT_provoking_vertex",
"VK_EXT_line_rasterization",
"VK_KHR_line_rasterization",
"VK_EXT_transform_feedback",
"VK_EXT_primitive_topology_list_restart",
"VK_EXT_index_type_uint8",
"VK_EXT_load_store_op_none",
"VK_EXT_swapchain_colorspace",
"VK_EXT_custom_border_color",
"VK_EXT_shader_stencil_export",
"VK_KHR_image_format_list",
"VK_KHR_incremental_present",
"VK_KHR_pipeline_executable_properties",
"VK_EXT_queue_family_foreign",
"VK_EXT_scalar_block_layout",
"VK_KHR_external_semaphore",
"VK_KHR_external_semaphore_fd",
"VK_KHR_external_memory",
"VK_KHR_external_fence",
"VK_KHR_external_fence_fd",
"VK_EXT_device_memory_report",
"VK_EXT_memory_budget",
"VK_KHR_create_renderpass2",
"VK_KHR_imageless_framebuffer",
"VK_KHR_descriptor_update_template",
"VK_EXT_depth_clip_enable",
"VK_EXT_robustness2",
"VK_KHR_multiview",
"VK_EXT_blend_operation_advanced",
"VK_EXT_frame_boundary",
# see aosp/2736079 + b/268351352
"VK_EXT_swapchain_maintenance1",
"VK_KHR_maintenance5",
"VK_EXT_host_image_copy",
"VK_EXT_image_compression_control",
"VK_EXT_image_compression_control_swapchain",
"VK_EXT_image_drm_format_modifier",
# VK1.3 extensions: see b/298704840
"VK_KHR_copy_commands2",
"VK_KHR_dynamic_rendering",
"VK_KHR_format_feature_flags2",
"VK_KHR_maintenance4",
"VK_KHR_shader_integer_dot_product",
"VK_KHR_shader_non_semantic_info",
"VK_KHR_shader_terminate_invocation",
"VK_KHR_synchronization2",
"VK_KHR_zero_initialize_workgroup_memory",
"VK_EXT_4444_formats",
"VK_EXT_extended_dynamic_state",
"VK_EXT_extended_dynamic_state2",
"VK_EXT_image_robustness",
"VK_EXT_inline_uniform_block",
"VK_EXT_pipeline_creation_cache_control",
"VK_EXT_pipeline_creation_feedback",
"VK_EXT_private_data",
"VK_EXT_shader_demote_to_helper_invocation",
"VK_EXT_subgroup_size_control",
"VK_EXT_texel_buffer_alignment",
"VK_EXT_texture_compression_astc_hdr",
"VK_EXT_tooling_info",
"VK_EXT_ycbcr_2plane_444_formats",
# Host dispatch
"VK_KHR_surface",
"VK_KHR_swapchain",
"VK_KHR_xcb_surface",
"VK_KHR_win32_surface",
"VK_EXT_metal_surface",
"VK_EXT_metal_objects",
"VK_EXT_external_memory_metal",
"VK_KHR_external_semaphore_win32",
"VK_KHR_external_memory_win32",
"VK_NV_device_diagnostic_checkpoints",
"VK_KHR_ray_tracing_pipeline",
"VK_KHR_pipeline_library",
"VK_MVK_macos_surface",
# Android
"VK_ANDROID_native_buffer",
"VK_ANDROID_external_memory_android_hardware_buffer",
"VK_KHR_android_surface",
# Linux
"VK_KHR_external_memory_fd",
# Custom
"VK_GOOGLE_gfxstream",
# Used in tests without proper support checks
"VK_EXT_graphics_pipeline_library",
# Used by guest ANGLE
"VK_EXT_vertex_attribute_divisor",
# QNX
"VK_QNX_screen_surface",
"VK_QNX_external_memory_screen_buffer",
# b/320855472 Chrome
"VK_EXT_fragment_density_map",
# b/349122558 Zink
"VK_EXT_color_write_enable",
"VK_EXT_primitives_generated_query",
# Android requirements
"VK_EXT_pipeline_protected_access",
"VK_KHR_maintenance6",
"VK_KHR_maintenance7",
"VK_KHR_maintenance8",
"VK_KHR_maintenance9",
"VK_KHR_swapchain_maintenance1",
]
HOST_MODULES = ["goldfish_vk_extension_structs", "goldfish_vk_marshaling",
"goldfish_vk_reserved_marshaling", "goldfish_vk_deepcopy",
"goldfish_vk_dispatch", "goldfish_vk_transform", "vk_decoder",
"vk_decoder_snapshot", "vk_sub_decoder"]
# By default, the all wrappers are run all on all features. In certain cases,
# we wish run wrappers when the module requires it. For example, `VK_GOOGLE_gfxstream`
# shouldn't generate a function table entry since it's an internal interface.
SUPPORTED_MODULES = {
"VK_EXT_external_memory_host": HOST_MODULES,
"VK_EXT_debug_utils": HOST_MODULES,
"VK_EXT_debug_report": HOST_MODULES,
"VK_EXT_validation_features": HOST_MODULES,
"VK_KHR_surface": ["goldfish_vk_dispatch"],
"VK_KHR_xcb_surface": ["goldfish_vk_dispatch"],
"VK_KHR_win32_surface": ["goldfish_vk_dispatch"],
"VK_EXT_metal_surface": ["goldfish_vk_dispatch"],
"VK_EXT_metal_objects": ["goldfish_vk_dispatch"],
"VK_EXT_external_memory_metal": ["goldfish_vk_dispatch"],
"VK_KHR_external_semaphore_win32" : ["goldfish_vk_dispatch"],
"VK_KHR_external_memory_win32" : ["goldfish_vk_dispatch"],
"VK_MVK_macos_surface" : ["goldfish_vk_dispatch"],
# Host dispatch for Linux hosts + and entrypoint for guests
"VK_KHR_external_memory_fd": ["goldfish_vk_dispatch", "func_table"],
"VK_QNX_screen_surface": ["goldfish_vk_dispatch"],
"VK_QNX_external_memory_screen_buffer": ["goldfish_vk_dispatch"],
"VK_ANDROID_external_memory_android_hardware_buffer": ["goldfish_vk_dispatch", "func_table"],
"VK_KHR_android_surface": ["func_table"],
"VK_EXT_swapchain_maintenance1" : HOST_MODULES,
"VK_KHR_swapchain_maintenance1" : HOST_MODULES,
"VK_KHR_swapchain" : HOST_MODULES,
"VK_NV_device_diagnostic_checkpoints": ["goldfish_vk_dispatch"],
"VK_KHR_ray_tracing_pipeline": ["goldfish_vk_dispatch"],
"VK_KHR_pipeline_library": HOST_MODULES,
}
# These modules will be used when the feature is not supported.
# This is necessary to cover all extensions where needed.
UNSUPPORTED_FEATURE_MODULES = {
"goldfish_vk_extension_structs",
}
REQUIRED_TYPES = {
"int",
"uint16_t",
"int64_t",
"double",
"VkPresentScalingFlagsEXT",
"VkPresentGravityFlagsEXT",
"VkRenderingAreaInfo",
"VkRenderingAreaInfoKHR",
}
copyrightHeader = """// Copyright (C) 2018 The Android Open Source Project
// Copyright (C) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
"""
coprightHeaderPy = """# Copyright (C) 2025 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
# We put the long generated commands in a separate paragraph, so that the formatter won't mess up
# with other texts.
autogeneratedHeaderTemplate = """
// Autogenerated module %s
//
// %s
//
// Please do not modify directly;
// re-run mesa3d/src/gfxstream/codegen/generate-gfxstream-vulkan.sh,
// or directly from Python by defining:
// VULKAN_REGISTRY_XML_DIR : Directory containing vk.xml
// VULKAN_REGISTRY_SCRIPTS_DIR : Directory containing genvk.py
// CEREAL_OUTPUT_DIR: Where to put the generated sources.
//
// python3 $VULKAN_REGISTRY_SCRIPTS_DIR/genvk.py -registry $VULKAN_REGISTRY_XML_DIR/vk.xml cereal -o $CEREAL_OUTPUT_DIR
//
"""
namespaceHostBegin ="""
namespace gfxstream {
namespace host {
namespace vk {\n
"""
namespaceHostEnd = """
} // namespace vk
} // namespace host
} // namespace gfxstream
"""
namespaceBegin ="""
namespace gfxstream {
namespace vk {\n
"""
namespaceEnd = """
} // namespace vk
} // namespace gfxstream
"""
def banner_command(argv):
"""Return sanitized command-line description.
|argv| must be a list of command-line parameters, e.g. sys.argv.
Return a string corresponding to the command, with platform-specific
paths removed."""
return os.path.basename(argv[0])
def envGetOrDefault(key, default=None):
if key in os.environ:
return os.environ[key]
return default
# ---- methods overriding base class ----
# beginFile(genOpts)
# endFile()
# beginFeature(interface, emit)
# endFeature()
# genType(typeinfo,name)
# genStruct(typeinfo,name)
# genGroup(groupinfo,name)
# genEnum(enuminfo, name)
# genCmd(cmdinfo)
class CerealGenerator(OutputGenerator):
"""Generate serialization code"""
def __init__(self, errFile = sys.stderr,
warnFile = sys.stderr,
diagFile = sys.stdout):
OutputGenerator.__init__(self, errFile, warnFile, diagFile)
self.typeInfo = cereal.VulkanTypeInfo(self)
self.modules = {}
self.protos = {}
self.moduleList = []
self.protoList = []
self.wrappers = []
self.codegen = cereal.CodeGen()
self.featureSupported = False
self.supportedModules = None
# The cereal variant should be an environmental variable of one of
# the following:
# - "guest"
# - "host"
# - "both"
cerealVariant = envGetOrDefault("CEREAL_VARIANT", "both")
if cerealVariant == "guest":
self.cerealFlags = VK_CEREAL_FLAG_GUEST
elif cerealVariant == "host":
self.cerealFlags = VK_CEREAL_FLAG_HOST
else:
self.cerealFlags = VK_CEREAL_FLAG_ALL
# THe host always needs all possible guest struct definitions, while the guest only needs
# platform sepcific headers.
self.hostCommonExtraVulkanHeaders = '#include "vk_android_native_buffer_gfxstream.h"'
encoderInclude = f"""
#include "goldfish_vk_private_defs.h"
#include <memory>
namespace gfxstream {{
namespace guest {{
class IOStream;
}} // namespace guest
}} // namespace gfxstream
"""
encoderImplInclude = f"""
#include "Resources.h"
#include "ResourceTracker.h"
#include "Validation.h"
#include "%s.h"
#include "gfxstream/guest/IOStream.h"
#include "AlignedBuf.h"
#include "BumpPool.h"
#include "goldfish_vk_marshaling_guest.h"
#include "goldfish_vk_reserved_marshaling_guest.h"
#include "goldfish_vk_deepcopy_guest.h"
#include "goldfish_vk_counting_guest.h"
#include "goldfish_vk_private_defs.h"
#include "goldfish_vk_transform_guest.h"
#include <memory>
#include <optional>
#include <unordered_map>
#include <string>
#include <vector>
""" % VULKAN_STREAM_TYPE_GUEST
functableImplInclude = """
#include "VkEncoder.h"
#include "ResourceTracker.h"
#include "gfxstream_vk_entrypoints.h"
#include "gfxstream_vk_private.h"
#include "goldfish_vk_private_defs.h"
#include <cstring>
// Stuff we are not going to use but if included,
// will cause compile errors. These are Android Vulkan
// required extensions, but the approach will be to
// implement them completely on the guest side.
#undef VK_KHR_android_surface
#if defined(LINUX_GUEST_BUILD) || DETECT_OS_FUCHSIA || DETECT_OS_WINDOWS
#undef VK_ANDROID_native_buffer
#endif
"""
marshalIncludeGuest = """
#include "goldfish_vk_marshaling_guest.h"
#include "goldfish_vk_private_defs.h"
#include "%s.h"
// Stuff we are not going to use but if included,
// will cause compile errors. These are Android Vulkan
// required extensions, but the approach will be to
// implement them completely on the guest side.
#undef VK_KHR_android_surface
#undef VK_ANDROID_external_memory_android_hardware_buffer
""" % VULKAN_STREAM_TYPE_GUEST
reservedmarshalIncludeGuest = """
#include "goldfish_vk_marshaling_guest.h"
#include "goldfish_vk_private_defs.h"
#include "%s.h"
// Stuff we are not going to use but if included,
// will cause compile errors. These are Android Vulkan
// required extensions, but the approach will be to
// implement them completely on the guest side.
#undef VK_KHR_android_surface
#undef VK_ANDROID_external_memory_android_hardware_buffer
""" % VULKAN_STREAM_TYPE_GUEST
reservedMarshalingHostIncludes = """
#include "vulkan_boxed_handles.h"
"""
reservedmarshalImplIncludeGuest = """
#include "Resources.h"
"""
vulkanStreamIncludeHost = f"""
{self.hostCommonExtraVulkanHeaders}
#include "goldfish_vk_private_defs.h"
#include "gfxstream/host/stream_utils.h"
#include "vulkan_stream.h"
"""
poolInclude = f"""
{self.hostCommonExtraVulkanHeaders}
#include "goldfish_vk_private_defs.h"
#include "gfxstream/BumpPool.h"
using gfxstream::base::Allocator;
using gfxstream::base::BumpPool;
"""
transformIncludeGuest = """
#include "goldfish_vk_private_defs.h"
"""
transformInclude = f"""
{self.hostCommonExtraVulkanHeaders}
#include "goldfish_vk_private_defs.h"
#include "goldfish_vk_extension_structs.h"
"""
transformImplIncludeGuest = """
#include "ResourceTracker.h"
"""
transformImplInclude = """
#include "vk_decoder_global_state.h"
"""
deepcopyInclude = """
#include "vk_util.h"
"""
deepcopyHostInclude = """
#include "vk_utils.h"
"""
poolIncludeGuest = f"""
#include "goldfish_vk_private_defs.h"
#include "BumpPool.h"
using gfxstream::aemu::Allocator;
using gfxstream::aemu::BumpPool;
// Stuff we are not going to use but if included,
// will cause compile errors. These are Android Vulkan
// required extensions, but the approach will be to
// implement them completely on the guest side.
#undef VK_KHR_android_surface
#undef VK_ANDROID_external_memory_android_hardware_buffer
"""
dispatchHeaderDefs = f"""
{self.hostCommonExtraVulkanHeaders}
#include "goldfish_vk_private_defs.h"
namespace gfxstream {{
namespace host {{
namespace vk {{
struct VulkanDispatch;
}} // namespace vk
}} // namespace host
}} // namespace gfxstream
using DlOpenFunc = void* (void);
using DlSymFunc = void* (void*, const char*);
"""
extensionStructsInclude = f"""
{self.hostCommonExtraVulkanHeaders}
#include "gfxstream/common/logging.h"
#include "goldfish_vk_private_defs.h"
#include "vulkan/vk_enum_string_helper.h"
"""
extensionStructsIncludeGuest = """
#include "vk_platform_compat.h"
#include "goldfish_vk_private_defs.h"
// Stuff we are not going to use but if included,
// will cause compile errors. These are Android Vulkan
// required extensions, but the approach will be to
// implement them completely on the guest side.
#undef VK_KHR_android_surface
#undef VK_ANDROID_external_memory_android_hardware_buffer
"""
commonCerealImplIncludes = """
#include "goldfish_vk_extension_structs.h"
#include "goldfish_vk_private_defs.h"
#include <string.h>
"""
commonCerealIncludesGuest = """
#include "vk_platform_compat.h"
"""
commonCerealImplIncludesGuest = """
#include "goldfish_vk_extension_structs_guest.h"
#include "goldfish_vk_private_defs.h"
#include <cstring>
"""
countingIncludes = """
#include "vk_platform_compat.h"
#include "goldfish_vk_private_defs.h"
"""
countingIncludeGuest = """
#include <cstdlib>
"""
dispatchImplIncludes = """
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
"""
decoderSnapshotHeaderIncludes = f"""
#include <memory>
#include "gfxstream/host/GfxApiLogger.h"
#include "goldfish_vk_private_defs.h"
#include "vk_snapshot_handles.h"
"""
decoderSnapshotImplIncludes = f"""
#include <mutex>
#include "gfxstream/ThreadAnnotations.h"
#include "vk_decoder_global_state.h"
#include "vk_reconstruction.h"
#include "vulkan_boxed_handles.h"
#include "vulkan_handle_mapping.h"
"""
decoderHeaderIncludes = f"""
#include "vk_decoder_context.h"
#include "gfxstream/host/process_resources.h"
#include <memory>
namespace gfxstream {{
namespace base {{
class BumpPool;
}} // namespace base
}} // namespace gfxstream
"""
decoderImplIncludes = f"""
#include <cstring>
#include <functional>
#include <optional>
#include <unordered_map>
#include "common/goldfish_vk_marshaling.h"
#include "common/goldfish_vk_reserved_marshaling.h"
#include "common/goldfish_vk_transform.h"
#include "frame_buffer.h"
#include "gfxstream/BumpPool.h"
#include "gfxstream/common/logging.h"
#include "gfxstream/host/iostream.h"
#include "gfxstream/host/tracing.h"
#include "gfxstream/system/System.h"
#include "gfxstream/threads/Thread.h"
#include "goldfish_vk_private_defs.h"
#include "vk_decoder_global_state.h"
#include "vk_decoder_snapshot.h"
#include "vulkan_boxed_handles.h"
#include "vulkan_dispatch.h"
#include "vulkan_stream.h"
"""
def createVkExtensionStructureTypePreamble(extensionName: str) -> str:
return f"""
#define {extensionName}_ENUM(type,id) \
((type)(1000000000 + (1000 * ({extensionName}_NUMBER - 1)) + (id)))
"""
self.guest_encoder_tag = "guest_encoder"
self.host_tag = "host"
default_guest_abs_encoder_destination = \
os.path.join(
os.getcwd(),
"..", "..",
"device", "generic", "goldfish-opengl",
"system", "vulkan_enc")
self.guest_abs_encoder_destination = \
envGetOrDefault("GFXSTREAM_GUEST_ENCODER_DIR",
default_guest_abs_encoder_destination)
default_host_abs_decoder_destination = \
os.path.join(
os.getcwd(),
"android", "android-emugl", "host",
"libs", "libOpenglRender", "vulkan")
self.host_abs_decoder_destination = \
envGetOrDefault("GFXSTREAM_HOST_DECODER_DIR",
default_host_abs_decoder_destination)
self.host_script_destination = envGetOrDefault("GFXSTREAM_SCRIPTS_DIR")
if self.cerealFlags & VK_CEREAL_FLAG_GUEST:
self.addGuestEncoderModule(
"VkEncoder",
extraHeader = encoderInclude,
extraImpl = encoderImplInclude)
self.addGuestEncoderModule("goldfish_vk_extension_structs_guest",
extraHeader=extensionStructsIncludeGuest)
self.addGuestEncoderModule("goldfish_vk_marshaling_guest",
extraHeader=commonCerealIncludesGuest + marshalIncludeGuest,
extraImpl=commonCerealImplIncludesGuest)
self.addGuestEncoderModule("goldfish_vk_reserved_marshaling_guest",
extraHeader=commonCerealIncludesGuest + reservedmarshalIncludeGuest,
extraImpl=commonCerealImplIncludesGuest + reservedmarshalImplIncludeGuest)
self.addGuestEncoderModule("goldfish_vk_deepcopy_guest",
extraHeader=commonCerealIncludesGuest + poolIncludeGuest,
extraImpl=commonCerealImplIncludesGuest + deepcopyInclude)
self.addGuestEncoderModule("goldfish_vk_counting_guest",
extraHeader=countingIncludes,
extraImpl=commonCerealImplIncludesGuest + countingIncludeGuest)
self.addGuestEncoderModule("goldfish_vk_transform_guest",
extraHeader=commonCerealIncludesGuest + transformIncludeGuest,
extraImpl=commonCerealImplIncludesGuest + transformImplIncludeGuest)
self.addGuestEncoderModule(
"vulkan_gfxstream_structure_type", headerOnly=True, suppressFeatureGuards=True,
moduleName="vulkan_gfxstream_structure_type_guest", useNamespace=False,
suppressVulkanHeaders=True,
extraHeader=createVkExtensionStructureTypePreamble('VK_GOOGLE_GFXSTREAM'))
self.addGuestEncoderModule("func_table", extraImpl=functableImplInclude, implOnly = True,
useNamespace = False)
self.addWrapper(cereal.VulkanEncoder, "VkEncoder")
self.addWrapper(cereal.VulkanExtensionStructs, "goldfish_vk_extension_structs_guest", variant = "guest")
self.addWrapper(cereal.VulkanMarshaling, "goldfish_vk_marshaling_guest", variant = "guest")
self.addWrapper(cereal.VulkanReservedMarshaling, "goldfish_vk_reserved_marshaling_guest", variant = "guest")
self.addWrapper(cereal.VulkanDeepcopy, "goldfish_vk_deepcopy_guest")
self.addWrapper(cereal.VulkanCounting, "goldfish_vk_counting_guest")
self.addWrapper(cereal.VulkanTransform, "goldfish_vk_transform_guest")
self.addWrapper(cereal.VulkanFuncTable, "func_table")
self.addWrapper(cereal.VulkanGfxstreamStructureType,
"vulkan_gfxstream_structure_type_guest")
if self.cerealFlags & VK_CEREAL_FLAG_HOST:
self.addCppModule("common", "goldfish_vk_extension_structs",
extraHeader=extensionStructsInclude)
self.addCppModule("common", "goldfish_vk_marshaling",
extraHeader=vulkanStreamIncludeHost,
extraImpl=commonCerealImplIncludes)
self.addCppModule("common", "goldfish_vk_reserved_marshaling",
extraHeader=vulkanStreamIncludeHost,
extraImpl=commonCerealImplIncludes + reservedMarshalingHostIncludes)
self.addCppModule("common", "goldfish_vk_deepcopy",
extraHeader=poolInclude,
extraImpl=commonCerealImplIncludes + deepcopyHostInclude)
self.addCppModule("common", "goldfish_vk_dispatch",
extraHeader=dispatchHeaderDefs,
extraImpl=dispatchImplIncludes)
self.addCppModule("common", "goldfish_vk_transform",
extraHeader=transformInclude,
extraImpl=transformImplInclude)
self.addHostModule("vk_decoder",
extraHeader=decoderHeaderIncludes,
extraImpl=decoderImplIncludes,
useNamespace=False)
self.addHostModule("vk_decoder_snapshot",
extraHeader=decoderSnapshotHeaderIncludes,
extraImpl=decoderSnapshotImplIncludes,
useNamespace=False)
self.addHostModule("vk_sub_decoder",
extraHeader="",
extraImpl="",
useNamespace=False,
implOnly=True)
self.addModule(cereal.PyScript(self.host_tag, "vulkan_printer", customAbsDir=Path(
self.host_script_destination) / "print_gfx_logs"), moduleName="ApiLogDecoder",
extraHeader=coprightHeaderPy)
self.addHostModule(
"vulkan_gfxstream_structure_type", headerOnly=True, suppressFeatureGuards=True,
moduleName="vulkan_gfxstream_structure_type_host", useNamespace=False,
suppressVulkanHeaders=True,
extraHeader=createVkExtensionStructureTypePreamble('VK_GOOGLE_GFXSTREAM'))
self.addHostModule(
"vk_android_native_buffer_structure_type", headerOnly=True, suppressFeatureGuards=True,
useNamespace=False, suppressVulkanHeaders=True,
extraHeader=createVkExtensionStructureTypePreamble('VK_ANDROID_NATIVE_BUFFER'))
self.addWrapper(cereal.VulkanExtensionStructs, "goldfish_vk_extension_structs", variant = "host")
self.addWrapper(cereal.VulkanMarshaling, "goldfish_vk_marshaling")
self.addWrapper(cereal.VulkanReservedMarshaling, "goldfish_vk_reserved_marshaling", variant = "host")
self.addWrapper(cereal.VulkanDeepcopy, "goldfish_vk_deepcopy")
self.addWrapper(cereal.VulkanDispatch, "goldfish_vk_dispatch")
self.addWrapper(cereal.VulkanTransform, "goldfish_vk_transform", resourceTrackerTypeName="VkDecoderGlobalState")
self.addWrapper(cereal.VulkanDecoder, "vk_decoder")
self.addWrapper(cereal.VulkanDecoderSnapshot, "vk_decoder_snapshot")
self.addWrapper(cereal.VulkanSubDecoder, "vk_sub_decoder")
self.addWrapper(cereal.ApiLogDecoder, "ApiLogDecoder")
self.addWrapper(cereal.VulkanGfxstreamStructureType, "vulkan_gfxstream_structure_type_host")
self.addWrapper(cereal.VulkanAndroidNativeBufferStructureType,
"vk_android_native_buffer_structure_type")
def addGuestEncoderModule(
self, basename, extraHeader="", extraImpl="", useNamespace=True, headerOnly=False,
suppressFeatureGuards=False, moduleName=None, suppressVulkanHeaders=False, implOnly=False):
if not os.path.exists(self.guest_abs_encoder_destination):
print("Path [%s] not found (guest encoder path), skipping" % self.guest_abs_encoder_destination)
return
self.addCppModule(self.guest_encoder_tag, basename, extraHeader=extraHeader,
extraImpl=extraImpl, customAbsDir=self.guest_abs_encoder_destination,
useNamespace=useNamespace, implOnly=implOnly, headerOnly=headerOnly,
suppressFeatureGuards=suppressFeatureGuards, moduleName=moduleName,
suppressVulkanHeaders=suppressVulkanHeaders)
def addHostModule(
self, basename, extraHeader="", extraImpl="", useNamespace=True, implOnly=False,
suppress=False, headerOnly=False, suppressFeatureGuards=False, moduleName=None,
suppressVulkanHeaders=False):
if not os.path.exists(self.host_abs_decoder_destination):
print("Path [%s] not found (host encoder path), skipping" %
self.host_abs_decoder_destination)
return
if not suppressVulkanHeaders:
extraHeader = self.hostCommonExtraVulkanHeaders + '\n' + extraHeader
self.addCppModule(
self.host_tag, basename, extraHeader=extraHeader, extraImpl=extraImpl,
customAbsDir=self.host_abs_decoder_destination, useNamespace=useNamespace,
implOnly=implOnly, suppress=suppress, headerOnly=headerOnly,
suppressFeatureGuards=suppressFeatureGuards, moduleName=moduleName,
suppressVulkanHeaders=suppressVulkanHeaders)
def addModule(self, module, moduleName=None, extraHeader=None):
if moduleName is None:
moduleName = module.basename
self.moduleList.append(moduleName)
if extraHeader:
module.preamble = extraHeader
self.modules[moduleName] = module
def addCppModule(
self, directory, basename, extraHeader="", extraImpl="", customAbsDir=None,
useNamespace=True, implOnly=False, suppress=False, headerOnly=False,
suppressFeatureGuards=False, moduleName=None, suppressVulkanHeaders=False):
module = cereal.Module(
directory, basename, customAbsDir=customAbsDir, suppress=suppress, implOnly=implOnly,
headerOnly=headerOnly, suppressFeatureGuards=suppressFeatureGuards)
self.addModule(module, moduleName=moduleName)
module.headerPreamble = copyrightHeader
module.headerPreamble += \
autogeneratedHeaderTemplate % \
(basename, "(header) generated by %s" % banner_command(sys.argv))
module.headerPreamble += "#pragma once\n"
if (not suppressVulkanHeaders):
module.headerPreamble += "#include <vulkan/vulkan.h>\n"
module.headerPreamble += '#include "vulkan_gfxstream.h"\n'
module.headerPreamble += '#include "vk_android_native_buffer_gfxstream.h"\n'
module.headerPreamble += extraHeader + '\n'
usedNamespaceBegin = namespaceHostBegin if self.cerealFlags & VK_CEREAL_FLAG_HOST else namespaceBegin
usedNamespaceEnd = namespaceHostEnd if self.cerealFlags & VK_CEREAL_FLAG_HOST else namespaceEnd
if useNamespace:
module.headerPreamble += usedNamespaceBegin
module.implPreamble = copyrightHeader
module.implPreamble += \
autogeneratedHeaderTemplate % \
(basename, "(impl) generated by %s" % \
banner_command(sys.argv))
if not implOnly:
module.implPreamble += '\n#include "%s.h"' % \
(basename)
module.implPreamble += extraImpl
if useNamespace:
module.implPreamble += usedNamespaceBegin
module.implPostamble += usedNamespaceEnd
module.headerPostamble += usedNamespaceEnd
def addWrapper(self, moduleType, moduleName, **kwargs):
if moduleName not in self.modules:
print(f'Unknown module: {moduleName}. All known modules are: {", ".join(self.modules)}.')
return
self.wrappers.append(
(moduleType(
self.modules[moduleName],
self.typeInfo, **kwargs),
moduleName)
)
def forEachModule(self, func):
for moduleName in self.moduleList:
func(self.modules[moduleName])
def forEachWrapper(self, func, supportedModules):
for wrapper in self.wrappers:
if supportedModules is None:
func(wrapper[0])
elif wrapper[1] in supportedModules:
func(wrapper[0])
## Overrides####################################################################
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
self.forEachModule(lambda m: m.begin(self.genOpts.directory))
self.forEachWrapper(lambda w: w.onBegin(), None)
def endFile(self):
OutputGenerator.endFile(self)
self.typeInfo.onEnd()
self.forEachWrapper(lambda w: w.onEnd(), None)
self.forEachModule(lambda m: m.end())
def beginFeature(self, interface, emit):
# Start processing in superclass
OutputGenerator.beginFeature(self, interface, emit)
self.featureSupported = False
self.supportedModules = None
for supportedFeature in SUPPORTED_FEATURES:
if self.featureName == supportedFeature:
self.featureSupported = True
if self.featureSupported == False and UNSUPPORTED_FEATURE_MODULES:
self.featureSupported = True
self.supportedModules = UNSUPPORTED_FEATURE_MODULES
elif self.featureSupported == False:
return
else:
self.supportedModules = SUPPORTED_MODULES.get(self.featureName)
self.typeInfo.onBeginFeature(self.featureName, self.featureType)
self.forEachModule(
lambda m: m.appendHeader("#ifdef %s\n" % self.featureName)
if isinstance(m, cereal.Module) and not m.suppressFeatureGuards else None)
self.forEachModule(
lambda m: m.appendImpl("#ifdef %s\n" % self.featureName)
if isinstance(m, cereal.Module) and not m.suppressFeatureGuards else None)
self.forEachWrapper(lambda w: w.onBeginFeature(self.featureName, self.featureType), self.supportedModules)
# functable needs to understand the feature type (device vs instance) of each cmd
for features in interface.findall('require'):
for c in features.findall('command'):
self.forEachWrapper(lambda w: w.onFeatureNewCmd(c.get('name')), self.supportedModules)
def endFeature(self):
# Finish processing in superclass
OutputGenerator.endFeature(self)
if self.featureSupported == False:
return
self.featureSupported = False
self.typeInfo.onEndFeature()
self.forEachModule(lambda m: m.appendHeader("#endif\n") if isinstance(
m, cereal.Module) and not m.suppressFeatureGuards else None)
self.forEachModule(lambda m: m.appendImpl("#endif\n") if isinstance(
m, cereal.Module) and not m.suppressFeatureGuards else None)
self.forEachWrapper(lambda w: w.onEndFeature(), self.supportedModules)
def genType(self, typeinfo: TypeInfo, name, alias):
OutputGenerator.genType(self, typeinfo, name, alias)
# Maybe this check can be removed if we refactor other things inside
# the cereal subdirectory.
if self.featureSupported == False and name in REQUIRED_TYPES:
self.typeInfo.onGenType(typeinfo, name, alias)
return
if self.featureSupported == False:
return
self.typeInfo.onGenType(typeinfo, name, alias)
self.forEachWrapper(lambda w: w.onGenType(typeinfo, name, alias), self.supportedModules)
def genStruct(self, typeinfo, typeName, alias):
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
if self.featureSupported == False:
return
self.typeInfo.onGenStruct(typeinfo, typeName, alias)
self.forEachWrapper(lambda w: w.onGenStruct(typeinfo, typeName, alias), self.supportedModules)
def genGroup(self, groupinfo: GroupInfo, groupName, alias = None):
OutputGenerator.genGroup(self, groupinfo, groupName, alias)
if self.featureSupported == False:
return
self.typeInfo.onGenGroup(groupinfo, groupName, alias)
self.forEachWrapper(lambda w: w.onGenGroup(groupinfo, groupName, alias), self.supportedModules)
def genEnum(self, enuminfo: EnumInfo, name, alias):
OutputGenerator.genEnum(self, enuminfo, name, alias)
if self.featureSupported == False:
return
self.typeInfo.onGenEnum(enuminfo, name, alias)
self.forEachWrapper(lambda w: w.onGenEnum(enuminfo, name, alias), self.supportedModules)
def genCmd(self, cmdinfo, name, alias):
OutputGenerator.genCmd(self, cmdinfo, name, alias)
if self.featureSupported == False:
return
self.typeInfo.onGenCmd(cmdinfo, name, alias)
self.forEachWrapper(lambda w: w.onGenCmd(cmdinfo, name, alias), self.supportedModules)
+292
View File
@@ -0,0 +1,292 @@
#!/usr/bin/python3 -i
#
# Copyright 2013-2023 The Khronos Group Inc.
# Copyright 2023-2024 Google Inc.
#
# SPDX-License-Identifier: Apache-2.0
import os
import re
from generator import (GeneratorOptions,
MissingGeneratorOptionsError, MissingRegistryError,
OutputGenerator, noneStr, regSortFeatures, write)
class CGeneratorOptions(GeneratorOptions):
"""CGeneratorOptions - subclass of GeneratorOptions.
Adds options used by COutputGenerator objects during C language header
generation."""
def __init__(self,
prefixText='',
apientry='',
apientryp='',
alignFuncParam=0,
**kwargs
):
"""Constructor.
Additional parameters beyond parent class:
- prefixText - list of strings to prefix generated header with
(usually a copyright statement + calling convention macros)
- apientry - string to use for the calling convention macro,
in typedefs, such as APIENTRY
- apientryp - string to use for the calling convention macro
in function pointer typedefs, such as APIENTRYP
- alignFuncParam - if nonzero and parameters are being put on a
separate line, align parameter names at the specified column"""
GeneratorOptions.__init__(self, **kwargs)
self.prefixText = prefixText
"""list of strings to prefix generated header with (usually a copyright statement + calling convention macros)."""
self.apientry = apientry
"""string to use for the calling convention macro, in typedefs, such as APIENTRY."""
self.apientryp = apientryp
"""string to use for the calling convention macro in function pointer typedefs, such as APIENTRYP."""
self.alignFuncParam = alignFuncParam
"""if nonzero and parameters are being put on a separate line, align parameter names at the specified column"""
class COutputGenerator(OutputGenerator):
"""Generates C-language API interfaces."""
# This is an ordered list of sections in the header file.
TYPE_SECTIONS = ['include', 'define', 'basetype', 'handle', 'enum',
'group', 'bitmask', 'funcpointer', 'struct']
ALL_SECTIONS = TYPE_SECTIONS + ['commandPointer', 'command']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Internal state - accumulators for different inner block text
self.sections = {section: [] for section in self.ALL_SECTIONS}
self.feature_not_empty = False
def beginFile(self, genOpts):
OutputGenerator.beginFile(self, genOpts)
if self.genOpts is None:
raise MissingGeneratorOptionsError()
# User-supplied prefix text, if any (list of strings)
if genOpts.prefixText:
for s in genOpts.prefixText:
write(s, file=self.outFile)
# C++ extern wrapper - after prefix lines so they can add includes.
self.newline()
write('#ifdef __cplusplus', file=self.outFile)
write('extern "C" {', file=self.outFile)
write('#endif', file=self.outFile)
self.newline()
def endFile(self):
# C-specific
# Finish C++ wrapper and multiple inclusion protection
if self.genOpts is None:
raise MissingGeneratorOptionsError()
self.newline()
write('#ifdef __cplusplus', file=self.outFile)
write('}', file=self.outFile)
write('#endif', file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFile(self)
def beginFeature(self, interface, emit):
# Start processing in superclass
OutputGenerator.beginFeature(self, interface, emit)
# C-specific
# Accumulate includes, defines, types, enums, function pointer typedefs,
# end function prototypes separately for this feature. They are only
# printed in endFeature().
self.sections = {section: [] for section in self.ALL_SECTIONS}
self.feature_not_empty = False
def endFeature(self):
"Actually write the interface to the output file."
# C-specific
if self.emit:
if self.feature_not_empty:
if self.genOpts is None:
raise MissingGeneratorOptionsError()
is_core = self.featureName and self.featureName.startswith('VK_VERSION_')
self.newline()
# Generate warning of possible use in IDEs
write(f'// {self.featureName} is a preprocessor guard. Do not pass it to API calls.', file=self.outFile)
write('#define', self.featureName, '1', file=self.outFile)
for section in self.TYPE_SECTIONS:
contents = self.sections[section]
if contents:
write('\n'.join(contents), file=self.outFile)
if self.sections['commandPointer']:
write('\n'.join(self.sections['commandPointer']), file=self.outFile)
self.newline()
if self.sections['command']:
write('\n'.join(self.sections['command']), end='', file=self.outFile)
# Finish processing in superclass
OutputGenerator.endFeature(self)
def appendSection(self, section, text):
"Append a definition to the specified section"
if section is None:
exit(1)
self.sections[section].append(text)
self.feature_not_empty = True
def genType(self, typeinfo, name, alias):
"Generate type."
OutputGenerator.genType(self, typeinfo, name, alias)
typeElem = typeinfo.elem
# Vulkan:
# Determine the category of the type, and the type section to add
# its definition to.
# 'funcpointer' is added to the 'struct' section as a workaround for
# internal issue #877, since structures and function pointer types
# can have cross-dependencies.
category = typeElem.get('category')
if category == 'funcpointer':
section = 'struct'
else:
section = category
if category in ('struct', 'union'):
# If the type is a struct type, generate it using the
# special-purpose generator.
self.genStruct(typeinfo, name, alias)
else:
if self.genOpts is None:
raise MissingGeneratorOptionsError()
# Replace <apientry /> tags with an APIENTRY-style string
# (from self.genOpts). Copy other text through unchanged.
# If the resulting text is an empty string, do not emit it.
body = noneStr(typeElem.text)
for elem in typeElem:
if elem.tag == 'apientry':
body += self.genOpts.apientry + noneStr(elem.tail)
else:
body += noneStr(elem.text) + noneStr(elem.tail)
if body:
# Add extra newline after multi-line entries.
if '\n' in body[0:-1]:
body += '\n'
self.appendSection(section, body)
def genProtectString(self, protect_str):
"""Generate protection string.
Protection strings are the strings defining the OS/Platform/Graphics
requirements for a given API command. When generating the
language header files, we need to make sure the items specific to a
graphics API or OS platform are properly wrapped in #ifs."""
protect_if_str = ''
protect_end_str = ''
if not protect_str:
return (protect_if_str, protect_end_str)
if ',' in protect_str:
protect_list = protect_str.split(',')
protect_defs = ('defined(%s)' % d for d in protect_list)
protect_def_str = ' && '.join(protect_defs)
protect_if_str = '#if %s\n' % protect_def_str
protect_end_str = '#endif // %s\n' % protect_def_str
else:
protect_if_str = '#ifdef %s\n' % protect_str
protect_end_str = '#endif // %s\n' % protect_str
return (protect_if_str, protect_end_str)
def genStruct(self, typeinfo, typeName, alias):
"""Generate struct (e.g. C "struct" type).
This is a special case of the <type> tag where the contents are
interpreted as a set of <member> tags instead of freeform C
C type declarations. The <member> tags are just like <param>
tags - they are a declaration of a struct or union member.
Only simple member declarations are supported (no nested
structs etc.)
If alias is not None, then this struct aliases another; just
generate a typedef of that alias."""
OutputGenerator.genStruct(self, typeinfo, typeName, alias)
if self.genOpts is None:
raise MissingGeneratorOptionsError()
typeElem = typeinfo.elem
if alias:
body = 'typedef ' + alias + ' ' + typeName + ';\n'
else:
body = ''
(protect_begin, protect_end) = self.genProtectString(typeElem.get('protect'))
if protect_begin:
body += protect_begin
body += 'typedef ' + typeElem.get('category')
body += ' ' + typeName + ' {\n'
targetLen = self.getMaxCParamTypeLength(typeinfo)
for member in typeElem.findall('.//member'):
body += self.makeCParamDecl(member, targetLen + 4)
body += ';\n'
body += '} ' + typeName + ';\n'
if protect_end:
body += protect_end
self.appendSection('struct', body)
def genGroup(self, groupinfo, groupName, alias=None):
"""Generate groups (e.g. C "enum" type).
These are concatenated together with other types.
If alias is not None, it is the name of another group type
which aliases this type; just generate that alias."""
OutputGenerator.genGroup(self, groupinfo, groupName, alias)
groupElem = groupinfo.elem
# After either enumerated type or alias paths, add the declaration
# to the appropriate section for the group being defined.
if groupElem.get('type') == 'bitmask':
section = 'bitmask'
else:
section = 'group'
if alias:
# If the group name is aliased, just emit a typedef declaration
# for the alias.
body = 'typedef ' + alias + ' ' + groupName + ';\n'
self.appendSection(section, body)
def genEnum(self, enuminfo, name, alias):
"""Generate the C declaration for a constant (a single <enum> value).
<enum> tags may specify their values in several ways, but are usually
just integers."""
OutputGenerator.genEnum(self, enuminfo, name, alias)
body = self.buildConstantCDecl(enuminfo, name, alias)
self.appendSection('enum', body)
def genCmd(self, cmdinfo, name, alias):
"Command generation"
OutputGenerator.genCmd(self, cmdinfo, name, alias)
if self.genOpts is None:
raise MissingGeneratorOptionsError()
prefix = ''
decls = self.makeCDecls(cmdinfo.elem)
self.appendSection('command', prefix + decls[0] + '\n')
self.appendSection('commandPointer', decls[1])
+803
View File
@@ -0,0 +1,803 @@
#!/usr/bin/python3 -i
#
# Copyright 2013-2023 The Khronos Group Inc.
# Copyright 2023-2024 Google Inc.
#
# SPDX-License-Identifier: Apache-2.0
"""Base class for source/header/doc generators, as well as some utility functions."""
from __future__ import unicode_literals
import io
import os
import re
import shutil
import sys
import tempfile
try:
from pathlib import Path
except ImportError:
from pathlib2 import Path # type: ignore
CATEGORIES_REQUIRING_VALIDATION = set(('handle',
'enum',
'bitmask',
'basetype',
None))
# These are basic C types pulled in via openxr_platform_defines.h
TYPES_KNOWN_ALWAYS_VALID = set(('char',
'float',
'int8_t', 'uint8_t',
'int16_t', 'uint16_t',
'int32_t', 'uint32_t',
'int64_t', 'uint64_t',
'size_t',
'intptr_t', 'uintptr_t',
'int',
))
def getElemName(elem, default=None):
"""Get the name associated with an element, either a name child or name attribute."""
name_elem = elem.find('name')
if name_elem is not None:
return name_elem.text
# Fallback if there is no child.
return elem.get('name', default)
def getElemType(elem, default=None):
"""Get the type associated with an element, either a type child or type attribute."""
type_elem = elem.find('type')
if type_elem is not None:
return type_elem.text
# Fallback if there is no child.
return elem.get('type', default)
def write(*args, **kwargs):
file = kwargs.pop('file', sys.stdout)
end = kwargs.pop('end', '\n')
file.write(' '.join(str(arg) for arg in args))
file.write(end)
def category_requires_validation(category):
"""Return True if the given type 'category' always requires validation.
Defaults to a reasonable implementation.
May override."""
return category in CATEGORIES_REQUIRING_VALIDATION
def type_always_valid(typename):
"""Return True if the given type name is always valid (never requires validation).
This is for things like integers.
Defaults to a reasonable implementation.
May override."""
return typename in TYPES_KNOWN_ALWAYS_VALID
def noneStr(s):
"""Return string argument, or "" if argument is None.
Used in converting etree Elements into text.
s - string to convert"""
if s:
return s
return ""
def regSortCategoryKey(feature):
"""Sort key for regSortFeatures.
Sorts by category of the feature name string:
- Core API features (those defined with a `<feature>` tag)
- (sort VKSC after VK - this is Vulkan-specific)
- ARB/KHR/OES (Khronos extensions)
- other (EXT/vendor extensions)"""
if feature.elem.tag == 'feature':
if feature.name.startswith('VKSC'):
return 0.5
else:
return 0
if (feature.category == 'ARB'
or feature.category == 'KHR'
or feature.category == 'OES'):
return 1
return 2
def regSortOrderKey(feature):
"""Sort key for regSortFeatures - key is the sortorder attribute."""
return feature.sortorder
def regSortNameKey(feature):
"""Sort key for regSortFeatures - key is the extension name."""
return feature.name
def regSortFeatureVersionKey(feature):
"""Sort key for regSortFeatures - key is the feature version.
`<extension>` elements all have version number 0."""
return float(feature.versionNumber)
def regSortExtensionNumberKey(feature):
"""Sort key for regSortFeatures - key is the extension number.
`<feature>` elements all have extension number 0."""
return int(feature.number)
def regSortFeatures(featureList):
"""Default sort procedure for features.
- Sorts by explicit sort order (default 0) relative to other features
- then by feature category ('feature' or 'extension'),
- then by version number (for features)
- then by extension number (for extensions)"""
featureList.sort(key=regSortExtensionNumberKey)
featureList.sort(key=regSortFeatureVersionKey)
featureList.sort(key=regSortCategoryKey)
featureList.sort(key=regSortOrderKey)
class MissingGeneratorOptionsError(RuntimeError):
"""Error raised when a Generator tries to do something that requires GeneratorOptions but it is None."""
def __init__(self, msg=None):
full_msg = 'Missing generator options object self.genOpts'
if msg:
full_msg += ': ' + msg
super().__init__(full_msg)
class MissingRegistryError(RuntimeError):
"""Error raised when a Generator tries to do something that requires a Registry object but it is None."""
def __init__(self, msg=None):
full_msg = 'Missing Registry object self.registry'
if msg:
full_msg += ': ' + msg
super().__init__(full_msg)
class GeneratorOptions:
"""Base class for options used during header/documentation production.
These options are target language independent, and used by
Registry.apiGen() and by base OutputGenerator objects."""
def __init__(self,
filename=None,
directory='.',
versions='.*',
emitversions='.*',
addExtensions=None,
emitExtensions=None,
sortProcedure=regSortFeatures,
):
"""Constructor.
Arguments:
an object that implements ConventionsBase
- filename - basename of file to generate, or None to write to stdout.
- directory - directory in which to generate filename
- versions - regex matching API versions to process interfaces for.
Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions.
- emitversions - regex matching API versions to actually emit
interfaces for (though all requested versions are considered
when deciding which interfaces to generate). For GL 4.3 glext.h,
this might be `'1[.][2-5]|[2-4][.][0-9]'`.
- addExtensions - regex matching names of additional extensions
to include. Defaults to None.
- emitExtensions - regex matching names of extensions to actually emit
interfaces for (though all requested versions are considered when
deciding which interfaces to generate). Defaults to None.
- sortProcedure - takes a list of FeatureInfo objects and sorts
them in place to a preferred order in the generated output.
Default is
- core API versions
- Khronos (ARB/KHR/OES) extensions
- All other extensions
- By core API version number or extension number in each group.
The regex patterns can be None or empty, in which case they match
nothing."""
self.filename = filename
"basename of file to generate, or None to write to stdout."
self.directory = directory
"directory in which to generate filename"
self.versions = self.emptyRegex(versions)
"""regex matching API versions to process interfaces for.
Normally `'.*'` or `'[0-9][.][0-9]'` to match all defined versions."""
self.emitversions = self.emptyRegex(emitversions)
"""regex matching API versions to actually emit
interfaces for (though all requested versions are considered
when deciding which interfaces to generate). For GL 4.3 glext.h,
this might be `'1[.][2-5]|[2-4][.][0-9]'`."""
self.addExtensions = self.emptyRegex(addExtensions)
"""regex matching names of additional extensions
to include. Defaults to None."""
self.emitExtensions = self.emptyRegex(emitExtensions)
"""regex matching names of extensions to actually emit
interfaces for (though all requested versions are considered when
deciding which interfaces to generate)."""
self.sortProcedure = sortProcedure
"""takes a list of FeatureInfo objects and sorts
them in place to a preferred order in the generated output.
Default is core API versions, ARB/KHR/OES extensions, all
other extensions, alphabetically within each group."""
self.registry = None
"""Populated later with the registry object."""
def emptyRegex(self, pat):
"""Substitute a regular expression which matches no version
or extension names for None or the empty string."""
if not pat:
return '_nomatch_^'
return pat
class OutputGenerator:
"""Generate specified API interfaces in a specific style, such as a C header.
Base class for generating API interfaces.
Manages basic logic, logging, and output file control.
Derived classes actually generate formatted output.
"""
# categoryToPath - map XML 'category' to include file directory name
categoryToPath = {
'bitmask': 'flags',
'enum': 'enums',
'funcpointer': 'funcpointers',
'handle': 'handles',
'define': 'defines',
'basetype': 'basetypes',
}
def __init__(self, errFile=sys.stderr, warnFile=sys.stderr, diagFile=sys.stdout):
"""Constructor
- errFile, warnFile, diagFile - file handles to write errors,
warnings, diagnostics to. May be None to not write."""
self.outFile = None
self.errFile = errFile
self.warnFile = warnFile
self.diagFile = diagFile
# Internal state
self.featureName = None
"""The current feature name being generated."""
self.featureType = None
"""The current feature type being generated."""
self.genOpts = None
"""The GeneratorOptions subclass instance."""
self.registry = None
"""The specification registry object."""
self.featureDictionary = {}
"""The dictionary of dictionaries of API features."""
# Used for extension enum value generation
self.extBase = 1000000000
self.extBlockSize = 1000
self.madeDirs = {}
# API dictionary, which may be loaded by the beginFile method of
# derived generators.
self.apidict = None
def enumToValue(self, elem, needsNum, bitwidth = 32,
forceSuffix = False, parent_for_alias_dereference=None):
"""Parse and convert an `<enum>` tag into a value.
- elem - <enum> Element
- needsNum - generate a numeric representation of the element value
- bitwidth - size of the numeric representation in bits (32 or 64)
- forceSuffix - if True, always use a 'U' / 'ULL' suffix on integers
- parent_for_alias_dereference - if not None, an Element containing
the parent of elem, used to look for elements this is an alias of
Returns a list:
- first element - integer representation of the value, or None
if needsNum is False. The value must be a legal number
if needsNum is True.
- second element - string representation of the value
There are several possible representations of values.
- A 'value' attribute simply contains the value.
- A 'bitpos' attribute defines a value by specifying the bit
position which is set in that value.
- An 'offset','extbase','extends' triplet specifies a value
as an offset to a base value defined by the specified
'extbase' extension name, which is then cast to the
typename specified by 'extends'. This requires probing
the registry database, and imbeds knowledge of the
API extension enum scheme in this function.
- An 'alias' attribute contains the name of another enum
which this is an alias of. The other enum must be
declared first when emitting this enum."""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
name = elem.get('name')
numVal = None
if 'value' in elem.keys():
value = elem.get('value')
# print('About to translate value =', value, 'type =', type(value))
if needsNum:
numVal = int(value, 0)
# If there is a non-integer, numeric 'type' attribute (e.g. 'u' or
# 'ull'), append it to the string value.
# t = enuminfo.elem.get('type')
# if t is not None and t != '' and t != 'i' and t != 's':
# value += enuminfo.type
if forceSuffix:
if bitwidth == 64:
value = value + 'ULL'
else:
value = value + 'U'
return [numVal, value]
if 'bitpos' in elem.keys():
value = elem.get('bitpos')
bitpos = int(value, 0)
numVal = 1 << bitpos
value = '0x%08x' % numVal
if bitwidth == 64 or bitpos >= 32:
value = value + 'ULL'
elif forceSuffix:
value = value + 'U'
return [numVal, value]
if 'offset' in elem.keys():
# Obtain values in the mapping from the attributes
enumNegative = False
offset = int(elem.get('offset'), 0)
extnumber = int(elem.get('extnumber'), 0)
extends = elem.get('extends')
if 'dir' in elem.keys():
enumNegative = True
# Now determine the actual enumerant value, as defined
# in the "Layers and Extensions" appendix of the spec.
numVal = self.extBase + (extnumber - 1) * self.extBlockSize + offset
if enumNegative:
numVal *= -1
value = '%d' % numVal
# More logic needed!
return [numVal, value]
if 'alias' in elem.keys():
alias_of = elem.get('alias')
if parent_for_alias_dereference is None:
return (None, alias_of)
siblings = parent_for_alias_dereference.findall('enum')
for sib in siblings:
sib_name = sib.get('name')
if sib_name == alias_of:
return self.enumToValue(sib, needsNum)
raise RuntimeError("Could not find the aliased enum value")
return [None, None]
def buildConstantCDecl(self, enuminfo, name, alias):
"""Generate the C declaration for a constant (a single <enum>
value).
<enum> tags may specify their values in several ways, but are
usually just integers or floating-point numbers."""
(_, strVal) = self.enumToValue(enuminfo.elem, False)
if enuminfo.elem.get('type') and not alias:
# Generate e.g.: #define x (~0ULL)
typeStr = enuminfo.elem.get('type');
invert = '~' in strVal
paren = '(' in strVal
number = strVal.strip("()~UL")
if typeStr != "float":
if typeStr == "uint64_t":
number += 'ULL'
else:
number += 'U'
strVal = "~" if invert else ""
strVal += number
if paren:
strVal = "(" + strVal + ")";
body = '#define ' + name.ljust(33) + ' ' + strVal;
else:
body = '#define ' + name.ljust(33) + ' ' + strVal
return body
def beginFile(self, genOpts):
"""Start a new interface file
- genOpts - GeneratorOptions controlling what is generated and how"""
self.genOpts = genOpts
if self.genOpts is None:
raise MissingGeneratorOptionsError()
# Open a temporary file for accumulating output.
if self.genOpts.filename is not None:
self.outFile = tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', newline='\n', delete=False)
else:
self.outFile = sys.stdout
def endFile(self):
if self.errFile:
self.errFile.flush()
if self.warnFile:
self.warnFile.flush()
if self.diagFile:
self.diagFile.flush()
if self.outFile:
self.outFile.flush()
if self.outFile != sys.stdout and self.outFile != sys.stderr:
self.outFile.close()
if self.genOpts is None:
raise MissingGeneratorOptionsError()
# On successfully generating output, move the temporary file to the
# target file.
if self.genOpts.filename is not None:
if sys.platform == 'win32':
directory = Path(self.genOpts.directory)
if not Path.exists(directory):
os.makedirs(directory)
shutil.copy(self.outFile.name, self.genOpts.directory + '/' + self.genOpts.filename)
os.remove(self.outFile.name)
self.genOpts = None
def beginFeature(self, interface, emit):
"""Write interface for a feature and tag generated features as having been done.
- interface - element for the `<version>` / `<extension>` to generate
- emit - actually write to the header only when True"""
self.emit = emit
self.featureName = interface.get('name')
self.featureType = interface.get('type')
def endFeature(self):
"""Finish an interface file, closing it when done.
Derived classes responsible for emitting feature"""
self.featureName = None
self.featureType = None
def validateFeature(self, featureType, featureName):
"""Validate we are generating something only inside a `<feature>` tag"""
if self.featureName is None:
raise UserWarning('Attempt to generate', featureType,
featureName, 'when not in feature')
def genType(self, typeinfo, name, alias):
"""Generate interface for a type
- typeinfo - TypeInfo for a type
Extend to generate as desired in your derived class."""
self.validateFeature('type', name)
def genStruct(self, typeinfo, typeName, alias):
"""Generate interface for a C "struct" type.
- typeinfo - TypeInfo for a type interpreted as a struct
Extend to generate as desired in your derived class."""
self.validateFeature('struct', typeName)
# The mixed-mode <member> tags may contain no-op <comment> tags.
# It is convenient to remove them here where all output generators
# will benefit.
for member in typeinfo.elem.findall('.//member'):
for comment in member.findall('comment'):
member.remove(comment)
def genGroup(self, groupinfo, groupName, alias):
"""Generate interface for a group of enums (C "enum")
- groupinfo - GroupInfo for a group.
Extend to generate as desired in your derived class."""
self.validateFeature('group', groupName)
def genEnum(self, enuminfo, typeName, alias):
"""Generate interface for an enum (constant).
- enuminfo - EnumInfo for an enum
- name - enum name
Extend to generate as desired in your derived class."""
self.validateFeature('enum', typeName)
def genCmd(self, cmd, cmdinfo, alias):
"""Generate interface for a command.
- cmdinfo - CmdInfo for a command
Extend to generate as desired in your derived class."""
self.validateFeature('command', cmdinfo)
def makeProtoName(self, name, tail):
"""Turn a `<proto>` `<name>` into C-language prototype
and typedef declarations for that name.
- name - contents of `<name>` tag
- tail - whatever text follows that tag in the Element"""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
return self.genOpts.apientry + name + tail
def makeTypedefName(self, name, tail):
"""Make the function-pointer typedef name for a command."""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
return '(' + self.genOpts.apientryp + 'PFN_' + name + tail + ')'
def makeCParamDecl(self, param, aligncol):
"""Return a string which is an indented, formatted
declaration for a `<param>` or `<member>` block (e.g. function parameter
or structure/union member).
- param - Element (`<param>` or `<member>`) to format
- aligncol - if non-zero, attempt to align the nested `<name>` element
at this column"""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
indent = ' '
paramdecl = indent
prefix = noneStr(param.text)
for elem in param:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if elem.tag == 'name' and aligncol > 0:
# Align at specified column, if possible
paramdecl = paramdecl.rstrip()
oldLen = len(paramdecl)
# This works around a problem where very long type names -
# longer than the alignment column - would run into the tail
# text.
paramdecl = paramdecl.ljust(aligncol - 1) + ' '
newLen = len(paramdecl)
paramdecl += prefix + text + tail
# Clear prefix for subsequent iterations
prefix = ''
paramdecl = paramdecl + prefix
if aligncol == 0:
# Squeeze out multiple spaces other than the indentation
paramdecl = indent + ' '.join(paramdecl.split())
return paramdecl
def getCParamTypeLength(self, param):
"""Return the length of the type field is an indented, formatted
declaration for a `<param>` or `<member>` block (e.g. function parameter
or structure/union member).
- param - Element (`<param>` or `<member>`) to identify"""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
# Allow for missing <name> tag
newLen = 0
paramdecl = ' ' + noneStr(param.text)
for elem in param:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if elem.tag == 'name':
# Align at specified column, if possible
newLen = len(paramdecl.rstrip())
paramdecl += text + tail
return newLen
def getMaxCParamTypeLength(self, info):
"""Return the length of the longest type field for a member/parameter.
- info - TypeInfo or CommandInfo.
"""
lengths = (self.getCParamTypeLength(member)
for member in info.getMembers())
return max(lengths)
def getTypeCategory(self, typename):
"""Get the category of a type."""
if self.registry is None:
raise MissingRegistryError()
info = self.registry.typedict.get(typename)
if info is None:
return None
elem = info.elem
if elem is not None:
return elem.get('category')
return None
def isStructAlwaysValid(self, structname):
"""Try to do check if a structure is always considered valid (i.e. there is no rules to its acceptance)."""
# A conventions object is required for this call.
if self.registry is None:
raise MissingRegistryError()
if type_always_valid(structname):
return True
category = self.getTypeCategory(structname)
if category_requires_validation(category):
return False
info = self.registry.typedict.get(structname)
members = info.getMembers()
for member in members:
member_name = getElemName(member)
if member_name in ('sType', 'pNext'):
return False
if member.get('noautovalidity'):
return False
member_type = getElemType(member)
if member_type in ('void', 'char') or self.paramIsArray(member) or self.paramIsPointer(member):
return False
if type_always_valid(member_type):
continue
member_category = self.getTypeCategory(member_type)
if category_requires_validation(member_category):
return False
if member_category in ('struct', 'union'):
if self.isStructAlwaysValid(member_type) is False:
return False
return True
def paramIsArray(self, param):
"""Check if the parameter passed in is a pointer to an array.
param the XML information for the param
"""
return param.get('len') is not None
def paramIsPointer(self, param):
"""Check if the parameter passed in is a pointer.
param the XML information for the param
"""
tail = param.find('type').tail
return tail is not None and '*' in tail
def isEnumRequired(self, elem):
"""Return True if this `<enum>` element is
required, False otherwise
- elem - `<enum>` element to test"""
required = elem.get('required') is not None
return required
# @@@ This code is overridden by equivalent code now run in
# @@@ Registry.generateFeature
required = False
extname = elem.get('extname')
if extname is not None:
# 'supported' attribute was injected when the <enum> element was
# moved into the <enums> group in Registry.parseTree()
if 'vulkan' == elem.get('supported'):
required = True
elif re.match(self.genOpts.addExtensions, extname) is not None:
required = True
elif elem.get('version') is not None:
required = re.match(self.genOpts.emitversions, elem.get('version')) is not None
else:
required = True
return required
def makeCDecls(self, cmd):
"""Return C prototype and function pointer typedef for a
`<command>` Element, as a two-element list of strings.
- cmd - Element containing a `<command>` tag"""
if self.genOpts is None:
raise MissingGeneratorOptionsError()
proto = cmd.find('proto')
params = cmd.findall('param')
# Begin accumulating prototype and typedef strings
pdecl = 'VKAPI_ATTR '
tdecl = 'typedef '
# Insert the function return type/name.
# For prototypes, add APIENTRY macro before the name
# For typedefs, add (APIENTRY *<name>) around the name and
# use the PFN_cmdnameproc naming convention.
# Done by walking the tree for <proto> element by element.
# etree has elem.text followed by (elem[i], elem[i].tail)
# for each child element and any following text
# Leading text
pdecl += noneStr(proto.text)
tdecl += noneStr(proto.text)
# For each child element, if it is a <name> wrap in appropriate
# declaration. Otherwise append its contents and tail contents.
for elem in proto:
text = noneStr(elem.text)
tail = noneStr(elem.tail)
if elem.tag == 'name':
pdecl += self.makeProtoName(text, tail)
tdecl += self.makeTypedefName(text, tail)
else:
pdecl += text + tail
tdecl += text + tail
if self.genOpts.alignFuncParam == 0:
# Squeeze out multiple spaces - there is no indentation
pdecl = ' '.join(pdecl.split())
tdecl = ' '.join(tdecl.split())
# Now add the parameter declaration list, which is identical
# for prototypes and typedefs. Concatenate all the text from
# a <param> node without the tags. No tree walking required
# since all tags are ignored.
# Uses: self.indentFuncProto
# self.indentFuncPointer
# self.alignFuncParam
n = len(params)
# Indented parameters
if n > 0:
indentdecl = '(\n'
indentdecl += ',\n'.join(self.makeCParamDecl(p, self.genOpts.alignFuncParam)
for p in params)
indentdecl += ');'
else:
indentdecl = '(void);'
# Non-indented parameters
paramdecl = '('
if n > 0:
paramnames = []
paramnames = (''.join(t for t in p.itertext())
for p in params)
paramdecl += ', '.join(paramnames)
else:
paramdecl += 'void'
paramdecl += ");"
return [pdecl + indentdecl, tdecl + paramdecl]
def newline(self):
"""Print a newline to the output file (utility function)"""
write('', file=self.outFile)
def setRegistry(self, registry):
self.registry = registry
+236
View File
@@ -0,0 +1,236 @@
#!/usr/bin/python3
#
# Copyright 2013-2023 The Khronos Group Inc.
# Copyright 2023-2024 Google Inc.
#
# SPDX-License-Identifier: Apache-2.0
import argparse
import os
import re
import sys
import xml.etree.ElementTree as etree
sys.path.append(os.path.abspath(os.path.dirname(__file__)))
from cgenerator import CGeneratorOptions, COutputGenerator
from generator import write, regSortFeatures
from reg import Registry
# gfxstream + cereal modules
from cerealgenerator import CerealGenerator, SUPPORTED_FEATURES
from typing import Optional
def cerealSortFeatures(featureList):
regSortFeatures(featureList)
featureList.sort(key=lambda f: 0 if f.name in SUPPORTED_FEATURES else 1)
def makeREstring(strings, default=None, strings_are_regex=False):
"""Turn a list of strings into a regexp string matching exactly those strings."""
if strings or default is None:
if not strings_are_regex:
strings = (re.escape(s) for s in strings)
return '^(' + '|'.join(strings) + ')$'
return default
def makeGenOpts(args):
"""Returns a directory of [ generator function, generator options ] indexed
by specified short names. The generator options incorporate the following
parameters:
args is an parsed argument object; see below for the fields that are used."""
global genOpts
genOpts = {}
# Output target directory
directory = args.directory
# Descriptive names for various regexp patterns used to select
# versions and extensions
allFormats = allFeatures = allExtensions = r'.*'
# Turn lists of names/patterns into matching regular expressions
emitExtensionsPat = makeREstring([], allExtensions)
emitFormatsPat = makeREstring([], allFormats)
featuresPat = makeREstring([], allFeatures)
# Copyright text prefixing all headers (list of strings).
# The SPDX formatting below works around constraints of the 'reuse' tool
prefixStrings = [
'/*',
'** Copyright 2025 The Android Open Source Project',
'** Copyright 2015-2023 The Khronos Group Inc.',
'**',
'** SPDX-License-Identifier' + ': Apache-2.0',
'*/',
''
]
# Text specific to Vulkan headers
vkPrefixStrings = [
'/*',
'** This header is generated from the Khronos Vulkan XML API Registry.',
'**',
'*/',
''
]
genOpts['cereal'] = [
CerealGenerator,
CGeneratorOptions(
directory = directory,
versions = featuresPat,
emitversions = featuresPat,
addExtensions = None,
emitExtensions = emitExtensionsPat,
prefixText = prefixStrings + vkPrefixStrings,
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48,
sortProcedure = cerealSortFeatures)
]
gfxstreamPrefixStrings = [
'#pragma once',
'#ifdef VK_GFXSTREAM_STRUCTURE_TYPE_EXT',
'#include "vulkan_gfxstream_structure_type.h"',
'#endif',
]
# gfxstream specific header
genOpts['vulkan_gfxstream.h'] = [
COutputGenerator,
CGeneratorOptions(
filename = 'vulkan_gfxstream.h',
directory = directory,
versions = featuresPat,
emitversions = None,
addExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None),
emitExtensions = makeREstring(['VK_GOOGLE_gfxstream'], None),
prefixText = prefixStrings + vkPrefixStrings + gfxstreamPrefixStrings,
# Use #pragma once in the prefixText instead, so that we can put the copyright comments
# at the beginning of the file.
apientry = 'VKAPI_CALL ',
apientryp = 'VKAPI_PTR *',
alignFuncParam = 48)
]
def genTarget(args):
"""Create an API generator and corresponding generator options based on
the requested target and command line options.
This is encapsulated in a function so it can be profiled and/or timed.
The args parameter is an parsed argument object containing the following
fields that are used:
- target - target to generate
- directory - directory to generate it in
- extensions - list of additional extensions to include in generated interfaces"""
# Create generator options with parameters specified on command line
makeGenOpts(args)
# Select a generator matching the requested target
if args.target in genOpts:
createGenerator = genOpts[args.target][0]
options = genOpts[args.target][1]
gen = createGenerator(errFile=errWarn,
warnFile=errWarn,
diagFile=diag)
return (gen, options)
else:
return None
# -feature name
# -extension name
# For both, "name" may be a single name, or a space-separated list
# of names, or a regular expression.
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('-registry', action='store',
default='vk.xml',
help='Use specified registry file instead of vk.xml')
parser.add_argument('-registryGfxstream', action='store',
default=None,
help='Use specified gfxstream registry file')
parser.add_argument('-o', action='store', dest='directory',
default='.',
help='Create target and related files in specified directory')
parser.add_argument('target', metavar='target', nargs='?',
help='Specify target')
args = parser.parse_args()
errWarn = sys.stderr
diag = None
# Create the API generator & generator options
(gen, options) = genTarget(args)
# Create the registry object with the specified generator and generator
# options. The options are set before XML loading as they may affect it.
reg = Registry(gen, options)
# Parse the specified registry XML into an ElementTree object
tree = etree.parse(args.registry)
# Merge the gfxstream registry with the official Vulkan registry if the
# target is the cereal generator
if args.registryGfxstream is not None and args.target == 'cereal':
treeGfxstream = etree.parse(args.registryGfxstream)
treeRoot = tree.getroot()
treeGfxstreamRoot = treeGfxstream.getroot()
def getEntryName(entry) -> Optional[str]:
name = entry.get("name")
if name is not None:
return name
try:
return entry.find("proto").find("name").text
except AttributeError:
return None
for entriesName in ['types', 'commands', 'extensions']:
treeEntries = treeRoot.find(entriesName)
originalEntryDict = {}
for entry in treeEntries:
name = getEntryName(entry)
if name is not None:
originalEntryDict[name] = entry
for entry in treeGfxstreamRoot.find(entriesName):
name = getEntryName(entry)
# New entry, just append to entry list
if name not in originalEntryDict.keys():
treeEntries.append(entry)
continue
originalEntry = originalEntryDict[name]
# Extending an existing entry. This happens for MVK.
if entriesName == "extensions":
for key, value in entry.attrib.items():
originalEntry.set(key, value)
require = entry.find("require")
if require is not None:
for child in require:
originalEntry.find("require").append(child)
continue
# Overwriting an existing entry. This happen for
# VkNativeBufferANDROID
if entriesName == "types" or entriesName == "commands":
originalEntry.clear()
originalEntry.attrib = entry.attrib
for child in entry:
originalEntry.append(child)
# Load the XML tree into the registry object
reg.loadElementTree(tree)
reg.apiGen()
+35
View File
@@ -0,0 +1,35 @@
# Copyright © 2026 Google
# SPDX-License-Identifier: MIT
genvk = files('genvk.py')
genvk_depends = files(
'cereal/__init__.py',
'cereal/api_log_decoder.py',
'cereal/common/__init__.py',
'cereal/common/codegen.py',
'cereal/common/vulkantypes.py',
'cereal/counting.py',
'cereal/decoder.py',
'cereal/decodersnapshot.py',
'cereal/deepcopy.py',
'cereal/dispatch.py',
'cereal/encoder.py',
'cereal/extensionstructs.py',
'cereal/frontend.py',
'cereal/functable.py',
'cereal/handlemap.py',
'cereal/marshaling.py',
'cereal/marshalingdefs.py',
'cereal/reservedmarshaling.py',
'cereal/subdecode.py',
'cereal/testing.py',
'cereal/transform.py',
'cereal/unbox.py',
'cereal/vkextensionstructuretype.py',
'cereal/wrapperdefs.py',
'cerealgenerator.py',
'cgenerator.py',
'generator.py',
'reg.py',
)
+917
View File
@@ -0,0 +1,917 @@
#!/usr/bin/python3 -i
#
# Copyright 2013-2023 The Khronos Group Inc.
# Copyright 2023-2024 Google Inc.
#
# SPDX-License-Identifier: Apache-2.0
"""Types and classes for manipulating an API registry."""
import copy
import re
import sys
import xml.etree.ElementTree as etree
from collections import defaultdict, deque, namedtuple
from generator import GeneratorOptions, OutputGenerator, noneStr, write
def apiNameMatch(str, supported):
"""Return whether a required api name matches a pattern specified for an
XML <feature> 'api' attribute or <extension> 'supported' attribute.
- str - API name such as 'vulkan' or 'openxr'. May be None, in which
case it never matches (this should not happen).
- supported - comma-separated list of XML API names. May be None, in
which case str always matches (this is the usual case)."""
if str is not None:
return supported is None or str in supported.split(',')
# Fallthrough case - either str is None or the test failed
return False
def matchAPIProfile(api, elem):
"""Return whether an API and profile
like `"gl(core)|gles1(common-lite)"`."""
# Match 'api', if present
elem_api = elem.get('api')
if elem_api:
if api is None:
raise UserWarning("No API requested, but 'api' attribute is present with value '"
+ elem_api + "'")
elif api != elem_api:
# Requested API does not match attribute
return False
return True
class BaseInfo:
"""Base class for information about a registry feature
(type/group/enum/command/API/extension).
Represents the state of a registry feature, used during API generation.
"""
def __init__(self, elem):
self.required = False
"""should this feature be defined during header generation
(has it been removed by a profile or version)?"""
self.declared = False
"has this feature been defined already?"
self.elem = elem
"etree Element for this feature"
def resetState(self):
"""Reset required/declared to initial values. Used
prior to generating a new API interface."""
self.required = False
self.declared = False
def compareKeys(self, info, key, required = False):
"""Return True if self.elem and info.elem have the same attribute
value for key.
If 'required' is not True, also returns True if neither element
has an attribute value for key."""
if required and key not in self.elem.keys():
return False
return self.elem.get(key) == info.elem.get(key)
def compareElem(self, info, infoName):
"""Return True if self.elem and info.elem have the same definition.
info - the other object
infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' /
'extension'"""
if infoName == 'enum':
if self.compareKeys(info, 'extends'):
# Either both extend the same type, or no type
if (self.compareKeys(info, 'value', required = True) or
self.compareKeys(info, 'bitpos', required = True)):
# If both specify the same value or bit position,
# they are equal
return True
elif (self.compareKeys(info, 'extnumber') and
self.compareKeys(info, 'offset') and
self.compareKeys(info, 'dir')):
# If both specify the same relative offset, they are equal
return True
elif (self.compareKeys(info, 'alias')):
# If both are aliases of the same value
return True
else:
return False
else:
# The same enum cannot extend two different types
return False
else:
# Non-<enum>s should never be redefined
return False
class TypeInfo(BaseInfo):
"""Registry information about a type. No additional state
beyond BaseInfo is required."""
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.additionalValidity = []
self.removedValidity = []
def getMembers(self):
"""Get a collection of all member elements for this type, if any."""
return self.elem.findall('member')
def resetState(self):
BaseInfo.resetState(self)
self.additionalValidity = []
self.removedValidity = []
class GroupInfo(BaseInfo):
"""Registry information about a group of related enums
in an <enums> block, generally corresponding to a C "enum" type."""
def __init__(self, elem):
BaseInfo.__init__(self, elem)
class EnumInfo(BaseInfo):
"""Registry information about an enum"""
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.type = elem.get('type')
"""numeric type of the value of the <enum> tag
( '' for GLint, 'u' for GLuint, 'ull' for GLuint64 )"""
if self.type is None:
self.type = ''
class CmdInfo(BaseInfo):
"""Registry information about a command"""
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.additionalValidity = []
self.removedValidity = []
def getParams(self):
"""Get a collection of all param elements for this command, if any."""
return self.elem.findall('param')
def resetState(self):
BaseInfo.resetState(self)
self.additionalValidity = []
self.removedValidity = []
class FeatureInfo(BaseInfo):
"""Registry information about an API <feature>
or <extension>."""
def __init__(self, elem):
BaseInfo.__init__(self, elem)
self.name = elem.get('name')
"feature name string (e.g. 'VK_KHR_surface')"
self.emit = False
"has this feature been defined already?"
self.sortorder = int(elem.get('sortorder', 0))
"""explicit numeric sort key within feature and extension groups.
Defaults to 0."""
# Determine element category (vendor). Only works
# for <extension> elements.
if elem.tag == 'feature':
# Element category (vendor) is meaningless for <feature>
self.category = 'VERSION'
"""category, e.g. VERSION or khr/vendor tag"""
self.version = elem.get('name')
"""feature name string"""
self.versionNumber = elem.get('number')
"""versionNumber - API version number, taken from the 'number'
attribute of <feature>. Extensions do not have API version
numbers and are assigned number 0."""
self.number = 0
self.supported = None
else:
# Extract vendor portion of <APIprefix>_<vendor>_<name>
self.category = self.name.split('_', 2)[1]
self.version = "0"
self.versionNumber = "0"
self.number = int(elem.get('number','0'))
"""extension number, used for ordering and for assigning
enumerant offsets. <feature> features do not have extension
numbers and are assigned number 0, as are extensions without
numbers, so sorting works."""
self.supported = elem.get('supported', 'disabled')
class Registry:
"""Object representing an API registry, loaded from an XML file."""
def __init__(self, gen=None, genOpts=None):
if gen is None:
# If not specified, give a default object so messaging will work
self.gen = OutputGenerator()
else:
self.gen = gen
"Output generator used to write headers / messages"
if genOpts is None:
# If no generator is provided, we may still need the XML API name
# (for example, in genRef.py).
self.genOpts = GeneratorOptions(apiname = 'vulkan')
else:
self.genOpts = genOpts
"Options controlling features to write and how to format them"
self.gen.registry = self
self.gen.genOpts = self.genOpts
self.gen.genOpts.registry = self
self.tree = None
"ElementTree containing the root `<registry>`"
self.typedict = {}
"dictionary of TypeInfo objects keyed by type name"
self.groupdict = {}
"dictionary of GroupInfo objects keyed by group name"
self.enumdict = {}
"dictionary of EnumInfo objects keyed by enum name"
self.cmddict = {}
"dictionary of CmdInfo objects keyed by command name"
self.apidict = {}
"dictionary of FeatureInfo objects for `<feature>` elements keyed by API name"
self.extensions = []
"list of `<extension>` Elements"
self.extdict = {}
"dictionary of FeatureInfo objects for `<extension>` elements keyed by extension name"
self.emitFeatures = False
"""True to actually emit features for a version / extension,
or False to just treat them as emitted"""
self.filename = None
def loadElementTree(self, tree):
"""Load ElementTree into a Registry object and parse it."""
self.tree = tree
self.parseTree()
def loadFile(self, file):
"""Load an API registry XML file into a Registry object and parse it"""
self.filename = file
self.tree = etree.parse(file)
self.parseTree()
def setGenerator(self, gen):
"""Specify output generator object.
`None` restores the default generator."""
self.gen = gen
self.gen.setRegistry(self)
def addElementInfo(self, elem, info, infoName, dictionary):
"""Add information about an element to the corresponding dictionary.
Intended for internal use only.
- elem - `<type>`/`<enums>`/`<enum>`/`<command>`/`<feature>`/`<extension>`/`<spirvextension>`/`<spirvcapability>`/`<format>`/`<syncstage>`/`<syncaccess>`/`<syncpipeline>` Element
- info - corresponding {Type|Group|Enum|Cmd|Feature|Spirv|Format|SyncStage|SyncAccess|SyncPipeline}Info object
- infoName - 'type' / 'group' / 'enum' / 'command' / 'feature' / 'extension' / 'spirvextension' / 'spirvcapability' / 'format' / 'syncstage' / 'syncaccess' / 'syncpipeline'
- dictionary - self.{type|group|enum|cmd|api|ext|format|spirvext|spirvcap|sync}dict
The dictionary key is the element 'name' attribute."""
key = elem.get('name')
if key in dictionary:
if not dictionary[key].compareElem(info, infoName):
return
else:
dictionary[key] = info
def lookupElementInfo(self, fname, dictionary):
"""Find a {Type|Enum|Cmd}Info object by name.
Intended for internal use only.
If an object qualified by API name exists, use that.
- fname - name of type / enum / command
- dictionary - self.{type|enum|cmd}dict"""
key = (fname, 'vulkan')
if key in dictionary:
return dictionary[key]
if fname in dictionary:
return dictionary[fname]
return None
def parseTree(self):
"""Parse the registry Element, once created"""
# This must be the Element for the root <registry>
if self.tree is None:
raise RuntimeError("Tree not initialized!")
self.reg = self.tree.getroot()
# There is usually one <types> block; more are OK
# Required <type> attributes: 'name' or nested <name> tag contents
self.typedict = {}
for type_elem in self.reg.findall('types/type'):
# If the <type> does not already have a 'name' attribute, set
# it from contents of its <name> tag, or from the contents of
# its <proto><name> tag for funcpointer types.
name = type_elem.get('name')
if name is None:
if type_elem.get('category') == 'funcpointer':
name_elem = type_elem.find('proto/name')
if name_elem is None:
name_elem = type_elem.find('name')
else:
name_elem = type_elem.find('name')
if name_elem is None or not name_elem.text:
raise RuntimeError("Type without a name!")
name = name_elem.text
type_elem.set('name', name)
self.addElementInfo(type_elem, TypeInfo(type_elem), 'type', self.typedict)
# Create dictionary of registry enum groups from <enums> tags.
#
# Required <enums> attributes: 'name'. If no name is given, one is
# generated, but that group cannot be identified and turned into an
# enum type definition - it is just a container for <enum> tags.
self.groupdict = {}
for group in self.reg.findall('enums'):
self.addElementInfo(group, GroupInfo(group), 'group', self.groupdict)
# Create dictionary of registry enums from <enum> tags
#
# <enums> tags usually define different namespaces for the values
# defined in those tags, but the actual names all share the
# same dictionary.
# Required <enum> attributes: 'name', 'value'
# For containing <enums> which have type="enum" or type="bitmask",
# tag all contained <enum>s are required. This is a stopgap until
# a better scheme for tagging core and extension enums is created.
self.enumdict = {}
for enums in self.reg.findall('enums'):
required = (enums.get('type') is not None)
for enum in enums.findall('enum'):
enumInfo = EnumInfo(enum)
enumInfo.required = required
self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
# Create dictionary of registry commands from <command> tags
# and add 'name' attribute to each <command> tag (where missing)
# based on its <proto><name> element.
#
# There is usually only one <commands> block; more are OK.
# Required <command> attributes: 'name' or <proto><name> tag contents
self.cmddict = {}
# List of commands which alias others. Contains
# [ aliasName, element ]
# for each alias
cmdAlias = []
for cmd in self.reg.findall('commands/command'):
# If the <command> does not already have a 'name' attribute, set
# it from contents of its <proto><name> tag.
name = cmd.get('name')
if name is None:
name_elem = cmd.find('proto/name')
if name_elem is None or not name_elem.text:
raise RuntimeError("Command without a name!")
name = cmd.set('name', name_elem.text)
ci = CmdInfo(cmd)
self.addElementInfo(cmd, ci, 'command', self.cmddict)
alias = cmd.get('alias')
if alias:
cmdAlias.append([name, alias, cmd])
# Now loop over aliases, injecting a copy of the aliased command's
# Element with the aliased prototype name replaced with the command
# name - if it exists.
for (name, alias, cmd) in cmdAlias:
if alias in self.cmddict:
aliasInfo = self.cmddict[alias]
cmdElem = copy.deepcopy(aliasInfo.elem)
cmdElem.find('proto/name').text = name
cmdElem.set('name', name)
cmdElem.set('alias', alias)
ci = CmdInfo(cmdElem)
# Replace the dictionary entry for the CmdInfo element
self.cmddict[name] = ci
# Create dictionaries of API and extension interfaces
# from toplevel <api> and <extension> tags.
self.apidict = {}
for feature in self.reg.findall('feature'):
featureInfo = FeatureInfo(feature)
self.addElementInfo(feature, featureInfo, 'feature', self.apidict)
# Add additional enums defined only in <feature> tags
# to the corresponding enumerated type.
# When seen here, the <enum> element, processed to contain the
# numeric enum value, is added to the corresponding <enums>
# element, as well as adding to the enum dictionary. It is no
# longer removed from the <require> element it is introduced in.
# Instead, generateRequiredInterface ignores <enum> elements
# that extend enumerated types.
#
# For <enum> tags which are actually just constants, if there is
# no 'extends' tag but there is a 'value' or 'bitpos' tag, just
# add an EnumInfo record to the dictionary. That works because
# output generation of constants is purely dependency-based, and
# does not need to iterate through the XML tags.
for elem in feature.findall('require'):
for enum in elem.findall('enum'):
addEnumInfo = False
groupName = enum.get('extends')
if groupName is not None:
# Add version number attribute to the <enum> element
enum.set('version', featureInfo.version)
# Look up the GroupInfo with matching groupName
if groupName in self.groupdict:
gi = self.groupdict[groupName]
gi.elem.append(copy.deepcopy(enum))
addEnumInfo = True
elif enum.get('value') or enum.get('bitpos') or enum.get('alias'):
addEnumInfo = True
if addEnumInfo:
enumInfo = EnumInfo(enum)
self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
self.extensions = self.reg.findall('extensions/extension')
self.extdict = {}
for feature in self.extensions:
featureInfo = FeatureInfo(feature)
self.addElementInfo(feature, featureInfo, 'extension', self.extdict)
# Add additional enums defined only in <extension> tags
# to the corresponding core type.
# Algorithm matches that of enums in a "feature" tag as above.
#
# This code also adds a 'extnumber' attribute containing the
# extension number, used for enumerant value calculation.
for elem in feature.findall('require'):
for enum in elem.findall('enum'):
addEnumInfo = False
groupName = enum.get('extends')
if groupName is not None:
# Add <extension> block's extension number attribute to
# the <enum> element unless specified explicitly, such
# as when redefining an enum in another extension.
extnumber = enum.get('extnumber')
if not extnumber:
enum.set('extnumber', str(featureInfo.number))
enum.set('extname', featureInfo.name)
enum.set('supported', noneStr(featureInfo.supported))
# Look up the GroupInfo with matching groupName
if groupName in self.groupdict:
gi = self.groupdict[groupName]
gi.elem.append(copy.deepcopy(enum))
addEnumInfo = True
elif enum.get('value') or enum.get('bitpos') or enum.get('alias'):
addEnumInfo = True
if addEnumInfo:
enumInfo = EnumInfo(enum)
self.addElementInfo(enum, enumInfo, 'enum', self.enumdict)
def markTypeRequired(self, typename, required):
"""Require (along with its dependencies) or remove (but not its dependencies) a type.
- typename - name of type
- required - boolean (to tag features as required or not)
"""
# Get TypeInfo object for <type> tag corresponding to typename
typeinfo = self.lookupElementInfo(typename, self.typedict)
if typeinfo is not None:
if required:
# Tag type dependencies in 'alias' and 'required' attributes as
# required. This does not un-tag dependencies in a <remove>
# tag. See comments in markRequired() below for the reason.
for attrib_name in ['requires', 'alias']:
depname = typeinfo.elem.get(attrib_name)
if depname:
# Do not recurse on self-referential structures.
if typename != depname:
self.markTypeRequired(depname, required)
# Tag types used in defining this type (e.g. in nested
# <type> tags)
# Look for <type> in entire <command> tree,
# not just immediate children
for subtype in typeinfo.elem.findall('.//type'):
if typename != subtype.text:
self.markTypeRequired(subtype.text, required)
# Tag enums used in defining this type, for example in
# <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member>
for subenum in typeinfo.elem.findall('.//enum'):
self.markEnumRequired(subenum.text, required)
# Tag type dependency in 'bitvalues' attributes as
# required. This ensures that the bit values for a flag
# are emitted
depType = typeinfo.elem.get('bitvalues')
if depType:
self.markTypeRequired(depType, required)
group = self.lookupElementInfo(depType, self.groupdict)
if group is not None:
group.flagType = typeinfo
typeinfo.required = required
def markEnumRequired(self, enumname, required):
"""Mark an enum as required or not.
- enumname - name of enum
- required - boolean (to tag features as required or not)"""
enum = self.lookupElementInfo(enumname, self.enumdict)
if enum is not None:
# If the enum is part of a group, and is being removed, then
# look it up in that <enums> tag and remove the Element there,
# so that it is not visible to generators (which traverse the
# <enums> tag elements rather than using the dictionaries).
if not required:
groupName = enum.elem.get('extends')
if groupName is not None:
# Look up the Info with matching groupName
if groupName in self.groupdict:
gi = self.groupdict[groupName]
gienum = gi.elem.find("enum[@name='" + enumname + "']")
if gienum is not None:
# Remove copy of this enum from the group
gi.elem.remove(gienum)
else:
# This enum is not an extending enum.
# The XML tree must be searched for all <enums> that
# might have it, so we know the parent to delete from.
enumName = enum.elem.get('name')
count = 0
for enums in self.reg.findall('enums'):
for thisEnum in enums.findall('enum'):
if thisEnum.get('name') == enumName:
# Actually remove it
count = count + 1
enums.remove(thisEnum)
enum.required = required
# Tag enum dependencies in 'alias' attribute as required
depname = enum.elem.get('alias')
if depname:
self.markEnumRequired(depname, required)
def markCmdRequired(self, cmdname, required):
"""Mark a command as required or not.
- cmdname - name of command
- required - boolean (to tag features as required or not)"""
cmd = self.lookupElementInfo(cmdname, self.cmddict)
if cmd is not None:
cmd.required = required
# Tag all parameter types of this command as required.
# This does not remove types of commands in a <remove>
# tag, because many other commands may use the same type.
# We could be more clever and reference count types,
# instead of using a boolean.
if required:
# Look for <type> in entire <command> tree,
# not just immediate children
for type_elem in cmd.elem.findall('.//type'):
self.markTypeRequired(type_elem.text, required)
def markRequired(self, featurename, feature, required):
"""Require or remove features specified in the Element.
- featurename - name of the feature
- feature - Element for `<require>` or `<remove>` tag
- required - boolean (to tag features as required or not)"""
# Loop over types, enums, and commands in the tag
# @@ It would be possible to respect 'api' and 'profile' attributes
# in individual features, but that is not done yet.
for typeElem in feature.findall('type'):
self.markTypeRequired(typeElem.get('name'), required)
for enumElem in feature.findall('enum'):
self.markEnumRequired(enumElem.get('name'), required)
for cmdElem in feature.findall('command'):
self.markCmdRequired(cmdElem.get('name'), required)
def fillFeatureDictionary(self, interface, featurename, api):
"""Capture added interfaces for a `<version>` or `<extension>`.
- interface - Element for `<version>` or `<extension>`, containing
`<require>` and `<remove>` tags
- featurename - name of the feature
- api - string specifying API name being generated
"""
# Explicitly initialize known types - errors for unhandled categories
self.gen.featureDictionary[featurename] = {
"enumconstant": {},
"command": {},
"enum": {},
"struct": {},
"handle": {},
"basetype": {},
"include": {},
"define": {},
"bitmask": {},
"union": {},
"funcpointer": {},
}
def requireFeatures(self, interface, featurename, api):
"""Process `<require>` tags for a `<version>` or `<extension>`.
- interface - Element for `<version>` or `<extension>`, containing
`<require>` tags
- featurename - name of the feature
- api - string specifying API name being generated
- profile - string specifying API profile being generated"""
# <require> marks things that are required by this version/profile
for feature in interface.findall('require'):
if matchAPIProfile(api, feature):
self.markRequired(featurename, feature, True)
def generateFeature(self, fname, ftype, dictionary, explicit=False):
"""Generate a single type / enum group / enum / command,
and all its dependencies as needed.
- fname - name of feature (`<type>`/`<enum>`/`<command>`)
- ftype - type of feature, 'type' | 'enum' | 'command'
- dictionary - of *Info objects - self.{type|enum|cmd}dict
- explicit - True if this is explicitly required by the top-level
XML <require> tag, False if it is a dependency of an explicit
requirement."""
f = self.lookupElementInfo(fname, dictionary)
if f is None:
return
if not f.required:
return
# If feature is not required, or has already been declared, return
if f.declared:
return
# Always mark feature declared, as though actually emitted
f.declared = True
# Determine if this is an alias, and of what, if so
alias = f.elem.get('alias')
# Pull in dependent declaration(s) of the feature.
# For types, there may be one type in the 'requires' attribute of
# the element, one in the 'alias' attribute, and many in
# embedded <type> and <enum> tags within the element.
# For commands, there may be many in <type> tags within the element.
# For enums, no dependencies are allowed (though perhaps if you
# have a uint64 enum, it should require that type).
genProc = None
followupFeature = None
if ftype == 'type':
genProc = self.gen.genType
# Generate type dependencies in 'alias' and 'requires' attributes
if alias:
self.generateFeature(alias, 'type', self.typedict)
requires = f.elem.get('requires')
if requires:
self.generateFeature(requires, 'type', self.typedict)
# Generate types used in defining this type (e.g. in nested
# <type> tags)
# Look for <type> in entire <command> tree,
# not just immediate children
for subtype in f.elem.findall('.//type'):
self.generateFeature(subtype.text, 'type', self.typedict)
# Generate enums used in defining this type, for example in
# <member><name>member</name>[<enum>MEMBER_SIZE</enum>]</member>
for subtype in f.elem.findall('.//enum'):
self.generateFeature(subtype.text, 'enum', self.enumdict)
# If the type is an enum group, look up the corresponding
# group in the group dictionary and generate that instead.
if f.elem.get('category') == 'enum':
group = self.lookupElementInfo(fname, self.groupdict)
if alias is not None:
# Now, pass the *aliased* GroupInfo to the genGroup, but
# with an additional parameter which is the alias name.
genProc = self.gen.genGroup
f = self.lookupElementInfo(alias, self.groupdict)
elif group is None:
return
else:
genProc = self.gen.genGroup
f = group
# @ The enum group is not ready for generation. At this
# @ point, it contains all <enum> tags injected by
# @ <extension> tags without any verification of whether
# @ they are required or not. It may also contain
# @ duplicates injected by multiple consistent
# @ definitions of an <enum>.
# @ Pass over each enum, marking its enumdict[] entry as
# @ required or not. Mark aliases of enums as required,
# @ too.
enums = group.elem.findall('enum')
# Check for required enums, including aliases
# LATER - Check for, report, and remove duplicates?
enumAliases = []
for elem in enums:
name = elem.get('name')
required = False
extname = elem.get('extname')
version = elem.get('version')
if extname is not None:
# 'supported' attribute was injected when the <enum> element was
# moved into the <enums> group in Registry.parseTree()
supported_list = elem.get('supported').split(",")
if 'vulkan' in supported_list:
required = True
elif re.match(self.genOpts.addExtensions, extname) is not None:
required = True
elif version is not None:
required = re.match(self.genOpts.emitversions, version) is not None
else:
required = True
if required:
# Mark this element as required (in the element, not the EnumInfo)
elem.set('required', 'true')
# If it is an alias, track that for later use
enumAlias = elem.get('alias')
if enumAlias:
enumAliases.append(enumAlias)
for elem in enums:
name = elem.get('name')
if name in enumAliases:
elem.set('required', 'true')
if f is None:
raise RuntimeError("Should not get here")
if f.elem.get('category') == 'bitmask':
followupFeature = f.elem.get('bitvalues')
elif ftype == 'command':
# Generate command dependencies in 'alias' attribute
if alias:
self.generateFeature(alias, 'command', self.cmddict)
genProc = self.gen.genCmd
for type_elem in f.elem.findall('.//type'):
depname = type_elem.text
self.generateFeature(depname, 'type', self.typedict)
elif ftype == 'enum':
# Generate enum dependencies in 'alias' attribute
if alias:
self.generateFeature(alias, 'enum', self.enumdict)
genProc = self.gen.genEnum
# Actually generate the type only if emitting declarations
if self.emitFeatures:
if genProc is None:
raise RuntimeError("genProc is None when we should be emitting")
genProc(f, fname, alias)
if followupFeature:
self.generateFeature(followupFeature, "type", self.typedict)
def generateRequiredInterface(self, interface):
"""Generate all interfaces required by an API version or extension.
- interface - Element for `<version>` or `<extension>`"""
# Loop over all features inside all <require> tags.
for features in interface.findall('require'):
for t in features.findall('type'):
self.generateFeature(t.get('name'), 'type', self.typedict, explicit=True)
for e in features.findall('enum'):
# If this is an enum extending an enumerated type, do not
# generate it - this has already been done in reg.parseTree,
# by copying this element into the enumerated type.
enumextends = e.get('extends')
if not enumextends:
self.generateFeature(e.get('name'), 'enum', self.enumdict, explicit=True)
for c in features.findall('command'):
self.generateFeature(c.get('name'), 'command', self.cmddict, explicit=True)
def apiGen(self):
"""Generate interface for specified versions using the current
generator and generator options"""
# Could reset required/declared flags for all features here.
# This has been removed as never used. The initial motivation was
# the idea of calling apiGen() repeatedly for different targets, but
# this has never been done. The 20% or so build-time speedup that
# might result is not worth the effort to make it actually work.
#
# self.apiReset()
# Compile regexps used to select versions & extensions
regVersions = re.compile(self.genOpts.versions)
regEmitVersions = re.compile(self.genOpts.emitversions)
regAddExtensions = re.compile(self.genOpts.addExtensions)
regEmitExtensions = re.compile(self.genOpts.emitExtensions)
# Get all matching API feature names & add to list of FeatureInfo
# Note we used to select on feature version attributes, not names.
features = []
apiMatch = False
for key in self.apidict:
fi = self.apidict[key]
api = fi.elem.get('api')
if apiNameMatch('vulkan', api):
apiMatch = True
if regVersions.match(fi.name):
# Matches API & version #s being generated. Mark for
# emission and add to the features[] list .
# @@ Could use 'declared' instead of 'emit'?
fi.emit = (regEmitVersions.match(fi.name) is not None)
features.append(fi)
# Get all matching extensions, in order by their extension number,
# and add to the list of features.
# Start with extensions whose 'supported' attributes match the API
# being generated. Add extensions matching the pattern specified in
# regExtensions, then remove extensions matching the pattern
# specified in regRemoveExtensions
for (extName, ei) in sorted(self.extdict.items(), key=lambda x: x[1].number if x[1].number is not None else '0'):
extName = ei.name
include = False
# Include extension if defaultExtensions is not None and is
# exactly matched by the 'supported' attribute.
if apiNameMatch('vulkan', ei.elem.get('supported')):
include = True
# Include additional extensions if the extension name matches
# the regexp specified in the generator options. This allows
# forcing extensions into an interface even if they are not
# tagged appropriately in the registry.
# However, we still respect the 'supported' attribute.
if regAddExtensions.match(extName) is not None:
if not apiNameMatch('vulkan', ei.elem.get('supported')):
include = False
else:
include = True
# If the extension is to be included, add it to the
# extension features list.
if include:
ei.emit = (regEmitExtensions.match(extName) is not None)
features.append(ei)
# Sort the features list, if a sort procedure is defined
if self.genOpts.sortProcedure:
self.genOpts.sortProcedure(features)
# Passes 1+2: loop over requested API versions and extensions tagging
# types/commands/features as required (in an <require> block) or no
# longer required (in an <remove> block). <remove>s are processed
# after all <require>s, so removals win.
# If a profile other than 'None' is being generated, it must
# match the profile attribute (if any) of the <require> and
# <remove> tags.
for f in features:
self.fillFeatureDictionary(f.elem, f.name, 'vulkan')
self.requireFeatures(f.elem, f.name, 'vulkan')
# @@May need to strip <spirvcapability> / <spirvextension> <enable>
# tags of these forms:
# <enable version="VK_API_VERSION_1_0"/>
# <enable struct="VkPhysicalDeviceFeatures" feature="geometryShader" requires="VK_VERSION_1_0"/>
# <enable extension="VK_KHR_shader_draw_parameters"/>
# <enable property="VkPhysicalDeviceVulkan12Properties" member="shaderDenormPreserveFloat16" value="VK_TRUE" requires="VK_VERSION_1_2,VK_KHR_shader_float_controls"/>
# Pass 3: loop over specified API versions and extensions printing
# declarations for required things which have not already been
# generated.
self.gen.beginFile(self.genOpts)
for f in features:
emit = self.emitFeatures = f.emit
# Generate the interface (or just tag its elements as having been
# emitted, if they have not been).
self.gen.beginFeature(f.elem, emit)
self.generateRequiredInterface(f.elem)
self.gen.endFeature()
self.gen.endFile()
+343
View File
@@ -0,0 +1,343 @@
<?xml version="1.0" encoding="UTF-8"?>
<registry>
<comment>
Copyright 2023 Google LLC
SPDX-License-Identifier: MIT
</comment>
<comment>
This file, vk_gfxstream.xml, is the Vulkan API Registry for gfxstream
specific entries.
</comment>
<types comment="Vulkan type definitions">
<!-- b/295587347 VkNativeBufferANDROID diverged from upstream -->
<type category="struct" name="VkNativeBufferANDROID" structextends="VkImageCreateInfo">
<member values="VK_STRUCTURE_TYPE_NATIVE_BUFFER_ANDROID"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member noautovalidity="true">const <type>uint32_t</type>* <name>handle</name></member>
<member><type>int</type> <name>stride</name></member>
<member><type>int</type> <name>format</name></member>
<member><type>int</type> <name>usage</name></member>
<member><type>VkNativeBufferUsage2ANDROID</type> <name>usage2</name></member>
</type>
<!-- VK_GOOGLE_gfxstream -->
<type category="struct" name="VkImportColorBufferGOOGLE" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member><type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>colorBuffer</name></member>
</type>
<type category="struct" name="VkImportBufferGOOGLE" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_IMPORT_BUFFER_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member><type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>buffer</name></member>
</type>
<type category="struct" name="VkCreateBlobGOOGLE" structextends="VkMemoryAllocateInfo">
<member values="VK_STRUCTURE_TYPE_CREATE_BLOB_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member><type>void</type>* <name>pNext</name></member>
<member><type>uint32_t</type> <name>blobMem</name></member>
<member><type>uint32_t</type> <name>blobFlags</name></member>
<member><type>uint64_t</type> <name>blobId</name></member>
</type>
<type category="struct" name="VkDebugMetadataGOOGLE">
<member values="VK_STRUCTURE_TYPE_DEBUG_METADATA_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
</type>
<type category="struct" name="VkDebugMetadataGuestProcessNameGOOGLE" structextends="VkDebugMetadataGOOGLE">
<member values="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_PROCESS_NAME_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true" len="null-terminated">const <type>char</type>* <name>pName</name></member>
</type>
<type category="struct" name="VkDebugMetadataGuestProcessIdGOOGLE" structextends="VkDebugMetadataGOOGLE">
<member values="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_PROCESS_ID_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint64_t</type> <name>id</name></member>
</type>
<type category="struct" name="VkDebugMetadataGuestThreadNameGOOGLE" structextends="VkDebugMetadataGOOGLE">
<member values="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_THREAD_NAME_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member optional="true" len="null-terminated">const <type>char</type>* <name>pName</name></member>
</type>
<type category="struct" name="VkDebugMetadataGuestThreadIdGOOGLE" structextends="VkDebugMetadataGOOGLE">
<member values="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_THREAD_ID_GOOGLE"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>uint64_t</type> <name>id</name></member>
</type>
<!-- b/519606352: removes `optional` from `pUserData` to avoid a backwards incompatible guest to host protocol change. -->
<type category="struct" name="VkDeviceDeviceMemoryReportCreateInfoEXT" allowduplicate="true" structextends="VkDeviceCreateInfo">
<member values="VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT"><type>VkStructureType</type> <name>sType</name></member>
<member optional="true">const <type>void</type>* <name>pNext</name></member>
<member><type>VkDeviceMemoryReportFlagsEXT</type> <name>flags</name></member>
<member><type>PFN_vkDeviceMemoryReportCallbackEXT</type> <name>pfnUserCallback</name></member>
<member><type>void</type>* <name>pUserData</name></member>
</type>
</types>
<commands comment="Vulkan command definitions">
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY,VK_ERROR_MEMORY_MAP_FAILED">
<proto><type>VkResult</type> <name>vkMapMemoryIntoAddressSpaceGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDeviceMemory</type> <name>memory</name></param>
<param optional="false,true"><type>uint64_t</type>* <name>pAddress</name></param>
</command>
<command>
<proto><type>void</type> <name>vkUpdateDescriptorSetWithTemplateSizedGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDescriptorSet</type> <name>descriptorSet</name></param>
<param><type>VkDescriptorUpdateTemplate</type> <name>descriptorUpdateTemplate</name></param>
<param><type>uint32_t</type> <name>imageInfoCount</name></param>
<param><type>uint32_t</type> <name>bufferInfoCount</name></param>
<param><type>uint32_t</type> <name>bufferViewCount</name></param>
<param optional="true" len="imageInfoCount">const <type>uint32_t</type>* <name>pImageInfoEntryIndices</name></param>
<param optional="true" len="bufferInfoCount">const <type>uint32_t</type>* <name>pBufferInfoEntryIndices</name></param>
<param optional="true" len="bufferViewCount">const <type>uint32_t</type>* <name>pBufferViewEntryIndices</name></param>
<param optional="true" len="imageInfoCount">const <type>VkDescriptorImageInfo</type>* <name>pImageInfos</name></param>
<param optional="true" len="bufferInfoCount">const <type>VkDescriptorBufferInfo</type>* <name>pBufferInfos</name></param>
<param optional="true" len="bufferViewCount">const <type>VkBufferView</type>* <name>pBufferViews</name></param>
</command>
<command>
<proto><type>void</type> <name>vkUpdateDescriptorSetWithTemplateSized2GOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDescriptorSet</type> <name>descriptorSet</name></param>
<param><type>VkDescriptorUpdateTemplate</type> <name>descriptorUpdateTemplate</name></param>
<param><type>uint32_t</type> <name>imageInfoCount</name></param>
<param><type>uint32_t</type> <name>bufferInfoCount</name></param>
<param><type>uint32_t</type> <name>bufferViewCount</name></param>
<param><type>uint32_t</type> <name>inlineUniformBlockCount</name></param>
<param optional="true" len="imageInfoCount">const <type>uint32_t</type>* <name>pImageInfoEntryIndices</name></param>
<param optional="true" len="bufferInfoCount">const <type>uint32_t</type>* <name>pBufferInfoEntryIndices</name></param>
<param optional="true" len="bufferViewCount">const <type>uint32_t</type>* <name>pBufferViewEntryIndices</name></param>
<param optional="true" len="imageInfoCount">const <type>VkDescriptorImageInfo</type>* <name>pImageInfos</name></param>
<param optional="true" len="bufferInfoCount">const <type>VkDescriptorBufferInfo</type>* <name>pBufferInfos</name></param>
<param optional="true" len="bufferViewCount">const <type>VkBufferView</type>* <name>pBufferViews</name></param>
<param optional="true" len="inlineUniformBlockCount">const <type>uint8_t</type>* <name>pInlineUniformBlockData</name></param>
</command>
<command>
<proto><type>void</type> <name>vkBeginCommandBufferAsyncGOOGLE</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param>const <type>VkCommandBufferBeginInfo</type>* <name>pBeginInfo</name></param>
<implicitexternsyncparams>
<param>the sname:VkCommandPool that pname:commandBuffer was allocated from</param>
</implicitexternsyncparams>
</command>
<command>
<proto><type>void</type> <name>vkEndCommandBufferAsyncGOOGLE</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<implicitexternsyncparams>
<param>the sname:VkCommandPool that pname:commandBuffer was allocated from</param>
</implicitexternsyncparams>
</command>
<command>
<proto><type>void</type> <name>vkResetCommandBufferAsyncGOOGLE</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param optional="true"><type>VkCommandBufferResetFlags</type> <name>flags</name></param>
</command>
<command>
<proto><type>void</type> <name>vkCommandBufferHostSyncGOOGLE</name></proto>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>uint32_t</type> <name>needHostSync</name></param>
<param><type>uint32_t</type> <name>sequenceNumber</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
<proto><type>VkResult</type> <name>vkCreateImageWithRequirementsGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param>const <type>VkImageCreateInfo</type>* <name>pCreateInfo</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<param><type>VkImage</type>* <name>pImage</name></param>
<param><type>VkMemoryRequirements</type>* <name>pMemoryRequirements</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
<proto><type>VkResult</type> <name>vkCreateBufferWithRequirementsGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param>const <type>VkBufferCreateInfo</type>* <name>pCreateInfo</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
<param><type>VkBuffer</type>* <name>pBuffer</name></param>
<param><type>VkMemoryRequirements</type>* <name>pMemoryRequirements</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
<proto><type>VkResult</type> <name>vkGetMemoryHostAddressInfoGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDeviceMemory</type> <name>memory</name></param>
<param optional="false,true"><type>uint64_t</type>* <name>pAddress</name></param>
<param optional="false,true"><type>uint64_t</type>* <name>pSize</name></param>
<param optional="false,true"><type>uint64_t</type>* <name>pHostmemId</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY,VK_ERROR_OUT_OF_DEVICE_MEMORY">
<proto><type>VkResult</type> <name>vkFreeMemorySyncGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param optional="true" externsync="true"><type>VkDeviceMemory</type> <name>memory</name></param>
<param optional="true">const <type>VkAllocationCallbacks</type>* <name>pAllocator</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueHostSyncGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param><type>uint32_t</type> <name>needHostSync</name></param>
<param><type>uint32_t</type> <name>sequenceNumber</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueSubmitAsyncGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>submitCount</name></param>
<param len="submitCount" externsync="pSubmits[].pWaitSemaphores[],pSubmits[].pSignalSemaphores[]">const <type>VkSubmitInfo</type>* <name>pSubmits</name></param>
<param optional="true" externsync="true"><type>VkFence</type> <name>fence</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueSubmitAsync2GOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>submitCount</name></param>
<param len="submitCount" externsync="pSubmits[].pWaitSemaphores[],pSubmits[].pSignalSemaphores[]">const <type>VkSubmitInfo2</type>* <name>pSubmits</name></param>
<param optional="true" externsync="true"><type>VkFence</type> <name>fence</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueWaitIdleAsyncGOOGLE</name></proto>
<param><type>VkQueue</type> <name>queue</name></param>
</command>
<command queues="sparse_binding">
<proto><type>void</type> <name>vkQueueBindSparseAsyncGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>bindInfoCount</name></param>
<param len="bindInfoCount" externsync="pBindInfo[].pWaitSemaphores[],pBindInfo[].pSignalSemaphores[],pBindInfo[].pBufferBinds[].buffer,pBindInfo[].pImageOpaqueBinds[].image,pBindInfo[].pImageBinds[].image">const <type>VkBindSparseInfo</type>* <name>pBindInfo</name></param>
<param optional="true" externsync="true"><type>VkFence</type> <name>fence</name></param>
</command>
<command>
<proto><type>void</type> <name>vkGetLinearImageLayoutGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param><type>VkFormat</type> <name>format</name></param>
<param><type>VkDeviceSize</type>* <name>pOffset</name></param>
<param><type>VkDeviceSize</type>* <name>pRowPitchAlignment</name></param>
</command>
<command>
<proto><type>void</type> <name>vkGetLinearImageLayout2GOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param>const <type>VkImageCreateInfo</type>* <name>pCreateInfo</name></param>
<param><type>VkDeviceSize</type>* <name>pOffset</name></param>
<param><type>VkDeviceSize</type>* <name>pRowPitchAlignment</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueFlushCommandsGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkDeviceSize</type> <name>dataSize</name></param>
<param len="dataSize">const <type>void</type>* <name>pData</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
<proto><type>VkResult</type> <name>vkGetBlobGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDeviceMemory</type> <name>memory</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueCommitDescriptorSetUpdatesGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param externsync="true"><type>uint32_t</type> <name>descriptorPoolCount</name></param>
<param len="descriptorPoolCount" externsync="true">const <type>VkDescriptorPool</type>* <name>pDescriptorPools</name></param>
<param externsync="true"><type>uint32_t</type> <name>descriptorSetCount</name></param>
<param len="descriptorSetCount" externsync="true">const <type>VkDescriptorSetLayout</type>* <name>pSetLayouts</name></param>
<param len="descriptorSetCount" externsync="true">const <type>uint64_t</type>* <name>pDescriptorSetPoolIds</name></param>
<param len="descriptorSetCount" externsync="true">const <type>uint32_t</type>* <name>pDescriptorSetWhichPool</name></param>
<param len="descriptorSetCount" externsync="true">const <type>uint32_t</type>* <name>pDescriptorSetPendingAllocation</name></param>
<param len="descriptorSetCount" externsync="true">const <type>uint32_t</type>* <name>pDescriptorWriteStartingIndices</name></param>
<param externsync="true"><type>uint32_t</type> <name>pendingDescriptorWriteCount</name></param>
<param len="pendingDescriptorWriteCount" externsync="true">const <type>VkWriteDescriptorSet</type>* <name>pPendingDescriptorWrites</name></param>
</command>
<command>
<proto><type>void</type> <name>vkCollectDescriptorPoolIdsGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param externsync="true"><type>VkDescriptorPool</type> <name>descriptorPool</name></param>
<param optiona="false,true"><type>uint32_t</type>* <name>pPoolIdCount</name></param>
<param optional="true" len="pPoolIdCount"><type>uint64_t</type>* <name>pPoolIds</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueSignalReleaseImageANDROIDAsyncGOOGLE</name></proto>
<param><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>waitSemaphoreCount</name></param>
<param noautovalidity="true" len="waitSemaphoreCount">const <type>VkSemaphore</type>* <name>pWaitSemaphores</name></param>
<param><type>VkImage</type> <name>image</name></param>
</command>
<command>
<proto><type>VkResult</type> <name>vkQueueSignalReleaseImageANDROID</name></proto>
<param><type>VkQueue</type> <name>queue</name></param>
<param optional="true"><type>uint32_t</type> <name>waitSemaphoreCount</name></param>
<param noautovalidity="true" len="waitSemaphoreCount">const <type>VkSemaphore</type>* <name>pWaitSemaphores</name></param>
<param><type>VkImage</type> <name>image</name></param>
<param><type>int</type>* <name>pNativeFenceFd</name></param>
</command>
<command>
<proto><type>void</type> <name>vkQueueFlushCommandsFromAuxMemoryGOOGLE</name></proto>
<param externsync="true"><type>VkQueue</type> <name>queue</name></param>
<param externsync="true"><type>VkCommandBuffer</type> <name>commandBuffer</name></param>
<param><type>VkDeviceMemory</type> <name>deviceMemory</name></param>
<param><type>VkDeviceSize</type> <name>dataOffset</name></param>
<param><type>VkDeviceSize</type> <name>dataSize</name></param>
</command>
<command successcodes="VK_SUCCESS" errorcodes="VK_ERROR_OUT_OF_HOST_MEMORY">
<proto><type>VkResult</type> <name>vkGetSemaphoreGOOGLE</name></proto>
<param><type>VkDevice</type> <name>device</name></param>
<param><type>VkSemaphore</type> <name>semaphore</name></param>
<param><type>uint64_t</type> <name>syncId</name></param>
</command>
<command>
<proto><type>void</type> <name>vkTraceAsyncGOOGLE</name></proto>
<param><type>uint64_t</type> <name>id</name></param>
</command>
<command>
<proto><type>void</type> <name>vkSetDebugMetadataAsyncGOOGLE</name></proto>
<param>const <type>VkDebugMetadataGOOGLE</type>* <name>pDebugMetadata</name></param>
</command>
</commands>
<extensions comment="Vulkan extension interface definitions">
<extension name="VK_ANDROID_native_buffer" supported="vulkan"/>
<extension name="VK_GOOGLE_gfxstream" number="386" author="GOOGLE" contact="Lingfeng Yang @frank,Roman Kiryanov @rkir,Yilong Li @liyl_google" supported="vulkan" type="instance">
<require>
<enum value="0" name="VK_GOOGLE_GFXSTREAM_SPEC_VERSION"/>
<enum value="386" name="VK_GOOGLE_GFXSTREAM_NUMBER"/>
<enum value="&quot;VK_GOOGLE_gfxstream&quot;" name="VK_GOOGLE_GFXSTREAM_EXTENSION_NAME"/>
<enum offset="0" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_COLOR_BUFFER_GOOGLE"/>
<enum offset="1" extends="VkStructureType" name="VK_STRUCTURE_TYPE_IMPORT_BUFFER_GOOGLE"/>
<enum offset="2" extends="VkStructureType" name="VK_STRUCTURE_TYPE_CREATE_BLOB_GOOGLE"/>
<enum offset="3" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_METADATA_GOOGLE"/>
<enum offset="4" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_PROCESS_NAME_GOOGLE"/>
<enum offset="5" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_PROCESS_ID_GOOGLE"/>
<enum offset="6" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_THREAD_NAME_GOOGLE"/>
<enum offset="7" extends="VkStructureType" name="VK_STRUCTURE_TYPE_DEBUG_METADATA_GUEST_THREAD_ID_GOOGLE"/>
<type name="VkImportColorBufferGOOGLE"/>
<type name="VkImportBufferGOOGLE"/>
<type name="VkCreateBlobGOOGLE"/>
<type name="VkDebugMetadataGuestGOOGLE"/>
<type name="VkDebugMetadataGuestProcessNameGOOGLE"/>
<type name="VkDebugMetadataGuestProcessIdGOOGLE"/>
<type name="VkDebugMetadataGuestThreadNameGOOGLE"/>
<type name="VkDebugMetadataGuestThreadIdGOOGLE"/>
<command name="vkMapMemoryIntoAddressSpaceGOOGLE"/>
<command name="vkUpdateDescriptorSetWithTemplateSizedGOOGLE"/>
<command name="vkBeginCommandBufferAsyncGOOGLE"/>
<command name="vkEndCommandBufferAsyncGOOGLE"/>
<command name="vkResetCommandBufferAsyncGOOGLE"/>
<command name="vkCommandBufferHostSyncGOOGLE"/>
<command name="vkCreateImageWithRequirementsGOOGLE"/>
<command name="vkCreateBufferWithRequirementsGOOGLE"/>
<command name="vkGetMemoryHostAddressInfoGOOGLE"/>
<command name="vkFreeMemorySyncGOOGLE"/>
<command name="vkQueueHostSyncGOOGLE"/>
<command name="vkQueueSubmitAsyncGOOGLE"/>
<command name="vkQueueWaitIdleAsyncGOOGLE"/>
<command name="vkQueueBindSparseAsyncGOOGLE"/>
<command name="vkGetLinearImageLayoutGOOGLE"/>
<command name="vkGetLinearImageLayout2GOOGLE"/>
<command name="vkQueueFlushCommandsGOOGLE"/>
<command name="vkQueueCommitDescriptorSetUpdatesGOOGLE"/>
<command name="vkCollectDescriptorPoolIdsGOOGLE"/>
<command name="vkQueueSignalReleaseImageANDROIDAsyncGOOGLE"/>
<command name="vkQueueFlushCommandsFromAuxMemoryGOOGLE"/>
<command name="vkGetBlobGOOGLE"/>
<command name="vkUpdateDescriptorSetWithTemplateSized2GOOGLE"/>
<command name="vkQueueSubmitAsync2GOOGLE"/>
<command name="vkGetSemaphoreGOOGLE"/>
<command name="vkTraceAsyncGOOGLE"/>
<command name="vkSetDebugMetadataAsyncGOOGLE"/>
</require>
</extension>
</extensions>
</registry>
@@ -0,0 +1,583 @@
/*
* Copyright 2020 Google
* SPDX-License-Identifier: MIT
*/
#include "AddressSpaceStream.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "VirtGpu.h"
#include "util/log.h"
#include "util/perf/cpu_trace.h"
static const size_t kReadSize = 512 * 1024;
static const size_t kWriteOffset = kReadSize;
AddressSpaceStream::AddressSpaceStream(address_space_handle_t handle, uint32_t version,
struct asg_context context, uint64_t ringOffset,
uint64_t writeBufferOffset, struct address_space_ops ops)
: IOStream(context.ring_config->flush_interval),
m_ops(ops),
m_tmpBuf(0),
m_tmpBufSize(0),
m_tmpBufXferSize(0),
m_usingTmpBuf(0),
m_readBuf(0),
m_read(0),
m_readLeft(0),
m_handle(handle),
m_version(version),
m_context(context),
m_ringOffset(ringOffset),
m_writeBufferOffset(writeBufferOffset),
m_writeBufferSize(context.ring_config->buffer_size),
m_writeBufferMask(m_writeBufferSize - 1),
m_buf((unsigned char*)context.buffer),
m_writeStart(m_buf),
m_writeStep(context.ring_config->flush_interval),
m_notifs(0),
m_written(0),
m_backoffIters(0),
m_backoffFactor(1),
m_ringStorageSize(sizeof(struct asg_ring_storage) + m_writeBufferSize) {
// We'll use this in the future, but at the moment,
// it's a potential compile Werror.
(void)m_ringStorageSize;
(void)m_version;
}
AddressSpaceStream::~AddressSpaceStream() {
flush();
ensureType3Finished();
ensureType1Finished();
if (!m_mapping) {
m_ops.unmap(m_context.to_host, sizeof(struct asg_ring_storage));
m_ops.unmap(m_context.buffer, m_writeBufferSize);
m_ops.unclaim_shared(m_handle, m_ringOffset);
m_ops.unclaim_shared(m_handle, m_writeBufferOffset);
}
m_ops.close(m_handle);
if (m_readBuf) free(m_readBuf);
if (m_tmpBuf) free(m_tmpBuf);
}
size_t AddressSpaceStream::idealAllocSize(size_t len) {
if (len > m_writeStep) return len;
return m_writeStep;
}
void* AddressSpaceStream::allocBuffer(size_t minSize) {
MESA_TRACE_SCOPE("allocBuffer");
ensureType3Finished();
if (!m_readBuf) {
m_readBuf = (unsigned char*)malloc(kReadSize);
}
size_t allocSize =
(m_writeStep < minSize ? minSize : m_writeStep);
if (m_writeStep < allocSize) {
if (!m_tmpBuf) {
m_tmpBufSize = allocSize * 2;
m_tmpBuf = (unsigned char*)malloc(m_tmpBufSize);
}
if (m_tmpBufSize < allocSize) {
m_tmpBufSize = allocSize * 2;
m_tmpBuf = (unsigned char*)realloc(m_tmpBuf, m_tmpBufSize);
}
if (!m_usingTmpBuf) {
flush();
}
m_usingTmpBuf = true;
m_tmpBufXferSize = allocSize;
return m_tmpBuf;
} else {
if (m_usingTmpBuf) {
writeFully(m_tmpBuf, m_tmpBufXferSize);
m_usingTmpBuf = false;
m_tmpBufXferSize = 0;
}
return m_writeStart;
}
}
int AddressSpaceStream::commitBuffer(size_t size)
{
if (size == 0) return 0;
if (m_usingTmpBuf) {
writeFully(m_tmpBuf, size);
m_tmpBufXferSize = 0;
m_usingTmpBuf = false;
return 0;
} else {
int res = type1Write(m_writeStart - m_buf, size);
advanceWrite();
return res;
}
}
const unsigned char *AddressSpaceStream::readFully(void *ptr, size_t totalReadSize)
{
unsigned char* userReadBuf = static_cast<unsigned char*>(ptr);
if (!userReadBuf) {
if (totalReadSize > 0) {
mesa_loge(
"AddressSpaceStream::commitBufferAndReadFully failed, userReadBuf=NULL, "
"totalReadSize %zu, lethal"
" error, exiting.",
totalReadSize);
abort();
}
return nullptr;
}
// Advance buffered read if not yet consumed.
size_t remaining = totalReadSize;
size_t bufferedReadSize =
m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf,
m_readBuf + (m_read - m_readLeft),
bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
}
if (!remaining) return userReadBuf;
// Read up to kReadSize bytes if all buffered read has been consumed.
size_t maxRead = m_readLeft ? 0 : kReadSize;
ssize_t actual = 0;
if (maxRead) {
actual = speculativeRead(m_readBuf, maxRead);
// Updated buffered read size.
if (actual > 0) {
m_read = m_readLeft = actual;
}
if (actual == 0) {
mesa_logd("%s: end of pipe", __FUNCTION__);
return NULL;
}
}
// Consume buffered read and read more if necessary.
while (remaining) {
bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf + (totalReadSize - remaining),
m_readBuf + (m_read - m_readLeft),
bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
continue;
}
actual = speculativeRead(m_readBuf, kReadSize);
if (actual == 0) {
mesa_logd("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
return NULL;
}
if (actual > 0) {
m_read = m_readLeft = actual;
continue;
}
}
resetBackoff();
return userReadBuf;
}
const unsigned char *AddressSpaceStream::read(void *buf, size_t *inout_len) {
unsigned char* dst = (unsigned char*)buf;
size_t wanted = *inout_len;
ssize_t actual = speculativeRead(dst, wanted);
if (actual >= 0) {
*inout_len = actual;
} else {
return nullptr;
}
return (const unsigned char*)dst;
}
int AddressSpaceStream::writeFully(const void* buf, size_t size) {
MESA_TRACE_SCOPE("writeFully");
ensureType3Finished();
ensureType1Finished();
m_context.ring_config->transfer_size = size;
m_context.ring_config->transfer_mode = 3;
size_t sent = 0;
size_t preferredChunkSize = m_writeBufferSize / 4;
size_t chunkSize = size < preferredChunkSize ? size : preferredChunkSize;
const uint8_t* bufferBytes = (const uint8_t*)buf;
bool hostPinged = false;
while (sent < size) {
size_t remaining = size - sent;
size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize;
long sentChunks =
ring_buffer_view_write(
m_context.to_host_large_xfer.ring,
&m_context.to_host_large_xfer.view,
bufferBytes + sent, sendThisTime, 1);
if (!hostPinged && *(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
*(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
notifyAvailable();
hostPinged = true;
}
if (sentChunks == 0) {
ring_buffer_yield();
backoff();
}
sent += sentChunks * sendThisTime;
if (isInError()) {
return -1;
}
}
bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
if (!isRenderingAfter) {
notifyAvailable();
}
ensureType3Finished();
resetBackoff();
m_context.ring_config->transfer_mode = 1;
m_written += size;
float mb = (float)m_written / 1048576.0f;
if (mb > 100.0f) {
mesa_logd("%s: %f mb in %d notifs. %f mb/notif\n", __func__, mb, m_notifs,
m_notifs ? mb / (float)m_notifs : 0.0f);
m_notifs = 0;
m_written = 0;
}
return 0;
}
int AddressSpaceStream::writeFullyAsync(const void* buf, size_t size) {
MESA_TRACE_SCOPE("writeFullyAsync");
ensureType3Finished();
ensureType1Finished();
__atomic_store_n(&m_context.ring_config->transfer_size, size, __ATOMIC_RELEASE);
m_context.ring_config->transfer_mode = 3;
size_t sent = 0;
size_t preferredChunkSize = m_writeBufferSize / 2;
size_t chunkSize = size < preferredChunkSize ? size : preferredChunkSize;
const uint8_t* bufferBytes = (const uint8_t*)buf;
bool pingedHost = false;
while (sent < size) {
size_t remaining = size - sent;
size_t sendThisTime = remaining < chunkSize ? remaining : chunkSize;
long sentChunks =
ring_buffer_view_write(
m_context.to_host_large_xfer.ring,
&m_context.to_host_large_xfer.view,
bufferBytes + sent, sendThisTime, 1);
uint32_t hostState = __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
if (!pingedHost &&
hostState != ASG_HOST_STATE_CAN_CONSUME &&
hostState != ASG_HOST_STATE_RENDERING) {
pingedHost = true;
notifyAvailable();
}
if (sentChunks == 0) {
ring_buffer_yield();
backoff();
}
sent += sentChunks * sendThisTime;
if (isInError()) {
return -1;
}
}
bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
if (!isRenderingAfter) {
notifyAvailable();
}
resetBackoff();
m_context.ring_config->transfer_mode = 1;
m_written += size;
float mb = (float)m_written / 1048576.0f;
if (mb > 100.0f) {
mesa_logd("%s: %f mb in %d notifs. %f mb/notif\n", __func__, mb, m_notifs,
m_notifs ? mb / (float)m_notifs : 0.0f);
m_notifs = 0;
m_written = 0;
}
return 0;
}
const unsigned char *AddressSpaceStream::commitBufferAndReadFully(
size_t writeSize, void *userReadBufPtr, size_t totalReadSize) {
if (m_usingTmpBuf) {
writeFully(m_tmpBuf, writeSize);
m_usingTmpBuf = false;
m_tmpBufXferSize = 0;
return readFully(userReadBufPtr, totalReadSize);
} else {
commitBuffer(writeSize);
return readFully(userReadBufPtr, totalReadSize);
}
}
bool AddressSpaceStream::isInError() const {
return 1 == m_context.ring_config->in_error;
}
ssize_t AddressSpaceStream::speculativeRead(unsigned char* readBuffer, size_t trySize) {
ensureType3Finished();
ensureType1Finished();
size_t actuallyRead = 0;
while (!actuallyRead) {
uint32_t readAvail =
ring_buffer_available_read(
m_context.from_host_large_xfer.ring,
&m_context.from_host_large_xfer.view);
if (!readAvail) {
ring_buffer_yield();
backoff();
continue;
}
uint32_t toRead = readAvail > trySize ? trySize : readAvail;
long stepsRead = ring_buffer_view_read(
m_context.from_host_large_xfer.ring,
&m_context.from_host_large_xfer.view,
readBuffer, toRead, 1);
actuallyRead += stepsRead * toRead;
if (isInError()) {
return -1;
}
}
return actuallyRead;
}
void AddressSpaceStream::notifyAvailable() {
MESA_TRACE_SCOPE("PING");
struct address_space_ping request;
request.metadata = ASG_NOTIFY_AVAILABLE;
request.resourceId = m_resourceId;
m_ops.ping(m_handle, &request);
++m_notifs;
}
uint32_t AddressSpaceStream::getRelativeBufferPos(uint32_t pos) {
return pos & m_writeBufferMask;
}
void AddressSpaceStream::advanceWrite() {
m_writeStart += m_context.ring_config->flush_interval;
if (m_writeStart == m_buf + m_context.ring_config->buffer_size) {
m_writeStart = m_buf;
}
}
void AddressSpaceStream::ensureConsumerFinishing() {
uint32_t currAvailRead = ring_buffer_available_read(m_context.to_host, 0);
while (currAvailRead) {
ring_buffer_yield();
uint32_t nextAvailRead = ring_buffer_available_read(m_context.to_host, 0);
if (nextAvailRead != currAvailRead) {
break;
}
if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
*(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
notifyAvailable();
break;
}
backoff();
}
}
void AddressSpaceStream::ensureType1Finished() {
MESA_TRACE_SCOPE("ensureType1Finished");
uint32_t currAvailRead =
ring_buffer_available_read(m_context.to_host, 0);
while (currAvailRead) {
backoff();
ring_buffer_yield();
currAvailRead = ring_buffer_available_read(m_context.to_host, 0);
if (isInError()) {
return;
}
}
}
void AddressSpaceStream::ensureType3Finished() {
MESA_TRACE_SCOPE("ensureType3Finished");
uint32_t availReadLarge =
ring_buffer_available_read(
m_context.to_host_large_xfer.ring,
&m_context.to_host_large_xfer.view);
while (availReadLarge) {
ring_buffer_yield();
backoff();
availReadLarge =
ring_buffer_available_read(
m_context.to_host_large_xfer.ring,
&m_context.to_host_large_xfer.view);
if (*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
*(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
notifyAvailable();
}
if (isInError()) {
return;
}
}
}
int AddressSpaceStream::type1Write(uint32_t bufferOffset, size_t size) {
MESA_TRACE_SCOPE("type1Write");
ensureType3Finished();
size_t sent = 0;
size_t sizeForRing = sizeof(struct asg_type1_xfer);
struct asg_type1_xfer xfer = {
bufferOffset,
(uint32_t)size,
};
uint8_t* writeBufferBytes = (uint8_t*)(&xfer);
uint32_t maxOutstanding = 1;
uint32_t maxSteps = m_context.ring_config->buffer_size /
m_context.ring_config->flush_interval;
if (maxSteps > 1) maxOutstanding = maxSteps - 1;
uint32_t ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0);
while (ringAvailReadNow >= maxOutstanding * sizeForRing) {
ringAvailReadNow = ring_buffer_available_read(m_context.to_host, 0);
}
bool hostPinged = false;
while (sent < sizeForRing) {
long sentChunks = ring_buffer_write(
m_context.to_host,
writeBufferBytes + sent,
sizeForRing - sent, 1);
if (!hostPinged &&
*(m_context.host_state) != ASG_HOST_STATE_CAN_CONSUME &&
*(m_context.host_state) != ASG_HOST_STATE_RENDERING) {
notifyAvailable();
hostPinged = true;
}
if (sentChunks == 0) {
ring_buffer_yield();
backoff();
}
sent += sentChunks * (sizeForRing - sent);
if (isInError()) {
return -1;
}
}
bool isRenderingAfter = ASG_HOST_STATE_RENDERING == __atomic_load_n(m_context.host_state, __ATOMIC_ACQUIRE);
if (!isRenderingAfter) {
notifyAvailable();
}
m_written += size;
float mb = (float)m_written / 1048576.0f;
if (mb > 100.0f) {
mesa_logd("%s: %f mb in %d notifs. %f mb/notif\n", __func__, mb, m_notifs,
m_notifs ? mb / (float)m_notifs : 0.0f);
m_notifs = 0;
m_written = 0;
}
resetBackoff();
return 0;
}
void AddressSpaceStream::backoff() {
static const uint32_t kBackoffItersThreshold = 50000000;
static const uint32_t kBackoffFactorDoublingIncrement = 50000000;
++m_backoffIters;
if (m_backoffIters > kBackoffItersThreshold) {
usleep(m_backoffFactor);
uint32_t itersSoFarAfterThreshold = m_backoffIters - kBackoffItersThreshold;
if (itersSoFarAfterThreshold > kBackoffFactorDoublingIncrement) {
m_backoffFactor = m_backoffFactor << 1;
if (m_backoffFactor > 1000) m_backoffFactor = 1000;
m_backoffIters = kBackoffItersThreshold;
}
}
}
void AddressSpaceStream::resetBackoff() {
m_backoffIters = 0;
m_backoffFactor = 1;
}
@@ -0,0 +1,120 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#include "GoldfishAddressSpaceStream.h"
#include "goldfish_address_space.h"
#include "util/log.h"
AddressSpaceStream* createGoldfishAddressSpaceStream(size_t ignored_bufSize) {
// Ignore incoming ignored_bufSize
(void)ignored_bufSize;
auto handle = goldfish_address_space_open();
address_space_handle_t child_device_handle;
if (!goldfish_address_space_set_subdevice_type(handle, GoldfishAddressSpaceSubdeviceType::Graphics, &child_device_handle)) {
mesa_loge("AddressSpaceStream::create failed (initial device create)\n");
goldfish_address_space_close(handle);
return nullptr;
}
struct address_space_ping request;
request.metadata = ASG_GET_RING;
if (!goldfish_address_space_ping(child_device_handle, &request)) {
mesa_loge("AddressSpaceStream::create failed (get ring)\n");
goldfish_address_space_close(child_device_handle);
return nullptr;
}
uint64_t ringOffset = request.metadata;
request.metadata = ASG_GET_BUFFER;
if (!goldfish_address_space_ping(child_device_handle, &request)) {
mesa_loge("AddressSpaceStream::create failed (get buffer)\n");
goldfish_address_space_close(child_device_handle);
return nullptr;
}
uint64_t bufferOffset = request.metadata;
uint64_t bufferSize = request.size;
if (!goldfish_address_space_claim_shared(
child_device_handle, ringOffset, sizeof(asg_ring_storage))) {
mesa_loge("AddressSpaceStream::create failed (claim ring storage)\n");
goldfish_address_space_close(child_device_handle);
return nullptr;
}
if (!goldfish_address_space_claim_shared(
child_device_handle, bufferOffset, bufferSize)) {
mesa_loge("AddressSpaceStream::create failed (claim buffer storage)\n");
goldfish_address_space_unclaim_shared(child_device_handle, ringOffset);
goldfish_address_space_close(child_device_handle);
return nullptr;
}
char* ringPtr = (char*)goldfish_address_space_map(
child_device_handle, ringOffset, sizeof(struct asg_ring_storage));
if (!ringPtr) {
mesa_loge("AddressSpaceStream::create failed (map ring storage)\n");
goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset);
goldfish_address_space_unclaim_shared(child_device_handle, ringOffset);
goldfish_address_space_close(child_device_handle);
return nullptr;
}
char* bufferPtr = (char*)goldfish_address_space_map(
child_device_handle, bufferOffset, bufferSize);
if (!bufferPtr) {
mesa_loge("AddressSpaceStream::create failed (map buffer storage)\n");
goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage));
goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset);
goldfish_address_space_unclaim_shared(child_device_handle, ringOffset);
goldfish_address_space_close(child_device_handle);
return nullptr;
}
struct asg_context context =
asg_context_create(
ringPtr, bufferPtr, bufferSize);
request.metadata = ASG_SET_VERSION;
request.size = 1; // version 1
if (!goldfish_address_space_ping(child_device_handle, &request)) {
mesa_loge("AddressSpaceStream::create failed (get buffer)\n");
goldfish_address_space_unmap(bufferPtr, bufferSize);
goldfish_address_space_unmap(ringPtr, sizeof(struct asg_ring_storage));
goldfish_address_space_unclaim_shared(child_device_handle, bufferOffset);
goldfish_address_space_unclaim_shared(child_device_handle, ringOffset);
goldfish_address_space_close(child_device_handle);
return nullptr;
}
uint32_t version = request.size;
context.ring_config->transfer_mode = 1;
context.ring_config->host_consumed_pos = 0;
context.ring_config->guest_write_pos = 0;
struct address_space_ops ops = {
.open = goldfish_address_space_open,
.close = goldfish_address_space_close,
.claim_shared = goldfish_address_space_claim_shared,
.unclaim_shared = goldfish_address_space_unclaim_shared,
.map = goldfish_address_space_map,
.unmap = goldfish_address_space_unmap,
.set_subdevice_type = goldfish_address_space_set_subdevice_type,
.ping = goldfish_address_space_ping,
};
AddressSpaceStream* res = new AddressSpaceStream(child_device_handle, version, context,
ringOffset, bufferOffset, ops);
return res;
}
@@ -0,0 +1,149 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#include "VirtioGpuAddressSpaceStream.h"
#include <errno.h>
#include "util/log.h"
#include "util/os_misc.h"
static bool GetRingParamsFromCapset(enum VirtGpuCapset capset, const VirtGpuCaps& caps,
uint32_t& ringSize, uint32_t& bufferSize,
uint32_t& blobAlignment) {
uint64_t guest_page_size = 4096;
os_get_page_size(&guest_page_size);
switch (capset) {
case kCapsetGfxStreamVulkan:
ringSize = caps.vulkanCapset.ringSize;
bufferSize = caps.vulkanCapset.bufferSize;
blobAlignment = caps.vulkanCapset.blobAlignment;
break;
case kCapsetGfxStreamGles:
ringSize = caps.glesCapset.ringSize;
bufferSize = caps.glesCapset.bufferSize;
blobAlignment = caps.glesCapset.blobAlignment;
break;
case kCapsetGfxStreamComposer:
ringSize = caps.composerCapset.ringSize;
bufferSize = caps.composerCapset.bufferSize;
blobAlignment = caps.composerCapset.blobAlignment;
break;
default:
return false;
}
blobAlignment = std::max(blobAlignment, (uint32_t)guest_page_size);
return true;
}
static address_space_handle_t virtgpu_address_space_open() {
return (address_space_handle_t)(-EINVAL);
}
static void virtgpu_address_space_close(address_space_handle_t) {
// Handle opened by VirtioGpuDevice wrapper
}
static bool virtgpu_address_space_ping(address_space_handle_t, struct address_space_ping* info) {
int ret;
struct VirtGpuExecBuffer exec = {};
VirtGpuDevice* instance = VirtGpuDevice::getInstance();
struct gfxstreamContextPing ping = {};
ping.hdr.opCode = GFXSTREAM_CONTEXT_PING;
ping.resourceId = info->resourceId;
exec.command = static_cast<void*>(&ping);
exec.command_size = sizeof(ping);
ret = instance->execBuffer(exec, nullptr);
if (ret)
return false;
return true;
}
AddressSpaceStream* createVirtioGpuAddressSpaceStream(enum VirtGpuCapset capset) {
VirtGpuResourcePtr pipe, blob;
VirtGpuResourceMappingPtr pipeMapping, blobMapping;
struct VirtGpuExecBuffer exec = {};
struct VirtGpuCreateBlob blobCreate = {};
struct gfxstreamContextCreate contextCreate = {};
uint32_t ringSize = 0;
uint32_t bufferSize = 0;
uint32_t blobAlignment = 0;
char* blobAddr, *bufferPtr;
int ret;
VirtGpuDevice* instance = VirtGpuDevice::getInstance();
auto caps = instance->getCaps();
if (!caps.params[kParamResourceBlob]) {
mesa_loge("VirtGpuDevice does not support blob-resources!");
return nullptr;
}
if (!caps.params[kParamHostVisible] && !caps.params[kParamCreateGuestHandle]) {
mesa_loge("VirtGpuDevice must support at least one of: 1) host-visible memory (kParamHostVisible) or 2) host handles created from guest memory (kParamCreateGuestHandle).");
return nullptr;
}
if (!GetRingParamsFromCapset(capset, caps, ringSize, bufferSize, blobAlignment)) {
mesa_loge("Failed to get ring parameters");
return nullptr;
}
blobCreate.blobId = 0;
blobCreate.blobMem = kBlobMemHost3d;
blobCreate.flags = kBlobFlagMappable;
blobCreate.size = ALIGN_POT(ringSize + bufferSize, blobAlignment);
blob = instance->createBlob(blobCreate);
if (!blob)
return nullptr;
// Context creation command
contextCreate.hdr.opCode = GFXSTREAM_CONTEXT_CREATE;
contextCreate.resourceId = blob->getResourceHandle();
exec.command = static_cast<void*>(&contextCreate);
exec.command_size = sizeof(contextCreate);
ret = instance->execBuffer(exec, blob.get());
if (ret)
return nullptr;
// Wait occurs on global timeline -- should we use context specific one?
ret = blob->wait();
if (ret)
return nullptr;
blobMapping = blob->createMapping();
if (!blobMapping)
return nullptr;
blobAddr = reinterpret_cast<char*>(blobMapping->asRawPtr());
bufferPtr = blobAddr + sizeof(struct asg_ring_storage);
struct asg_context context = asg_context_create(blobAddr, bufferPtr, bufferSize);
context.ring_config->transfer_mode = 1;
context.ring_config->host_consumed_pos = 0;
context.ring_config->guest_write_pos = 0;
struct address_space_ops ops = {
.open = virtgpu_address_space_open,
.close = virtgpu_address_space_close,
.ping = virtgpu_address_space_ping,
};
AddressSpaceStream* res =
new AddressSpaceStream((address_space_handle_t)(-1), 1, context, 0, 0, ops);
res->setMapping(blobMapping);
res->setResourceId(contextCreate.resourceId);
return res;
}
@@ -0,0 +1,538 @@
/*
* Copyright 2019 Google
* SPDX-License-Identifier: MIT
*/
#include "goldfish_address_space.h"
#include <errno.h>
#include <fcntl.h>
#include <linux/ioctl.h>
#include <linux/types.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <cstdlib>
#include <cstring>
#include <memory>
#include "util/log.h"
// See virgl_hw.h and p_defines.h
#define VIRGL_FORMAT_R8_UNORM 64
#define VIRGL_BIND_CUSTOM (1 << 17)
#define PIPE_BUFFER 0
#ifdef PAGE_SIZE
constexpr size_t kPageSize = PAGE_SIZE;
#else
static const size_t kPageSize = getpagesize();
#endif
namespace {
struct goldfish_address_space_allocate_block {
__u64 size;
__u64 offset;
__u64 phys_addr;
};
struct goldfish_address_space_claim_shared {
__u64 offset;
__u64 size;
};
#define GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC 'G'
#define GOLDFISH_ADDRESS_SPACE_IOCTL_OP(OP, T) _IOWR(GOLDFISH_ADDRESS_SPACE_IOCTL_MAGIC, OP, T)
#define GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(10, struct goldfish_address_space_allocate_block)
#define GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK GOLDFISH_ADDRESS_SPACE_IOCTL_OP(11, __u64)
#define GOLDFISH_ADDRESS_SPACE_IOCTL_PING GOLDFISH_ADDRESS_SPACE_IOCTL_OP(12, struct address_space_ping)
#define GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED GOLDFISH_ADDRESS_SPACE_IOCTL_OP(13, struct goldfish_address_space_claim_shared)
#define GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED GOLDFISH_ADDRESS_SPACE_IOCTL_OP(14, __u64)
const char GOLDFISH_ADDRESS_SPACE_DEVICE_NAME[] = "/dev/goldfish_address_space";
const int HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID = 1;
const int HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID = 2;
int create_address_space_fd()
{
return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR);
}
long ioctl_allocate(int fd, struct goldfish_address_space_allocate_block *request)
{
return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_ALLOCATE_BLOCK, request);
}
long ioctl_deallocate(int fd, uint64_t offset)
{
return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_DEALLOCATE_BLOCK, &offset);
}
long ioctl_ping(int fd, struct address_space_ping *request)
{
return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_PING, request);
}
long set_address_space_subdevice_type(int fd, uint64_t type)
{
struct address_space_ping request;
::memset(&request, 0, sizeof(request));
request.resourceId = sizeof(request);
request.metadata = type;
long ret = ioctl_ping(fd, &request);
if (ret) {
return ret;
}
return request.metadata;
}
long ioctl_claim_shared(int fd, struct goldfish_address_space_claim_shared *request)
{
return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_CLAIM_SHARED, request);
}
long ioctl_unclaim_shared(int fd, uint64_t offset)
{
return ::ioctl(fd, GOLDFISH_ADDRESS_SPACE_IOCTL_UNCLAIM_SHARED, &offset);
}
} // namespace
GoldfishAddressSpaceBlockProvider::GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice)
: m_handle(create_address_space_fd())
{
if ((subdevice != GoldfishAddressSpaceSubdeviceType::NoSubdevice) && is_opened()) {
const long ret = set_address_space_subdevice_type(m_handle, subdevice);
if (ret != 0 && ret != subdevice) { // TODO: retire the 'ret != subdevice' check
mesa_loge("%s: set_address_space_subdevice_type failed for device_type=%lu, ret=%ld",
__func__, static_cast<unsigned long>(subdevice), ret);
close();
}
}
}
GoldfishAddressSpaceBlockProvider::~GoldfishAddressSpaceBlockProvider()
{
if (is_opened()) {
::close(m_handle);
}
}
bool GoldfishAddressSpaceBlockProvider::is_opened() const
{
return m_handle >= 0;
}
void GoldfishAddressSpaceBlockProvider::close()
{
if (is_opened()) {
::close(m_handle);
m_handle = -1;
}
}
address_space_handle_t GoldfishAddressSpaceBlockProvider::release()
{
address_space_handle_t handle = m_handle;
m_handle = -1;
return handle;
}
void GoldfishAddressSpaceBlockProvider::closeHandle(address_space_handle_t handle)
{
::close(handle);
}
GoldfishAddressSpaceBlock::GoldfishAddressSpaceBlock()
: m_handle(-1)
, m_mmaped_ptr(NULL)
, m_phys_addr(0)
, m_host_addr(0)
, m_offset(0)
, m_size(0) {}
GoldfishAddressSpaceBlock::~GoldfishAddressSpaceBlock()
{
destroy();
}
GoldfishAddressSpaceBlock &GoldfishAddressSpaceBlock::operator=(const GoldfishAddressSpaceBlock &rhs)
{
m_mmaped_ptr = rhs.m_mmaped_ptr;
m_phys_addr = rhs.m_phys_addr;
m_host_addr = rhs.m_host_addr;
m_offset = rhs.m_offset;
m_size = rhs.m_size;
m_handle = rhs.m_handle;
return *this;
}
bool GoldfishAddressSpaceBlock::allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size)
{
destroy();
if (!provider->is_opened()) {
return false;
}
struct goldfish_address_space_allocate_block request;
::memset(&request, 0, sizeof(request));
request.size = size;
long res = ioctl_allocate(provider->m_handle, &request);
if (res) {
return false;
} else {
m_phys_addr = request.phys_addr;
m_offset = request.offset;
m_size = request.size;
m_handle = provider->m_handle;
m_is_shared_mapping = false;
return true;
}
}
bool GoldfishAddressSpaceBlock::claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size)
{
destroy();
if (!provider->is_opened()) {
return false;
}
struct goldfish_address_space_claim_shared request;
request.offset = offset;
request.size = size;
long res = ioctl_claim_shared(provider->m_handle, &request);
if (res) {
return false;
}
m_offset = offset;
m_size = size;
m_handle = provider->m_handle;
m_is_shared_mapping = true;
return true;
}
uint64_t GoldfishAddressSpaceBlock::physAddr() const
{
return m_phys_addr;
}
uint64_t GoldfishAddressSpaceBlock::hostAddr() const
{
return m_host_addr;
}
void *GoldfishAddressSpaceBlock::mmap(uint64_t host_addr)
{
if (m_size == 0) {
mesa_loge("%s: called with zero size\n", __func__);
return NULL;
}
if (m_mmaped_ptr) {
mesa_loge("'mmap' called for an already mmaped address block");
::abort();
}
void *result;
const int res = memoryMap(NULL, m_size, m_handle, m_offset, &result);
if (res) {
mesa_loge(
"%s: host memory map failed with size 0x%llx "
"off 0x%llx errno %d\n",
__func__, (unsigned long long)m_size, (unsigned long long)m_offset, res);
return NULL;
} else {
m_mmaped_ptr = result;
m_host_addr = host_addr;
return guestPtr();
}
}
void *GoldfishAddressSpaceBlock::guestPtr() const
{
return reinterpret_cast<char *>(m_mmaped_ptr) + (m_host_addr & (kPageSize - 1));
}
void GoldfishAddressSpaceBlock::destroy()
{
if (m_mmaped_ptr && m_size) {
memoryUnmap(m_mmaped_ptr, m_size);
m_mmaped_ptr = NULL;
}
if (m_size) {
long res = -EINVAL;
if (m_is_shared_mapping) {
res = ioctl_unclaim_shared(m_handle, m_offset);
if (res) {
mesa_loge("ioctl_unclaim_shared failed, res=%ld", res);
::abort();
}
} else {
res = ioctl_deallocate(m_handle, m_offset);
if (res) {
mesa_loge("ioctl_deallocate failed, res=%ld", res);
::abort();
}
}
m_is_shared_mapping = false;
m_phys_addr = 0;
m_host_addr = 0;
m_offset = 0;
m_size = 0;
}
}
void GoldfishAddressSpaceBlock::release()
{
m_handle = -1;
m_mmaped_ptr = NULL;
m_phys_addr = 0;
m_host_addr = 0;
m_offset = 0;
m_size = 0;
}
int GoldfishAddressSpaceBlock::memoryMap(void *addr,
size_t len,
address_space_handle_t fd,
uint64_t off,
void** dst) {
void* ptr = ::mmap64(addr, len, PROT_WRITE, MAP_SHARED, fd, off);
if (MAP_FAILED == ptr) {
return errno;
} else {
*dst = ptr;
return 0;
}
}
void GoldfishAddressSpaceBlock::memoryUnmap(void *ptr, size_t size)
{
::munmap(ptr, size);
}
GoldfishAddressSpaceHostMemoryAllocator::GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots)
: m_provider(useSharedSlots
? GoldfishAddressSpaceSubdeviceType::SharedSlotsHostMemoryAllocator
: GoldfishAddressSpaceSubdeviceType::HostMemoryAllocator),
m_useSharedSlots(useSharedSlots)
{}
bool GoldfishAddressSpaceHostMemoryAllocator::is_opened() const { return m_provider.is_opened(); }
long GoldfishAddressSpaceHostMemoryAllocator::hostMalloc(GoldfishAddressSpaceBlock *block, size_t size)
{
if (size == 0) {
return -EINVAL;
}
if (block->size() > 0) {
return -EINVAL;
}
if (!m_provider.is_opened()) {
return -ENODEV;
}
struct address_space_ping request;
if (m_useSharedSlots) {
// shared memory slots are supported
::memset(&request, 0, sizeof(request));
request.resourceId = sizeof(request);
request.size = size;
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
long ret = ioctl_ping(m_provider.m_handle, &request);
if (ret) {
return ret;
}
ret = static_cast<long>(request.metadata);
if (ret) {
return ret;
}
block->claimShared(&m_provider, request.offset, request.size);
} else {
// shared memory slots are not supported
if (!block->allocate(&m_provider, size)) {
return -ENOMEM;
}
::memset(&request, 0, sizeof(request));
request.resourceId = sizeof(request);
request.offset = block->offset();
request.size = block->size();
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_ALLOCATE_ID;
long ret = ioctl_ping(m_provider.m_handle, &request);
if (ret) {
return ret;
}
ret = static_cast<long>(request.metadata);
if (ret) {
return ret;
}
}
block->mmap(0);
return 0;
}
void GoldfishAddressSpaceHostMemoryAllocator::hostFree(GoldfishAddressSpaceBlock *block)
{
if (block->size() == 0) {
return;
}
if (!m_provider.is_opened()) {
mesa_loge("%s: device is not available", __func__);
::abort();
}
if (block->guestPtr()) {
struct address_space_ping request;
::memset(&request, 0, sizeof(request));
request.resourceId = sizeof(request);
request.offset = block->offset();
request.metadata = HOST_MEMORY_ALLOCATOR_COMMAND_UNALLOCATE_ID;
const long ret = ioctl_ping(m_provider.m_handle, &request);
if (ret) {
mesa_loge("%s: ioctl_ping failed, ret=%ld", __func__, ret);
::abort();
}
}
block->replace(NULL);
}
address_space_handle_t goldfish_address_space_open() {
return ::open(GOLDFISH_ADDRESS_SPACE_DEVICE_NAME, O_RDWR);
}
void goldfish_address_space_close(address_space_handle_t handle) {
::close(handle);
}
bool goldfish_address_space_allocate(
address_space_handle_t handle,
size_t size, uint64_t* phys_addr, uint64_t* offset) {
struct goldfish_address_space_allocate_block request;
::memset(&request, 0, sizeof(request));
request.size = size;
long res = ioctl_allocate(handle, &request);
if (res) return false;
*phys_addr = request.phys_addr;
*offset = request.offset;
return true;
}
bool goldfish_address_space_free(
address_space_handle_t handle, uint64_t offset) {
long res = ioctl_deallocate(handle, offset);
if (res) {
mesa_loge("ioctl_deallocate failed, res=%ld", res);
::abort();
}
return true;
}
bool goldfish_address_space_claim_shared(
address_space_handle_t handle, uint64_t offset, uint64_t size) {
struct goldfish_address_space_claim_shared request;
request.offset = offset;
request.size = size;
long res = ioctl_claim_shared(handle, &request);
if (res) return false;
return true;
}
bool goldfish_address_space_unclaim_shared(
address_space_handle_t handle, uint64_t offset) {
long res = ioctl_unclaim_shared(handle, offset);
if (res) {
mesa_loge("ioctl_unclaim_shared failed, res=%ld", res);
::abort();
}
return true;
}
// pgoff is the offset into the page to return in the result
void* goldfish_address_space_map(
address_space_handle_t handle,
uint64_t offset, uint64_t size,
uint64_t pgoff) {
void* res = ::mmap64(0, size, PROT_WRITE, MAP_SHARED, handle, offset);
if (res == MAP_FAILED) {
mesa_loge("%s: failed to map. errno: %d\n", __func__, errno);
return 0;
}
return (void*)(((char*)res) + (uintptr_t)(pgoff & (kPageSize - 1)));
}
void goldfish_address_space_unmap(void* ptr, uint64_t size) {
void* pagePtr = (void*)(((uintptr_t)ptr) & ~(kPageSize - 1));
::munmap(pagePtr, size);
}
bool goldfish_address_space_set_subdevice_type(
address_space_handle_t handle, GoldfishAddressSpaceSubdeviceType type,
address_space_handle_t* handle_out) {
struct address_space_ping request;
request.metadata = (uint64_t)type;
*handle_out = handle;
return goldfish_address_space_ping(handle, &request);
}
bool goldfish_address_space_ping(
address_space_handle_t handle,
struct address_space_ping* ping) {
long res = ioctl_ping(handle, ping);
if (res) {
mesa_loge("%s: ping failed: errno: %d\n", __func__, errno);
return false;
}
return true;
}
void GoldfishAddressSpaceBlock::replace(GoldfishAddressSpaceBlock *other)
{
destroy();
if (other) {
*this = *other;
*other = GoldfishAddressSpaceBlock();
}
}
@@ -0,0 +1,85 @@
/*
* Copyright 2011 Google
* SPDX-License-Identifier: MIT
*/
#ifndef __ADDRESS_SPACE_STREAM_H
#define __ADDRESS_SPACE_STREAM_H
#include "VirtGpu.h"
#include "address_space.h"
#include "address_space_graphics_types.h"
#include "gfxstream/guest/IOStream.h"
using gfxstream::guest::IOStream;
class AddressSpaceStream : public IOStream {
public:
explicit AddressSpaceStream(address_space_handle_t handle, uint32_t version,
struct asg_context context, uint64_t ringOffset,
uint64_t writeBufferOffset, struct address_space_ops ops);
~AddressSpaceStream();
virtual size_t idealAllocSize(size_t len);
virtual void* allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char* readFully(void* buf, size_t len);
virtual const unsigned char* read(void* buf, size_t* inout_len);
virtual int writeFully(const void* buf, size_t len);
virtual int writeFullyAsync(const void* buf, size_t len);
virtual const unsigned char* commitBufferAndReadFully(size_t size, void* buf, size_t len);
void setMapping(VirtGpuResourceMappingPtr mapping) { m_mapping = mapping; }
void setResourceId(uint32_t id) { m_resourceId = id; }
private:
bool isInError() const;
ssize_t speculativeRead(unsigned char* readBuffer, size_t trySize);
void notifyAvailable();
uint32_t getRelativeBufferPos(uint32_t pos);
void advanceWrite();
void ensureConsumerFinishing();
void ensureType1Finished();
void ensureType3Finished();
int type1Write(uint32_t offset, size_t size);
void backoff();
void resetBackoff();
VirtGpuResourceMappingPtr m_mapping = nullptr;
struct address_space_ops m_ops;
unsigned char* m_tmpBuf;
size_t m_tmpBufSize;
size_t m_tmpBufXferSize;
bool m_usingTmpBuf;
unsigned char* m_readBuf;
size_t m_read;
size_t m_readLeft;
address_space_handle_t m_handle;
uint32_t m_version;
struct asg_context m_context;
uint64_t m_ringOffset;
uint64_t m_writeBufferOffset;
uint32_t m_writeBufferSize;
uint32_t m_writeBufferMask;
unsigned char* m_buf;
unsigned char* m_writeStart;
uint32_t m_writeStep;
uint32_t m_notifs;
uint32_t m_written;
uint64_t m_backoffIters;
uint64_t m_backoffFactor;
size_t m_ringStorageSize;
uint32_t m_resourceId = 0;
};
#endif
@@ -0,0 +1,10 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "AddressSpaceStream.h"
AddressSpaceStream* createGoldfishAddressSpaceStream(size_t bufSize);
@@ -0,0 +1,10 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "AddressSpaceStream.h"
AddressSpaceStream* createVirtioGpuAddressSpaceStream(enum VirtGpuCapset capset);
@@ -0,0 +1,66 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <inttypes.h>
#include <stddef.h>
typedef int address_space_handle_t;
enum AddressSpaceSubdeviceType {
NoSubdevice = -1,
Graphics = 0,
Media = 1,
HostMemoryAllocator = 5,
SharedSlotsHostMemoryAllocator = 6,
VirtioGpuGraphics = 10,
};
// We also expose the ping info struct that is shared between host and guest.
struct address_space_ping {
uint64_t offset;
uint64_t size;
uint64_t metadata;
uint32_t resourceId;
uint32_t wait_fd;
uint32_t wait_flags;
uint32_t direction;
};
// typedef/struct to abstract over goldfish vs virtio-gpu implementations
typedef address_space_handle_t (*address_space_open_t)(void);
typedef void (*address_space_close_t)(address_space_handle_t);
typedef bool (*address_space_allocate_t)(
address_space_handle_t, size_t size, uint64_t* phys_addr, uint64_t* offset);
typedef bool (*address_space_free_t)(
address_space_handle_t, uint64_t offset);
typedef bool (*address_space_claim_shared_t)(
address_space_handle_t, uint64_t offset, uint64_t size);
typedef bool (*address_space_unclaim_shared_t)(
address_space_handle_t, uint64_t offset);
// pgoff is the offset into the page to return in the result
typedef void* (*address_space_map_t)(
address_space_handle_t, uint64_t offset, uint64_t size, uint64_t pgoff);
typedef void (*address_space_unmap_t)(void* ptr, uint64_t size);
typedef bool (*address_space_set_subdevice_type_t)(
address_space_handle_t, AddressSpaceSubdeviceType type, address_space_handle_t*);
typedef bool (*address_space_ping_t)(
address_space_handle_t, struct address_space_ping*);
struct address_space_ops {
address_space_open_t open;
address_space_close_t close;
address_space_claim_shared_t claim_shared;
address_space_unclaim_shared_t unclaim_shared;
address_space_map_t map;
address_space_unmap_t unmap;
address_space_set_subdevice_type_t set_subdevice_type;
address_space_ping_t ping;
};
@@ -0,0 +1,353 @@
/*
* Copyright 2019 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "ring_buffer.h"
#include <functional>
#include <cstddef>
// This file defines common types for address space graphics and provides
// documentation.
// Address space graphics======================================================
//
// Basic idea
//
// Address space graphics (ASG) is a subdevice of the address space device that
// provides a way to run graphics commands and data with fewer VM exits by
// leveraging shared memory ring buffers.
//
// Each GL/Vk thread in the guest is associated with a context (asg_context).
// asg_context consists of pointers into the shared memory that view it as a
// collection of ring buffers and a common write buffer.
//
// Consumer concept
//
// ASG does not assume a particular rendering backend (though we will use
// RenderThread's). This is for ease of coding/testing and flexibility; the
// implementation is not coupled to emugl/libOpenglRender.
//
// Instead, there is the concept of a "Consumer" of ASG that will do something
// with the data arriving from the shared memory region, and possibly reply
// back to the guest. We register functions to construct and deconstruct
// Consumers as part of emulator init (setConsumer).
//
// Guest workflow
//
// 1. Open address space device
//
// 2. Create the graphics context as the subdevice
//
// 3. ping(ASG_GET_RING) to get the offset/size of the ring buffer admin. info
//
// 4. ping(ASG_GET_BUFFER) to get the offset/size of the shared transfer buffer.
//
// 5. ioctl(CLAIM_SHARED) and mmap on those two offset/size pairs to get a
// guest-side mapping.
//
// 6. call asg_context_create on the ring and buffer pointers to create the asg_context.
//
// 7. Now the guest and host share asg_context pts and can communicate.
//
// 8. But usually the guest will sometimes need to ping(ASG_NOTIFY_AVAILABLE)
// so that the host side (which is usually a separate thread that we don't want
// to spin too much) wakes up and processes data.
namespace gfxstream {
namespace guest {
class Stream;
} // namespace base
} // namespace android
#define ADDRESS_SPACE_GRAPHICS_DEVICE_ID 0
#define ADDRESS_SPACE_GRAPHICS_PAGE_SIZE 4096
#define ADDRESS_SPACE_GRAPHICS_BLOCK_SIZE (16ULL * 1048576ULL)
// AddressSpaceGraphicsContext shares memory with
// the guest via the following layout:
extern "C" {
struct asg_ring_storage { // directly shared with guest
char to_host[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
char to_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
char from_host_large_xfer[ADDRESS_SPACE_GRAPHICS_PAGE_SIZE];
};
// Set by the address space graphics device to notify the guest that the host
// has slept or is able to consume something, or we are exiting, or there is an
// error.
enum asg_host_state {
// The host renderthread is asleep and needs to be woken up.
ASG_HOST_STATE_NEED_NOTIFY = 0,
// The host renderthread is active and can consume new data
// without notification.
ASG_HOST_STATE_CAN_CONSUME = 1,
// Normal exit
ASG_HOST_STATE_EXIT = 2,
// Error: Something weird happened and we need to exit.
ASG_HOST_STATE_ERROR = 3,
// Host is rendering
ASG_HOST_STATE_RENDERING = 4,
};
struct asg_ring_config;
// Each context has a pair of ring buffers for communication
// to and from the host. There is another ring buffer for large xfers
// to the host (all xfers from the host are already considered "large").
//
// Each context also comes with _one_ auxiliary buffer to hold both its own
// commands and to perform private DMA transfers.
struct asg_context { // ptrs into RingStorage
struct ring_buffer* to_host;
char* buffer;
asg_host_state* host_state;
asg_ring_config* ring_config;
struct ring_buffer_with_view to_host_large_xfer;
struct ring_buffer_with_view from_host_large_xfer;
};
// Helper function that will be common between guest and host:
// Given ring storage and a write buffer, returns asg_context that
// is the correct view into it.
static inline struct asg_context asg_context_create(
char* ring_storage,
char* buffer,
uint32_t buffer_size) {
struct asg_context res;
res.to_host =
reinterpret_cast<struct ring_buffer*>(
ring_storage +
offsetof(struct asg_ring_storage, to_host));
res.to_host_large_xfer.ring =
reinterpret_cast<struct ring_buffer*>(
ring_storage +
offsetof(struct asg_ring_storage, to_host_large_xfer));
res.from_host_large_xfer.ring =
reinterpret_cast<struct ring_buffer*>(
ring_storage +
offsetof(struct asg_ring_storage, from_host_large_xfer));
ring_buffer_init(res.to_host);
res.buffer = buffer;
res.host_state =
reinterpret_cast<asg_host_state*>(
&res.to_host->state);
res.ring_config =
reinterpret_cast<asg_ring_config*>(
res.to_host->config);
ring_buffer_view_init(
res.to_host_large_xfer.ring,
&res.to_host_large_xfer.view,
(uint8_t*)res.buffer, buffer_size);
ring_buffer_view_init(
res.from_host_large_xfer.ring,
&res.from_host_large_xfer.view,
(uint8_t*)res.buffer, buffer_size);
return res;
}
// During operation, the guest sends commands and data over the auxiliary
// buffer while using the |to_host| ring to communicate what parts of the auxiliary
// buffer is outstanding traffic needing to be consumed by the host.
// After a transfer completes to the host, the host may write back data.
// The guest then reads the results on the same auxiliary buffer
// while being notified of which parts to read via the |from_host| ring.
//
// The size of the auxiliary buffer and flush interval is defined by
// the following config.ini android_hw setting:
//
// 1) android_hw->hw_gltransport_asg_writeBufferSize
// 2) android_hw->hw_gltransport_asg_writeStepSize
//
// 1) the size for the auxiliary buffer
// 2) the step size over which commands are flushed to the host
//
// When transferring commands, command data is built up in writeStepSize
// chunks and flushed to the host when either writeStepSize is reached or
// the guest flushes explicitly.
//
// Command vs. Data Modes
//
// For command data larger than writeStepSize or when transferring data, we
// fall back to using a different mode where the entire auxiliary buffer is
// used to perform the transfer, |asg_writeBufferSize| steps at a time. The
// host is also notified of the total transport size.
//
// When writing back to the guest, it is assumed that the write buffer will
// be completely empty as the guest has already flushed and the host has
// already consumed all commands/data, and is writing back. In this case,
// the full auxiliary buffer is used at the same time for writing back to
// the guest.
//
// Larger / Shared transfers
//
// Each of |to_host| and |from_host| can contain elements of type 1, 2, or 3:
// Type 1: 8 bytes: 4 bytes offset, 4 bytes size. Relative to write buffer.
struct __attribute__((__packed__)) asg_type1_xfer {
uint32_t offset;
uint32_t size;
};
// Type 2: 16 bytes: 16 bytes offset into address space PCI space, 8 bytes
// size.
struct __attribute__((__packed__)) asg_type2_xfer {
uint64_t physAddr;
uint64_t size;
};
// Type 3: There is a large transfer of known size and the entire write buffer
// will be used to send it over.
//
// For type 1 transfers, we get the corresponding host virtual address by
// adding the offset to the beginning of the write buffer. For type 2
// transfers, we need to calculate the guest physical address and then call
// addressspacecontrolops.gethostptr, which is slower since it goes through
// a data structure map of existing mappings.
//
// The rings never contain a mix of type 1 and 2 elements. For to_host,
// the guest initiates changes between type 1 and 2.
//
// The config fields:
//
struct asg_ring_config {
// config[0]: size of the auxiliary buffer
uint32_t buffer_size;
// config[1]: flush interval for the auxiliary buffer
uint32_t flush_interval;
// the position of the interval in the auxiliary buffer
// that the host has read so far
uint32_t host_consumed_pos;
// the start of the places the guest might write to next
uint32_t guest_write_pos;
// 1 if transfers are of type 1, 2 if transfers of type 2,
// 3 if the overall transfer size is known and we are sending something large.
uint32_t transfer_mode;
// the size of the transfer, used if transfer size is known.
// Set before setting config[2] to 3.
uint32_t transfer_size;
// error state
uint32_t in_error;
};
// State/config changes may only occur if the ring is empty, or the state
// is transitioning to Error. That way, the host and guest have a chance to
// synchronize on the same state.
//
// Thus far we've established how commands and data are transferred
// to and from the host. Next, let's discuss how AddressSpaceGraphicsContext
// talks to the code that actually does something with the commands
// and sends data back.
} // extern "C"
namespace android {
namespace emulation {
namespace asg {
// Consumer Concept
//
// AddressSpaceGraphicsContext's are each associated with a consumer that
// takes data off the auxiliary buffer and to_host, while sending back data
// over the auxiliary buffer / from_host.
//
// will read the commands and write back data.
//
// The consumer type is fixed at startup. The interface is as follows:
// Called by the consumer, implemented in AddressSpaceGraphicsContext:
//
// Called when the consumer doesn't find anything to
// read in to_host. Will make the consumer sleep
// until another Ping(NotifyAvailable).
using OnUnavailableReadCallback =
std::function<int()>;
// Unpacks a type 2 transfer into host pointer and size.
using GetPtrCallback =
std::function<char*(uint64_t)>;
struct ConsumerCallbacks {
OnUnavailableReadCallback onUnavailableRead;
GetPtrCallback getPtr;
};
using ConsumerCreateCallback =
std::function<void* (struct asg_context, ConsumerCallbacks)>;
using ConsumerDestroyCallback =
std::function<void(void*)>;
using ConsumerSaveCallback =
std::function<void(void*, gfxstream::guest::Stream*)>;
using ConsumerLoadCallback =
std::function<void(void*, gfxstream::guest::Stream*)>;
struct ConsumerInterface {
ConsumerCreateCallback create;
ConsumerDestroyCallback destroy;
ConsumerSaveCallback save;
ConsumerLoadCallback load;
};
} // namespace asg
} // namespace emulation
} // namespace android
// The interface for the guest:
extern "C" {
// Handled outside in address_space_device.cpp:
//
// Ping(device id): Create the device. On the host, the two rings and
// auxiliary buffer are allocated. The two rings are allocated up front.
// Both the auxiliary buffers and the rings are allocated from blocks of
// rings and auxiliary buffers. New blocks are created if we run out either
// way.
enum asg_command {
// Ping(get_ring): Returns, in the fields:
// metadata: offset to give to claimShared and mmap() in the guest
// size: size to give to claimShared and mmap() in the guest
ASG_GET_RING = 0,
// Ping(get_buffer): Returns, in the fields:
// metadata: offset to give to claimShared and mmap() in the guest
// size: size to give to claimShared and mmap() in the guest
ASG_GET_BUFFER = 1,
// Ping(set_version): Run after the guest reads and negotiates its
// version of the device with the host. The host now knows the guest's
// version and can proceed with a protocol that works for both.
// size (in): the version of the guest
// size (out): the version of the host
// metadata (out): hostmem id
// After this command runs, the consumer is
// implicitly created.
ASG_SET_VERSION = 2,
// Ping(notiy_available): Wakes up the consumer from sleep so it
// can read data via toHost
ASG_NOTIFY_AVAILABLE = 3,
// Retrieve the host config
ASG_GET_CONFIG = 4,
};
} // extern "C"
@@ -0,0 +1,111 @@
/*
* Copyright 2018 Google
* SPDX-License-Identifier: MIT
*/
#ifndef ANDROID_INCLUDE_HARDWARE_GOLDFISH_ADDRESS_SPACE_H
#define ANDROID_INCLUDE_HARDWARE_GOLDFISH_ADDRESS_SPACE_H
#include <inttypes.h>
#include <stddef.h>
#include "address_space.h"
class GoldfishAddressSpaceBlock;
class GoldfishAddressSpaceHostMemoryAllocator;
using GoldfishAddressSpaceSubdeviceType = AddressSpaceSubdeviceType;
class GoldfishAddressSpaceBlockProvider {
public:
GoldfishAddressSpaceBlockProvider(GoldfishAddressSpaceSubdeviceType subdevice);
~GoldfishAddressSpaceBlockProvider();
private:
GoldfishAddressSpaceBlockProvider(const GoldfishAddressSpaceBlockProvider &rhs);
GoldfishAddressSpaceBlockProvider &operator=(const GoldfishAddressSpaceBlockProvider &rhs);
bool is_opened() const;
void close();
address_space_handle_t release();
static void closeHandle(address_space_handle_t handle);
address_space_handle_t m_handle;
friend class GoldfishAddressSpaceBlock;
friend class GoldfishAddressSpaceHostMemoryAllocator;
};
class GoldfishAddressSpaceBlock {
public:
GoldfishAddressSpaceBlock();
~GoldfishAddressSpaceBlock();
bool allocate(GoldfishAddressSpaceBlockProvider *provider, size_t size);
bool claimShared(GoldfishAddressSpaceBlockProvider *provider, uint64_t offset, uint64_t size);
uint64_t physAddr() const;
uint64_t hostAddr() const;
uint64_t offset() const { return m_offset; }
size_t size() const { return m_size; }
void *mmap(uint64_t opaque);
void *guestPtr() const;
void replace(GoldfishAddressSpaceBlock *other);
void release();
static int memoryMap(void *addr, size_t len, address_space_handle_t fd, uint64_t off, void** dst);
static void memoryUnmap(void *ptr, size_t size);
private:
void destroy();
GoldfishAddressSpaceBlock &operator=(const GoldfishAddressSpaceBlock &);
address_space_handle_t m_handle;
void *m_mmaped_ptr;
uint64_t m_phys_addr;
uint64_t m_host_addr;
uint64_t m_offset;
uint64_t m_size;
bool m_is_shared_mapping;
};
class GoldfishAddressSpaceHostMemoryAllocator {
public:
GoldfishAddressSpaceHostMemoryAllocator(bool useSharedSlots);
long hostMalloc(GoldfishAddressSpaceBlock *block, size_t size);
void hostFree(GoldfishAddressSpaceBlock *block);
bool is_opened() const;
address_space_handle_t release() { return m_provider.release(); }
static void closeHandle(address_space_handle_t handle) { GoldfishAddressSpaceBlockProvider::closeHandle(handle); }
private:
GoldfishAddressSpaceBlockProvider m_provider;
bool m_useSharedSlots;
};
// Convenience functions that run address space driver api without wrapping in
// a class. Useful for when a client wants to manage the driver handle directly
// (e.g., mmaping() more than one region associated with a single handle will
// require different lifetime expectations versus GoldfishAddressSpaceBlock).
address_space_handle_t goldfish_address_space_open();
void goldfish_address_space_close(address_space_handle_t);
bool goldfish_address_space_allocate(
address_space_handle_t, size_t size, uint64_t* phys_addr, uint64_t* offset);
bool goldfish_address_space_free(
address_space_handle_t, uint64_t offset);
bool goldfish_address_space_claim_shared(
address_space_handle_t, uint64_t offset, uint64_t size);
bool goldfish_address_space_unclaim_shared(
address_space_handle_t, uint64_t offset);
// pgoff is the offset into the page to return in the result
void* goldfish_address_space_map(
address_space_handle_t, uint64_t offset, uint64_t size, uint64_t pgoff = 0);
void goldfish_address_space_unmap(void* ptr, uint64_t size);
bool goldfish_address_space_set_subdevice_type(address_space_handle_t, GoldfishAddressSpaceSubdeviceType type, address_space_handle_t*);
bool goldfish_address_space_ping(address_space_handle_t, struct address_space_ping*);
#endif // #ifndef ANDROID_INCLUDE_HARDWARE_GOLDFISH_ADDRESS_SPACE_H
@@ -0,0 +1,30 @@
# Copyright 2022 Android Open Source Project
# SPDX-License-Identifier: MIT
inc_goldfish_address_space = include_directories('include')
files_lib_goldfish_address_space = files(
'AddressSpaceStream.cpp',
'VirtioGpuAddressSpaceStream.cpp',
)
if host_machine.system() == 'android'
files_lib_goldfish_address_space += files('GoldfishAddressSpaceStream.cpp')
files_lib_goldfish_address_space += files('goldfish_address_space.cpp')
endif
lib_goldfish_address_space = static_library(
'goldfish_address_space',
files_lib_goldfish_address_space,
cpp_args: gfxstream_guest_args,
include_directories: [
inc_goldfish_address_space,
inc_guest_iostream,
inc_platform_virtgpu,
inc_include,
inc_aemu,
inc_src,
],
link_with: [libplatform_virtgpu],
dependencies: dep_libdrm,
)
@@ -0,0 +1,218 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "ANativeWindowAndroid.h"
#include "util/detect_os.h"
#if DETECT_OS_ANDROID
#include <android/native_window.h>
#include <system/window.h>
#endif // DETECT_OS_ANDROID
namespace gfxstream {
bool ANativeWindowHelperAndroid::isValid(EGLNativeWindowType window) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
return anw->common.magic == ANDROID_NATIVE_WINDOW_MAGIC;
#else
(void)window;
return false;
#endif // DETECT_OS_ANDROID
}
bool ANativeWindowHelperAndroid::isValid(EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
if (anwb->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) {
return false;
}
if (anwb->common.version != sizeof(android_native_buffer_t)) {
return false;
}
if (anwb->handle == nullptr) {
return false;
}
return true;
#else
(void)buffer;
return false;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::acquire(EGLNativeWindowType window) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
ANativeWindow_acquire(anw);
#else
(void)window;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::release(EGLNativeWindowType window) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
ANativeWindow_release(anw);
#else
(void)window;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::acquire(EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
anwb->incStrong(anwb);
#else
(void)buffer;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::release(EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
anwb->decStrong(anwb);
#else
(void)buffer;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getConsumerUsage(EGLNativeWindowType window, int* usage) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
return anw->query(anw, NATIVE_WINDOW_CONSUMER_USAGE_BITS, usage);
#else
(void)window;
(void)usage;
return -1;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::setUsage(EGLNativeWindowType window, int usage) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
ANativeWindow_setUsage(anw, usage);
#else
(void)window;
(void)usage;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getWidth(EGLNativeWindowType window) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
return ANativeWindow_getWidth(anw);
#else
(void)window;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getHeight(EGLNativeWindowType window) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
return ANativeWindow_getHeight(anw);
#else
(void)window;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getWidth(EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return anwb->width;
#else
(void)buffer;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getHeight(EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anwb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return anwb->height;
#else
(void)buffer;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getFormat(EGLClientBuffer buffer, Gralloc* gralloc) {
#if DETECT_OS_ANDROID
auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return gralloc->getFormat(anb->handle);
#else
(void)buffer;
(void)gralloc;
return -1;
#endif // DETECT_OS_ANDROID
}
void ANativeWindowHelperAndroid::setSwapInterval(EGLNativeWindowType window, int interval) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
anw->setSwapInterval(anw, interval);
#else
(void)window;
(void)interval;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
int fence) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return ANativeWindow_queueBuffer(anw, anb, fence);
#else
(void)window;
(void)buffer;
(void)fence;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
int* fence) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
auto* anb = reinterpret_cast<ANativeWindowBuffer**>(buffer);
return ANativeWindow_dequeueBuffer(anw, anb, fence);
#else
(void)window;
(void)buffer;
(void)fence;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
#if DETECT_OS_ANDROID
auto* anw = reinterpret_cast<ANativeWindow*>(window);
auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return ANativeWindow_cancelBuffer(anw, anb, -1);
#else
(void)window;
(void)buffer;
return -1;
#endif // DETECT_OS_ANDROID
}
int ANativeWindowHelperAndroid::getHostHandle(EGLClientBuffer buffer, Gralloc* gralloc) {
#if DETECT_OS_ANDROID
auto* anb = reinterpret_cast<ANativeWindowBuffer*>(buffer);
return gralloc->getHostHandle(anb->handle);
#else
(void)buffer;
(void)gralloc;
return -1;
#endif // DETECT_OS_ANDROID
}
ANativeWindowHelper* createPlatformANativeWindowHelper() {
return new ANativeWindowHelperAndroid();
}
} // namespace gfxstream
@@ -0,0 +1,48 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "gfxstream/guest/ANativeWindow.h"
namespace gfxstream {
class ANativeWindowHelperAndroid : public ANativeWindowHelper {
public:
ANativeWindowHelperAndroid() = default;
bool isValid(EGLNativeWindowType window);
bool isValid(EGLClientBuffer buffer);
void acquire(EGLNativeWindowType window);
void release(EGLNativeWindowType window);
void acquire(EGLClientBuffer buffer);
void release(EGLClientBuffer buffer);
int getConsumerUsage(EGLNativeWindowType window, int* usage);
void setUsage(EGLNativeWindowType window, int usage);
int getWidth(EGLNativeWindowType window);
int getHeight(EGLNativeWindowType window);
int getWidth(EGLClientBuffer buffer);
int getHeight(EGLClientBuffer buffer);
int getFormat(EGLClientBuffer buffer, Gralloc* helper);
void setSwapInterval(EGLNativeWindowType window, int interval);
int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence);
int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence);
int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer);
int getHostHandle(EGLClientBuffer buffer, Gralloc* helper);
};
} // namespace gfxstream
@@ -0,0 +1,188 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "ANativeWindowEmulated.h"
#include "util/log.h"
namespace gfxstream {
EmulatedANativeWindow::EmulatedANativeWindow(
uint32_t width, uint32_t height, uint32_t format,
std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers)
: mRefCount(1), mWidth(width), mHeight(height), mFormat(format), mBuffers(std::move(buffers)) {
for (auto& buffer : mBuffers) {
mBufferQueue.push_back(QueuedAHB{
.ahb = buffer.get(),
.fence = -1,
});
}
}
EGLNativeWindowType EmulatedANativeWindow::asEglNativeWindowType() {
return reinterpret_cast<EGLNativeWindowType>(this);
}
uint32_t EmulatedANativeWindow::getWidth() const { return mWidth; }
uint32_t EmulatedANativeWindow::getHeight() const { return mHeight; }
int EmulatedANativeWindow::getFormat() const { return mFormat; }
int EmulatedANativeWindow::queueBuffer(EGLClientBuffer buffer, int fence) {
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
mBufferQueue.push_back(QueuedAHB{
.ahb = ahb,
.fence = fence,
});
return 0;
}
int EmulatedANativeWindow::dequeueBuffer(EGLClientBuffer* buffer, int* fence) {
auto queuedAhb = mBufferQueue.front();
mBufferQueue.pop_front();
*buffer = queuedAhb.ahb->asEglClientBuffer();
*fence = queuedAhb.fence;
return 0;
}
int EmulatedANativeWindow::cancelBuffer(EGLClientBuffer buffer) {
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
mBufferQueue.push_back(QueuedAHB{
.ahb = ahb,
.fence = -1,
});
return 0;
}
void EmulatedANativeWindow::acquire() { ++mRefCount; }
void EmulatedANativeWindow::release() {
--mRefCount;
if (mRefCount == 0) {
delete this;
}
}
bool EmulatedANativeWindowHelper::isValid(EGLNativeWindowType window) {
// TODO: maybe a registry of valid EmulatedANativeWindow-s?
(void)window;
return true;
}
bool EmulatedANativeWindowHelper::isValid(EGLClientBuffer buffer) {
// TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
(void)buffer;
return true;
}
void EmulatedANativeWindowHelper::acquire(EGLNativeWindowType window) {
auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
anw->acquire();
}
void EmulatedANativeWindowHelper::release(EGLNativeWindowType window) {
auto* anw = reinterpret_cast<EmulatedANativeWindow*>(window);
anw->release();
}
void EmulatedANativeWindowHelper::acquire(EGLClientBuffer buffer) {
// TODO: maybe a registry of valid EmulatedAHardwareBuffer-s?
(void)buffer;
}
void EmulatedANativeWindowHelper::release(EGLClientBuffer buffer) { (void)buffer; }
int EmulatedANativeWindowHelper::getConsumerUsage(EGLNativeWindowType window, int* usage) {
(void)window;
(void)usage;
return 0;
}
void EmulatedANativeWindowHelper::setUsage(EGLNativeWindowType window, int usage) {
(void)window;
(void)usage;
}
int EmulatedANativeWindowHelper::getWidth(EGLNativeWindowType window) {
auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
return anw->getWidth();
}
int EmulatedANativeWindowHelper::getHeight(EGLNativeWindowType window) {
auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
return anw->getHeight();
}
int EmulatedANativeWindowHelper::getWidth(EGLClientBuffer buffer) {
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
return ahb->getWidth();
}
int EmulatedANativeWindowHelper::getHeight(EGLClientBuffer buffer) {
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
return ahb->getHeight();
}
int EmulatedANativeWindowHelper::getFormat(EGLClientBuffer buffer, Gralloc* helper) {
(void)helper;
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
return ahb->getAndroidFormat();
}
void EmulatedANativeWindowHelper::setSwapInterval(EGLNativeWindowType window, int interval) {
mesa_loge("Unimplemented");
(void)window;
(void)interval;
}
int EmulatedANativeWindowHelper::queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer,
int fence) {
auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
return anw->queueBuffer(buffer, fence);
}
int EmulatedANativeWindowHelper::dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer,
int* fence) {
auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
return anw->dequeueBuffer(buffer, fence);
}
int EmulatedANativeWindowHelper::cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) {
auto anw = reinterpret_cast<EmulatedANativeWindow*>(window);
return anw->cancelBuffer(buffer);
}
int EmulatedANativeWindowHelper::getHostHandle(EGLClientBuffer buffer, Gralloc*) {
auto ahb = reinterpret_cast<EmulatedAHardwareBuffer*>(buffer);
return ahb->getResourceId();
}
EGLNativeWindowType EmulatedANativeWindowHelper::createNativeWindowForTesting(Gralloc* gralloc,
uint32_t width,
uint32_t height) {
std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers;
for (int i = 0; i < 3; i++) {
AHardwareBuffer* ahb = nullptr;
if (gralloc->allocate(width, height, GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM, -1, &ahb) != 0) {
mesa_loge("Failed to allocate gralloc buffer.");
return nullptr;
}
buffers.emplace_back(reinterpret_cast<EmulatedAHardwareBuffer*>(ahb));
}
return reinterpret_cast<EGLNativeWindowType>(
new EmulatedANativeWindow(width, height, GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM, std::move(buffers)));
}
ANativeWindowHelper* createPlatformANativeWindowHelper() {
return new EmulatedANativeWindowHelper();
}
} // namespace gfxstream
@@ -0,0 +1,86 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include <cstdint>
#include <memory>
#include <optional>
#include <queue>
#include <unordered_map>
#include <vector>
#include "gfxstream/guest/ANativeWindow.h"
#include "GrallocEmulated.h"
namespace gfxstream {
class EmulatedANativeWindow {
public:
EmulatedANativeWindow(uint32_t width, uint32_t height, uint32_t format,
std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> buffers);
EGLNativeWindowType asEglNativeWindowType();
uint32_t getWidth() const;
uint32_t getHeight() const;
int getFormat() const;
int queueBuffer(EGLClientBuffer buffer, int fence);
int dequeueBuffer(EGLClientBuffer* buffer, int* fence);
int cancelBuffer(EGLClientBuffer buffer);
void acquire();
void release();
private:
uint32_t mRefCount;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mFormat;
std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mBuffers;
struct QueuedAHB {
EmulatedAHardwareBuffer* ahb;
int fence = -1;
};
std::deque<QueuedAHB> mBufferQueue;
};
class EmulatedANativeWindowHelper : public ANativeWindowHelper {
public:
bool isValid(EGLNativeWindowType window) override;
bool isValid(EGLClientBuffer buffer) override;
void acquire(EGLNativeWindowType window) override;
void release(EGLNativeWindowType window) override;
void acquire(EGLClientBuffer buffer) override;
void release(EGLClientBuffer buffer) override;
int getConsumerUsage(EGLNativeWindowType window, int* usage) override;
void setUsage(EGLNativeWindowType window, int usage) override;
int getWidth(EGLNativeWindowType window) override;
int getHeight(EGLNativeWindowType window) override;
int getWidth(EGLClientBuffer buffer) override;
int getHeight(EGLClientBuffer buffer) override;
int getFormat(EGLClientBuffer buffer, Gralloc* helper) override;
void setSwapInterval(EGLNativeWindowType window, int interval) override;
int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) override;
int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) override;
int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) override;
int getHostHandle(EGLClientBuffer buffer, Gralloc*) override;
EGLNativeWindowType createNativeWindowForTesting(Gralloc* gralloc, uint32_t width,
uint32_t height) override;
};
} // namespace gfxstream
@@ -0,0 +1,30 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "gfxstream/guest/GfxStreamGralloc.h"
#include "util/detect_os.h"
#if DETECT_OS_ANDROID
#include <string>
#include "GrallocGoldfish.h"
#include "GrallocMinigbm.h"
#include "android-base/properties.h"
namespace gfxstream {
Gralloc* createPlatformGralloc(int32_t descriptor) {
const std::string value = android::base::GetProperty("ro.hardware.gralloc", "");
if (value == "minigbm") {
auto gralloc = new MinigbmGralloc(descriptor);
return gralloc;
}
return new GoldfishGralloc();
}
} // namespace gfxstream
#endif
@@ -0,0 +1,571 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GrallocEmulated.h"
#include <optional>
#include <unordered_map>
#include "drm-uapi/drm_fourcc.h"
#include "util/log.h"
namespace gfxstream {
namespace {
static constexpr int numFds = 0;
static constexpr int numInts = 1;
#define DRM_FORMAT_R8_BLOB fourcc_code('9', '9', '9', '9')
template <typename T, typename N>
T DivideRoundUp(T n, N divisor) {
const T div = static_cast<T>(divisor);
const T q = n / div;
return n % div == 0 ? q : q + 1;
}
template <typename T, typename N>
T Align(T number, N n) {
return DivideRoundUp(number, n) * n;
}
std::optional<uint32_t> GlFormatToDrmFormat(uint32_t glFormat) {
switch (glFormat) {
case kGlRGB:
return DRM_FORMAT_BGR888;
case kGlRGB565:
return DRM_FORMAT_BGR565;
case kGlRGBA:
return DRM_FORMAT_ABGR8888;
}
return std::nullopt;
}
std::optional<uint32_t> AhbToDrmFormat(uint32_t ahbFormat) {
switch (ahbFormat) {
case GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM:
return DRM_FORMAT_ABGR8888;
case GFXSTREAM_AHB_FORMAT_R8G8B8X8_UNORM:
return DRM_FORMAT_XBGR8888;
case GFXSTREAM_AHB_FORMAT_R8G8B8_UNORM:
return DRM_FORMAT_BGR888;
/*
* Confusingly, AHARDWAREBUFFER_FORMAT_RGB_565 is defined as:
*
* "16-bit packed format that has 5-bit R, 6-bit G, and 5-bit B components, in that
* order, from the most-sigfinicant bits to the least-significant bits."
*
* so the order of the components is intentionally not flipped between the pixel
* format and the DRM format.
*/
case GFXSTREAM_AHB_FORMAT_R5G6B5_UNORM:
return DRM_FORMAT_RGB565;
case GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM:
return DRM_FORMAT_ARGB8888;
case GFXSTREAM_AHB_FORMAT_BLOB:
return DRM_FORMAT_R8_BLOB;
case GFXSTREAM_AHB_FORMAT_R8_UNORM:
return DRM_FORMAT_R8;
case GFXSTREAM_AHB_FORMAT_YV12:
return DRM_FORMAT_YVU420;
case GFXSTREAM_AHB_FORMAT_R16G16B16A16_FLOAT:
return DRM_FORMAT_ABGR16161616F;
case GFXSTREAM_AHB_FORMAT_R10G10B10A2_UNORM:
return DRM_FORMAT_ABGR2101010;
case GFXSTREAM_AHB_FORMAT_Y8Cb8Cr8_420:
return DRM_FORMAT_NV12;
}
return std::nullopt;
}
struct DrmFormatPlaneInfo {
uint32_t horizontalSubsampling;
uint32_t verticalSubsampling;
uint32_t bytesPerPixel;
};
struct DrmFormatInfo {
uint32_t androidFormat;
uint32_t virglFormat;
bool isYuv;
uint32_t horizontalAlignmentPixels;
uint32_t verticalAlignmentPixels;
std::vector<DrmFormatPlaneInfo> planes;
};
const std::unordered_map<uint32_t, DrmFormatInfo>& GetDrmFormatInfoMap() {
static const auto* kFormatInfoMap = new std::unordered_map<uint32_t, DrmFormatInfo>({
{DRM_FORMAT_ABGR8888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM,
.virglFormat = VIRGL_FORMAT_R8G8B8A8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_ARGB8888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM,
.virglFormat = VIRGL_FORMAT_B8G8R8A8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_BGR888,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8G8B8_UNORM,
.virglFormat = VIRGL_FORMAT_R8G8B8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 3,
},
},
}},
{DRM_FORMAT_BGR565,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R5G6B5_UNORM,
.virglFormat = VIRGL_FORMAT_B5G6R5_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 2,
},
},
}},
{DRM_FORMAT_R8,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R8_UNORM,
.virglFormat = VIRGL_FORMAT_R8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
},
}},
{DRM_FORMAT_R8_BLOB,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_BLOB,
.virglFormat = VIRGL_FORMAT_R8_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
},
}},
{DRM_FORMAT_ABGR16161616F,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R16G16B16A16_FLOAT,
.virglFormat = VIRGL_FORMAT_R16G16B16A16_FLOAT,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 8,
},
},
}},
{DRM_FORMAT_ABGR2101010,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_R10G10B10A2_UNORM,
.virglFormat = VIRGL_FORMAT_R10G10B10A2_UNORM,
.isYuv = false,
.horizontalAlignmentPixels = 1,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 4,
},
},
}},
{DRM_FORMAT_NV12,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_Y8Cb8Cr8_420,
.virglFormat = VIRGL_FORMAT_NV12,
.isYuv = true,
.horizontalAlignmentPixels = 2,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 2,
},
},
}},
{DRM_FORMAT_YVU420,
{
.androidFormat = GFXSTREAM_AHB_FORMAT_YV12,
.virglFormat = VIRGL_FORMAT_YV12,
.isYuv = true,
.horizontalAlignmentPixels = 32,
.verticalAlignmentPixels = 1,
.planes =
{
{
.horizontalSubsampling = 1,
.verticalSubsampling = 1,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 1,
},
{
.horizontalSubsampling = 2,
.verticalSubsampling = 2,
.bytesPerPixel = 1,
},
},
}},
});
return *kFormatInfoMap;
}
} // namespace
EmulatedAHardwareBuffer::EmulatedAHardwareBuffer(uint32_t width, uint32_t height,
uint32_t drmFormat, VirtGpuResourcePtr resource)
: mRefCount(1), mWidth(width), mHeight(height), mDrmFormat(drmFormat), mResource(resource) {}
EmulatedAHardwareBuffer::~EmulatedAHardwareBuffer() {}
uint32_t EmulatedAHardwareBuffer::getResourceId() const { return mResource->getResourceHandle(); }
uint32_t EmulatedAHardwareBuffer::getWidth() const { return mWidth; }
uint32_t EmulatedAHardwareBuffer::getHeight() const { return mHeight; }
int EmulatedAHardwareBuffer::getAndroidFormat() const {
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(mDrmFormat);
if (formatInfoIt == formatInfosMap.end()) {
mesa_loge("Unhandled DRM format:%u", mDrmFormat);
return -1;
}
const auto& formatInfo = formatInfoIt->second;
return formatInfo.androidFormat;
}
uint32_t EmulatedAHardwareBuffer::getDrmFormat() const { return mDrmFormat; }
AHardwareBuffer* EmulatedAHardwareBuffer::asAHardwareBuffer() {
return reinterpret_cast<AHardwareBuffer*>(this);
}
buffer_handle_t EmulatedAHardwareBuffer::asBufferHandle() {
return reinterpret_cast<buffer_handle_t>(this);
}
EGLClientBuffer EmulatedAHardwareBuffer::asEglClientBuffer() {
return reinterpret_cast<EGLClientBuffer>(this);
}
void EmulatedAHardwareBuffer::acquire() { ++mRefCount; }
void EmulatedAHardwareBuffer::release() {
--mRefCount;
if (mRefCount == 0) {
delete this;
}
}
int EmulatedAHardwareBuffer::lock(uint8_t** ptr) {
if (!mMapped) {
mMapped = mResource->createMapping();
if (!mMapped) {
mesa_loge("Failed to lock EmulatedAHardwareBuffer: failed to create mapping.");
return -1;
}
mResource->transferFromHost(0, 0, mWidth, mHeight);
mResource->wait();
}
*ptr = (*mMapped)->asRawPtr();
return 0;
}
int EmulatedAHardwareBuffer::lockPlanes(std::vector<Gralloc::LockedPlane>* ahbPlanes) {
uint8_t* data = 0;
int ret = lock(&data);
if (ret) {
return ret;
}
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(mDrmFormat);
if (formatInfoIt == formatInfosMap.end()) {
mesa_loge("Failed to lock: failed to find format info for drm format:%u", mDrmFormat);
return -1;
}
const auto& formatInfo = formatInfoIt->second;
const uint32_t alignedWidth = Align(mWidth, formatInfo.horizontalAlignmentPixels);
const uint32_t alignedHeight = Align(mHeight, formatInfo.verticalAlignmentPixels);
uint32_t cumulativeSize = 0;
for (const DrmFormatPlaneInfo& planeInfo : formatInfo.planes) {
const uint32_t planeWidth = DivideRoundUp(alignedWidth, planeInfo.horizontalSubsampling);
const uint32_t planeHeight = DivideRoundUp(alignedHeight, planeInfo.verticalSubsampling);
const uint32_t planeBpp = planeInfo.bytesPerPixel;
const uint32_t planeStrideBytes = planeWidth * planeBpp;
const uint32_t planeSizeBytes = planeHeight * planeStrideBytes;
ahbPlanes->emplace_back(Gralloc::LockedPlane{
.data = data + cumulativeSize,
.pixelStrideBytes = planeBpp,
.rowStrideBytes = planeStrideBytes,
});
cumulativeSize += planeSizeBytes;
}
if (mDrmFormat == DRM_FORMAT_NV12) {
const auto& uPlane = (*ahbPlanes)[1];
auto vPlane = uPlane;
vPlane.data += 1;
ahbPlanes->push_back(vPlane);
} else if (mDrmFormat == DRM_FORMAT_YVU420) {
// Note: lockPlanes() always returns Y, then U, then V but YV12 is Y, then V, then U.
auto& plane1 = (*ahbPlanes)[1];
auto& plane2 = (*ahbPlanes)[2];
std::swap(plane1, plane2);
}
return 0;
}
int EmulatedAHardwareBuffer::unlock() {
if (!mMapped) {
mesa_loge("Failed to unlock EmulatedAHardwareBuffer: never locked?");
return -1;
}
mResource->transferToHost(0, 0, mWidth, mHeight);
mResource->wait();
mMapped.reset();
return 0;
}
EmulatedGralloc::EmulatedGralloc(int32_t descriptor) {
mDevice.reset(createPlatformVirtGpuDevice(kCapsetNone, descriptor));
}
EmulatedGralloc::~EmulatedGralloc() { mOwned.clear(); }
GrallocType EmulatedGralloc::getGrallocType() { return GRALLOC_TYPE_EMULATED; }
uint32_t EmulatedGralloc::createColorBuffer(int width, int height, uint32_t glFormat) {
auto drmFormat = GlFormatToDrmFormat(glFormat);
if (!drmFormat) {
mesa_loge("Unhandled format");
return -1;
}
auto ahb = allocate(width, height, *drmFormat);
if (ahb == nullptr) {
return -1;
}
EmulatedAHardwareBuffer* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
mOwned.emplace_back(rahb);
return rahb->getResourceId();
}
int EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t ahbFormat, uint64_t usage,
AHardwareBuffer** outputAhb) {
(void)usage;
auto drmFormat = AhbToDrmFormat(ahbFormat);
if (!drmFormat) {
mesa_loge("Unhandled AHB format:%u", ahbFormat);
return -1;
}
*outputAhb = allocate(width, height, *drmFormat);
if (*outputAhb == nullptr) {
return -1;
}
return 0;
}
AHardwareBuffer* EmulatedGralloc::allocate(uint32_t width, uint32_t height, uint32_t drmFormat) {
mesa_logd("Allocating AHB w:%u, h:%u, format %u", width, height, drmFormat);
const auto& formatInfosMap = GetDrmFormatInfoMap();
auto formatInfoIt = formatInfosMap.find(drmFormat);
if (formatInfoIt == formatInfosMap.end()) {
mesa_loge("Failed to allocate: failed to find format info for drm format:%u", drmFormat);
return nullptr;
}
const auto& formatInfo = formatInfoIt->second;
const uint32_t alignedWidth = Align(width, formatInfo.horizontalAlignmentPixels);
const uint32_t alignedHeight = Align(height, formatInfo.verticalAlignmentPixels);
uint32_t stride = 0;
uint32_t size = 0;
for (uint32_t i = 0; i < formatInfo.planes.size(); i++) {
const DrmFormatPlaneInfo& planeInfo = formatInfo.planes[i];
const uint32_t planeWidth = DivideRoundUp(alignedWidth, planeInfo.horizontalSubsampling);
const uint32_t planeHeight = DivideRoundUp(alignedHeight, planeInfo.verticalSubsampling);
const uint32_t planeBpp = planeInfo.bytesPerPixel;
const uint32_t planeStrideBytes = planeWidth * planeBpp;
const uint32_t planeSizeBytes = planeHeight * planeStrideBytes;
size += planeSizeBytes;
if (i == 0) stride = planeStrideBytes;
}
const uint32_t bind = (drmFormat == DRM_FORMAT_R8_BLOB || drmFormat == DRM_FORMAT_NV12 ||
drmFormat == DRM_FORMAT_YVU420)
? VIRGL_BIND_LINEAR
: VIRGL_BIND_RENDER_TARGET;
auto resource = mDevice->createResource(width, height, stride, size, formatInfo.virglFormat,
PIPE_TEXTURE_2D, bind);
if (!resource) {
mesa_loge("Failed to allocate: failed to create virtio resource.");
return nullptr;
}
resource->wait();
return reinterpret_cast<AHardwareBuffer*>(
new EmulatedAHardwareBuffer(width, height, drmFormat, std::move(resource)));
}
void EmulatedGralloc::acquire(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
rahb->acquire();
}
void EmulatedGralloc::release(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
rahb->release();
}
int EmulatedGralloc::lock(AHardwareBuffer* ahb, uint8_t** ptr) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->lock(ptr);
}
int EmulatedGralloc::lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->lockPlanes(ahbPlanes);
}
int EmulatedGralloc::unlock(AHardwareBuffer* ahb) {
auto* rahb = reinterpret_cast<EmulatedAHardwareBuffer*>(ahb);
return rahb->unlock();
}
uint32_t EmulatedGralloc::getHostHandle(const native_handle_t* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getResourceId();
}
uint32_t EmulatedGralloc::getHostHandle(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getResourceId();
}
const native_handle_t* EmulatedGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
return reinterpret_cast<const native_handle_t*>(ahb);
}
int EmulatedGralloc::getFormat(const native_handle_t* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getAndroidFormat();
}
int EmulatedGralloc::getFormat(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getAndroidFormat();
}
uint32_t EmulatedGralloc::getFormatDrmFourcc(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getDrmFormat();
}
uint32_t EmulatedGralloc::getWidth(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getWidth();
}
uint32_t EmulatedGralloc::getHeight(const AHardwareBuffer* handle) {
const auto* ahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(handle);
return ahb->getHeight();
}
size_t EmulatedGralloc::getAllocatedSize(const native_handle_t*) {
mesa_loge("Unimplemented.");
return 0;
}
size_t EmulatedGralloc::getAllocatedSize(const AHardwareBuffer*) {
mesa_loge("Unimplemented.");
return 0;
}
int EmulatedGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
const auto* rahb = reinterpret_cast<const EmulatedAHardwareBuffer*>(ahb);
*id = rahb->getResourceId();
return 0;
}
Gralloc* createPlatformGralloc(int32_t descriptor) { return new EmulatedGralloc(descriptor); }
} // namespace gfxstream
@@ -0,0 +1,100 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
#include "VirtGpu.h"
#include "gfxstream/guest/GfxStreamGralloc.h"
namespace gfxstream {
using EGLClientBuffer = void*;
class EmulatedAHardwareBuffer {
public:
EmulatedAHardwareBuffer(uint32_t width, uint32_t height, uint32_t drmFormat,
VirtGpuResourcePtr resource);
~EmulatedAHardwareBuffer();
uint32_t getResourceId() const;
uint32_t getWidth() const;
uint32_t getHeight() const;
int getAndroidFormat() const;
uint32_t getDrmFormat() const;
AHardwareBuffer* asAHardwareBuffer();
buffer_handle_t asBufferHandle();
EGLClientBuffer asEglClientBuffer();
void acquire();
void release();
int lock(uint8_t** ptr);
int lockPlanes(std::vector<Gralloc::LockedPlane>* ahbPlanes);
int unlock();
private:
uint32_t mRefCount;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mDrmFormat;
VirtGpuResourcePtr mResource;
std::optional<VirtGpuResourceMappingPtr> mMapped;
};
class EmulatedGralloc : public Gralloc {
public:
EmulatedGralloc(int32_t descriptor);
~EmulatedGralloc();
GrallocType getGrallocType() override;
uint32_t createColorBuffer(int width, int height, uint32_t glFormat) override;
int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
AHardwareBuffer** outputAhb) override;
AHardwareBuffer* allocate(uint32_t width, uint32_t height, uint32_t format);
void acquire(AHardwareBuffer* ahb) override;
void release(AHardwareBuffer* ahb) override;
int lock(AHardwareBuffer* ahb, uint8_t** ptr) override;
int lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) override;
int unlock(AHardwareBuffer* ahb) override;
uint32_t getHostHandle(const native_handle_t* handle) override;
uint32_t getHostHandle(const AHardwareBuffer* handle) override;
const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
int getFormat(const native_handle_t* handle) override;
int getFormat(const AHardwareBuffer* handle) override;
uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
uint32_t getWidth(const AHardwareBuffer* ahb) override;
uint32_t getHeight(const AHardwareBuffer* ahb) override;
size_t getAllocatedSize(const native_handle_t*) override;
size_t getAllocatedSize(const AHardwareBuffer*) override;
int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
private:
std::unique_ptr<VirtGpuDevice> mDevice;
std::vector<std::unique_ptr<EmulatedAHardwareBuffer>> mOwned;
};
} // namespace gfxstream
@@ -0,0 +1,116 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GrallocGoldfish.h"
#include <gralloc_cb_bp.h>
#include <vndk/hardware_buffer.h>
namespace gfxstream {
GrallocType GoldfishGralloc::getGrallocType() { return GRALLOC_TYPE_GOLDFISH; }
uint32_t GoldfishGralloc::createColorBuffer(int width, int height, uint32_t glformat) { return 0; }
int GoldfishGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
AHardwareBuffer** outputAhb) {
struct AHardwareBuffer_Desc desc = {
.width = width,
.height = height,
.layers = 1,
.format = format,
.usage = usage,
};
return AHardwareBuffer_allocate(&desc, outputAhb);
}
void GoldfishGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
void GoldfishGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
int GoldfishGralloc::lock(AHardwareBuffer* ahb, uint8_t** ptr) {
return AHardwareBuffer_lock(ahb, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1, nullptr,
reinterpret_cast<void**>(ptr));
}
int GoldfishGralloc::lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) {
return -1;
}
int GoldfishGralloc::unlock(AHardwareBuffer* ahb) { return AHardwareBuffer_unlock(ahb, nullptr); }
uint32_t GoldfishGralloc::getHostHandle(native_handle_t const* handle) {
const uint32_t INVALID_HOST_HANDLE = 0;
const cb_handle_t* cb = cb_handle_t::from(handle);
if (cb) {
return cb->hostHandle;
} else {
return INVALID_HOST_HANDLE;
}
}
uint32_t GoldfishGralloc::getHostHandle(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getHostHandle(handle);
}
const native_handle_t* GoldfishGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
return AHardwareBuffer_getNativeHandle(ahb);
}
int GoldfishGralloc::getFormat(const native_handle_t* handle) {
return cb_handle_t::from(handle)->format;
}
int GoldfishGralloc::getFormat(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getFormat(handle);
}
uint32_t GoldfishGralloc::getFormatDrmFourcc(const native_handle_t* handle) {
return cb_handle_t::from(handle)->drmformat;
}
uint32_t GoldfishGralloc::getFormatDrmFourcc(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getFormatDrmFourcc(handle);
}
uint32_t GoldfishGralloc::getWidth(const AHardwareBuffer* ahb) {
AHardwareBuffer_Desc desc = {};
AHardwareBuffer_describe(ahb, &desc);
return desc.width;
}
uint32_t GoldfishGralloc::getHeight(const AHardwareBuffer* ahb) {
AHardwareBuffer_Desc desc = {};
AHardwareBuffer_describe(ahb, &desc);
return desc.height;
}
size_t GoldfishGralloc::getAllocatedSize(const native_handle_t* handle) {
return static_cast<size_t>(cb_handle_t::from(handle)->allocatedSize());
}
size_t GoldfishGralloc::getAllocatedSize(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getAllocatedSize(handle);
}
int GoldfishGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
#if ANDROID_API_LEVEL >= 31
return AHardwareBuffer_getId(ahb, id);
#else
(void)ahb;
*id = 0;
return 0;
#endif
}
bool GoldfishGralloc::treatBlobAsImage() { return true; }
} // namespace gfxstream
@@ -0,0 +1,49 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "gfxstream/guest/GfxStreamGralloc.h"
namespace gfxstream {
class GoldfishGralloc : public Gralloc {
public:
GrallocType getGrallocType() override;
uint32_t createColorBuffer(int width, int height, uint32_t glformat) override;
int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
AHardwareBuffer** outputAhb) override;
void acquire(AHardwareBuffer* ahb) override;
void release(AHardwareBuffer* ahb) override;
int lock(AHardwareBuffer* ahb, uint8_t** ptr) override;
int lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) override;
int unlock(AHardwareBuffer* ahb) override;
uint32_t getHostHandle(native_handle_t const* handle) override;
uint32_t getHostHandle(const AHardwareBuffer* handle) override;
const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
int getFormat(const native_handle_t* handle) override;
int getFormat(const AHardwareBuffer* handle) override;
uint32_t getFormatDrmFourcc(const native_handle_t* handle) override;
uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
uint32_t getWidth(const AHardwareBuffer* ahb) override;
uint32_t getHeight(const AHardwareBuffer* ahb) override;
size_t getAllocatedSize(const native_handle_t* handle) override;
size_t getAllocatedSize(const AHardwareBuffer* handle) override;
int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
bool treatBlobAsImage() override;
};
} // namespace gfxstream
@@ -0,0 +1,190 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GrallocMinigbm.h"
#include <cros_gralloc/cros_gralloc_handle.h>
#include <stdlib.h>
#include <sys/user.h>
#include <unistd.h>
#include <vndk/hardware_buffer.h>
#include <cinttypes>
#include <cstring>
#include "VirtGpu.h"
#include "util/log.h"
namespace gfxstream {
MinigbmGralloc::MinigbmGralloc(int32_t descriptor) {
mDevice.reset(createPlatformVirtGpuDevice(kCapsetNone, descriptor));
}
GrallocType MinigbmGralloc::getGrallocType() { return GRALLOC_TYPE_MINIGBM; }
uint32_t MinigbmGralloc::createColorBuffer(int width, int height, uint32_t glformat) {
// Only supported format for pbuffers in gfxstream should be RGBA8
const uint32_t kVirglFormatRGBA = 67; // VIRGL_FORMAT_R8G8B8A8_UNORM;
uint32_t virtgpu_format = 0;
uint32_t bpp = 0;
switch (glformat) {
case kGlRGB:
mesa_logi("Note: egl wanted GL_RGB, still using RGBA");
virtgpu_format = kVirglFormatRGBA;
bpp = 4;
break;
case kGlRGBA:
virtgpu_format = kVirglFormatRGBA;
bpp = 4;
break;
default:
mesa_logi("Note: egl wanted 0x%x, still using RGBA", glformat);
virtgpu_format = kVirglFormatRGBA;
bpp = 4;
break;
}
uint32_t stride = bpp * width;
auto resource = mDevice->createResource(width, height, stride, stride * height, virtgpu_format,
PIPE_TEXTURE_2D, VIRGL_BIND_RENDER_TARGET);
uint32_t handle = resource->getResourceHandle();
resource->intoRaw();
return handle;
}
int MinigbmGralloc::allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
AHardwareBuffer** outputAhb) {
struct AHardwareBuffer_Desc desc = {
.width = width,
.height = height,
.layers = 1,
.format = format,
.usage = usage,
};
return AHardwareBuffer_allocate(&desc, outputAhb);
}
void MinigbmGralloc::acquire(AHardwareBuffer* ahb) { AHardwareBuffer_acquire(ahb); }
void MinigbmGralloc::release(AHardwareBuffer* ahb) { AHardwareBuffer_release(ahb); }
int MinigbmGralloc::lock(AHardwareBuffer* ahb, uint8_t** ptr) {
return AHardwareBuffer_lock(ahb, AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, -1, nullptr,
reinterpret_cast<void**>(ptr));
}
int MinigbmGralloc::lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) {
mesa_loge("%s: unimplemented", __func__);
return -1;
}
int MinigbmGralloc::unlock(AHardwareBuffer* ahb) { return AHardwareBuffer_unlock(ahb, nullptr); }
uint32_t MinigbmGralloc::getHostHandle(const native_handle_t* handle) {
cros_gralloc_handle const* cros_handle = reinterpret_cast<cros_gralloc_handle const*>(handle);
struct VirtGpuExternalHandle hnd = {
.osHandle = dup(cros_handle->fds[0]),
.type = kMemHandleDmabuf,
};
auto resource = mDevice->importBlob(hnd);
if (!resource) {
return 0;
}
if (resource->wait()) {
return 0;
}
return resource->getResourceHandle();
}
uint32_t MinigbmGralloc::getHostHandle(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getHostHandle(handle);
}
const native_handle_t* MinigbmGralloc::getNativeHandle(const AHardwareBuffer* ahb) {
return AHardwareBuffer_getNativeHandle(ahb);
}
int MinigbmGralloc::getFormat(const native_handle_t* handle) {
return ((cros_gralloc_handle*)handle)->droid_format;
}
int MinigbmGralloc::getFormat(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return ((cros_gralloc_handle*)handle)->droid_format;
}
uint32_t MinigbmGralloc::getFormatDrmFourcc(const native_handle_t* handle) {
return ((cros_gralloc_handle*)handle)->format;
}
uint32_t MinigbmGralloc::getFormatDrmFourcc(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getFormatDrmFourcc(handle);
}
uint32_t MinigbmGralloc::getWidth(const AHardwareBuffer* ahb) {
AHardwareBuffer_Desc desc = {};
AHardwareBuffer_describe(ahb, &desc);
return desc.width;
}
uint32_t MinigbmGralloc::getHeight(const AHardwareBuffer* ahb) {
AHardwareBuffer_Desc desc = {};
AHardwareBuffer_describe(ahb, &desc);
return desc.height;
}
size_t MinigbmGralloc::getAllocatedSize(const native_handle_t* handle) {
cros_gralloc_handle const* cros_handle = reinterpret_cast<cros_gralloc_handle const*>(handle);
struct VirtGpuExternalHandle hnd = {
.osHandle = dup(cros_handle->fds[0]),
.type = kMemHandleDmabuf,
};
auto resource = mDevice->importBlob(hnd);
if (!resource) {
return 0;
}
if (resource->wait()) {
return 0;
}
return resource->getSize();
}
size_t MinigbmGralloc::getAllocatedSize(const AHardwareBuffer* ahb) {
const native_handle_t* handle = AHardwareBuffer_getNativeHandle(ahb);
return getAllocatedSize(handle);
}
int MinigbmGralloc::getId(const AHardwareBuffer* ahb, uint64_t* id) {
#if ANDROID_API_LEVEL >= 31
return AHardwareBuffer_getId(ahb, id);
#else
(void)ahb;
*id = 0;
return 0;
#endif
}
int32_t MinigbmGralloc::getDataspace(const AHardwareBuffer* ahb) {
#if ANDROID_API_LEVEL >= 35
return AHardwareBuffer_getDataSpace(ahb);
#else
(void)ahb;
return GFXSTREAM_AHB_DATASPACE_UNKNOWN;
#endif
}
} // namespace gfxstream
@@ -0,0 +1,59 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <memory>
#include "VirtGpu.h"
#include "gfxstream/guest/GfxStreamGralloc.h"
namespace gfxstream {
class MinigbmGralloc : public Gralloc {
public:
MinigbmGralloc(int32_t descriptor);
~MinigbmGralloc(){};
GrallocType getGrallocType() override;
uint32_t createColorBuffer(int width, int height, uint32_t glformat) override;
int allocate(uint32_t width, uint32_t height, uint32_t format, uint64_t usage,
AHardwareBuffer** outputAhb) override;
void acquire(AHardwareBuffer* ahb) override;
void release(AHardwareBuffer* ahb) override;
int lock(AHardwareBuffer* ahb, uint8_t** ptr) override;
int lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) override;
int unlock(AHardwareBuffer* ahb) override;
uint32_t getHostHandle(native_handle_t const* handle) override;
uint32_t getHostHandle(const AHardwareBuffer* handle) override;
const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) override;
int getFormat(const native_handle_t* handle) override;
int getFormat(const AHardwareBuffer* handle) override;
uint32_t getFormatDrmFourcc(const native_handle_t* handle) override;
uint32_t getFormatDrmFourcc(const AHardwareBuffer* handle) override;
uint32_t getWidth(const AHardwareBuffer* ahb) override;
uint32_t getHeight(const AHardwareBuffer* ahb) override;
size_t getAllocatedSize(const native_handle_t* handle) override;
size_t getAllocatedSize(const AHardwareBuffer* handle) override;
int getId(const AHardwareBuffer* ahb, uint64_t* id) override;
int32_t getDataspace(const AHardwareBuffer* ahb) override;
private:
std::unique_ptr<VirtGpuDevice> mDevice;
};
} // namespace gfxstream
@@ -0,0 +1,61 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#if defined(ANDROID)
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include "GfxStreamGralloc.h"
using EGLClientBuffer = void*;
namespace gfxstream {
// Abstraction around libnativewindow to support testing.
class ANativeWindowHelper {
public:
virtual ~ANativeWindowHelper() {}
virtual bool isValid(EGLNativeWindowType window) = 0;
virtual bool isValid(EGLClientBuffer buffer) = 0;
virtual void acquire(EGLNativeWindowType window) = 0;
virtual void release(EGLNativeWindowType window) = 0;
virtual void acquire(EGLClientBuffer buffer) = 0;
virtual void release(EGLClientBuffer buffer) = 0;
virtual int getConsumerUsage(EGLNativeWindowType window, int* usage) = 0;
virtual void setUsage(EGLNativeWindowType window, int usage) = 0;
virtual int getWidth(EGLNativeWindowType window) = 0;
virtual int getHeight(EGLNativeWindowType window) = 0;
virtual int getWidth(EGLClientBuffer buffer) = 0;
virtual int getHeight(EGLClientBuffer buffer) = 0;
virtual int getFormat(EGLClientBuffer buffer, Gralloc* helper) = 0;
virtual int getHostHandle(EGLClientBuffer buffer, Gralloc* helper) = 0;
virtual void setSwapInterval(EGLNativeWindowType window, int interval) = 0;
virtual int queueBuffer(EGLNativeWindowType window, EGLClientBuffer buffer, int fence) = 0;
virtual int dequeueBuffer(EGLNativeWindowType window, EGLClientBuffer* buffer, int* fence) = 0;
virtual int cancelBuffer(EGLNativeWindowType window, EGLClientBuffer buffer) = 0;
virtual EGLNativeWindowType createNativeWindowForTesting(Gralloc* /*gralloc*/,
uint32_t /*width*/,
uint32_t /*height*/) {
return (EGLNativeWindowType)0;
}
};
ANativeWindowHelper* createPlatformANativeWindowHelper();
} // namespace gfxstream
#endif // defined(ANDROID)
@@ -0,0 +1,189 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#if defined(ANDROID)
#include <cutils/native_handle.h>
#include <stddef.h>
#include <stdint.h>
#include <vector>
typedef struct AHardwareBuffer AHardwareBuffer;
namespace gfxstream {
constexpr uint32_t kGlRGB = 0x1907;
constexpr uint32_t kGlRGBA = 0x1908;
constexpr uint32_t kGlRGB565 = 0x8D62;
// Enums mirrored from Android to avoid extra build dependencies.
enum {
GFXSTREAM_AHB_FORMAT_R8G8B8A8_UNORM = 1,
GFXSTREAM_AHB_FORMAT_R8G8B8X8_UNORM = 2,
GFXSTREAM_AHB_FORMAT_R8G8B8_UNORM = 3,
GFXSTREAM_AHB_FORMAT_R5G6B5_UNORM = 4,
GFXSTREAM_AHB_FORMAT_B8G8R8A8_UNORM = 5,
GFXSTREAM_AHB_FORMAT_B5G5R5A1_UNORM = 6,
GFXSTREAM_AHB_FORMAT_B4G4R4A4_UNORM = 7,
GFXSTREAM_AHB_FORMAT_R16G16B16A16_FLOAT = 0x16,
GFXSTREAM_AHB_FORMAT_R10G10B10A2_UNORM = 0x2b,
GFXSTREAM_AHB_FORMAT_BLOB = 0x21,
GFXSTREAM_AHB_FORMAT_D16_UNORM = 0x30,
GFXSTREAM_AHB_FORMAT_D24_UNORM = 0x31,
GFXSTREAM_AHB_FORMAT_D24_UNORM_S8_UINT = 0x32,
GFXSTREAM_AHB_FORMAT_D32_FLOAT = 0x33,
GFXSTREAM_AHB_FORMAT_D32_FLOAT_S8_UINT = 0x34,
GFXSTREAM_AHB_FORMAT_S8_UINT = 0x35,
GFXSTREAM_AHB_FORMAT_Y8Cb8Cr8_420 = 0x23,
GFXSTREAM_AHB_FORMAT_YV12 = 0x32315659,
GFXSTREAM_AHB_FORMAT_IMPLEMENTATION_DEFINED = 0x22,
GFXSTREAM_AHB_FORMAT_R8_UNORM = 0x38,
};
enum GfxstreamAhbDataspace {
GFXSTREAM_AHB_DATASPACE_UNKNOWN = 0,
GFXSTREAM_AHB_DATASPACE_ARBITRARY = 1,
GFXSTREAM_AHB_DATASPACE_STANDARD_SHIFT = 16,
GFXSTREAM_AHB_DATASPACE_STANDARD_MASK = 4128768, // (63 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_UNSPECIFIED = 0, // (0 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT709 = 65536, // (1 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT601_625 = 131072, // (2 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT601_625_UNADJUSTED = 196608, // (3 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT601_525 = 262144, // (4 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT601_525_UNADJUSTED = 327680, // (5 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT2020 = 393216, // (6 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT2020_CONSTANT_LUMINANCE = 458752, // (7 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_BT470M = 524288, // (8 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_FILM = 589824, // (9 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_DCI_P3 = 655360, // (10 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_STANDARD_ADOBE_RGB = 720896, // (11 << STANDARD_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_SHIFT = 22,
GFXSTREAM_AHB_DATASPACE_TRANSFER_MASK = 130023424, // (31 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_UNSPECIFIED = 0, // (0 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_LINEAR = 4194304, // (1 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_SRGB = 8388608, // (2 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_SMPTE_170M = 12582912, // (3 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_GAMMA2_2 = 16777216, // (4 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_GAMMA2_6 = 20971520, // (5 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_GAMMA2_8 = 25165824, // (6 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_ST2084 = 29360128, // (7 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_TRANSFER_HLG = 33554432, // (8 << TRANSFER_SHIFT)
GFXSTREAM_AHB_DATASPACE_RANGE_SHIFT = 27,
GFXSTREAM_AHB_DATASPACE_RANGE_MASK = 939524096, // (7 << RANGE_SHIFT)
GFXSTREAM_AHB_DATASPACE_RANGE_UNSPECIFIED = 0, // (0 << RANGE_SHIFT)
GFXSTREAM_AHB_DATASPACE_RANGE_FULL = 134217728, // (1 << RANGE_SHIFT)
GFXSTREAM_AHB_DATASPACE_RANGE_LIMITED = 268435456, // (2 << RANGE_SHIFT)
GFXSTREAM_AHB_DATASPACE_RANGE_EXTENDED = 402653184, // (3 << RANGE_SHIFT)
GFXSTREAM_AHB_DATASPACE_SRGB_LINEAR = 512,
GFXSTREAM_AHB_DATASPACE_V0_SRGB_LINEAR =
138477568, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_V0_SCRGB_LINEAR =
406913024, // ((STANDARD_BT709 | TRANSFER_LINEAR) | RANGE_EXTENDED)
GFXSTREAM_AHB_DATASPACE_SRGB = 513,
GFXSTREAM_AHB_DATASPACE_V0_SRGB = 142671872, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_V0_SCRGB =
411107328, // ((STANDARD_BT709 | TRANSFER_SRGB) | RANGE_EXTENDED)
GFXSTREAM_AHB_DATASPACE_JFIF = 257,
GFXSTREAM_AHB_DATASPACE_V0_JFIF =
146931712, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_BT601_625 = 258,
GFXSTREAM_AHB_DATASPACE_V0_BT601_625 =
281149440, // ((STANDARD_BT601_625 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
GFXSTREAM_AHB_DATASPACE_BT601_525 = 259,
GFXSTREAM_AHB_DATASPACE_V0_BT601_525 =
281280512, // ((STANDARD_BT601_525 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
GFXSTREAM_AHB_DATASPACE_BT709 = 260,
GFXSTREAM_AHB_DATASPACE_V0_BT709 =
281083904, // ((STANDARD_BT709 | TRANSFER_SMPTE_170M) | RANGE_LIMITED)
GFXSTREAM_AHB_DATASPACE_DCI_P3_LINEAR =
139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_DCI_P3 =
155844608, // ((STANDARD_DCI_P3 | TRANSFER_GAMMA2_6) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_DISPLAY_P3_LINEAR =
139067392, // ((STANDARD_DCI_P3 | TRANSFER_LINEAR) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_DISPLAY_P3 =
143261696, // ((STANDARD_DCI_P3 | TRANSFER_SRGB) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_ADOBE_RGB =
151715840, // ((STANDARD_ADOBE_RGB | TRANSFER_GAMMA2_2) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_BT2020_LINEAR =
138805248, // ((STANDARD_BT2020 | TRANSFER_LINEAR) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_BT2020 =
147193856, // ((STANDARD_BT2020 | TRANSFER_SMPTE_170M) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_BT2020_PQ =
163971072, // ((STANDARD_BT2020 | TRANSFER_ST2084) | RANGE_FULL)
GFXSTREAM_AHB_DATASPACE_DEPTH = 4096,
GFXSTREAM_AHB_DATASPACE_SENSOR = 4097,
};
enum GrallocType {
GRALLOC_TYPE_GOLDFISH = 1,
GRALLOC_TYPE_MINIGBM = 2,
GRALLOC_TYPE_EMULATED = 3,
};
// Abstraction for gralloc handle conversion
class Gralloc {
public:
virtual ~Gralloc() {}
virtual GrallocType getGrallocType() = 0;
virtual uint32_t createColorBuffer(int width, int height, uint32_t glformat) = 0;
virtual void acquire(AHardwareBuffer* ahb) = 0;
virtual void release(AHardwareBuffer* ahb) = 0;
virtual int allocate(uint32_t width, uint32_t height, uint32_t ahbFormat, uint64_t usage,
AHardwareBuffer** outputAhb) = 0;
virtual int lock(AHardwareBuffer* ahb, uint8_t** ptr) = 0;
struct LockedPlane {
uint8_t* data = nullptr;
uint32_t pixelStrideBytes = 0;
uint32_t rowStrideBytes = 0;
};
// If AHB is a YUV format, always returns Y, then U, then V.
virtual int lockPlanes(AHardwareBuffer* ahb, std::vector<LockedPlane>* ahbPlanes) = 0;
virtual int unlock(AHardwareBuffer* ahb) = 0;
virtual const native_handle_t* getNativeHandle(const AHardwareBuffer* ahb) = 0;
virtual uint32_t getHostHandle(const native_handle_t* handle) = 0;
virtual uint32_t getHostHandle(const AHardwareBuffer* handle) = 0;
virtual int getFormat(const native_handle_t* handle) = 0;
virtual int getFormat(const AHardwareBuffer* handle) = 0;
virtual uint32_t getFormatDrmFourcc(const AHardwareBuffer* /*handle*/) {
// Equal to DRM_FORMAT_INVALID -- see <drm_fourcc.h>
return 0;
}
virtual uint32_t getFormatDrmFourcc(const native_handle_t* /*handle*/) {
// Equal to DRM_FORMAT_INVALID -- see <drm_fourcc.h>
return 0;
}
virtual uint32_t getWidth(const AHardwareBuffer* ahb) = 0;
virtual uint32_t getHeight(const AHardwareBuffer* ahb) = 0;
virtual size_t getAllocatedSize(const native_handle_t* handle) = 0;
virtual size_t getAllocatedSize(const AHardwareBuffer* handle) = 0;
virtual int getId(const AHardwareBuffer* ahb, uint64_t* id) = 0;
virtual bool treatBlobAsImage() { return false; }
virtual int32_t getDataspace(const AHardwareBuffer* /*ahb*/) {
return GFXSTREAM_AHB_DATASPACE_UNKNOWN;
}
};
Gralloc* createPlatformGralloc(int32_t descriptor = -1);
} // namespace gfxstream
#endif // defined(ANDROID)
@@ -0,0 +1,80 @@
/*
* Copyright 2016 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef ANDROID_INCLUDE_HARDWARE_GOLDFISH_SYNC_H
#define ANDROID_INCLUDE_HARDWARE_GOLDFISH_SYNC_H
#define GOLDFISH_SYNC_VULKAN_SEMAPHORE_SYNC 0x00000001
#define GOLDFISH_SYNC_VULKAN_QSRI 0x00000002
#include <errno.h>
#include <linux/ioctl.h>
#include <linux/types.h>
#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
// Make it conflict with ioctls that are not likely to be used
// in the emulator.
//
// '@' 00-0F linux/radeonfb.h conflict!
// '@' 00-0F drivers/video/aty/aty128fb.c conflict!
#define GOLDFISH_SYNC_IOC_MAGIC '@'
struct goldfish_sync_ioctl_info {
uint64_t host_glsync_handle_in;
uint64_t host_syncthread_handle_in;
int fence_fd_out;
};
#define GOLDFISH_SYNC_IOC_QUEUE_WORK _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 0, struct goldfish_sync_ioctl_info)
#define GOLDFISH_SYNC_IOC_SIGNAL _IOWR(GOLDFISH_SYNC_IOC_MAGIC, 1, struct goldfish_sync_ioctl_info)
static __inline__ int goldfish_sync_open() {
return open("/dev/goldfish_sync", O_RDWR);
}
static __inline__ int goldfish_sync_close(int sync_fd) {
return close(sync_fd);
}
static unsigned int sQueueWorkIoctlCmd = GOLDFISH_SYNC_IOC_QUEUE_WORK;
// If we are running on a 64-bit kernel.
static unsigned int sQueueWorkIoctlCmd64Kernel = 0xc0184000;
static __inline__ int goldfish_sync_queue_work(int goldfish_sync_fd,
uint64_t host_glsync,
uint64_t host_thread,
int* fd_out) {
struct goldfish_sync_ioctl_info info;
int err;
info.host_glsync_handle_in = host_glsync;
info.host_syncthread_handle_in = host_thread;
info.fence_fd_out = -1;
err = ioctl(goldfish_sync_fd, sQueueWorkIoctlCmd, &info);
if (err < 0 && errno == ENOTTY) {
sQueueWorkIoctlCmd = sQueueWorkIoctlCmd64Kernel;
err = ioctl(goldfish_sync_fd, sQueueWorkIoctlCmd, &info);
if (err < 0) {
sQueueWorkIoctlCmd = GOLDFISH_SYNC_IOC_QUEUE_WORK;
}
}
if (fd_out) *fd_out = info.fence_fd_out;
return err;
}
static __inline__ int goldfish_sync_signal(int goldfish_sync_fd) {
return ioctl(goldfish_sync_fd, GOLDFISH_SYNC_IOC_SIGNAL, 0);
}
#endif
+44
View File
@@ -0,0 +1,44 @@
# Copyright 2026 Google
# SPDX-License-Identifier: MIT
inc_gfxstream_android = include_directories('include')
deps_gfxstream_android = [dependency('android-utils')]
if host_machine.system() == 'android'
dep_android_minigbm_headers = dependency('android-minigbm-headers')
dep_android_nativebase_headers = dependency('android-nativebase-headers')
dep_android_aemu_gralloc_headers = dependency('android-aemu-gralloc-headers')
files_gfxstream_android = files(
'ANativeWindowAndroid.cpp',
'GfxStreamGralloc.cpp',
'GrallocGoldfish.cpp',
'GrallocMinigbm.cpp',
)
deps_gfxstream_android += [
dep_android_minigbm_headers,
dep_android_nativebase_headers,
dep_android_aemu_gralloc_headers,
]
else
files_gfxstream_android = files(
'ANativeWindowEmulated.cpp',
'GrallocEmulated.cpp',
)
endif
libgfxstream_guest_android = static_library(
'gfxstream_android',
files_gfxstream_android,
cpp_args: gfxstream_guest_args,
include_directories: [inc_gfxstream_android, inc_platform_virtgpu, inc_src, inc_include],
link_with: [libplatform_virtgpu],
dependencies: deps_gfxstream_android + dep_android,
)
idep_gfxstream_guest_android = declare_dependency(
link_with: libgfxstream_guest_android,
include_directories: inc_gfxstream_android,
)
gfxstream_deps += idep_gfxstream_guest_android
@@ -0,0 +1,10 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GfxStreamConnection.h"
GfxStreamConnection::GfxStreamConnection() {}
GfxStreamConnection::~GfxStreamConnection() {}
@@ -0,0 +1,17 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef GFXSTREAM_CONNECTION_H
#define GFXSTREAM_CONNECTION_H
class GfxStreamConnection {
public:
GfxStreamConnection();
virtual ~GfxStreamConnection();
virtual void* getEncoder() = 0;
};
#endif
@@ -0,0 +1,193 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "GfxStreamConnectionManager.h"
#include <cerrno>
#include "GoldfishAddressSpaceStream.h"
#include "QemuPipeStream.h"
#include "VirtGpu.h"
#include "VirtioGpuAddressSpaceStream.h"
#include "VirtioGpuPipeStream.h"
#include "c11/threads.h"
#include "util/log.h"
#define STREAM_BUFFER_SIZE (4 * 1024 * 1024)
static tss_t gfxstream_connection_manager_tls_key;
static bool gfxstream_connection_manager_tls_key_valid;
static void gfxstream_connection_manager_tls_free(void* tls) {
if (tls) {
delete ((GfxStreamConnectionManager*)tls);
}
}
static void gfxstream_connection_manager_tls_key_create_once(void) {
gfxstream_connection_manager_tls_key_valid =
tss_create(&gfxstream_connection_manager_tls_key, gfxstream_connection_manager_tls_free) ==
thrd_success;
if (!gfxstream_connection_manager_tls_key_valid) {
mesa_logd("WARNING: failed to create gfxstream_connection_manager_tls_key");
}
}
GfxStreamConnectionManager* GfxStreamConnectionManager::getThreadLocalInstance(
GfxStreamTransportType type, VirtGpuCapset capset) {
static once_flag once = ONCE_FLAG_INIT;
call_once(&once, gfxstream_connection_manager_tls_key_create_once);
if (unlikely(!gfxstream_connection_manager_tls_key_valid)) {
return nullptr;
}
GfxStreamConnectionManager* tls =
(GfxStreamConnectionManager*)tss_get(gfxstream_connection_manager_tls_key);
if (likely(tls)) {
return tls;
}
tls = new GfxStreamConnectionManager(type, capset);
if (!tls) {
return nullptr;
}
if (!tls->initialize()) {
delete tls;
return nullptr;
}
if (tss_set(gfxstream_connection_manager_tls_key, tls) != thrd_success) {
delete tls;
return nullptr;
}
return tls;
}
void GfxStreamConnectionManager::resetThreadLocalInstance() {
if (unlikely(!gfxstream_connection_manager_tls_key_valid)) {
return;
}
GfxStreamConnectionManager* tls =
(GfxStreamConnectionManager*)tss_get(gfxstream_connection_manager_tls_key);
if (unlikely(!tls)) {
return;
}
delete tls;
void* null_ptr = nullptr;
tss_set(gfxstream_connection_manager_tls_key, null_ptr);
}
GfxStreamConnectionManager::GfxStreamConnectionManager(GfxStreamTransportType type,
VirtGpuCapset capset)
: mTransportType(type), mCapset(capset) {}
GfxStreamConnectionManager::~GfxStreamConnectionManager() {}
bool GfxStreamConnectionManager::initialize() {
switch (mTransportType) {
#ifdef GFXSTREAM_ENABLE_GUEST_GOLDFISH
case GFXSTREAM_TRANSPORT_ADDRESS_SPACE: {
mStream = createGoldfishAddressSpaceStream(STREAM_BUFFER_SIZE);
if (!mStream) {
mesa_loge("Failed to create AddressSpaceStream for host connection\n");
return false;
}
break;
}
#endif
case GFXSTREAM_TRANSPORT_QEMU_PIPE: {
mStream = new QemuPipeStream(STREAM_BUFFER_SIZE);
if (mStream->connect() < 0) {
mesa_loge("Failed to connect to host (QemuPipeStream)\n");
return false;
}
break;
}
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE: {
VirtioGpuPipeStream* pipeStream =
new VirtioGpuPipeStream(STREAM_BUFFER_SIZE, INVALID_DESCRIPTOR);
if (!pipeStream) {
mesa_loge("Failed to create VirtioGpu for host connection\n");
return false;
}
if (pipeStream->connect() < 0) {
mesa_loge("Failed to connect to host (VirtioGpu)\n");
return false;
}
mDescriptor = pipeStream->getRendernodeFd();
VirtGpuDevice::getInstance(mCapset);
mStream = pipeStream;
break;
}
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE: {
// Use kCapsetGfxStreamVulkan for now, Ranchu HWC needs to be modified to pass in
// right capset.
auto device = VirtGpuDevice::getInstance(kCapsetGfxStreamVulkan);
if (!device) {
mesa_logd("Failed to get VirtGpuDevice\n");
return false;
}
mDescriptor = device->getDeviceHandle();
mStream = createVirtioGpuAddressSpaceStream(kCapsetGfxStreamVulkan);
if (!mStream) {
mesa_loge("Failed to create virtgpu AddressSpaceStream\n");
return false;
}
break;
}
default:
return false;
}
// send zero 'clientFlags' to the host. This is actually part of the gfxstream protocol.
unsigned int* pClientFlags = (unsigned int*)mStream->allocBuffer(sizeof(unsigned int));
*pClientFlags = 0;
mStream->commitBuffer(sizeof(unsigned int));
return true;
}
int32_t GfxStreamConnectionManager::addConnection(GfxStreamConnectionType type,
std::unique_ptr<GfxStreamConnection> connection) {
if (mConnections.find(type) != mConnections.end()) {
return -EINVAL;
}
mConnections[type] = std::move(connection);
return 0;
}
void* GfxStreamConnectionManager::getEncoder(GfxStreamConnectionType type) {
auto iterator = mConnections.find(type);
if (iterator == mConnections.end()) {
return nullptr;
}
return iterator->second->getEncoder();
}
gfxstream::guest::IOStream* GfxStreamConnectionManager::getStream() { return mStream; }
gfxstream::guest::IOStream* GfxStreamConnectionManager::processPipeStream(
GfxStreamTransportType transportType) {
switch (transportType) {
case GFXSTREAM_TRANSPORT_ADDRESS_SPACE:
case GFXSTREAM_TRANSPORT_QEMU_PIPE:
return new QemuPipeStream(STREAM_BUFFER_SIZE);
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE:
case GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE:
return new VirtioGpuPipeStream(STREAM_BUFFER_SIZE, mDescriptor);
default:
return nullptr;
}
}
@@ -0,0 +1,56 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef GFXSTREAM_CONNECTION_MANAGER_H
#define GFXSTREAM_CONNECTION_MANAGER_H
#include <memory>
#include <unordered_map>
#include "GfxStreamConnection.h"
#include "VirtGpu.h"
#include "gfxstream/guest/IOStream.h"
enum GfxStreamConnectionType {
GFXSTREAM_CONNECTION_GLES = 1,
GFXSTREAM_CONNECTION_GLES2 = 2,
GFXSTREAM_CONNECTION_RENDER_CONTROL = 3,
GFXSTREAM_CONNECTION_VULKAN = 4,
};
enum GfxStreamTransportType {
GFXSTREAM_TRANSPORT_QEMU_PIPE = 1,
GFXSTREAM_TRANSPORT_ADDRESS_SPACE = 2,
GFXSTREAM_TRANSPORT_VIRTIO_GPU_PIPE = 3,
GFXSTREAM_TRANSPORT_VIRTIO_GPU_ADDRESS_SPACE = 4,
};
class GfxStreamConnectionManager {
public:
static GfxStreamConnectionManager* getThreadLocalInstance(GfxStreamTransportType type,
VirtGpuCapset capset);
static void resetThreadLocalInstance();
GfxStreamConnectionManager(GfxStreamTransportType type, VirtGpuCapset capset);
~GfxStreamConnectionManager();
bool initialize();
int32_t addConnection(GfxStreamConnectionType type,
std::unique_ptr<GfxStreamConnection> connection);
void* getEncoder(GfxStreamConnectionType type);
gfxstream::guest::IOStream* getStream();
gfxstream::guest::IOStream* processPipeStream(GfxStreamTransportType transportType);
private:
// intrusively refcounted
gfxstream::guest::IOStream* mStream = nullptr;
int32_t mDescriptor = -1;
GfxStreamTransportType mTransportType;
VirtGpuCapset mCapset;
std::unordered_map<GfxStreamConnectionType, std::unique_ptr<GfxStreamConnection>> mConnections;
};
#endif
@@ -0,0 +1,247 @@
/*
* Copyright 2011 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "QemuPipeStream.h"
#include <errno.h>
#include <qemu_pipe_bp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "util/log.h"
static const size_t kReadSize = 512 * 1024;
static const size_t kWriteOffset = kReadSize;
QemuPipeStream::QemuPipeStream(size_t bufSize)
: IOStream(bufSize), m_sock(-1), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
QemuPipeStream::QemuPipeStream(QEMU_PIPE_HANDLE sock, size_t bufSize)
: IOStream(bufSize), m_sock(sock), m_bufsize(bufSize), m_buf(NULL), m_read(0), m_readLeft(0) {}
QemuPipeStream::~QemuPipeStream() {
if (valid()) {
flush();
qemu_pipe_close((QEMU_PIPE_HANDLE)m_sock);
}
if (m_buf != NULL) {
free(m_buf);
}
}
int QemuPipeStream::connect(const char* serviceName) {
m_sock = (int)qemu_pipe_open("opengles");
if (!valid()) {
mesa_loge("%s: failed to connect to opengles pipe", __FUNCTION__);
qemu_pipe_print_error(m_sock);
return -1;
}
return 0;
}
uint64_t QemuPipeStream::processPipeInit() {
QEMU_PIPE_HANDLE processPipe = qemu_pipe_open("GLProcessPipe");
uint64_t procUID = 0;
if (!qemu_pipe_valid(processPipe)) {
processPipe = 0;
mesa_logi("Process pipe failed");
return 0;
}
// Send a confirmation int to the host
int32_t confirmInt = 100;
if (qemu_pipe_write_fully(processPipe, &confirmInt, sizeof(confirmInt))) { // failed
qemu_pipe_close(processPipe);
processPipe = 0;
mesa_logi("Process pipe failed");
return 0;
}
// Ask the host for per-process unique ID
if (qemu_pipe_read_fully(processPipe, &procUID, sizeof(procUID))) {
qemu_pipe_close(processPipe);
processPipe = 0;
procUID = 0;
mesa_logi("Process pipe failed");
return 0;
}
return procUID;
}
void* QemuPipeStream::allocBuffer(size_t minSize) {
// Add dedicated read buffer space at the front of the buffer.
minSize += kReadSize;
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char*)malloc(allocSize);
} else if (m_bufsize < allocSize) {
unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
mesa_loge("realloc (%zu) failed\n", allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf + kWriteOffset;
};
int QemuPipeStream::commitBuffer(size_t size) {
if (size == 0) return 0;
return writeFully(m_buf + kWriteOffset, size);
}
int QemuPipeStream::writeFully(const void* buf, size_t len) {
return qemu_pipe_write_fully(m_sock, buf, len);
}
const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
return commitBufferAndReadFully(0, buf, len);
}
const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
unsigned char* userReadBuf = static_cast<unsigned char*>(userReadBufPtr);
if (!valid()) return NULL;
if (!userReadBuf) {
if (totalReadSize > 0) {
mesa_loge(
"QemuPipeStream::commitBufferAndReadFully failed, userReadBuf=NULL, totalReadSize "
"%zu, lethal"
" error, exiting.",
totalReadSize);
abort();
}
if (!writeSize) {
return NULL;
}
}
// Advance buffered read if not yet consumed.
size_t remaining = totalReadSize;
size_t bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf, m_buf + (m_read - m_readLeft), bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
}
// Early out if nothing left to do.
if (!writeSize && !remaining) {
return userReadBuf;
}
writeFully(m_buf + kWriteOffset, writeSize);
// Now done writing. Early out if no reading left to do.
if (!remaining) {
return userReadBuf;
}
// Read up to kReadSize bytes if all buffered read has been consumed.
size_t maxRead = m_readLeft ? 0 : kReadSize;
ssize_t actual = 0;
if (maxRead) {
actual = qemu_pipe_read(m_sock, m_buf, maxRead);
// Updated buffered read size.
if (actual > 0) {
m_read = m_readLeft = actual;
}
if (actual == 0) {
mesa_logi("%s: end of pipe", __FUNCTION__);
return NULL;
}
}
// Consume buffered read and read more if necessary.
while (remaining) {
bufferedReadSize = m_readLeft < remaining ? m_readLeft : remaining;
if (bufferedReadSize) {
memcpy(userReadBuf + (totalReadSize - remaining), m_buf + (m_read - m_readLeft),
bufferedReadSize);
remaining -= bufferedReadSize;
m_readLeft -= bufferedReadSize;
continue;
}
actual = qemu_pipe_read(m_sock, m_buf, kReadSize);
if (actual == 0) {
mesa_logi("%s: Failed reading from pipe: %d", __FUNCTION__, errno);
return NULL;
}
if (actual > 0) {
m_read = m_readLeft = actual;
continue;
}
if (!qemu_pipe_try_again(actual)) {
mesa_logi("%s: Error reading from pipe: %d", __FUNCTION__, errno);
return NULL;
}
}
return userReadBuf;
}
const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
if (!valid()) return NULL;
if (!buf) {
mesa_loge("QemuPipeStream::read failed, buf=NULL");
return NULL; // do not allow NULL buf in that implementation
}
int n = recv(buf, *inout_len);
if (n > 0) {
*inout_len = n;
return (const unsigned char*)buf;
}
return NULL;
}
bool QemuPipeStream::valid() { return qemu_pipe_valid(m_sock); }
int QemuPipeStream::recv(void* buf, size_t len) {
if (!valid()) return int(ERR_INVALID_SOCKET);
char* p = (char*)buf;
int ret = 0;
while (len > 0) {
int res = qemu_pipe_read(m_sock, p, len);
if (res > 0) {
p += res;
ret += res;
len -= res;
continue;
}
if (res == 0) { /* EOF */
break;
}
if (qemu_pipe_try_again(res)) {
continue;
}
/* A real error */
if (ret == 0) ret = -1;
break;
}
return ret;
}
@@ -0,0 +1,48 @@
/*
* Copyright 2011 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef __QEMU_PIPE_STREAM_H
#define __QEMU_PIPE_STREAM_H
/* This file implements an IOStream that uses a QEMU fast-pipe
* to communicate with the emulator's 'opengles' service. See
* <hardware/qemu_pipe.h> for more details.
*/
#include <stdlib.h>
#include <memory>
#include "gfxstream/guest/IOStream.h"
class QemuPipeStream : public gfxstream::guest::IOStream {
public:
typedef enum { ERR_INVALID_SOCKET = -1000 } QemuPipeStreamError;
explicit QemuPipeStream(size_t bufsize = 10000);
~QemuPipeStream();
virtual int connect(const char* serviceName = nullptr);
virtual uint64_t processPipeInit();
virtual void* allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char* readFully(void* buf, size_t len);
virtual const unsigned char* commitBufferAndReadFully(size_t size, void* buf, size_t len);
virtual const unsigned char* read(void* buf, size_t* inout_len);
bool valid();
int recv(void* buf, size_t len);
virtual int writeFully(const void* buf, size_t len);
private:
int m_sock;
size_t m_bufsize;
unsigned char* m_buf;
size_t m_read;
size_t m_readLeft;
QemuPipeStream(int sock, size_t bufSize);
};
#endif
@@ -0,0 +1,62 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "QemuPipeStream.h"
QemuPipeStream::QemuPipeStream(size_t bufSize) : IOStream(bufSize) { (void)bufSize; }
QemuPipeStream::~QemuPipeStream() {}
int QemuPipeStream::connect(const char* serviceName) {
(void)serviceName;
return 0;
}
uint64_t QemuPipeStream::processPipeInit() { return 0; }
void* QemuPipeStream::allocBuffer(size_t minSize) {
(void)minSize;
return nullptr;
};
int QemuPipeStream::commitBuffer(size_t size) {
(void)size;
return 0;
}
int QemuPipeStream::writeFully(const void* buf, size_t len) {
(void)buf;
(void)len;
return 0;
}
const unsigned char* QemuPipeStream::readFully(void* buf, size_t len) {
(void)buf;
(void)len;
return nullptr;
}
const unsigned char* QemuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
(void)writeSize;
(void)userReadBufPtr;
(void)totalReadSize;
return nullptr;
}
const unsigned char* QemuPipeStream::read(void* buf, size_t* inout_len) {
(void)buf;
(void)inout_len;
return nullptr;
}
bool QemuPipeStream::valid() { return false; }
int QemuPipeStream::recv(void* buf, size_t len) {
(void)buf;
(void)len;
return 0;
}
@@ -0,0 +1,335 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "VirtioGpuPipeStream.h"
#include <errno.h>
#include "util/detect_os.h"
#if DETECT_OS_LINUX
#include <sys/mman.h>
#include <unistd.h>
#endif
#include <sys/types.h>
#include <cstring>
#include <string>
#include "VirtGpu.h"
#include "util/log.h"
#include "virtio/virtio-gpu/virgl_hw.h"
static const size_t kTransferBufferSize = (1048576);
static const size_t kReadSize = 512 * 1024;
static const size_t kWriteOffset = kReadSize;
VirtioGpuPipeStream::VirtioGpuPipeStream(size_t bufSize, int32_t descriptor)
: IOStream(bufSize),
m_fd(descriptor),
m_virtio_mapped(nullptr),
m_bufsize(bufSize),
m_buf(nullptr),
m_writtenPos(0) {}
VirtioGpuPipeStream::~VirtioGpuPipeStream() { free(m_buf); }
bool VirtioGpuPipeStream::valid() { return m_device != nullptr; }
int VirtioGpuPipeStream::getRendernodeFd() {
if (m_device == nullptr) {
return -1;
}
return m_device->getDeviceHandle();
}
int VirtioGpuPipeStream::connect(const char* serviceName) {
if (!m_device) {
m_device.reset(createPlatformVirtGpuDevice(kCapsetNone, m_fd));
if (!m_device) {
mesa_loge("Failed to create VirtioGpuPipeStream VirtGpuDevice.");
return -1;
}
m_resource = m_device->createResource(/*width=*/kTransferBufferSize,
/*height=*/1,
/*stride=*/kTransferBufferSize,
/*size=*/kTransferBufferSize, VIRGL_FORMAT_R8_UNORM,
PIPE_BUFFER, VIRGL_BIND_CUSTOM);
if (!m_resource) {
mesa_loge("Failed to create VirtioGpuPipeStream resource.");
return -1;
}
m_resourceMapping = m_resource->createMapping();
if (!m_resourceMapping) {
mesa_loge("Failed to create VirtioGpuPipeStream resource mapping.");
return -1;
}
m_virtio_mapped = m_resourceMapping->asRawPtr();
if (!m_virtio_mapped) {
mesa_loge("Failed to create VirtioGpuPipeStream resource mapping ptr.");
return -1;
}
}
wait();
if (serviceName) {
writeFully(serviceName, strlen(serviceName) + 1);
} else {
static const char kPipeString[] = "pipe:opengles";
std::string pipeStr(kPipeString);
writeFully(kPipeString, sizeof(kPipeString));
}
return 0;
}
uint64_t VirtioGpuPipeStream::processPipeInit() {
connect("pipe:GLProcessPipe");
int32_t confirmInt = 100;
writeFully(&confirmInt, sizeof(confirmInt));
uint64_t res;
readFully(&res, sizeof(res));
return res;
}
void* VirtioGpuPipeStream::allocBuffer(size_t minSize) {
size_t allocSize = (m_bufsize < minSize ? minSize : m_bufsize);
if (!m_buf) {
m_buf = (unsigned char*)malloc(allocSize);
} else if (m_bufsize < allocSize) {
unsigned char* p = (unsigned char*)realloc(m_buf, allocSize);
if (p != NULL) {
m_buf = p;
m_bufsize = allocSize;
} else {
mesa_loge("realloc (%zu) failed\n", allocSize);
free(m_buf);
m_buf = NULL;
m_bufsize = 0;
}
}
return m_buf;
}
int VirtioGpuPipeStream::commitBuffer(size_t size) {
if (size == 0) return 0;
return writeFully(m_buf, size);
}
int VirtioGpuPipeStream::writeFully(const void* buf, size_t len) {
// DBG(">> VirtioGpuPipeStream::writeFully %d\n", len);
if (!valid()) return -1;
if (!buf) {
if (len > 0) {
// If len is non-zero, buf must not be NULL. Otherwise the pipe would be
// in a corrupted state, which is lethal for the emulator.
mesa_loge(
"VirtioGpuPipeStream::writeFully failed, buf=NULL, len %zu,"
" lethal error, exiting",
len);
abort();
}
return 0;
}
size_t res = len;
int retval = 0;
while (res > 0) {
ssize_t stat = transferToHost((const char*)(buf) + (len - res), res);
if (stat > 0) {
res -= stat;
continue;
}
if (stat == 0) { /* EOF */
mesa_loge("VirtioGpuPipeStream::writeFully failed: premature EOF\n");
retval = -1;
break;
}
if (errno == EAGAIN) {
continue;
}
retval = stat;
mesa_loge("VirtioGpuPipeStream::writeFully failed: %s, lethal error, exiting.\n",
strerror(errno));
abort();
}
// DBG("<< VirtioGpuPipeStream::writeFully %d\n", len );
return retval;
}
const unsigned char* VirtioGpuPipeStream::readFully(void* buf, size_t len) {
flush();
if (!valid()) return NULL;
if (!buf) {
if (len > 0) {
// If len is non-zero, buf must not be NULL. Otherwise the pipe would be
// in a corrupted state, which is lethal for the emulator.
mesa_loge(
"VirtioGpuPipeStream::readFully failed, buf=NULL, len %zu, lethal"
" error, exiting.",
len);
abort();
}
}
size_t res = len;
while (res > 0) {
ssize_t stat = transferFromHost((char*)(buf) + len - res, res);
if (stat == 0) {
// client shutdown;
return NULL;
} else if (stat < 0) {
if (errno == EAGAIN) {
continue;
} else {
mesa_loge(
"VirtioGpuPipeStream::readFully failed (buf %p, len %zu"
", res %zu): %s, lethal error, exiting.",
buf, len, res, strerror(errno));
abort();
}
} else {
res -= stat;
}
}
// DBG("<< VirtioGpuPipeStream::readFully %d\n", len);
return (const unsigned char*)buf;
}
const unsigned char* VirtioGpuPipeStream::commitBufferAndReadFully(size_t writeSize,
void* userReadBufPtr,
size_t totalReadSize) {
return commitBuffer(writeSize) ? nullptr : readFully(userReadBufPtr, totalReadSize);
}
const unsigned char* VirtioGpuPipeStream::read(void* buf, size_t* inout_len) {
// DBG(">> VirtioGpuPipeStream::read %d\n", *inout_len);
if (!valid()) return NULL;
if (!buf) {
mesa_loge("VirtioGpuPipeStream::read failed, buf=NULL");
return NULL; // do not allow NULL buf in that implementation
}
int n = recv(buf, *inout_len);
if (n > 0) {
*inout_len = n;
return (const unsigned char*)buf;
}
// DBG("<< VirtioGpuPipeStream::read %d\n", *inout_len);
return NULL;
}
int VirtioGpuPipeStream::recv(void* buf, size_t len) {
if (!valid()) return -EINVAL;
char* p = (char*)buf;
int ret = 0;
while (len > 0) {
int res = transferFromHost(p, len);
if (res > 0) {
p += res;
ret += res;
len -= res;
continue;
}
if (res == 0) { /* EOF */
break;
}
if (errno != EAGAIN) {
continue;
}
/* A real error */
if (ret == 0) ret = -1;
break;
}
return ret;
}
void VirtioGpuPipeStream::wait() {
int ret = m_resource->wait();
if (ret) {
mesa_loge("VirtioGpuPipeStream: DRM_IOCTL_VIRTGPU_WAIT failed with %d (%s)\n", errno,
strerror(errno));
}
m_writtenPos = 0;
}
ssize_t VirtioGpuPipeStream::transferToHost(const void* buffer, size_t len) {
size_t todo = len;
size_t done = 0;
int ret = EAGAIN;
unsigned char* virtioPtr = m_virtio_mapped;
const unsigned char* readPtr = reinterpret_cast<const unsigned char*>(buffer);
while (done < len) {
size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
if (toXfer > (kTransferBufferSize - m_writtenPos)) {
wait();
}
memcpy(virtioPtr + m_writtenPos, readPtr, toXfer);
ret = m_resource->transferToHost(m_writtenPos, toXfer);
if (ret) {
mesa_loge("VirtioGpuPipeStream: failed to transferToHost() with errno %d (%s)\n", errno,
strerror(errno));
return (ssize_t)ret;
}
done += toXfer;
readPtr += toXfer;
todo -= toXfer;
m_writtenPos += toXfer;
}
return len;
}
ssize_t VirtioGpuPipeStream::transferFromHost(void* buffer, size_t len) {
size_t todo = len;
size_t done = 0;
int ret = EAGAIN;
const unsigned char* virtioPtr = m_virtio_mapped;
unsigned char* readPtr = reinterpret_cast<unsigned char*>(buffer);
if (m_writtenPos) {
wait();
}
while (done < len) {
size_t toXfer = todo > kTransferBufferSize ? kTransferBufferSize : todo;
ret = m_resource->transferFromHost(0, toXfer);
if (ret) {
mesa_loge("VirtioGpuPipeStream: failed to transferFromHost() with errno %d (%s)\n",
errno, strerror(errno));
return (ssize_t)ret;
}
wait();
memcpy(readPtr, virtioPtr, toXfer);
done += toXfer;
readPtr += toXfer;
todo -= toXfer;
}
return len;
}
@@ -0,0 +1,57 @@
/*
* Copyright 2018 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdlib.h>
#include "VirtGpu.h"
#include "gfxstream/guest/IOStream.h"
/* This file implements an IOStream that uses VIRTGPU TRANSFER* ioctls on a
* virtio-gpu DRM rendernode device to communicate with a goldfish-pipe
* service on the host side.
*/
class VirtioGpuPipeStream : public gfxstream::guest::IOStream {
public:
explicit VirtioGpuPipeStream(size_t bufsize, int32_t descriptor);
~VirtioGpuPipeStream();
virtual int connect(const char* serviceName = nullptr);
virtual uint64_t processPipeInit();
virtual void* allocBuffer(size_t minSize);
virtual int commitBuffer(size_t size);
virtual const unsigned char* readFully(void* buf, size_t len);
virtual const unsigned char* commitBufferAndReadFully(size_t size, void* buf, size_t len);
virtual const unsigned char* read(void* buf, size_t* inout_len);
bool valid();
int getRendernodeFd();
int recv(void* buf, size_t len);
virtual int writeFully(const void* buf, size_t len);
private:
// sync. Also resets the write position.
void wait();
// transfer to/from host ops
ssize_t transferToHost(const void* buffer, size_t len);
ssize_t transferFromHost(void* buffer, size_t len);
int32_t m_fd = -1;
std::unique_ptr<VirtGpuDevice> m_device;
VirtGpuResourcePtr m_resource;
VirtGpuResourceMappingPtr m_resourceMapping;
unsigned char* m_virtio_mapped; // user mapping of bo
// intermediate buffer
size_t m_bufsize;
unsigned char* m_buf;
size_t m_writtenPos;
};
@@ -0,0 +1,32 @@
# Copyright 2024 Android Open Source Project
# SPDX-License-Identifier: MIT
inc_connection_manager = include_directories('.')
files_libconnection_manager = files(
'GfxStreamConnection.cpp',
'GfxStreamConnectionManager.cpp',
'VirtioGpuPipeStream.cpp',
)
if with_platform_android
files_libconnection_manager += files('QemuPipeStream.cpp')
else
files_libconnection_manager += files('QemuPipeStreamStub.cpp')
endif
libconnection_manager = static_library(
'connection_manager',
files_libconnection_manager,
cpp_args: gfxstream_guest_args,
include_directories: [
inc_connection_manager,
inc_guest_iostream,
inc_goldfish_address_space,
inc_platform_virtgpu,
inc_src,
inc_aemu,
],
link_with: [lib_goldfish_address_space, libplatform_virtgpu],
dependencies: [dep_libdrm, dep_android_aemu_qemupipe, idep_mesautil],
)
@@ -0,0 +1,139 @@
/*
* Copyright 2011 Google LLC
* SPDX-License-Identifier: MIT
*/
#ifndef __IO_STREAM_H__
#define __IO_STREAM_H__
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
namespace gfxstream {
namespace guest {
class IOStream {
public:
IOStream(size_t bufSize) {
m_iostreamBuf = NULL;
m_bufsizeOrig = bufSize;
m_bufsize = bufSize;
m_free = 0;
m_refcount = 1;
}
void incRef() {
__atomic_add_fetch(&m_refcount, 1, __ATOMIC_SEQ_CST);
}
bool decRef() {
if (0 == __atomic_sub_fetch(&m_refcount, 1, __ATOMIC_SEQ_CST)) {
delete this;
return true;
}
return false;
}
virtual size_t idealAllocSize(size_t len) {
return m_bufsize < len ? len : m_bufsize;
}
virtual int connect(const char* /*serviceName*/ = nullptr) { return 0; }
virtual uint64_t processPipeInit() { return 0; }
virtual void *allocBuffer(size_t minSize) = 0;
virtual int commitBuffer(size_t size) = 0;
virtual const unsigned char *readFully( void *buf, size_t len) = 0;
virtual const unsigned char *commitBufferAndReadFully(size_t size, void *buf, size_t len) = 0;
virtual const unsigned char *read( void *buf, size_t *inout_len) = 0;
virtual int writeFully(const void* buf, size_t len) = 0;
virtual int writeFullyAsync(const void* buf, size_t len) {
return writeFully(buf, len);
}
virtual ~IOStream() {
// NOTE: m_iostreamBuf is 'owned' by the child class thus we expect it to be released by it
}
virtual unsigned char *alloc(size_t len) {
if (m_iostreamBuf && len > m_free) {
if (flush() < 0) {
return NULL; // we failed to flush so something is wrong
}
}
if (!m_iostreamBuf || len > m_bufsize) {
size_t allocLen = this->idealAllocSize(len);
m_iostreamBuf = (unsigned char *)allocBuffer(allocLen);
if (!m_iostreamBuf) {
return NULL;
}
m_bufsize = m_free = allocLen;
}
unsigned char *ptr;
ptr = m_iostreamBuf + (m_bufsize - m_free);
m_free -= len;
return ptr;
}
virtual int flush() {
if (!m_iostreamBuf || m_free == m_bufsize) return 0;
int stat = commitBuffer(m_bufsize - m_free);
m_iostreamBuf = NULL;
m_free = 0;
return stat;
}
const unsigned char *readback(void *buf, size_t len) {
if (m_iostreamBuf && m_free != m_bufsize) {
size_t size = m_bufsize - m_free;
m_iostreamBuf = NULL;
m_free = 0;
return commitBufferAndReadFully(size, buf, len);
}
return readFully(buf, len);
}
// These two methods are defined and used in GLESv2_enc. Any reference
// outside of GLESv2_enc will produce a link error. This is intentional
// (technical debt).
void readbackPixels(void* context, int width, int height, unsigned int format, unsigned int type, void* pixels);
void uploadPixels(void* context, int width, int height, int depth, unsigned int format, unsigned int type, const void* pixels);
protected:
void rewind() {
m_iostreamBuf = NULL;
m_bufsize = m_bufsizeOrig;
m_free = 0;
}
private:
unsigned char *m_iostreamBuf;
size_t m_bufsizeOrig;
size_t m_bufsize;
size_t m_free;
uint32_t m_refcount;
};
} // namespace guest
} // namespace gfxstream
//
// When a client opens a connection to the renderer, it should
// send unsigned int value indicating the "clientFlags".
// The following are the bitmask of the clientFlags.
// currently only one bit is used which flags the server
// it should exit.
//
#define IOSTREAM_CLIENT_EXIT_SERVER 1
#endif
+4
View File
@@ -0,0 +1,4 @@
# Copyright 2023 Android Open Source Project
# SPDX-License-Identifier: MIT
inc_guest_iostream = include_directories('include')
+126
View File
@@ -0,0 +1,126 @@
# Copyright 2022 Android Open Source Project
# SPDX-License-Identifier: MIT
#===============#
# Options #
#===============#
with_vulkan_icd_dir = get_option('vulkan-icd-dir')
if with_vulkan_icd_dir == ''
with_vulkan_icd_dir = join_paths(get_option('datadir'), 'vulkan/icd.d')
endif
#===============#
# Configuration #
#===============#
gfxstream_guest_args = cpp_msvc_compat_args + soong_compat_cpp_args + ['-D_FILE_OFFSET_BITS=64']
# Our internal guest build
if host_machine.system() == 'windows'
gfxstream_guest_args += '-DWINDOWS_GUEST_BUILD'
elif host_machine.system() != 'fuchsia'
gfxstream_guest_args += '-DLINUX_GUEST_BUILD'
endif
# Include the gfxstream private VkStructureType definitions
gfxstream_guest_args += '-DVK_GFXSTREAM_STRUCTURE_TYPE_EXT'
gfxstream_guest_args += '-DVK_BASE_VERSION_1_0'
gfxstream_guest_args += '-DVK_GRAPHICS_VERSION_1_0'
gfxstream_guest_args += '-DVK_COMPUTE_VERSION_1_0'
gfxstream_guest_args += '-DVK_BASE_VERSION_1_1'
gfxstream_guest_args += '-DVK_GRAPHICS_VERSION_1_1'
gfxstream_guest_args += '-DVK_COMPUTE_VERSION_1_1'
gfxstream_guest_args += '-DVK_BASE_VERSION_1_2'
gfxstream_guest_args += '-DVK_GRAPHICS_VERSION_1_2'
gfxstream_guest_args += '-DVK_COMPUTE_VERSION_1_2'
gfxstream_guest_args += '-DVK_BASE_VERSION_1_3'
gfxstream_guest_args += '-DVK_GRAPHICS_VERSION_1_3'
gfxstream_guest_args += '-DVK_COMPUTE_VERSION_1_3'
gfxstream_guest_args += '-DVK_BASE_VERSION_1_4'
gfxstream_guest_args += '-DVK_GRAPHICS_VERSION_1_4'
gfxstream_guest_args += '-DVK_COMPUTE_VERSION_1_4'
if with_gfxstream_emulated_android
gfxstream_guest_args += '-DEND2END_TESTS'
endif
if with_platform_android
gfxstream_guest_args += '-DGFXSTREAM_ENABLE_GUEST_GOLDFISH'
endif
#====================================#
# Standard deps #
#====================================#
gfxstream_deps = [
dep_libdrm,
idep_mesautil,
idep_vulkan_wsi_headers,
idep_vulkan_lite_runtime,
idep_vulkan_util_headers,
idep_vulkan_wsi,
dep_valgrind,
]
if with_perfetto
gfxstream_deps += dep_perfetto
endif
#====================================#
# Android deps (mostly Goldfish) #
# Only works with meson2hermetic #
#====================================#
dep_android_base = null_dep
dep_android_arect = null_dep
dep_android_aemu_base = null_dep
dep_android_aemu_gl_codec = null_dep
dep_android_aemu_qemupipe = null_dep
dep_android_aemu_ringbuffer = null_dep
dep_android_aemu_rc_encoder = null_dep
if with_platform_android or with_gfxstream_emulated_android
gfxstream_guest_args += '-DVK_USE_PLATFORM_ANDROID_KHR'
dep_android_base = dependency('android-base')
dep_android_arect = dependency('android-arect')
dep_android_aemu_base = dependency('android-aemu-base')
dep_android_aemu_gl_codec = dependency('android-aemu-gl-codec')
dep_android_aemu_qemupipe = dependency('android-aemu-qemupipe')
dep_android_aemu_ringbuffer = dependency('android-aemu-ringbuffer')
dep_android_aemu_rc_encoder = dependency('android-aemu-rc-encoder')
gfxstream_deps += dep_android
gfxstream_deps += [
dep_android_base,
dep_android_arect,
dep_android_aemu_base,
dep_android_aemu_gl_codec,
dep_android_aemu_ringbuffer,
dep_android_aemu_rc_encoder,
dep_android_aemu_qemupipe,
]
endif
#===============#
# Includes #
#===============#
inc_vulkan_headers = include_directories('../../../include/vulkan')
inc_vulkan_enc = include_directories('vulkan_enc')
#================#
# Subdirectories #
#================#
subdir('iostream')
subdir('platform')
subdir('GoldfishAddressSpace')
subdir('connection-manager')
if with_platform_android or with_gfxstream_emulated_android
subdir('android')
endif
subdir('vulkan_enc')
subdir('vulkan')
+61
View File
@@ -0,0 +1,61 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include "VirtGpu.h"
#include <cstdlib>
#include "Sync.h"
#include "util/log.h"
#include "util/os_misc.h"
namespace {
static VirtGpuDevice* sDevice = nullptr;
} // namespace
VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) {
if (os_get_option("VIRTGPU_KUMQUAT")) {
return kumquatCreateVirtGpuDevice(capset, descriptor);
} else {
return osCreateVirtGpuDevice(capset, descriptor);
}
}
VirtGpuDevice* VirtGpuDevice::getInstance(enum VirtGpuCapset capset, int32_t descriptor) {
// If kCapsetNone is passed, we return a device that was created with any capset.
// Otherwise, the created device's capset must match the requested capset.
// We could support multiple capsets with a map of devices but that case isn't needed
// currently, and with multiple devices it's unclear how to handle kCapsetNone.
if (capset != kCapsetNone && sDevice && sDevice->getCapset() != capset) {
mesa_loge("Requested VirtGpuDevice capset %u, already created capset %u", capset,
sDevice->getCapset());
return nullptr;
}
if (!sDevice) {
sDevice = createPlatformVirtGpuDevice(capset, descriptor);
}
return sDevice;
}
void VirtGpuDevice::resetInstance() {
if (sDevice) {
delete sDevice;
sDevice = nullptr;
}
}
namespace gfxstream {
SyncHelper* createPlatformSyncHelper() {
if (os_get_option("VIRTGPU_KUMQUAT")) {
return kumquatCreateSyncHelper();
} else {
return osCreateSyncHelper();
}
}
} // namespace gfxstream
@@ -0,0 +1,47 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#include "DrmSync.h"
#include <unistd.h>
#include "util/libsync.h"
#include "util/log.h"
#include "util/macros.h"
#include "util/u_process.h"
namespace gfxstream {
DrmSyncHelper::DrmSyncHelper() {}
int DrmSyncHelper::wait(int syncFd, int timeoutMilliseconds) {
return sync_wait(syncFd, timeoutMilliseconds);
}
void DrmSyncHelper::debugPrint(int syncFd) {
struct sync_file_info* info = sync_file_info(syncFd);
if (!info) {
mesa_loge("failed to get sync file info");
return;
}
struct sync_fence_info* fence_info = (struct sync_fence_info*)info->sync_fence_info;
for (uint32_t i = 0; i < info->num_fences; i++) {
uint64_t time_ms = DIV_ROUND_UP(fence_info[i].timestamp_ns, 1e6);
mesa_logi("[%s] Fence: %s, status: %i, timestamp (ms): %llu", util_get_process_name(),
info->name, fence_info[i].status, (unsigned long long)time_ms);
}
free(info);
}
int DrmSyncHelper::dup(int syncFd) { return ::dup(syncFd); }
int DrmSyncHelper::close(int syncFd) { return ::close(syncFd); }
SyncHelper* osCreateSyncHelper() { return new DrmSyncHelper(); }
} // namespace gfxstream
@@ -0,0 +1,25 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "Sync.h"
namespace gfxstream {
class DrmSyncHelper : public SyncHelper {
public:
DrmSyncHelper();
int wait(int syncFd, int timeoutMilliseconds) override;
void debugPrint(int syncFd) override;
int dup(int syncFd) override;
int close(int syncFd) override;
};
} // namespace gfxstream
@@ -0,0 +1,89 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "VirtGpu.h"
#include <xf86drm.h>
class DrmVirtGpuResource : public std::enable_shared_from_this<DrmVirtGpuResource>,
public VirtGpuResource {
public:
DrmVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle, uint32_t resourceHandle,
uint64_t size);
~DrmVirtGpuResource();
void intoRaw() override;
uint32_t getResourceHandle() const override;
uint32_t getBlobHandle() const override;
uint64_t getSize() const override;
int wait() override;
VirtGpuResourceMappingPtr createMapping(void) override;
int exportBlob(struct VirtGpuExternalHandle& handle) override;
int transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
int transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
private:
// Not owned. Really should use a ScopedFD for this, but doesn't matter since we have a
// singleton deviceimplemenentation anyways.
int64_t mDeviceHandle;
uint32_t mBlobHandle;
uint32_t mResourceHandle;
uint64_t mSize;
};
class DrmVirtGpuResourceMapping : public VirtGpuResourceMapping {
public:
DrmVirtGpuResourceMapping(VirtGpuResourcePtr blob, uint8_t* ptr, uint64_t size);
~DrmVirtGpuResourceMapping(void);
uint8_t* asRawPtr(void) override;
private:
VirtGpuResourcePtr mBlob;
uint8_t* mPtr;
uint64_t mSize;
};
class DrmVirtGpuDevice : public VirtGpuDevice {
public:
DrmVirtGpuDevice(enum VirtGpuCapset capset);
int32_t init(int32_t descriptor);
virtual ~DrmVirtGpuDevice();
virtual int64_t getDeviceHandle(void);
virtual struct VirtGpuCaps getCaps(void);
VirtGpuResourcePtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
VirtGpuResourcePtr createResource(uint32_t width, uint32_t height, uint32_t stride,
uint32_t size, uint32_t virglFormat, uint32_t target,
uint32_t bind) override;
virtual VirtGpuResourcePtr importBlob(const struct VirtGpuExternalHandle& handle);
virtual int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuResource* blob);
virtual bool getDrmInfo(VirtGpuDrmInfo* drmInfo) override;
virtual bool getPciBusInfo(VirtGpuPciBusInfo* pciBusInfo) override;
private:
int64_t mDeviceHandle = -1;
struct VirtGpuCaps mCaps;
int openDevice();
bool mHasPrimary;
int mPrimaryMajor;
int mPrimaryMinor;
int mRenderMajor;
int mRenderMinor;
int mBusType;
drmPciBusInfo mPciBusInfo;
};
@@ -0,0 +1,185 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <xf86drm.h>
#include <cerrno>
#include <cstring>
#include <mutex>
#include <unordered_map>
#include "DrmVirtGpu.h"
#include "drm-uapi/virtgpu_drm.h"
#include "util/log.h"
// As per the warning in xf86drm.h, callers of drmPrimeFDToHandle are expected to perform
// reference counting on the underlying GEM handle that is returned. With Vulkan, for example,
// it is entirely possible that an FD, which points to the same underlying GEM handle, is both
// exported then imported across Vulkan objects. As the VirtGpuResource is stored as a
// shared_ptr with it's own ref-countng, the ref-counting for the underyling GEM has to be
// internal to this implementation. Otherwise, a GEM handle which is active in another Vulkan object
// in the same process, may be erroneously closed in the desctructor of one of the shared ptrs.
static std::mutex sDrmObjectRefMutex;
static std::unordered_map<uint32_t, int> sDrmObjectRefMap;
DrmVirtGpuResource::DrmVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle,
uint32_t resourceHandle, uint64_t size)
: mDeviceHandle(deviceHandle),
mBlobHandle(blobHandle),
mResourceHandle(resourceHandle),
mSize(size) {
const std::lock_guard<std::mutex> lock(sDrmObjectRefMutex);
auto refMapIt = sDrmObjectRefMap.find(blobHandle);
if (refMapIt == sDrmObjectRefMap.end()) {
sDrmObjectRefMap[blobHandle] = 1;
} else {
refMapIt->second++;
}
}
DrmVirtGpuResource::~DrmVirtGpuResource() {
if (mBlobHandle == static_cast<uint32_t>(INVALID_DESCRIPTOR)) {
return;
}
const std::lock_guard<std::mutex> lock(sDrmObjectRefMutex);
auto refMapIt = sDrmObjectRefMap.find(mBlobHandle);
if (refMapIt == sDrmObjectRefMap.end()) {
mesa_logw(
"DrmVirtGpuResource::~DrmVirtGpuResource() could not find the blobHandle: %d in "
"internal map",
mBlobHandle);
return;
}
refMapIt->second--;
if (refMapIt->second <= 0) {
sDrmObjectRefMap.erase(refMapIt);
struct drm_gem_close gem_close {
.handle = mBlobHandle, .pad = 0,
};
int ret = drmIoctl(mDeviceHandle, DRM_IOCTL_GEM_CLOSE, &gem_close);
if (ret) {
mesa_loge("DRM_IOCTL_GEM_CLOSE failed with : [%s, blobHandle %u, resourceHandle: %u]",
strerror(errno), mBlobHandle, mResourceHandle);
}
}
}
void DrmVirtGpuResource::intoRaw() {
mBlobHandle = INVALID_DESCRIPTOR;
mResourceHandle = INVALID_DESCRIPTOR;
}
uint32_t DrmVirtGpuResource::getBlobHandle() const { return mBlobHandle; }
uint32_t DrmVirtGpuResource::getResourceHandle() const { return mResourceHandle; }
uint64_t DrmVirtGpuResource::getSize() const { return mSize; }
VirtGpuResourceMappingPtr DrmVirtGpuResource::createMapping() {
int ret;
struct drm_virtgpu_map map {
.handle = mBlobHandle, .pad = 0,
};
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_MAP, &map);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_MAP failed with %s", strerror(errno));
return nullptr;
}
uint8_t* ptr = static_cast<uint8_t*>(
mmap(nullptr, mSize, PROT_WRITE | PROT_READ, MAP_SHARED, mDeviceHandle, map.offset));
if (ptr == MAP_FAILED) {
mesa_loge("mmap64 failed with (%s)", strerror(errno));
return nullptr;
}
return std::make_shared<DrmVirtGpuResourceMapping>(shared_from_this(), ptr, mSize);
}
int DrmVirtGpuResource::exportBlob(struct VirtGpuExternalHandle& handle) {
int ret, fd;
uint32_t flags = DRM_CLOEXEC;
ret = drmPrimeHandleToFD(mDeviceHandle, mBlobHandle, flags, &fd);
if (ret) {
mesa_loge("drmPrimeHandleToFD failed with %s", strerror(errno));
return ret;
}
handle.osHandle = static_cast<int64_t>(fd);
handle.type = kMemHandleDmabuf;
return 0;
}
int DrmVirtGpuResource::wait() {
int ret;
struct drm_virtgpu_3d_wait wait_3d = {0};
int retry = 0;
do {
if (retry > 0 && (retry % 10 == 0)) {
mesa_loge("DRM_IOCTL_VIRTGPU_WAIT failed with EBUSY for %d times.", retry);
}
wait_3d.handle = mBlobHandle;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_WAIT, &wait_3d);
++retry;
} while (ret < 0 && errno == EBUSY);
if (ret < 0) {
mesa_loge("DRM_IOCTL_VIRTGPU_WAIT failed with %s", strerror(errno));
return ret;
}
return 0;
}
int DrmVirtGpuResource::transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
int ret;
struct drm_virtgpu_3d_transfer_to_host xfer = {0};
xfer.box.x = x;
xfer.box.y = y;
xfer.box.w = w;
xfer.box.h = h;
xfer.box.d = 1;
xfer.bo_handle = mBlobHandle;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST, &xfer);
if (ret < 0) {
mesa_loge("DRM_IOCTL_VIRTGPU_TRANSFER_TO_HOST failed with %s", strerror(errno));
return ret;
}
return 0;
}
int DrmVirtGpuResource::transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
int ret;
struct drm_virtgpu_3d_transfer_from_host xfer = {0};
xfer.box.x = x;
xfer.box.y = y;
xfer.box.w = w;
xfer.box.h = h;
xfer.box.d = 1;
xfer.bo_handle = mBlobHandle;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST, &xfer);
if (ret < 0) {
mesa_loge("DRM_IOCTL_VIRTGPU_TRANSFER_FROM_HOST failed with %s", strerror(errno));
return ret;
}
return 0;
}
@@ -0,0 +1,17 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include <sys/mman.h>
#include "DrmVirtGpu.h"
#include "drm-uapi/virtgpu_drm.h"
DrmVirtGpuResourceMapping::DrmVirtGpuResourceMapping(VirtGpuResourcePtr blob, uint8_t* ptr,
uint64_t size)
: mBlob(blob), mPtr(ptr), mSize(size) {}
DrmVirtGpuResourceMapping::~DrmVirtGpuResourceMapping(void) { munmap(mPtr, mSize); }
uint8_t* DrmVirtGpuResourceMapping::asRawPtr(void) { return mPtr; }
@@ -0,0 +1,415 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include <fcntl.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <xf86drm.h>
#include <cerrno>
#include <cstring>
#include <fstream>
#include <string>
#include "DrmVirtGpu.h"
#include "drm-uapi/virtgpu_drm.h"
#include "util/detect_os.h"
#include "util/log.h"
#include "util/os_misc.h"
#ifdef MAJOR_IN_MKDEV
#include <sys/mkdev.h>
#endif
#ifdef MAJOR_IN_SYSMACROS
#include <sys/sysmacros.h>
#endif
#define VIRTGPU_PCI_VENDOR_ID 0x1af4
#define VIRTGPU_PCI_DEVICE_ID 0x1050
#define VIRTGPU_PARAM_CREATE_FENCE_PASSING 9 /* Fence passing */
#define VIRTGPU_PARAM_CREATE_GUEST_HANDLE 10 /* Host OS handle can be created from guest memory. */
#define PARAM(x) \
(struct VirtGpuParam) { x, #x, 0 }
static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; }
int32_t DrmVirtGpuDevice::openDevice() {
drmDevicePtr devs[8];
int32_t ret = -EINVAL;
int count = drmGetDevices2(0, devs, ARRAY_SIZE(devs));
if (count <= 0) {
mesa_logd("failed to enumerate DRM devices");
goto out;
}
for (int i = 0; i < count; i++) {
drmDevicePtr dev = devs[i];
bool supported_bus = false;
switch (dev->bustype) {
case DRM_BUS_PCI:
if (dev->deviceinfo.pci->vendor_id == VIRTGPU_PCI_VENDOR_ID &&
dev->deviceinfo.pci->device_id == VIRTGPU_PCI_DEVICE_ID)
supported_bus = true;
break;
case DRM_BUS_PLATFORM:
supported_bus = true;
break;
default:
break;
}
if (!supported_bus || !(dev->available_nodes & (1 << DRM_NODE_RENDER))) {
const char* name = "unknown";
for (uint32_t i = 0; i < DRM_NODE_MAX; i++) {
if (dev->available_nodes & (1 << i)) {
name = dev->nodes[i];
break;
}
}
mesa_logd("skipping DRM device %s", name);
continue;
}
const char* primary_path = dev->nodes[DRM_NODE_PRIMARY];
const char* node_path = dev->nodes[DRM_NODE_RENDER];
int fd = open(node_path, O_RDWR | O_CLOEXEC);
if (fd < 0) {
mesa_logd("failed to open %s", node_path);
ret = -errno;
goto out;
}
drmVersionPtr version = drmGetVersion(fd);
if (!version || strcmp(version->name, "virtio_gpu") || version->version_major != 0) {
if (version) {
mesa_logd("unknown DRM driver %s version %d", version->name,
version->version_major);
} else {
mesa_logd("failed to get DRM driver version");
}
if (version) {
drmFreeVersion(version);
}
close(fd);
ret = -errno;
goto out;
}
struct stat st;
if (stat(primary_path, &st) == 0) {
mHasPrimary = true;
mPrimaryMajor = major(st.st_rdev);
mPrimaryMinor = minor(st.st_rdev);
} else {
mHasPrimary = false;
mPrimaryMajor = 0;
mPrimaryMinor = 0;
}
stat(node_path, &st);
mRenderMajor = major(st.st_rdev);
mRenderMinor = minor(st.st_rdev);
mBusType = dev->bustype;
if (dev->bustype == DRM_BUS_PCI) {
mPciBusInfo = *dev->businfo.pci;
}
drmFreeVersion(version);
mesa_logd("using DRM device %s", node_path);
mDeviceHandle = static_cast<int64_t>(fd);
ret = 0;
break;
}
out:
drmFreeDevices(devs, count);
return ret;
}
DrmVirtGpuDevice::DrmVirtGpuDevice(enum VirtGpuCapset capset) : VirtGpuDevice(capset) {}
int32_t DrmVirtGpuDevice::init(int32_t descriptor) {
struct VirtGpuParam params[] = {
PARAM(VIRTGPU_PARAM_3D_FEATURES), PARAM(VIRTGPU_PARAM_CAPSET_QUERY_FIX),
PARAM(VIRTGPU_PARAM_RESOURCE_BLOB), PARAM(VIRTGPU_PARAM_HOST_VISIBLE),
PARAM(VIRTGPU_PARAM_CROSS_DEVICE), PARAM(VIRTGPU_PARAM_CONTEXT_INIT),
PARAM(VIRTGPU_PARAM_SUPPORTED_CAPSET_IDs), PARAM(VIRTGPU_PARAM_EXPLICIT_DEBUG_NAME),
PARAM(VIRTGPU_PARAM_CREATE_FENCE_PASSING),
PARAM(VIRTGPU_PARAM_CREATE_GUEST_HANDLE),
};
int ret = -EINVAL;
struct drm_virtgpu_get_caps get_caps = {0};
struct drm_virtgpu_context_init init = {0};
struct drm_virtgpu_context_set_param ctx_set_params[3] = {{0}};
const char* processName = nullptr;
memset(&mCaps, 0, sizeof(struct VirtGpuCaps));
#if DETECT_OS_ANDROID
processName = getprogname();
#endif
if (descriptor < 0) {
#if DETECT_OS_ANDROID
// Somebody needs to modify CF's SELinux rules to account for the syscalls used by
// drmGetDevices2().
mDeviceHandle = static_cast<int64_t>(drmOpenRender(128));
if (mDeviceHandle < 0) {
mesa_loge("Failed to open rendernode: %s", strerror(errno));
return -EINVAL;
}
#else
ret = openDevice();
if (ret < 0) {
mesa_logd("no virtio_gpu devices found");
return ret;
}
#endif
} else {
mDeviceHandle = dup(descriptor);
if (mDeviceHandle < 0) {
mesa_loge("Failed to dup rendernode: %s", strerror(errno));
return ret;
}
}
for (uint32_t i = 0; i < kParamMax; i++) {
struct drm_virtgpu_getparam get_param = {0};
get_param.param = params[i].param;
get_param.value = (uint64_t)(uintptr_t)&params[i].value;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_GETPARAM, &get_param);
if (ret) {
mesa_logd("Could not query virtgpu param from device: %s", params[i].name);
continue;
}
mCaps.params[i] = params[i].value;
}
#if !DETECT_OS_ANDROID
if ((mCaps.params[kParamSupportedCapsetIds] & (1 << VIRTGPU_DRM_CAPSET_GFXSTREAM_VULKAN)) ==
0) {
return -EINVAL;
}
#endif
auto capset = getCapset();
get_caps.cap_set_id = static_cast<uint32_t>(capset);
switch (capset) {
case kCapsetGfxStreamVulkan:
get_caps.size = sizeof(struct vulkanCapset);
get_caps.addr = (unsigned long long)&mCaps.vulkanCapset;
break;
case kCapsetGfxStreamGles:
get_caps.size = sizeof(struct glesCapset);
get_caps.addr = (unsigned long long)&mCaps.glesCapset;
break;
case kCapsetGfxStreamComposer:
get_caps.size = sizeof(struct composerCapset);
get_caps.addr = (unsigned long long)&mCaps.composerCapset;
break;
default:
get_caps.size = 0;
}
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_GET_CAPS, &get_caps);
if (ret) {
// Don't fail get capabilities just yet, AEMU doesn't use this API
// yet (b/272121235);
mesa_loge("DRM_IOCTL_VIRTGPU_GET_CAPS failed with %s", strerror(errno));
}
// We always need an ASG blob in some cases, so always define blobAlignment
if (!mCaps.vulkanCapset.blobAlignment) {
mCaps.vulkanCapset.blobAlignment = 4096;
}
uint64_t guest_page_size = 4096;
os_get_page_size(&guest_page_size);
mCaps.vulkanCapset.blobAlignment =
std::max((uint32_t)guest_page_size, mCaps.vulkanCapset.blobAlignment);
ctx_set_params[0].param = VIRTGPU_CONTEXT_PARAM_NUM_RINGS;
ctx_set_params[0].value = 2;
init.num_params = 1;
if (capset != kCapsetNone) {
ctx_set_params[init.num_params].param = VIRTGPU_CONTEXT_PARAM_CAPSET_ID;
ctx_set_params[init.num_params].value = static_cast<uint32_t>(capset);
init.num_params++;
}
if (mCaps.params[kParamExplicitDebugName] && processName) {
ctx_set_params[init.num_params].param = VIRTGPU_CONTEXT_PARAM_DEBUG_NAME;
ctx_set_params[init.num_params].value = reinterpret_cast<uint64_t>(processName);
init.num_params++;
}
init.ctx_set_params = (unsigned long long)&ctx_set_params[0];
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_CONTEXT_INIT, &init);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_CONTEXT_INIT failed with %s, continuing without context...",
strerror(errno));
}
return 0;
}
DrmVirtGpuDevice::~DrmVirtGpuDevice() { close(mDeviceHandle); }
struct VirtGpuCaps DrmVirtGpuDevice::getCaps(void) { return mCaps; }
int64_t DrmVirtGpuDevice::getDeviceHandle(void) { return mDeviceHandle; }
VirtGpuResourcePtr DrmVirtGpuDevice::createResource(uint32_t width, uint32_t height,
uint32_t stride, uint32_t size,
uint32_t virglFormat, uint32_t target,
uint32_t bind) {
drm_virtgpu_resource_create create = {
.target = target,
.format = virglFormat,
.bind = bind,
.width = width,
.height = height,
.depth = 1U,
.array_size = 1U,
.last_level = 0,
.nr_samples = 0,
.size = size,
.stride = stride,
};
int ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE, &create);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_RESOURCE_CREATE failed with %s", strerror(errno));
return nullptr;
}
return std::make_shared<DrmVirtGpuResource>(
mDeviceHandle, create.bo_handle, create.res_handle, static_cast<uint64_t>(create.size));
}
VirtGpuResourcePtr DrmVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
int ret;
struct drm_virtgpu_resource_create_blob create = {0};
create.size = blobCreate.size;
create.blob_mem = blobCreate.blobMem;
create.blob_flags = blobCreate.flags;
create.blob_id = blobCreate.blobId;
create.cmd = (uint64_t)(uintptr_t)blobCreate.blobCmd;
create.cmd_size = blobCreate.blobCmdSize;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_CREATE_BLOB, &create);
if (ret < 0) {
mesa_loge("DRM_VIRTGPU_RESOURCE_CREATE_BLOB failed with %s", strerror(errno));
return nullptr;
}
return std::make_shared<DrmVirtGpuResource>(mDeviceHandle, create.bo_handle,
create.res_handle, blobCreate.size);
}
VirtGpuResourcePtr DrmVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle& handle) {
struct drm_virtgpu_resource_info info = {0};
uint32_t blobHandle;
int ret;
ret = drmPrimeFDToHandle(mDeviceHandle, handle.osHandle, &blobHandle);
close(handle.osHandle);
if (ret) {
mesa_loge("DRM_IOCTL_PRIME_FD_TO_HANDLE failed: %s", strerror(errno));
return nullptr;
}
info.bo_handle = blobHandle;
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_RESOURCE_INFO, &info);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_RESOURCE_INFO failed: %s", strerror(errno));
return nullptr;
}
return std::make_shared<DrmVirtGpuResource>(mDeviceHandle, blobHandle, info.res_handle,
static_cast<uint64_t>(info.size));
}
int DrmVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
const VirtGpuResource* blob) {
int ret;
struct drm_virtgpu_execbuffer exec = {0};
uint32_t blobHandle;
exec.flags = execbuffer.flags;
exec.size = execbuffer.command_size;
exec.ring_idx = execbuffer.ring_idx;
exec.command = (uint64_t)(uintptr_t)(execbuffer.command);
exec.fence_fd = -1;
if (blob) {
blobHandle = blob->getBlobHandle();
exec.bo_handles = (uint64_t)(uintptr_t)(&blobHandle);
exec.num_bo_handles = 1;
}
ret = drmIoctl(mDeviceHandle, DRM_IOCTL_VIRTGPU_EXECBUFFER, &exec);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_EXECBUFFER failed: %s", strerror(errno));
return ret;
}
if (execbuffer.flags & kFenceOut) {
execbuffer.handle.osHandle = exec.fence_fd;
execbuffer.handle.type = kFenceHandleSyncFd;
}
return 0;
}
VirtGpuDevice* osCreateVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) {
auto device = new DrmVirtGpuDevice(capset);
int32_t ret = device->init(descriptor);
if (ret) {
delete device;
return nullptr;
}
return device;
}
bool DrmVirtGpuDevice::getDrmInfo(VirtGpuDrmInfo* drmInfo) {
drmInfo->hasPrimary = mHasPrimary;
drmInfo->hasRender = true;
drmInfo->primaryMajor = mPrimaryMajor;
drmInfo->primaryMinor = mPrimaryMinor;
drmInfo->renderMajor = mRenderMajor;
drmInfo->renderMinor = mRenderMinor;
return true;
}
bool DrmVirtGpuDevice::getPciBusInfo(VirtGpuPciBusInfo* pciBusInfo) {
if (mBusType != DRM_BUS_PCI) {
return false;
}
pciBusInfo->domain = mPciBusInfo.domain;
pciBusInfo->bus = mPciBusInfo.bus;
pciBusInfo->device = mPciBusInfo.dev;
pciBusInfo->function = mPciBusInfo.func;
return true;
}
@@ -0,0 +1,17 @@
# Copyright 2022 Google
# SPDX-License-Identifier: MIT
files_libplatform_virtgpu_drm = files(
'DrmSync.cpp',
'DrmVirtGpuBlob.cpp',
'DrmVirtGpuBlobMapping.cpp',
'DrmVirtGpuDevice.cpp',
)
libplatform_virtgpu_drm = static_library(
'platform_virtgpu_drm',
files_libplatform_virtgpu_drm,
cpp_args: gfxstream_guest_args,
include_directories: [inc_platform_virtgpu, inc_src, inc_include],
dependencies: [dep_libdrm, idep_mesautil],
)
@@ -0,0 +1,59 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <lib/magma/magma.h>
#include "VirtGpu.h"
class FuchsiaVirtGpuResource : public std::enable_shared_from_this<FuchsiaVirtGpuResource>,
public VirtGpuResource {
public:
FuchsiaVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle, uint32_t resourceHandle,
uint64_t size);
~FuchsiaVirtGpuResource();
uint32_t getResourceHandle() const override;
uint32_t getBlobHandle() const override;
uint64_t getSize() const override;
int wait() override;
int exportBlob(struct VirtGpuExternalHandle& handle) override;
int transferFromHost(uint32_t offset, uint32_t size) override;
int transferToHost(uint32_t offset, uint32_t size) override;
VirtGpuResourceMappingPtr createMapping(void) override;
};
class FuchsiaVirtGpuResourceMapping : public VirtGpuResourceMapping {
public:
FuchsiaVirtGpuResourceMapping(VirtGpuResourcePtr blob, uint8_t* ptr, uint64_t size);
~FuchsiaVirtGpuResourceMapping(void);
uint8_t* asRawPtr(void) override;
};
class FuchsiaVirtGpuDevice : public VirtGpuDevice {
public:
FuchsiaVirtGpuDevice(enum VirtGpuCapset capset, magma_device_t device);
~FuchsiaVirtGpuDevice();
int64_t getDeviceHandle(void) override;
struct VirtGpuCaps getCaps(void) override;
VirtGpuResourcePtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
VirtGpuResourcePtr createResource(uint32_t width, uint32_t height, uint32_t stride,
uint32_t size, uint32_t virglFormat, uint32_t target,
uint32_t bind) override;
VirtGpuResourcePtr importBlob(const struct VirtGpuExternalHandle& handle) override;
int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuResource* blob) override;
private:
magma_device_t device_;
struct VirtGpuCaps mCaps;
};
@@ -0,0 +1,49 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "FuchsiaVirtGpu.h"
#include "util/log.h"
FuchsiaVirtGpuResource::FuchsiaVirtGpuResource(int64_t deviceHandle, uint32_t blobHandle,
uint32_t resourceHandle, uint64_t size) {}
FuchsiaVirtGpuResource::~FuchsiaVirtGpuResource(void) {}
uint32_t FuchsiaVirtGpuResource::getBlobHandle() const {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
uint32_t FuchsiaVirtGpuResource::getResourceHandle() const {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
uint64_t FuchsiaVirtGpuResource::getSize() const {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
VirtGpuResourceMappingPtr FuchsiaVirtGpuResource::createMapping(void) {
mesa_loge("%s: unimplemented", __func__);
return nullptr;
}
int FuchsiaVirtGpuResource::wait() { return -1; }
int FuchsiaVirtGpuResource::exportBlob(struct VirtGpuExternalHandle& handle) {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
int FuchsiaVirtGpuResource::transferFromHost(uint32_t offset, uint32_t size) {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
int FuchsiaVirtGpuResource::transferToHost(uint32_t offset, uint32_t size) {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
@@ -0,0 +1,13 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "FuchsiaVirtGpu.h"
FuchsiaVirtGpuResourceMapping::FuchsiaVirtGpuResourceMapping(VirtGpuResourcePtr blob, uint8_t* ptr,
uint64_t size) {}
FuchsiaVirtGpuResourceMapping::~FuchsiaVirtGpuResourceMapping(void) {}
uint8_t* FuchsiaVirtGpuResourceMapping::asRawPtr(void) { return nullptr; }
@@ -0,0 +1,143 @@
/*
* Copyright 2023 Google LLC
* SPDX-License-Identifier: MIT
*/
#include <lib/zx/vmo.h>
#include <msd-virtio-gpu/magma-virtio-gpu-defs.h>
#include <os_dirent.h>
#include <services/service_connector.h>
#include <climits>
#include <cstdio>
#include <cstdlib>
#include "FuchsiaVirtGpu.h"
#include "Sync.h"
#include "util/log.h"
FuchsiaVirtGpuDevice::FuchsiaVirtGpuDevice(enum VirtGpuCapset capset, magma_device_t device)
: VirtGpuDevice(capset), device_(device) {
memset(&mCaps, 0, sizeof(struct VirtGpuCaps));
// Hard-coded values that may be assumed on Fuchsia.
mCaps.params[kParam3D] = 1;
mCaps.params[kParamCapsetFix] = 1;
mCaps.params[kParamResourceBlob] = 1;
mCaps.params[kParamHostVisible] = 1;
mCaps.params[kParamCrossDevice] = 0;
mCaps.params[kParamContextInit] = 1;
mCaps.params[kParamSupportedCapsetIds] = 0;
mCaps.params[kParamExplicitDebugName] = 0;
mCaps.params[kParamCreateGuestHandle] = 0;
if (capset == kCapsetGfxStreamVulkan) {
uint64_t query_id = 0;
query_id |= static_cast<uint64_t>(kCapsetGfxStreamVulkan) << 32;
constexpr uint16_t kVersion = 0;
query_id |= static_cast<uint64_t>(kVersion) << 16;
magma_handle_t buffer;
magma_status_t status = magma_device_query(device_, query_id, &buffer, nullptr);
if (status == MAGMA_STATUS_OK) {
zx::vmo capset_info(buffer);
zx_status_t status =
capset_info.read(&mCaps.vulkanCapset, /*offset=*/0, sizeof(struct vulkanCapset));
mesa_logi("Got capset result, read status %d", status);
} else {
mesa_loge("Query(%lu) failed: status %d, expected buffer result", query_id, status);
}
// We always need an ASG blob in some cases, so always define blobAlignment
if (!mCaps.vulkanCapset.blobAlignment) {
mCaps.vulkanCapset.blobAlignment = 4096;
}
}
}
FuchsiaVirtGpuDevice::~FuchsiaVirtGpuDevice() { magma_device_release(device_); }
int64_t FuchsiaVirtGpuDevice::getDeviceHandle(void) { return device_; }
VirtGpuResourcePtr FuchsiaVirtGpuDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
mesa_loge("%s: unimplemented", __func__);
return nullptr;
}
VirtGpuResourcePtr FuchsiaVirtGpuDevice::createResource(uint32_t width, uint32_t height,
uint32_t stride, uint32_t size,
uint32_t virglFormat, uint32_t target,
uint32_t bind) {
mesa_loge("%s: unimplemented", __func__);
return nullptr;
}
VirtGpuResourcePtr FuchsiaVirtGpuDevice::importBlob(const struct VirtGpuExternalHandle& handle) {
mesa_loge("%s: unimplemented", __func__);
return nullptr;
}
int FuchsiaVirtGpuDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
const VirtGpuResource* blob) {
mesa_loge("%s: unimplemented", __func__);
return 0;
}
struct VirtGpuCaps FuchsiaVirtGpuDevice::getCaps(void) { return {}; }
VirtGpuDevice* osCreateVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) {
// We don't handle the VirtioGpuPipeStream case.
if (descriptor >= 0) {
mesa_loge("Fuchsia: fd not handled");
return nullptr;
}
const char kDevGpu[] = "/loader-gpu-devices/class/gpu";
struct os_dirent* de;
os_dir_t* dir = os_opendir(kDevGpu);
if (!dir) {
mesa_loge("Error opening %s", kDevGpu);
return nullptr;
}
mesa_logi("Opened dir %s", kDevGpu);
VirtGpuDevice* gpu_device = nullptr;
while ((de = os_readdir(dir)) != NULL) {
mesa_logi("Got name %s", de->d_name);
if (strcmp(de->d_name, ".") == 0) {
continue;
}
// extra +1 ensures space for null termination
char name[sizeof(kDevGpu) + sizeof('/') + sizeof(de->d_name) + 1];
snprintf(name, sizeof(name), "%s/%s", kDevGpu, de->d_name);
zx_handle_t device_channel = GetConnectToServiceFunction()(name);
if (device_channel == ZX_HANDLE_INVALID) {
mesa_loge("Failed to open device: %s", name);
continue;
}
magma_device_t magma_device;
magma_status_t status = magma_device_import(device_channel, &magma_device);
if (status != MAGMA_STATUS_OK) {
mesa_loge("magma_device_import failed: %d", status);
continue;
}
gpu_device = new FuchsiaVirtGpuDevice(capset, magma_device);
break;
}
os_closedir(dir);
return gpu_device;
}
namespace gfxstream {
SyncHelper* osCreateSyncHelper() { return nullptr; }
} // namespace gfxstream
@@ -0,0 +1,94 @@
/*
* Copyright 2024 Google LLC
* SPDX-License-Identifier: MIT
*/
#include <lib/zxio/zxio.h>
#include <services/service_connector.h>
#include <string.h>
#include "os_dirent.h"
#include "util/log.h"
struct os_dir {
~os_dir() {
if (dir_iterator_init_) {
zxio_dirent_iterator_destroy(&iterator_);
}
if (zxio_init_) {
zxio_close(&io_storage_.io, /*should_wait=*/true);
}
}
// Always consumes |dir_channel|
bool Init(zx_handle_t dir_channel) {
zx_status_t status = zxio_create(dir_channel, &io_storage_);
if (status != ZX_OK) {
mesa_loge("zxio_create failed: %d", status);
return false;
}
zxio_init_ = true;
status = zxio_dirent_iterator_init(&iterator_, &io_storage_.io);
if (status != ZX_OK) {
mesa_loge("zxio_dirent_iterator_init failed: %d", status);
return false;
}
dir_iterator_init_ = true;
return true;
}
bool Next(struct os_dirent* entry) {
// dirent is an in-out parameter.
// name must be initialized to point to a buffer of at least ZXIO_MAX_FILENAME bytes.
static_assert(sizeof(entry->d_name) >= ZXIO_MAX_FILENAME);
zxio_dirent_t dirent = {.name = entry->d_name};
zx_status_t status = zxio_dirent_iterator_next(&iterator_, &dirent);
if (status != ZX_OK) {
if (status != ZX_ERR_NOT_FOUND)
mesa_loge("zxio_dirent_iterator_next failed: %d", status);
return false;
}
entry->d_ino = dirent.has.id ? dirent.id : OS_INO_UNKNOWN;
entry->d_name[dirent.name_length] = '\0';
return true;
}
private:
bool zxio_init_ = false;
bool dir_iterator_init_ = false;
zxio_storage_t io_storage_;
zxio_dirent_iterator_t iterator_;
};
os_dir_t* os_opendir(const char* path) {
zx_handle_t dir_channel = GetConnectToServiceFunction()(path);
if (dir_channel == ZX_HANDLE_INVALID) {
mesa_loge("fuchsia_open(%s) failed", path);
return nullptr;
}
auto dir = new os_dir();
if (!dir->Init(dir_channel)) {
delete dir;
return nullptr;
}
return dir;
}
int os_closedir(os_dir_t* dir) {
delete dir;
return 0;
}
struct os_dirent* os_readdir(os_dir_t* dir) {
static struct os_dirent dirent = {};
return reinterpret_cast<os_dirent*>(dir->Next(&dirent)) ? &dirent : nullptr;
}
@@ -0,0 +1,31 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <cstdint>
#include <stddef.h>
namespace gfxstream {
// Abstraction around libsync for testing.
class SyncHelper {
public:
virtual ~SyncHelper() {}
virtual int wait(int syncFd, int timeoutMilliseconds) = 0;
virtual void debugPrint(int syncFd) = 0;
virtual int dup(int syncFd) = 0;
virtual int close(int syncFd) = 0;
};
SyncHelper* osCreateSyncHelper();
SyncHelper* kumquatCreateSyncHelper();
SyncHelper* createPlatformSyncHelper();
} // namespace gfxstream
@@ -0,0 +1,223 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#ifndef VIRTGPU_DEVICE_H
#define VIRTGPU_DEVICE_H
#include <cstdint>
#include <memory>
#include "virtio/virtio-gpu/virtgpu_gfxstream_protocol.h"
#include "virtio/virtio-gpu/virgl_hw.h"
#define PIPE_BUFFER 0
#define PIPE_TEXTURE_2D 2
enum VirtGpuParamId : uint32_t {
kParam3D = 0,
kParamCapsetFix = 1,
kParamResourceBlob = 2,
kParamHostVisible = 3,
kParamCrossDevice = 4,
kParamContextInit = 5,
kParamSupportedCapsetIds = 6,
kParamExplicitDebugName = 7,
// Experimental, not in upstream Linux
kParamFencePassing = 8,
kParamCreateGuestHandle = 9,
kParamMax = 10,
};
enum VirtGpuExecBufferFlags : uint32_t {
kFenceIn = 0x0001,
kFenceOut = 0x0002,
kRingIdx = 0x0004,
kShareableIn = 0x0008,
kShareableOut = 0x0010,
};
enum VirtGpuCapset {
kCapsetNone = 0,
kCapsetVirgl = 1,
kCapsetVirgl2 = 2,
kCapsetGfxStreamVulkan = 3,
kCapsetVenus = 4,
kCapsetCrossDomain = 5,
kCapsetDrm = 6,
kCapsetGfxStreamGles = 8,
kCapsetGfxStreamComposer = 9,
};
// Try to keep aligned with vulkan-cereal / rutabaga.
enum VirtGpuHandleType {
kMemHandleOpaqueFd = 0x0001,
kMemHandleDmabuf = 0x0002,
kMemHandleOpaqueWin32 = 0x0003,
kMemHandleShm = 0x0004,
kMemHandleZircon = 0x0008,
kFenceHandleOpaqueFd = 0x0010,
kFenceHandleSyncFd = 0x0020,
kFenceHandleOpaqueWin32 = 0x0040,
kFenceHandleZircon = 0x0080,
};
enum VirtGpuResourceFlags : uint32_t {
kBlobFlagMappable = 0x0001,
kBlobFlagShareable = 0x0002,
kBlobFlagCrossDevice = 0x0004,
kBlobFlagCreateGuestHandle = 0x0008,
};
enum VirtGpuResourceMem {
kBlobMemGuest = 0x0001,
kBlobMemHost3d = 0x0002,
kBlobMemHost3dGuest = 0x0003,
};
struct VirtGpuExternalHandle {
int64_t osHandle;
enum VirtGpuHandleType type;
};
struct VirtGpuExecBuffer {
void* command;
uint32_t command_size;
uint32_t ring_idx;
enum VirtGpuExecBufferFlags flags;
struct VirtGpuExternalHandle handle;
};
struct VirtGpuParam {
uint64_t param;
const char* name;
uint64_t value;
};
struct VirtGpuCreateBlob {
uint64_t size;
enum VirtGpuResourceFlags flags;
enum VirtGpuResourceMem blobMem;
uint64_t blobId;
uint8_t* blobCmd;
uint32_t blobCmdSize;
};
struct VirtGpuCaps {
uint64_t params[kParamMax];
struct vulkanCapset vulkanCapset;
struct magmaCapset magmaCapset;
struct glesCapset glesCapset;
struct composerCapset composerCapset;
};
struct VirtGpuDrmInfo {
bool hasPrimary;
bool hasRender;
int64_t primaryMajor;
int64_t primaryMinor;
int64_t renderMajor;
int64_t renderMinor;
};
// Note: Fields match equivalent structure for Magma
struct VirtGpuPciBusInfo {
uint16_t domain;
uint8_t bus;
uint8_t device;
uint8_t function;
};
constexpr int32_t INVALID_DESCRIPTOR = -1;
class VirtGpuResourceMapping;
class VirtGpuResource;
using VirtGpuResourcePtr = std::shared_ptr<VirtGpuResource>;
using VirtGpuResourceMappingPtr = std::shared_ptr<VirtGpuResourceMapping>;
class VirtGpuResource {
public:
virtual ~VirtGpuResource() {}
// The `intoRaw()` function drops ownerships of the OS-handle underlying
// the resource. It is the responsibility of the caller to manage lifetimes
// of the virtio-gpu resource. This function is mostly for gfxstream EGL
// compatibility and shouldn't be used elsewhere.
virtual void intoRaw(){};
virtual uint32_t getResourceHandle() const = 0;
virtual uint32_t getBlobHandle() const = 0;
virtual uint64_t getSize() const = 0;
virtual int wait() = 0;
virtual VirtGpuResourceMappingPtr createMapping(void) = 0;
virtual int exportBlob(struct VirtGpuExternalHandle& handle) = 0;
virtual int transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
virtual int transferFromHost(uint32_t offset, uint32_t size) {
return transferFromHost(offset, 0, size, 1);
}
virtual int transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) = 0;
virtual int transferToHost(uint32_t offset, uint32_t size) {
return transferToHost(offset, 0, size, 1);
}
};
class VirtGpuResourceMapping {
public:
virtual ~VirtGpuResourceMapping(void) {}
virtual uint8_t* asRawPtr(void) = 0;
};
class VirtGpuDevice {
public:
static VirtGpuDevice* getInstance(enum VirtGpuCapset capset = kCapsetNone,
int32_t descriptor = INVALID_DESCRIPTOR);
static void resetInstance();
VirtGpuDevice(enum VirtGpuCapset capset) : mCapset(capset) {}
virtual ~VirtGpuDevice() {}
enum VirtGpuCapset getCapset() { return mCapset; }
virtual int64_t getDeviceHandle(void) = 0;
virtual struct VirtGpuCaps getCaps(void) = 0;
virtual VirtGpuResourcePtr createBlob(const struct VirtGpuCreateBlob& blobCreate) = 0;
virtual VirtGpuResourcePtr createResource(uint32_t width, uint32_t height, uint32_t stride,
uint32_t size, uint32_t virglFormat, uint32_t target,
uint32_t bind) = 0;
virtual VirtGpuResourcePtr importBlob(const struct VirtGpuExternalHandle& handle) = 0;
virtual int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuResource* blob) = 0;
virtual bool getDrmInfo(VirtGpuDrmInfo* /*drmInfo*/) { return false; }
virtual bool getPciBusInfo(VirtGpuPciBusInfo* /*pciBusInfo*/) { return false; }
private:
enum VirtGpuCapset mCapset;
};
VirtGpuDevice* kumquatCreateVirtGpuDevice(enum VirtGpuCapset capset = kCapsetNone, int fd = -1);
VirtGpuDevice* osCreateVirtGpuDevice(enum VirtGpuCapset capset = kCapsetNone, int fd = -1);
VirtGpuDevice* createPlatformVirtGpuDevice(enum VirtGpuCapset capset = kCapsetNone, int fd = -1);
// HACK: We can use gfxstream::guest::EnumFlags, but we'll have to do more guest
// refactorings to figure out our end goal. We can either depend more on base or
// try to transition to something else (b:202552093) [atleast for guests].
constexpr enum VirtGpuResourceFlags operator|(const enum VirtGpuResourceFlags self,
const enum VirtGpuResourceFlags other) {
return (enum VirtGpuResourceFlags)(uint32_t(self) | uint32_t(other));
}
constexpr enum VirtGpuExecBufferFlags operator |(const enum VirtGpuExecBufferFlags self,
const enum VirtGpuExecBufferFlags other) {
return (enum VirtGpuExecBufferFlags)(uint32_t(self) | uint32_t(other));
}
#endif
@@ -0,0 +1,83 @@
/*
* Copyright 2023 Google
* SPDX-License-Identifier: MIT
*/
#pragma once
#include "GfxStreamVulkanMapper.h"
#include "VirtGpu.h"
#include "virtgpu_kumquat_ffi.h"
class VirtGpuKumquatResource : public std::enable_shared_from_this<VirtGpuKumquatResource>,
public VirtGpuResource {
public:
VirtGpuKumquatResource(struct virtgpu_kumquat* virtGpu, uint32_t blobHandle,
uint32_t resourceHandle, uint64_t size);
~VirtGpuKumquatResource();
uint32_t getResourceHandle() const override;
uint32_t getBlobHandle() const override;
uint64_t getSize() const override;
int wait() override;
VirtGpuResourceMappingPtr createMapping(void) override;
int exportBlob(struct VirtGpuExternalHandle& handle) override;
int transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
int transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) override;
private:
// Not owned. Really should use a ScopedFD for this, but doesn't matter since we have a
// singleton deviceimplemenentation anyways.
struct virtgpu_kumquat* mVirtGpu = nullptr;
uint32_t mBlobHandle;
uint32_t mResourceHandle;
uint64_t mSize;
};
class VirtGpuKumquatResourceMapping : public VirtGpuResourceMapping {
public:
VirtGpuKumquatResourceMapping(VirtGpuResourcePtr blob, struct virtgpu_kumquat* virtGpu,
uint8_t* ptr, uint64_t size);
VirtGpuKumquatResourceMapping(VirtGpuResourcePtr blob, struct virtgpu_kumquat* virtGpu,
struct VulkanMapperData& data, uint64_t size);
~VirtGpuKumquatResourceMapping(void);
uint8_t* asRawPtr(void) override;
private:
VirtGpuResourcePtr mBlob;
struct virtgpu_kumquat* mVirtGpu = nullptr;
struct VulkanMapperData mVulkanData;
uint8_t* mPtr = nullptr;
uint64_t mSize = 0;
};
class VirtGpuKumquatDevice : public VirtGpuDevice {
public:
VirtGpuKumquatDevice(enum VirtGpuCapset capset, int fd = -1);
virtual ~VirtGpuKumquatDevice();
virtual int64_t getDeviceHandle(void);
virtual struct VirtGpuCaps getCaps(void);
VirtGpuResourcePtr createBlob(const struct VirtGpuCreateBlob& blobCreate) override;
VirtGpuResourcePtr createResource(uint32_t width, uint32_t height, uint32_t stride,
uint32_t size, uint32_t virglFormat, uint32_t target,
uint32_t bind) override;
virtual VirtGpuResourcePtr importBlob(const struct VirtGpuExternalHandle& handle);
virtual int execBuffer(struct VirtGpuExecBuffer& execbuffer, const VirtGpuResource* blob);
private:
int32_t mDescriptor = 0;
struct virtgpu_kumquat* mVirtGpu = nullptr;
struct VirtGpuCaps mCaps;
};
@@ -0,0 +1,164 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include "GfxStreamVulkanMapper.h"
#include "VirtGpuKumquat.h"
#include "util/log.h"
VirtGpuKumquatResource::VirtGpuKumquatResource(struct virtgpu_kumquat* virtGpu, uint32_t blobHandle,
uint32_t resourceHandle, uint64_t size)
: mVirtGpu(virtGpu), mBlobHandle(blobHandle), mResourceHandle(resourceHandle), mSize(size) {}
VirtGpuKumquatResource::~VirtGpuKumquatResource() {
struct drm_kumquat_resource_unref unref {
.bo_handle = mBlobHandle, .pad = 0,
};
int ret = virtgpu_kumquat_resource_unref(mVirtGpu, &unref);
if (ret) {
mesa_loge("Closed failed with : [%s, blobHandle %u, resourceHandle: %u]", strerror(errno),
mBlobHandle, mResourceHandle);
}
}
uint32_t VirtGpuKumquatResource::getBlobHandle() const { return mBlobHandle; }
uint32_t VirtGpuKumquatResource::getResourceHandle() const { return mResourceHandle; }
uint64_t VirtGpuKumquatResource::getSize() const { return mSize; }
VirtGpuResourceMappingPtr VirtGpuKumquatResource::createMapping() {
int ret;
struct drm_kumquat_resource_info info = {};
struct drm_kumquat_map map {
.bo_handle = mBlobHandle, .ptr = NULL, .size = mSize,
};
info.bo_handle = mBlobHandle;
ret = virtgpu_kumquat_resource_info(mVirtGpu, &info);
if (info.vulkan_info.device_id.device_uuid[0]) {
struct drm_kumquat_resource_export resource_export = {};
struct DeviceId deviceId = {};
resource_export.bo_handle = mBlobHandle;
ret = virtgpu_kumquat_resource_export(mVirtGpu, &resource_export);
if (ret) {
mesa_loge("External memory export from kumquat failed: %s", strerror(errno));
return nullptr;
}
memcpy(&deviceId, &info.vulkan_info.device_id, sizeof(struct DeviceId));
std::optional<DeviceId> deviceIdOpt{deviceId};
auto mapper = GfxStreamVulkanMapper::getInstance(deviceIdOpt);
struct VulkanMapperData mapData = {0};
mapData.handle = resource_export.os_handle;
mapData.handleType = resource_export.handle_type;
mapData.memoryIdx = info.vulkan_info.memory_idx;
mapData.size = mSize;
ret = mapper->map(&mapData);
if (ret < 0) {
mesa_loge("Mapping failed with %s for resource %u blob %u", strerror(errno),
mResourceHandle, mBlobHandle);
return nullptr;
}
return std::make_shared<VirtGpuKumquatResourceMapping>(shared_from_this(), mVirtGpu,
mapData, mSize);
} else {
ret = virtgpu_kumquat_resource_map(mVirtGpu, &map);
if (ret < 0) {
mesa_loge("Mapping failed with %s for resource %u blob %u", strerror(errno),
mResourceHandle, mBlobHandle);
return nullptr;
}
return std::make_shared<VirtGpuKumquatResourceMapping>(shared_from_this(), mVirtGpu,
(uint8_t*)map.ptr, mSize);
}
}
int VirtGpuKumquatResource::exportBlob(struct VirtGpuExternalHandle& handle) {
int ret;
struct drm_kumquat_resource_export exp = {0};
exp.bo_handle = mBlobHandle;
ret = virtgpu_kumquat_resource_export(mVirtGpu, &exp);
if (ret) {
mesa_loge("Failed to export blob with %s", strerror(errno));
return ret;
}
handle.osHandle = static_cast<int64_t>(exp.os_handle);
handle.type = static_cast<VirtGpuHandleType>(exp.handle_type);
return 0;
}
int VirtGpuKumquatResource::wait() {
int ret;
struct drm_kumquat_wait wait = {
.handle = mBlobHandle,
.flags = 0,
};
ret = virtgpu_kumquat_wait(mVirtGpu, &wait);
if (ret < 0) {
mesa_loge("Wait failed with %s", strerror(errno));
return ret;
}
return 0;
}
int VirtGpuKumquatResource::transferToHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
int ret;
struct drm_kumquat_transfer_to_host xfer = {0};
xfer.box.x = x;
xfer.box.y = y;
xfer.box.w = w;
xfer.box.h = h;
xfer.box.d = 1;
xfer.bo_handle = mBlobHandle;
ret = virtgpu_kumquat_transfer_to_host(mVirtGpu, &xfer);
if (ret < 0) {
mesa_loge("Transfer to host failed with %s", strerror(errno));
return ret;
}
return 0;
}
int VirtGpuKumquatResource::transferFromHost(uint32_t x, uint32_t y, uint32_t w, uint32_t h) {
int ret;
struct drm_kumquat_transfer_from_host xfer = {0};
xfer.box.x = x;
xfer.box.y = y;
xfer.box.w = w;
xfer.box.h = h;
xfer.box.d = 1;
xfer.bo_handle = mBlobHandle;
ret = virtgpu_kumquat_transfer_from_host(mVirtGpu, &xfer);
if (ret < 0) {
mesa_loge("Transfer from host failed with %s", strerror(errno));
return ret;
}
return 0;
}
@@ -0,0 +1,31 @@
/*
* Copyright 2022 Google LLC
* SPDX-License-Identifier: MIT
*/
#include "VirtGpuKumquat.h"
#include "util/log.h"
VirtGpuKumquatResourceMapping::VirtGpuKumquatResourceMapping(VirtGpuResourcePtr blob,
struct virtgpu_kumquat* virtGpu,
uint8_t* ptr, uint64_t size)
: mBlob(blob), mVirtGpu(virtGpu), mPtr(ptr), mSize(size) {}
VirtGpuKumquatResourceMapping::VirtGpuKumquatResourceMapping(VirtGpuResourcePtr blob,
struct virtgpu_kumquat* virtGpu,
VulkanMapperData& data, uint64_t size)
: mBlob(blob), mVirtGpu(virtGpu), mVulkanData(data), mPtr(data.ptr), mSize(size) {}
VirtGpuKumquatResourceMapping::~VirtGpuKumquatResourceMapping(void) {
if (mVulkanData.ptr && mVulkanData.memory != VK_NULL_HANDLE) {
auto mapper = GfxStreamVulkanMapper::getInstance();
mapper->unmap(&mVulkanData);
} else {
int32_t ret = virtgpu_kumquat_resource_unmap(mVirtGpu, mBlob->getBlobHandle());
if (ret) {
mesa_loge("failed to unmap buffer");
}
}
}
uint8_t* VirtGpuKumquatResourceMapping::asRawPtr(void) { return mPtr; }
@@ -0,0 +1,239 @@
/*
* Copyright 2022 Google
* SPDX-License-Identifier: MIT
*/
#include <pthread.h>
#include <stdlib.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#include <fstream>
#include <string>
#include "GfxStreamVulkanMapper.h"
#include "VirtGpuKumquat.h"
#include "util/log.h"
#define PARAM(x) \
(struct VirtGpuParam) { x, #x, 0 }
static inline uint32_t align_up(uint32_t n, uint32_t a) { return ((n + a - 1) / a) * a; }
VirtGpuKumquatDevice::VirtGpuKumquatDevice(enum VirtGpuCapset capset, int32_t descriptor)
: VirtGpuDevice(capset) {
struct VirtGpuParam params[] = {
PARAM(VIRTGPU_KUMQUAT_PARAM_3D_FEATURES),
PARAM(VIRTGPU_KUMQUAT_PARAM_CAPSET_QUERY_FIX),
PARAM(VIRTGPU_KUMQUAT_PARAM_RESOURCE_BLOB),
PARAM(VIRTGPU_KUMQUAT_PARAM_HOST_VISIBLE),
PARAM(VIRTGPU_KUMQUAT_PARAM_CROSS_DEVICE),
PARAM(VIRTGPU_KUMQUAT_PARAM_CONTEXT_INIT),
PARAM(VIRTGPU_KUMQUAT_PARAM_SUPPORTED_CAPSET_IDs),
PARAM(VIRTGPU_KUMQUAT_PARAM_EXPLICIT_DEBUG_NAME),
PARAM(VIRTGPU_KUMQUAT_PARAM_FENCE_PASSING),
PARAM(VIRTGPU_KUMQUAT_PARAM_CREATE_GUEST_HANDLE),
};
int ret;
struct drm_kumquat_get_caps get_caps = {0};
struct drm_kumquat_context_init init = {0};
struct drm_kumquat_context_set_param ctx_set_params[3] = {{0}};
const char* processName = nullptr;
std::string gpu_socket_path = "/tmp/kumquat-gpu-";
memset(&mCaps, 0, sizeof(struct VirtGpuCaps));
#if DETECT_OS_ANDROID
processName = getprogname();
#endif
if (descriptor >= 0) {
gpu_socket_path.append(std::to_string(descriptor));
mDescriptor = descriptor;
} else {
gpu_socket_path.append("0");
}
ret = virtgpu_kumquat_init(&mVirtGpu, gpu_socket_path.c_str());
if (ret) {
mesa_logi("Failed to init virtgpu kumquat");
return;
}
for (uint32_t i = 0; i < kParamMax; i++) {
struct drm_kumquat_getparam get_param = {0};
get_param.param = params[i].param;
ret = virtgpu_kumquat_get_param(mVirtGpu, &get_param);
if (ret) {
mesa_logi("virtgpu backend not enabling %s", params[i].name);
continue;
}
mCaps.params[i] = get_param.value;
}
get_caps.cap_set_id = static_cast<uint32_t>(capset);
switch (capset) {
case kCapsetGfxStreamVulkan:
get_caps.size = sizeof(struct vulkanCapset);
get_caps.addr = (unsigned long long)&mCaps.vulkanCapset;
break;
case kCapsetGfxStreamGles:
get_caps.size = sizeof(struct glesCapset);
get_caps.addr = (unsigned long long)&mCaps.glesCapset;
break;
case kCapsetGfxStreamComposer:
get_caps.size = sizeof(struct composerCapset);
get_caps.addr = (unsigned long long)&mCaps.composerCapset;
break;
default:
get_caps.size = 0;
}
ret = virtgpu_kumquat_get_caps(mVirtGpu, &get_caps);
if (ret) {
// Don't fail get capabilities just yet, AEMU doesn't use this API
// yet (b/272121235);
mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_GET_CAPS failed with %s", strerror(errno));
}
// We always need an ASG blob in some cases, so always define blobAlignment
if (!mCaps.vulkanCapset.blobAlignment) {
mCaps.vulkanCapset.blobAlignment = 4096;
}
ctx_set_params[0].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_NUM_RINGS;
ctx_set_params[0].value = 2;
init.num_params = 1;
if (capset != kCapsetNone) {
ctx_set_params[init.num_params].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_CAPSET_ID;
ctx_set_params[init.num_params].value = static_cast<uint32_t>(capset);
init.num_params++;
}
if (mCaps.params[kParamExplicitDebugName] && processName) {
ctx_set_params[init.num_params].param = VIRTGPU_KUMQUAT_CONTEXT_PARAM_DEBUG_NAME;
ctx_set_params[init.num_params].value = reinterpret_cast<uint64_t>(processName);
init.num_params++;
}
init.ctx_set_params = (unsigned long long)&ctx_set_params[0];
ret = virtgpu_kumquat_context_init(mVirtGpu, &init);
if (ret) {
mesa_loge(
"DRM_IOCTL_VIRTGPU_KUMQUAT_CONTEXT_INIT failed with %s, continuing without context...",
strerror(errno));
}
}
VirtGpuKumquatDevice::~VirtGpuKumquatDevice() { virtgpu_kumquat_finish(&mVirtGpu); }
struct VirtGpuCaps VirtGpuKumquatDevice::getCaps(void) { return mCaps; }
int64_t VirtGpuKumquatDevice::getDeviceHandle(void) { return mDescriptor; }
VirtGpuResourcePtr VirtGpuKumquatDevice::createResource(uint32_t width, uint32_t height,
uint32_t stride, uint32_t size,
uint32_t virglFormat, uint32_t target,
uint32_t bind) {
struct drm_kumquat_resource_create_3d create = {
.target = target,
.format = virglFormat,
.bind = bind,
.width = width,
.height = height,
.depth = 1U,
.array_size = 1U,
.last_level = 0,
.nr_samples = 0,
.size = size,
.stride = stride,
};
int ret = virtgpu_kumquat_resource_create_3d(mVirtGpu, &create);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_RESOURCE_CREATE failed with %s", strerror(errno));
return nullptr;
}
return std::make_shared<VirtGpuKumquatResource>(mVirtGpu, create.bo_handle, create.res_handle,
static_cast<uint64_t>(create.size));
}
VirtGpuResourcePtr VirtGpuKumquatDevice::createBlob(const struct VirtGpuCreateBlob& blobCreate) {
int ret;
struct drm_kumquat_resource_create_blob create = {0};
create.size = blobCreate.size;
create.blob_mem = blobCreate.blobMem;
create.blob_flags = blobCreate.flags;
create.blob_id = blobCreate.blobId;
create.cmd = (uint64_t)(uintptr_t)blobCreate.blobCmd;
create.cmd_size = blobCreate.blobCmdSize;
ret = virtgpu_kumquat_resource_create_blob(mVirtGpu, &create);
if (ret < 0) {
mesa_loge("DRM_VIRTGPU_KUMQUAT_RESOURCE_CREATE_BLOB failed with %s", strerror(errno));
return nullptr;
}
return std::make_shared<VirtGpuKumquatResource>(mVirtGpu, create.bo_handle, create.res_handle,
blobCreate.size);
}
VirtGpuResourcePtr VirtGpuKumquatDevice::importBlob(const struct VirtGpuExternalHandle& handle) {
int ret;
struct drm_kumquat_resource_import resource_import = {0};
resource_import.os_handle = static_cast<uint64_t>(handle.osHandle);
resource_import.handle_type = static_cast<uint32_t>(handle.type);
ret = virtgpu_kumquat_resource_import(mVirtGpu, &resource_import);
if (ret < 0) {
mesa_loge("DRM_VIRTGPU_KUMQUAT_RESOURCE_IMPORT failed with %s", strerror(errno));
return nullptr;
}
return std::make_shared<VirtGpuKumquatResource>(
mVirtGpu, resource_import.bo_handle, resource_import.res_handle, resource_import.size);
}
int VirtGpuKumquatDevice::execBuffer(struct VirtGpuExecBuffer& execbuffer,
const VirtGpuResource* blob) {
int ret;
struct drm_kumquat_execbuffer exec = {0};
uint32_t blobHandle;
exec.flags = execbuffer.flags;
exec.size = execbuffer.command_size;
exec.ring_idx = execbuffer.ring_idx;
exec.command = (uint64_t)(uintptr_t)(execbuffer.command);
exec.fence_handle = -1;
if (blob) {
blobHandle = blob->getBlobHandle();
exec.bo_handles = (uint64_t)(uintptr_t)(&blobHandle);
exec.num_bo_handles = 1;
}
ret = virtgpu_kumquat_execbuffer(mVirtGpu, &exec);
if (ret) {
mesa_loge("DRM_IOCTL_VIRTGPU_KUMQUAT_EXECBUFFER failed: %s", strerror(errno));
return ret;
}
if (execbuffer.flags & kFenceOut) {
execbuffer.handle.osHandle = exec.fence_handle;
execbuffer.handle.type = kFenceHandleSyncFd;
}
return 0;
}
VirtGpuDevice* kumquatCreateVirtGpuDevice(enum VirtGpuCapset capset, int32_t descriptor) {
return new VirtGpuKumquatDevice(capset, descriptor);
}
Loaded 100 of 12511 files, more files were not shown because too many files have changed in this diff. Show more