|
|
|
@@ -0,0 +1,295 @@ |
|
|
|
#include <gbm.h> |
|
|
|
#include <libdrm/drm_fourcc.h> |
|
|
|
#include <EGL/egl.h> |
|
|
|
#include <EGL/eglext.h> |
|
|
|
#include <GLES3/gl32.h> |
|
|
|
|
|
|
|
#include <array> |
|
|
|
#include <fcntl.h> |
|
|
|
#include <fstream> |
|
|
|
#include <iostream> |
|
|
|
#include <linux/dma-buf.h> |
|
|
|
#include <sstream> |
|
|
|
#include <string> |
|
|
|
#include <sys/ioctl.h> |
|
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
const uint32_t kWidth = 1024; |
|
|
|
const uint32_t kHeight = 1024; |
|
|
|
const uint64_t kFramebufferModifier = I915_FORMAT_MOD_Y_TILED; |
|
|
|
const uint64_t kTextureModifier = I915_FORMAT_MOD_Y_TILED_GEN12_RC_CCS; |
|
|
|
const std::string kVertexShaderPath = "dma_import.vert"; |
|
|
|
const std::string kFragmentShaderPath = "dma_import.frag"; |
|
|
|
|
|
|
|
struct Vertex { |
|
|
|
float position[3]; |
|
|
|
float color[3]; |
|
|
|
}; |
|
|
|
|
|
|
|
EGLDisplay display; |
|
|
|
EGLContext context; |
|
|
|
|
|
|
|
#define ERROR(message) \ |
|
|
|
std::cerr << message << std::endl; \ |
|
|
|
std::exit(EXIT_FAILURE) |
|
|
|
|
|
|
|
void InitializeEGL() { |
|
|
|
display = eglGetDisplay(EGL_DEFAULT_DISPLAY); |
|
|
|
eglInitialize(display, nullptr, nullptr); |
|
|
|
eglBindAPI(EGL_OPENGL_ES_API); |
|
|
|
|
|
|
|
const EGLint config_attribs[] = { |
|
|
|
EGL_SURFACE_TYPE, EGL_DONT_CARE, |
|
|
|
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, |
|
|
|
EGL_NONE }; |
|
|
|
EGLConfig egl_config; |
|
|
|
EGLint num_configs; |
|
|
|
if (!eglChooseConfig(display, config_attribs, &egl_config, 1, &num_configs)) { |
|
|
|
ERROR("Unable to choose EGL config"); |
|
|
|
} |
|
|
|
|
|
|
|
const EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; |
|
|
|
context = eglCreateContext(display, egl_config, EGL_NO_CONTEXT, context_attribs); |
|
|
|
if (context == EGL_NO_CONTEXT) { |
|
|
|
ERROR("Failed to create GLES context"); |
|
|
|
} |
|
|
|
|
|
|
|
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, context)) { |
|
|
|
ERROR("Failed to make GLES context current"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
gbm_bo* CreateGbmBO(gbm_device* gbm, uint64_t width, uint32_t height, uint64_t modifier) { |
|
|
|
std::vector<uint64_t> modifiers = { modifier }; |
|
|
|
gbm_bo* bo = gbm_bo_create_with_modifiers(gbm, width, height, |
|
|
|
DRM_FORMAT_ABGR8888, modifiers.data(), 1); |
|
|
|
if (!bo) { |
|
|
|
ERROR("Failed to create GBM bo"); |
|
|
|
} |
|
|
|
if (gbm_bo_get_modifier(bo) != modifier) { |
|
|
|
ERROR("Modifier not accepted when creating GBM bo"); |
|
|
|
} |
|
|
|
return bo; |
|
|
|
} |
|
|
|
|
|
|
|
EGLImageKHR CreateEGLImage(gbm_bo* bo) { |
|
|
|
uint64_t modifier = gbm_bo_get_modifier(bo); |
|
|
|
std::vector<uint32_t> fds = {}; |
|
|
|
for (int i = 0; i < gbm_bo_get_plane_count(bo); i++) { |
|
|
|
fds.push_back(gbm_bo_get_fd_for_plane(bo, i)); |
|
|
|
} |
|
|
|
std::vector<EGLint> egl_image_attrs; |
|
|
|
egl_image_attrs.push_back(EGL_WIDTH); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_width(bo)); |
|
|
|
egl_image_attrs.push_back(EGL_HEIGHT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_height(bo)); |
|
|
|
egl_image_attrs.push_back(EGL_LINUX_DRM_FOURCC_EXT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_format(bo)); |
|
|
|
|
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE0_FD_EXT); |
|
|
|
egl_image_attrs.push_back(fds[0]); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE0_OFFSET_EXT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_offset(bo, 0)); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE0_PITCH_EXT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_stride_for_plane(bo, 0)); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT); |
|
|
|
egl_image_attrs.push_back(modifier & 0xfffffffful); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT); |
|
|
|
egl_image_attrs.push_back(modifier >> 32); |
|
|
|
|
|
|
|
if (gbm_bo_get_plane_count(bo) == 2) { |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE1_FD_EXT); |
|
|
|
egl_image_attrs.push_back(fds[1]); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE1_OFFSET_EXT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_offset(bo, 1)); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE1_PITCH_EXT); |
|
|
|
egl_image_attrs.push_back(gbm_bo_get_stride_for_plane(bo, 1)); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT); |
|
|
|
egl_image_attrs.push_back(modifier & 0xfffffffful); |
|
|
|
egl_image_attrs.push_back(EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT); |
|
|
|
egl_image_attrs.push_back(modifier >> 32); |
|
|
|
} |
|
|
|
|
|
|
|
egl_image_attrs.push_back(EGL_NONE); |
|
|
|
|
|
|
|
PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR = (PFNEGLCREATEIMAGEKHRPROC)eglGetProcAddress("eglCreateImageKHR"); |
|
|
|
EGLImageKHR image = eglCreateImageKHR(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, |
|
|
|
nullptr, egl_image_attrs.data()); |
|
|
|
if (!image) { |
|
|
|
ERROR("Failed to create EGL image"); |
|
|
|
} |
|
|
|
|
|
|
|
for (int i = 0; i < gbm_bo_get_plane_count(bo); i++) { |
|
|
|
close(fds[i]); |
|
|
|
} |
|
|
|
return image; |
|
|
|
} |
|
|
|
|
|
|
|
typedef void *GLeglImageOES; |
|
|
|
typedef void (GL_APIENTRYP PFNGLEGLIMAGETARGETTEXTURE2DOESPROC) (GLenum target, GLeglImageOES image); |
|
|
|
|
|
|
|
GLuint CreateTexture(EGLImageKHR image) { |
|
|
|
PFNGLEGLIMAGETARGETTEXTURE2DOESPROC glEGLImageTargetTexture2DOES; |
|
|
|
glEGLImageTargetTexture2DOES = (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES"); |
|
|
|
GLuint texture; |
|
|
|
glGenTextures(1, &texture); |
|
|
|
glBindTexture(GL_TEXTURE_2D, texture); |
|
|
|
glEGLImageTargetTexture2DOES(GL_TEXTURE_2D, (GLeglImageOES)image); |
|
|
|
glBindTexture(GL_TEXTURE_2D, 0); |
|
|
|
if (glGetError()) { |
|
|
|
ERROR("Failed to bind EGL image to texture"); |
|
|
|
} |
|
|
|
|
|
|
|
return texture; |
|
|
|
} |
|
|
|
|
|
|
|
void SetUpFramebuffer(gbm_device* gbm) { |
|
|
|
gbm_bo* bo = CreateGbmBO(gbm, kWidth, kHeight, kFramebufferModifier); |
|
|
|
EGLImageKHR image = CreateEGLImage(bo); |
|
|
|
GLuint texture = CreateTexture(image); |
|
|
|
GLuint framebuffer; |
|
|
|
glGenFramebuffers(1, &framebuffer); |
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
|
|
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
|
|
|
if (glGetError()) { |
|
|
|
ERROR("Failed to attach texture to fbo"); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
GLuint CompileShader(GLenum type, std::string path) { |
|
|
|
std::ifstream fstream(path); |
|
|
|
if (!fstream) { |
|
|
|
ERROR("Unable to read shader: " << path); |
|
|
|
} |
|
|
|
std::stringstream buffer; |
|
|
|
buffer << fstream.rdbuf(); |
|
|
|
std::string shader_source_tmp = buffer.str(); |
|
|
|
const char* shader_source = shader_source_tmp.c_str(); |
|
|
|
|
|
|
|
GLuint shader = glCreateShader(type); |
|
|
|
if (!shader) { |
|
|
|
ERROR("Failed to create shader of type: " << type); |
|
|
|
} |
|
|
|
glShaderSource(shader, 1, &shader_source, nullptr); |
|
|
|
glCompileShader(shader); |
|
|
|
GLint status; |
|
|
|
glGetShaderiv(shader, GL_COMPILE_STATUS, &status); |
|
|
|
if (!status) { |
|
|
|
GLint log_length; |
|
|
|
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); |
|
|
|
char shader_log[log_length]; |
|
|
|
glGetShaderInfoLog(shader, log_length, nullptr, shader_log); |
|
|
|
ERROR("Failed to compile shader: " << shader_log); |
|
|
|
} |
|
|
|
return shader; |
|
|
|
} |
|
|
|
|
|
|
|
GLuint CreateProgram() { |
|
|
|
GLuint vertex_shader = CompileShader(GL_VERTEX_SHADER, kVertexShaderPath); |
|
|
|
GLuint fragment_shader = CompileShader(GL_FRAGMENT_SHADER, kFragmentShaderPath); |
|
|
|
|
|
|
|
GLuint program = glCreateProgram(); |
|
|
|
if (!program) { |
|
|
|
ERROR("Failed to create program"); |
|
|
|
} |
|
|
|
glAttachShader(program, vertex_shader); |
|
|
|
glAttachShader(program, fragment_shader); |
|
|
|
glLinkProgram(program); |
|
|
|
GLint status; |
|
|
|
glGetProgramiv(program, GL_LINK_STATUS, &status); |
|
|
|
if (!status) { |
|
|
|
GLint log_length = 0; |
|
|
|
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); |
|
|
|
char program_log[log_length]; |
|
|
|
glGetProgramInfoLog(program, log_length, nullptr, program_log); |
|
|
|
ERROR("Failed to link program: " << program_log); |
|
|
|
} |
|
|
|
glDetachShader(program, vertex_shader); |
|
|
|
glDetachShader(program, fragment_shader); |
|
|
|
glDeleteShader(vertex_shader); |
|
|
|
glDeleteShader(fragment_shader); |
|
|
|
|
|
|
|
return program; |
|
|
|
} |
|
|
|
|
|
|
|
GLuint CreateVBO() { |
|
|
|
std::array<Vertex, 6> vertices = {}; |
|
|
|
// Triangle 1 is upside-down and red. |
|
|
|
vertices[0] = { .position = {0.0, -0.5, 0.0}, .color = {1.0, 0.0, 0.0} }; |
|
|
|
vertices[1] = { .position = {0.5, 0.5, 0.0}, .color = {0.0, 1.0, 0.0} }; |
|
|
|
vertices[2] = { .position = {-0.5, 0.5, 0.0}, .color = {0.0, 0.0, 1.0} }; |
|
|
|
// Triangle 1 is upright and green. |
|
|
|
vertices[3] = { .position = {0.0, -0.5, 0.0}, .color = {0.0, 1.0, 0.0} }; |
|
|
|
vertices[4] = { .position = {0.5, 0.5, 0.0}, .color = {0.0, 1.0, 0.0} }; |
|
|
|
vertices[5] = { .position = {-0.5, 0.5, 0.0}, .color = {0.0, 1.0, 0.0} }; |
|
|
|
|
|
|
|
GLuint vbo; |
|
|
|
glGenBuffers(1, &vbo); |
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo); |
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW); |
|
|
|
return vbo; |
|
|
|
} |
|
|
|
|
|
|
|
GLuint CreateVAO(GLuint vbo, const std::array<uint16_t, 3>& indices) { |
|
|
|
GLuint vao; |
|
|
|
glGenVertexArrays(1, &vao); |
|
|
|
glBindVertexArray(vao); |
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo); |
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), |
|
|
|
(void*)offsetof(Vertex, position)); |
|
|
|
glEnableVertexAttribArray(0); |
|
|
|
|
|
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), |
|
|
|
(void*)offsetof(Vertex, color)); |
|
|
|
glEnableVertexAttribArray(1); |
|
|
|
|
|
|
|
GLuint ebo; |
|
|
|
glGenBuffers(1, &ebo); |
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); |
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices.data(), GL_STATIC_DRAW); |
|
|
|
return vao; |
|
|
|
} |
|
|
|
|
|
|
|
void Draw(gbm_device* gbm, GLuint program, GLuint vao) { |
|
|
|
gbm_bo* bo = CreateGbmBO(gbm, kWidth, kHeight, kTextureModifier); |
|
|
|
EGLImageKHR image = CreateEGLImage(bo); |
|
|
|
GLuint texture = CreateTexture(image); |
|
|
|
|
|
|
|
glViewport(0, 0, kWidth, kHeight); |
|
|
|
glClearColor(1.0f, 1.0f, 0.0f, 1.0f); |
|
|
|
glClear(GL_COLOR_BUFFER_BIT); |
|
|
|
|
|
|
|
glUseProgram(program); |
|
|
|
glActiveTexture(GL_TEXTURE0); |
|
|
|
glBindTexture(GL_TEXTURE_2D, texture); |
|
|
|
glBindVertexArray(vao); |
|
|
|
glDrawArrays(GL_TRIANGLES, 0, 3); |
|
|
|
glFinish(); |
|
|
|
|
|
|
|
glDeleteTextures(1, &texture); |
|
|
|
glBindTexture(GL_TEXTURE_2D, 0); |
|
|
|
PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR = (PFNEGLDESTROYIMAGEKHRPROC)eglGetProcAddress("eglDestroyImageKHR"); |
|
|
|
eglDestroyImageKHR(display, image); |
|
|
|
gbm_bo_destroy(bo); |
|
|
|
} |
|
|
|
|
|
|
|
int main() { |
|
|
|
InitializeEGL(); |
|
|
|
int drm_fd = open("/dev/dri/renderD128", O_RDWR); |
|
|
|
gbm_device* gbm = gbm_create_device(drm_fd); |
|
|
|
|
|
|
|
SetUpFramebuffer(gbm); |
|
|
|
GLuint program = CreateProgram(); |
|
|
|
GLuint vbo = CreateVBO(); |
|
|
|
GLuint vao = CreateVAO(vbo, { 0, 1, 2 }); |
|
|
|
|
|
|
|
while (true) { |
|
|
|
Draw(gbm, program, vao); |
|
|
|
} |
|
|
|
|
|
|
|
eglMakeCurrent(display, EGL_NO_SURFACE,EGL_NO_SURFACE, EGL_NO_CONTEXT); |
|
|
|
eglDestroyContext(display, context); |
|
|
|
eglTerminate(display); |
|
|
|
return EXIT_SUCCESS; |
|
|
|
} |