mirror of
https://github.com/elwrcl/intel-hard-graphics.git
synced 2026-10-31 19:08:03 +03:00
init
This commit is contained in:
12511 files changed
+6156641
No files matched your search
@@ -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"]
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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])
|
||||
@@ -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
@@ -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"
|
||||
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)
|
||||
@@ -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])
|
||||
@@ -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
|
||||
Executable
+236
@@ -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()
|
||||
@@ -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',
|
||||
)
|
||||
@@ -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()
|
||||
@@ -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=""VK_GOOGLE_gfxstream"" 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
|
||||
@@ -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
|
||||
@@ -0,0 +1,4 @@
|
||||
# Copyright 2023 Android Open Source Project
|
||||
# SPDX-License-Identifier: MIT
|
||||
|
||||
inc_guest_iostream = include_directories('include')
|
||||
@@ -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')
|
||||
@@ -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)¶ms[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
Reference in new issue
Block a user