Files
plezy/linux/runner/mpv/mpv_gpu_bootstrap.cc
T

130 lines
5.5 KiB
C++

#include "mpv_gpu_bootstrap.h"
#include <cstdlib>
#include <cstring>
namespace mpv {
namespace {
bool HasExtension(const char* extensions, const char* requested) {
if (!extensions || !requested || requested[0] == '\0' || std::strchr(requested, ' ')) return false;
const size_t requested_length = std::strlen(requested);
const char* current = extensions;
while ((current = std::strstr(current, requested)) != nullptr) {
const bool starts_token = current == extensions || current[-1] == ' ';
const char following = current[requested_length];
if (starts_token && (following == '\0' || following == ' ')) return true;
current += requested_length;
}
return false;
}
bool ParseEglVersion(const char* version, int* major, int* minor) {
if (!version || !major || !minor) return false;
char* end = nullptr;
const long parsed_major = std::strtol(version, &end, 10);
if (end == version || *end != '.') return false;
const char* minor_start = end + 1;
const long parsed_minor = std::strtol(minor_start, &end, 10);
if (end == minor_start || parsed_major < 0 || parsed_minor < 0) return false;
*major = static_cast<int>(parsed_major);
*minor = static_cast<int>(parsed_minor);
return true;
}
bool AtLeastEgl15(const GpuBootstrapProbe& probe) {
return probe.egl_major > 1 || (probe.egl_major == 1 && probe.egl_minor >= 5);
}
bool Fail(std::string* error, const char* message) {
if (error) *error = message;
return false;
}
} // namespace
EGLImageKHR GpuImageDispatch::Create(EGLDisplay display, EGLContext context, EGLClientBuffer buffer) const {
if (uses_core) {
if (!create_image_core) return EGL_NO_IMAGE_KHR;
const EGLAttrib attributes[] = {EGL_NONE};
return reinterpret_cast<EGLImageKHR>(
create_image_core(display, context, EGL_GL_TEXTURE_2D_KHR, buffer, attributes));
}
if (!create_image_khr) return EGL_NO_IMAGE_KHR;
const EGLint attributes[] = {EGL_NONE};
return create_image_khr(display, context, EGL_GL_TEXTURE_2D_KHR, buffer, attributes);
}
bool GpuImageDispatch::Destroy(EGLDisplay display, EGLImageKHR image) const {
if (image == EGL_NO_IMAGE_KHR) return true;
if (uses_core) {
return destroy_image_core && destroy_image_core(display, reinterpret_cast<EGLImage>(image)) == EGL_TRUE;
}
return destroy_image_khr && destroy_image_khr(display, image) == EGL_TRUE;
}
GpuImageDispatch::operator bool() const {
const bool image_functions =
uses_core ? create_image_core && destroy_image_core : create_image_khr && destroy_image_khr;
return image_functions && image_target_texture;
}
bool ValidateGpuBootstrapProbe(const GpuBootstrapProbe& probe, std::string* error) {
const bool egl15 = AtLeastEgl15(probe);
if (!egl15 && !HasExtension(probe.egl_extensions, "EGL_KHR_surfaceless_context")) {
return Fail(error, "EGL surfaceless contexts are unavailable");
}
const bool core_images = egl15 && probe.create_image_core && probe.destroy_image_core;
const bool has_khr_image_extension =
HasExtension(probe.egl_extensions, "EGL_KHR_image") || HasExtension(probe.egl_extensions, "EGL_KHR_image_base");
const bool khr_images = has_khr_image_extension && probe.create_image_khr && probe.destroy_image_khr;
if (!core_images && !khr_images) {
return Fail(error, "EGL image creation is unavailable");
}
if (!HasExtension(probe.gl_extensions, "GL_OES_EGL_image")) {
return Fail(error, "OpenGL EGL image binding is unavailable");
}
if (!probe.image_target_texture) {
return Fail(error, "OpenGL EGL image entry point is unavailable");
}
if (error) error->clear();
return true;
}
bool ResolveGpuImageDispatch(EGLDisplay display, GpuImageDispatch* dispatch, std::string* error) {
if (!dispatch || display == EGL_NO_DISPLAY || eglGetCurrentContext() == EGL_NO_CONTEXT) {
return Fail(error, "No current EGL context is available");
}
GpuBootstrapProbe probe;
if (!ParseEglVersion(eglQueryString(display, EGL_VERSION), &probe.egl_major, &probe.egl_minor)) {
return Fail(error, "EGL version is unavailable");
}
probe.egl_extensions = eglQueryString(display, EGL_EXTENSIONS);
probe.gl_extensions = reinterpret_cast<const char*>(glGetString(GL_EXTENSIONS));
probe.create_image_core = reinterpret_cast<void*>(eglGetProcAddress("eglCreateImage"));
probe.destroy_image_core = reinterpret_cast<void*>(eglGetProcAddress("eglDestroyImage"));
probe.create_image_khr = reinterpret_cast<void*>(eglGetProcAddress("eglCreateImageKHR"));
probe.destroy_image_khr = reinterpret_cast<void*>(eglGetProcAddress("eglDestroyImageKHR"));
probe.image_target_texture = reinterpret_cast<void*>(eglGetProcAddress("glEGLImageTargetTexture2DOES"));
if (!ValidateGpuBootstrapProbe(probe, error)) return false;
GpuImageDispatch resolved;
const bool egl15 = AtLeastEgl15(probe);
if (egl15 && probe.create_image_core && probe.destroy_image_core) {
resolved.uses_core = true;
resolved.create_image_core = reinterpret_cast<EglCreateImageCoreProc>(probe.create_image_core);
resolved.destroy_image_core = reinterpret_cast<EglDestroyImageCoreProc>(probe.destroy_image_core);
} else {
resolved.create_image_khr = reinterpret_cast<EglCreateImageKhrProc>(probe.create_image_khr);
resolved.destroy_image_khr = reinterpret_cast<EglDestroyImageKhrProc>(probe.destroy_image_khr);
}
resolved.image_target_texture = reinterpret_cast<GlImageTargetTextureProc>(probe.image_target_texture);
if (!resolved) return Fail(error, "GPU image dispatch is incomplete");
*dispatch = resolved;
return true;
}
} // namespace mpv