mirror of
https://github.com/Cuteminded/jellyframe.git
synced 2026-10-26 18:00:38 +01:00
108 lines
3.6 KiB
C++
108 lines
3.6 KiB
C++
#include "video_plane.h"
|
|
extern "C" {
|
|
#include <libavutil/buffer.h>
|
|
#include <libavutil/hwcontext.h>
|
|
}
|
|
#include <cstring>
|
|
#ifdef _WIN32
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#define CUDA_CALL __stdcall
|
|
#else
|
|
#include <dlfcn.h>
|
|
#define CUDA_CALL
|
|
#endif
|
|
|
|
struct CudaApi {
|
|
int (CUDA_CALL *push)(void *) = nullptr;
|
|
int (CUDA_CALL *pop)(void **) = nullptr;
|
|
int (CUDA_CALL *host_register)(void *, size_t, unsigned) = nullptr;
|
|
int (CUDA_CALL *host_unregister)(void *) = nullptr;
|
|
|
|
CudaApi() {
|
|
#ifdef _WIN32
|
|
HMODULE library = LoadLibraryExW(L"nvcuda.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
|
auto symbol = [library](const char *name) -> void * { return library ? reinterpret_cast<void *>(GetProcAddress(library, name)) : nullptr; };
|
|
#else
|
|
void *library = dlopen("libcuda.so.1", RTLD_NOW | RTLD_LOCAL);
|
|
auto symbol = [library](const char *name) -> void * { return library ? dlsym(library, name) : nullptr; };
|
|
#endif
|
|
push = reinterpret_cast<decltype(push)>(symbol("cuCtxPushCurrent_v2"));
|
|
pop = reinterpret_cast<decltype(pop)>(symbol("cuCtxPopCurrent_v2"));
|
|
host_register = reinterpret_cast<decltype(host_register)>(symbol("cuMemHostRegister_v2"));
|
|
host_unregister = reinterpret_cast<decltype(host_unregister)>(symbol("cuMemHostUnregister"));
|
|
}
|
|
};
|
|
|
|
static const CudaApi &cuda_api() {
|
|
static const CudaApi api;
|
|
return api;
|
|
}
|
|
|
|
|
|
class CudaHostPin {
|
|
public:
|
|
CudaHostPin(AVBufferRef *owner, void *address, size_t size) : address(address) {
|
|
auto *device = reinterpret_cast<AVHWDeviceContext *>(owner->data);
|
|
if (device->type != AV_HWDEVICE_TYPE_CUDA) return;
|
|
const auto &api = cuda_api();
|
|
if (!api.push || !api.pop || !api.host_register || !api.host_unregister) return;
|
|
// CUcontext is the first member of FFmpeg's public AVCUDADeviceContext.
|
|
std::memcpy(&context, device->hwctx, sizeof(context));
|
|
reference = av_buffer_ref(owner);
|
|
if (!reference || api.push(context) != 0) return;
|
|
registered = api.host_register(address, size, 1) == 0;
|
|
void *previous = nullptr;
|
|
api.pop(&previous);
|
|
}
|
|
|
|
~CudaHostPin() {
|
|
const auto &api = cuda_api();
|
|
if (registered && api.push(context) == 0) {
|
|
api.host_unregister(address);
|
|
void *previous = nullptr;
|
|
api.pop(&previous);
|
|
}
|
|
av_buffer_unref(&reference);
|
|
}
|
|
|
|
bool valid() const { return registered; }
|
|
void *get_address() const { return address; }
|
|
private:
|
|
AVBufferRef *reference = nullptr;
|
|
void *context = nullptr;
|
|
void *address = nullptr;
|
|
bool registered = false;
|
|
};
|
|
|
|
VideoPlane::VideoPlane() = default;
|
|
VideoPlane::~VideoPlane() = default;
|
|
VideoPlane::VideoPlane(VideoPlane &&other) noexcept = default;
|
|
|
|
VideoPlane &VideoPlane::operator=(VideoPlane &&other) noexcept {
|
|
if (this == &other) return *this;
|
|
registration.reset();
|
|
bytes = std::move(other.bytes);
|
|
registration = std::move(other.registration);
|
|
return *this;
|
|
}
|
|
|
|
godot::Error VideoPlane::resize(int64_t size) {
|
|
if (bytes.size() == size) return godot::OK;
|
|
registration.reset();
|
|
return godot::Error(bytes.resize(size));
|
|
}
|
|
|
|
bool VideoPlane::pin(AVBufferRef *device) {
|
|
uint8_t *address = ptrw();
|
|
if (registration) return registration->valid();
|
|
registration = std::make_unique<CudaHostPin>(device, address, bytes.size());
|
|
return registration->valid();
|
|
}
|
|
|
|
uint8_t *VideoPlane::ptrw() {
|
|
uint8_t *address = bytes.ptrw();
|
|
if (registration && registration->get_address() != address) registration.reset();
|
|
return address;
|
|
}
|