diff --git a/engine/engine-sources.gypi b/engine/engine-sources.gypi index 882667c3abf..5d2d980056b 100644 --- a/engine/engine-sources.gypi +++ b/engine/engine-sources.gypi @@ -13,6 +13,8 @@ 'src/customfont.h', 'src/font.h', 'src/freetype-font.h', + 'src/glcontext.h', + 'src/glhardwarebuffer.h', 'src/gradient.h', 'src/graphics_util.h', 'src/graphicscontext.h', @@ -38,7 +40,7 @@ 'src/fonttable.cpp', 'src/freetype-font.cpp', 'src/glcontext.cpp', - 'src/glcontext.h', + 'src/glhardwarebuffer-android.cpp', 'src/gradient.cpp', 'src/graphics_util.cpp', 'src/graphicscontext.cpp', diff --git a/engine/src/glcontext.cpp b/engine/src/glcontext.cpp index 9ed7350b4ff..2e875ce044f 100644 --- a/engine/src/glcontext.cpp +++ b/engine/src/glcontext.cpp @@ -47,6 +47,31 @@ struct MCGLProgramConfig MCGAffineTransform texture_transform; }; +struct color_program_t +{ + // Program + GLuint program; + + // Uniform + GLuint world_transform; + + // Vertex array object + GLuint vao; +}; + +struct texture_program_t +{ + // Program + GLuint program; + + // Uniformas + GLuint world_transform; + GLuint texture_transform; + + // Certex array object + GLuint vao; +}; + struct __MCGLContext { // The version of OpenGL in use. @@ -56,22 +81,14 @@ struct __MCGLContext // Context initialization bool is_initialized; - // programs - GLuint color_program; - GLuint texture_program; - - // uniforms - GLuint c_transform; - GLuint t_transform; - GLuint t_texture_transform; + // Programs + color_program_t color_program; + texture_program_t texture_program; + texture_program_t external_texture_program; // buffer GLuint buffer; - // Vertex Array Objects - GLuint color_vao; - GLuint texture_vao; - // current context state MCGLProgramConfig config; }; @@ -110,7 +127,7 @@ bool MCGLCheckError(const char *p_prefix) t_error = glGetError(); } - return true; + return false; } static bool MCGLCompileShader(GLuint p_shader_type, const char *p_source, GLuint &r_shader) @@ -281,73 +298,67 @@ static MCGLShaderSourceInfo s_texture_shaders[] = { {GL_FRAGMENT_SHADER, s_texture_fragment_shader_source}, }; -bool MCGLContextInit(MCGLContextRef self) +static const char *s_external_texture_fragment_shader_source = +R"glsl(#version 300 es + +#extension GL_OES_EGL_image_external_essl3 : require + +precision mediump float; +uniform mat3x2 pTextureTransform; + +in vec2 pVertexTextureCoord; + +out vec4 outColor; + +uniform samplerExternalOES tex; + +void main() { - MCLog("MCGLContextInit(<%p>)"); - if (self == nil) - return false; + outColor = texture(tex, pTextureTransform * vec3(pVertexTextureCoord, 1.0)); +} +)glsl"; - if (self->is_initialized) - return true; +static MCGLShaderSourceInfo s_external_texture_shaders[] = { + {GL_VERTEX_SHADER, s_texture_vertex_shader_source}, + {GL_FRAGMENT_SHADER, s_external_texture_fragment_shader_source}, +}; - /* UNCHECKED */ MCGLCheckError("pre-init: "); +static void MCGLContextFreeColorProgram(color_program_t &x_program) +{ + glDeleteProgram(x_program.program); + glDeleteVertexArrays(1, &x_program.vao); + MCMemoryClear(x_program); +} + +static bool MCGLContextCreateColorProgram(GLuint p_buffer, color_program_t &r_program) +{ bool t_success; t_success = true; - MCLog("gl Version String: %s", glGetString(GL_VERSION)); - - // Get the OpenGL version. - glGetIntegerv(GL_MAJOR_VERSION, &self->opengl_major_version); - glGetIntegerv(GL_MINOR_VERSION, &self->opengl_minor_version); - /* UNCHECKED */ MCGLDebugCheckError("get version: "); - - /* Compile shader programs for drawing solid color & texture */ - GLuint t_color_program; - t_color_program = 0; - if (t_success) - t_success = MCGLCompileProgram(s_color_shaders, 2, t_color_program); - /* UNCHECKED */ MCGLDebugCheckError("compile color shaders: "); - - GLuint t_texture_program; - t_texture_program = 0; - if (t_success) - t_success = MCGLCompileProgram(s_texture_shaders, 2, t_texture_program); - /* UNCHECKED */ MCGLDebugCheckError("compile texture shaders: "); + color_program_t t_program; - /* Fetch uniform values */ - GLuint t_cprog_world_transform; - t_cprog_world_transform = 0; - GLuint t_tprog_world_transform; - t_tprog_world_transform = 0; - GLuint t_tprog_texture_transform; - t_tprog_texture_transform = 0; + /* Compile shader program for drawing solid color */ + t_program.program = 0; if (t_success) { - t_cprog_world_transform = glGetUniformLocation(t_color_program, "pTransform"); - t_tprog_world_transform = glGetUniformLocation(t_texture_program, "pTransform"); - t_tprog_texture_transform = glGetUniformLocation(t_texture_program, "pTextureTransform"); - /* UNCHECKED */ MCGLDebugCheckError("get uniform locations: "); + t_success = MCGLCompileProgram(s_color_shaders, 2, t_program.program); + /* UNCHECKED */ MCGLDebugCheckError("compile color program: "); } - /* Create input buffers */ - GLuint t_buffer; - t_buffer = 0; + /* Fetch uniform values */ + t_program.world_transform = 0; if (t_success) { - glGenBuffers(1, &t_buffer); - /* UNCHECKED */ MCGLDebugCheckError("create buffers: "); + t_program.world_transform = glGetUniformLocation(t_program.program, "pTransform"); + /* UNCHECKED */ MCGLDebugCheckError("get color program uniform locations: "); } /* Create vertex arrays to manage attributes */ - GLuint t_color_vao; - t_color_vao = 0; - GLuint t_texture_vao; - t_texture_vao = 0; + t_program.vao = 0; if (t_success) { - glGenVertexArrays(1, &t_color_vao); - glGenVertexArrays(1, &t_texture_vao); - /* UNCHECKED */ MCGLDebugCheckError("create VAOs: "); + glGenVertexArrays(1, &t_program.vao); + /* UNCHECKED */ MCGLDebugCheckError("create VAO: "); } /* Set up vertex attributes */ @@ -355,13 +366,13 @@ bool MCGLContextInit(MCGLContextRef self) { GLint t_position; GLint t_color; - t_position = glGetAttribLocation(t_color_program, "pPosition"); - t_color = glGetAttribLocation(t_color_program, "pColor"); + t_position = glGetAttribLocation(t_program.program, "pPosition"); + t_color = glGetAttribLocation(t_program.program, "pColor"); t_success = t_position != -1 && t_color != -1; if (t_success) { - glBindVertexArray(t_color_vao); - glBindBuffer(GL_ARRAY_BUFFER, t_buffer); + glBindVertexArray(t_program.vao); + glBindBuffer(GL_ARRAY_BUFFER, p_buffer); glEnableVertexAttribArray(t_position); glVertexAttribPointer(t_position, 2, GL_SHORT, GL_FALSE, sizeof(MCGLColorVertex), 0); glEnableVertexAttribArray(t_color); @@ -370,17 +381,78 @@ bool MCGLContextInit(MCGLContextRef self) /* UNCHECKED */ MCGLDebugCheckError("set up color VAO: "); } + if (t_success) + { + r_program = t_program; + + MCLog("color program: %d, uniform: %d, vao: %d", t_program.program, t_program.world_transform, t_program.vao); + } + else + { + MCGLContextFreeColorProgram(t_program); + /* UNCHECKED */ MCGLDebugCheckError("color program cleanup: "); + } + + return t_success; +} + +static void MCGLContextFreeTextureProgram(texture_program_t &x_program) +{ + glDeleteProgram(x_program.program); + glDeleteVertexArrays(1, &x_program.vao); + MCMemoryClear(x_program); +} + +static bool MCGLContextCreateTextureProgram(GLuint p_buffer, bool p_external_texture, texture_program_t &r_program) +{ + bool t_success; + t_success = true; + + /* Select the shader set to compile into the program based on + * whether or not external textures will be used */ + MCGLShaderSourceInfo *t_shaders; + if (p_external_texture) + t_shaders = s_external_texture_shaders; + else + t_shaders = s_texture_shaders; + + /* Compile shader programs for drawing solid color & texture */ + texture_program_t t_program; + t_program.program = 0; + if (t_success) + t_success = MCGLCompileProgram(t_shaders, 2, t_program.program); + /* UNCHECKED */ MCGLDebugCheckError("compile texture program: "); + + /* Fetch uniform values */ + t_program.world_transform = 0; + t_program.texture_transform = 0; + if (t_success) + { + t_program.world_transform = glGetUniformLocation(t_program.program, "pTransform"); + t_program.texture_transform = glGetUniformLocation(t_program.program, "pTextureTransform"); + /* UNCHECKED */ MCGLDebugCheckError("get uniform locations: "); + } + + /* Create vertex arrays to manage attributes */ + t_program.vao = 0; + if (t_success) + { + glGenVertexArrays(1, &t_program.vao); + /* UNCHECKED */ MCGLDebugCheckError("create VAOs: "); + } + + /* Set up vertex attributes */ if (t_success) { GLint t_position; GLint t_texture_coord; - t_position = glGetAttribLocation(t_texture_program, "pPosition"); - t_texture_coord = glGetAttribLocation(t_texture_program, "pTextureCoord"); + t_position = glGetAttribLocation(t_program.program, "pPosition"); + t_texture_coord = glGetAttribLocation(t_program.program, "pTextureCoord"); t_success = t_position != -1 && t_texture_coord != -1; if (t_success) { - glBindVertexArray(t_texture_vao); - glBindBuffer(GL_ARRAY_BUFFER, t_buffer); + glBindVertexArray(t_program.vao); + glBindBuffer(GL_ARRAY_BUFFER, p_buffer); glEnableVertexAttribArray(t_position); glVertexAttribPointer(t_position, 2, GL_SHORT, GL_FALSE, sizeof(MCGLTextureVertex), nil); glEnableVertexAttribArray(t_texture_coord); @@ -389,32 +461,84 @@ bool MCGLContextInit(MCGLContextRef self) /* UNCHECKED */ MCGLDebugCheckError("set up texture VAO: "); } + if (t_success) + { + r_program = t_program; + + MCLog("texture program: %d, uniforms: [%d, %d], vao: %d", t_program.program, t_program.world_transform, t_program.texture_transform, t_program.vao); + } + else + { + MCGLContextFreeTextureProgram(t_program); + /* UNCHECKED */ MCGLDebugCheckError("texture program cleanup: "); + } + + return t_success; +} + +bool MCGLContextInit(MCGLContextRef self) +{ + MCLog("MCGLContextInit(<%p>)"); + if (self == nil) + return false; + + if (self->is_initialized) + return true; + + /* UNCHECKED */ MCGLCheckError("pre-init: "); + bool t_success; + t_success = true; + + MCLog("gl Version String: %s", glGetString(GL_VERSION)); + + // Get the OpenGL version. + glGetIntegerv(GL_MAJOR_VERSION, &self->opengl_major_version); + glGetIntegerv(GL_MINOR_VERSION, &self->opengl_minor_version); + /* UNCHECKED */ MCGLDebugCheckError("get version: "); + + + /* Create input buffers */ + GLuint t_buffer; + t_buffer = 0; + if (t_success) + { + glGenBuffers(1, &t_buffer); + /* UNCHECKED */ MCGLDebugCheckError("create buffers: "); + } + + /* Create programs */ + color_program_t t_color_program; + MCMemoryClear(t_color_program); + if (t_success) + t_success = MCGLContextCreateColorProgram(t_buffer, t_color_program); + texture_program_t t_texture_program; + MCMemoryClear(t_texture_program); + if (t_success) + t_success = MCGLContextCreateTextureProgram(t_buffer, false, t_texture_program); + + /* Create optional external texture program */ + texture_program_t t_external_texture_program; + MCMemoryClear(t_external_texture_program); + if (t_success) + { + /* UNCHECKED */ MCGLContextCreateTextureProgram(t_buffer, true, t_external_texture_program); + } + if (t_success) { self->color_program = t_color_program; self->texture_program = t_texture_program; + self->external_texture_program = t_external_texture_program; - self->c_transform = t_cprog_world_transform; - self->t_transform = t_tprog_world_transform; - self->t_texture_transform = t_tprog_texture_transform; - - MCLog("uniforms: %d, %d, %d", - self->c_transform, - self->t_transform, - self->t_texture_transform); self->buffer = t_buffer; - self->color_vao = t_color_vao; - self->texture_vao = t_texture_vao; - self->is_initialized = true; } else { - glDeleteProgram(t_color_program); - glDeleteProgram(t_texture_program); - glDeleteVertexArrays(1, &t_color_vao); - glDeleteVertexArrays(1, &t_texture_vao); + MCGLContextFreeColorProgram(t_color_program); + MCGLContextFreeTextureProgram(t_texture_program); + MCGLContextFreeTextureProgram(t_external_texture_program); glDeleteBuffers(1, &t_buffer); /* UNCHECKED */ MCGLDebugCheckError("init cleanup: "); } @@ -426,10 +550,10 @@ static void MCGLContextFinalize(MCGLContextRef p_context) { MCGLContextSelectProgram(p_context, kMCGLProgramTypeNone); - glDeleteProgram(p_context->color_program); - glDeleteProgram(p_context->texture_program); - glDeleteVertexArrays(1, &p_context->color_vao); - glDeleteVertexArrays(1, &p_context->texture_vao); + MCGLContextFreeColorProgram(p_context->color_program); + MCGLContextFreeTextureProgram(p_context->texture_program); + MCGLContextFreeTextureProgram(p_context->external_texture_program); + glDeleteBuffers(1, &p_context->buffer); } @@ -464,11 +588,35 @@ void MCGLContextReset(MCGLContextRef p_context) MCMemoryClear(p_context, sizeof(__MCGLContext)); } +bool MCGLContextGetProgramSupported(MCGLContextRef p_context, MCGLProgramType p_program) +{ + if (p_context == nil) + return false; + + switch (p_program) + { + case kMCGLProgramTypeNone: + return true; + + case kMCGLProgramTypeColor: + return p_context->color_program.program != 0; + + case kMCGLProgramTypeTexture: + return p_context->texture_program.program != 0; + + case kMCGLProgramTypeExternalTexture: + return p_context->external_texture_program.program != 0; + } +} + bool MCGLContextSelectProgram(MCGLContextRef p_context, MCGLProgramType p_program) { if (p_context == nil) return false; + if (!MCGLContextGetProgramSupported(p_context, p_program)) + return false; + p_context->config.program = p_program; switch (p_program) @@ -481,15 +629,22 @@ bool MCGLContextSelectProgram(MCGLContextRef p_context, MCGLProgramType p_progra case kMCGLProgramTypeColor: // select color program and related vao and buffer - glUseProgram(p_context->color_program); - glBindVertexArray(p_context->color_vao); + glUseProgram(p_context->color_program.program); + glBindVertexArray(p_context->color_program.vao); glBindBuffer(GL_ARRAY_BUFFER, p_context->buffer); break; case kMCGLProgramTypeTexture: // select texture program and related vao and buffer - glUseProgram(p_context->texture_program); - glBindVertexArray(p_context->texture_vao); + glUseProgram(p_context->texture_program.program); + glBindVertexArray(p_context->texture_program.vao); + glBindBuffer(GL_ARRAY_BUFFER, p_context->buffer); + break; + + case kMCGLProgramTypeExternalTexture: + // select external texture program and related vao and buffer + glUseProgram(p_context->external_texture_program.program); + glBindVertexArray(p_context->external_texture_program.vao); glBindBuffer(GL_ARRAY_BUFFER, p_context->buffer); break; } @@ -508,10 +663,13 @@ static inline bool MCGLContextUpdateTransform(MCGLContextRef p_context) return false; case kMCGLProgramTypeColor: - return MCGLUniformAffineTransform(p_context->c_transform, t_transform); + return MCGLUniformAffineTransform(p_context->color_program.world_transform, t_transform); case kMCGLProgramTypeTexture: - return MCGLUniformAffineTransform(p_context->t_transform, t_transform); + return MCGLUniformAffineTransform(p_context->texture_program.world_transform, t_transform); + + case kMCGLProgramTypeExternalTexture: + return MCGLUniformAffineTransform(p_context->external_texture_program.world_transform, t_transform); } } @@ -565,12 +723,19 @@ bool MCGLContextSetTextureTransform(MCGLContextRef p_context, const MCGAffineTra if (p_context == nil) return false; - if (p_context->config.program != kMCGLProgramTypeTexture) - return false; - p_context->config.texture_transform = p_transform; - return MCGLUniformAffineTransform(p_context->t_texture_transform, p_context->config.texture_transform); + switch (p_context->config.program) + { + case kMCGLProgramTypeTexture: + return MCGLUniformAffineTransform(p_context->texture_program.texture_transform, p_context->config.texture_transform); + + case kMCGLProgramTypeExternalTexture: + return MCGLUniformAffineTransform(p_context->external_texture_program.texture_transform, p_context->config.texture_transform); + + default: + return false; + } } bool MCGLContextConcatTextureTransform(MCGLContextRef p_context, const MCGAffineTransform &p_transform) @@ -579,10 +744,5 @@ bool MCGLContextConcatTextureTransform(MCGLContextRef p_context, const MCGAffine if (p_context == nil) return false; - if (p_context->config.program != kMCGLProgramTypeTexture) - return false; - - p_context->config.texture_transform = MCGAffineTransformConcat(p_context->config.texture_transform, p_transform); - - return MCGLUniformAffineTransform(p_context->t_texture_transform, p_context->config.texture_transform); + return MCGLContextSetTextureTransform(p_context, MCGAffineTransformConcat(p_context->config.texture_transform, p_transform)); } diff --git a/engine/src/glcontext.h b/engine/src/glcontext.h index a9a6d84fbc7..b38f7cc012b 100644 --- a/engine/src/glcontext.h +++ b/engine/src/glcontext.h @@ -40,6 +40,7 @@ enum MCGLProgramType kMCGLProgramTypeNone, kMCGLProgramTypeColor, kMCGLProgramTypeTexture, + kMCGLProgramTypeExternalTexture, }; extern bool MCGLCheckError(const char *p_prefix); @@ -56,9 +57,9 @@ extern bool MCGLContextConcatWorldTransform(MCGLContextRef p_context, const MCGA extern bool MCGLContextSetTextureTransform(MCGLContextRef p_context, const MCGAffineTransform &p_transform); extern bool MCGLContextConcatTextureTransform(MCGLContextRef p_context, const MCGAffineTransform &p_transform); +extern bool MCGLContextGetProgramSupported(MCGLContextRef p_context, MCGLProgramType p_program); extern bool MCGLContextSelectProgram(MCGLContextRef p_context, MCGLProgramType p_program); - extern void MCPlatformEnableOpenGLMode(void); extern void MCPlatformDisableOpenGLMode(void); extern MCGLContextRef MCPlatformGetOpenGLContext(void); diff --git a/engine/src/glhardwarebuffer-android.cpp b/engine/src/glhardwarebuffer-android.cpp new file mode 100644 index 00000000000..496515c7ccc --- /dev/null +++ b/engine/src/glhardwarebuffer-android.cpp @@ -0,0 +1,318 @@ +/* Copyright (C) 2020 LiveCode Ltd. + +This file is part of LiveCode. + +LiveCode is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License v3 as published by the Free +Software Foundation. + +LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with LiveCode. If not see . */ + +/* TODO - Don't attempt to build on 32bit armv7 - needs ndk update */ +#if !defined(__arm__) +#define MCANDROIDUSEHARDWAREBUFFER +#endif + +#if defined(MCANDROIDUSEHARDWAREBUFFER) + +#define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES +#include +#include + +#include + +// required for glEGLImageTargetTexture2DOES and GL_TEXTURE_EXTERNAL_OES definition +#include + +#include +#include + +#include + +#include "glcontext.h" +#include "glhardwarebuffer.h" + +#include + +struct __MCGLHardwareBuffer +{ + AHardwareBuffer* handle; + EGLImageKHR egl_image; + uindex_t stride; + void *lock; +}; + +typedef int (*AHardwareBuffer_allocate_func)(const AHardwareBuffer_Desc* desc, AHardwareBuffer** outBuffer); +typedef void (*AHardwareBuffer_release_func)(AHardwareBuffer* buffer); +typedef void (*AHardwareBuffer_describe_func)(const AHardwareBuffer* buffer, AHardwareBuffer_Desc* outDesc); +typedef int (*AHardwareBuffer_lock_func)(AHardwareBuffer* buffer, uint64_t usage, int32_t fence, const ARect* rect, void** outVirtualAddress); +typedef int (*AHardwareBuffer_unlock_func)(AHardwareBuffer* buffer, int32_t* fence); + +typedef EGLClientBuffer (*eglGetNativeClientBufferANDROID_func)(const struct AHardwareBuffer *buffer); + +static AHardwareBuffer_allocate_func s_AHardwareBuffer_allocate = nil; +static AHardwareBuffer_release_func s_AHardwareBuffer_release = nil; +static AHardwareBuffer_describe_func s_AHardwareBuffer_describe = nil; +static AHardwareBuffer_lock_func s_AHardwareBuffer_lock = nil; +static AHardwareBuffer_unlock_func s_AHardwareBuffer_unlock = nil; +static eglGetNativeClientBufferANDROID_func s_eglGetNativeClientBufferANDROID = nil; + +static bool MCAndroidHardwareBufferInitialize() +{ + static bool s_initialized = false; + if (!s_initialized) + { + s_initialized = true; + s_AHardwareBuffer_allocate = (AHardwareBuffer_allocate_func)dlsym(NULL, "AHardwareBuffer_allocate"); + s_AHardwareBuffer_release = (AHardwareBuffer_release_func)dlsym(NULL, "AHardwareBuffer_release"); + s_AHardwareBuffer_describe = (AHardwareBuffer_describe_func)dlsym(NULL, "AHardwareBuffer_describe"); + s_AHardwareBuffer_lock = (AHardwareBuffer_lock_func)dlsym(NULL, "AHardwareBuffer_lock"); + s_AHardwareBuffer_unlock = (AHardwareBuffer_unlock_func)dlsym(NULL, "AHardwareBuffer_unlock"); + + s_eglGetNativeClientBufferANDROID = (eglGetNativeClientBufferANDROID_func)dlsym(NULL, "eglGetNativeClientBufferANDROID"); + + MCLog("MCGLHardwareBuffer - load AHardwareBuffer functions: %p %p %p %p %p", s_AHardwareBuffer_allocate, s_AHardwareBuffer_release, s_AHardwareBuffer_describe, s_AHardwareBuffer_lock, s_AHardwareBuffer_unlock); + MCLog("MCGLHardwareBuffer - load egl functions: %p", s_eglGetNativeClientBufferANDROID); + } + + return s_AHardwareBuffer_allocate != nil && + s_AHardwareBuffer_release != nil && + s_AHardwareBuffer_describe != nil && + s_AHardwareBuffer_lock != nil && + s_AHardwareBuffer_unlock != nil && + s_eglGetNativeClientBufferANDROID != nil; +} + +static bool MCAndroidHardwareBufferAllocate(uindex_t p_width, uindex_t p_height, AHardwareBuffer* &r_buffer, uint32_t &r_stride) +{ + AHardwareBuffer_Desc t_desc; + t_desc.format = AHARDWAREBUFFER_FORMAT_R8G8B8A8_UNORM; + t_desc.height = p_width; + t_desc.width = p_height; + t_desc.layers = 1; + t_desc.rfu0 = 0; + t_desc.rfu1 = 0; + t_desc.stride = 0; + t_desc.usage = + AHARDWAREBUFFER_USAGE_CPU_READ_NEVER | + AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN | + AHARDWAREBUFFER_USAGE_GPU_SAMPLED_IMAGE; + + AHardwareBuffer *t_hw_buffer = nil; + if (0 != s_AHardwareBuffer_allocate(&t_desc, &t_hw_buffer)) + return false; + + s_AHardwareBuffer_describe(t_hw_buffer, &t_desc); + + r_buffer = t_hw_buffer; + r_stride = t_desc.stride * sizeof(uint32_t); + + return true; +} + +static bool MCAndroidHardwareBufferBindToEGLImage(MCGLHardwareBufferRef p_buffer) +{ + if (p_buffer == nil) + return false; + + if (p_buffer->egl_image != EGL_NO_IMAGE_KHR) + return true; + + bool t_success = true; + + EGLClientBuffer t_client_buffer = nil; + if (t_success) + { + t_client_buffer = s_eglGetNativeClientBufferANDROID(p_buffer->handle); + t_success = t_client_buffer != nil; + } + + EGLDisplay t_display = EGL_NO_DISPLAY; + if (t_success) + { + t_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + t_success = t_display != EGL_NO_DISPLAY; + } + + EGLImageKHR t_image = EGL_NO_IMAGE_KHR; + if (t_success) + { + EGLint t_image_attribs[] = { + EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, + EGL_NONE, + }; + + t_image = eglCreateImageKHR(t_display, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID, t_client_buffer, t_image_attribs); + t_success = t_image != EGL_NO_IMAGE_KHR; + } + + if (t_success) + p_buffer->egl_image = t_image; + + return t_success; +} + +static void MCAndroidHardwareBufferUnbindEGLImage(MCGLHardwareBufferRef p_buffer) +{ + if (p_buffer == nil) + return; + + if (p_buffer->egl_image == EGL_NO_IMAGE_KHR) + return; + + EGLDisplay t_display; + t_display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + + eglDestroyImageKHR(t_display, p_buffer->egl_image); + p_buffer->egl_image = EGL_NO_IMAGE_KHR; +} + +bool MCGLHardwareBufferIsSupported(void) +{ + return MCAndroidHardwareBufferInitialize(); +} + +bool MCGLHardwareBufferCreate(uindex_t p_width, uindex_t p_height, MCGLHardwareBufferRef &r_buffer) +{ + MCLog("MCGLHardwareBufferCreate"); + if (!MCAndroidHardwareBufferInitialize()) + return false; + + bool t_success = true; + + MCGLHardwareBufferRef t_buffer = nil; + if (t_success) + t_success = MCMemoryNew(t_buffer); + + if (t_success) + t_success = MCAndroidHardwareBufferAllocate(p_width, p_height, t_buffer->handle, t_buffer->stride); + + if (t_success) + t_success = MCAndroidHardwareBufferBindToEGLImage(t_buffer); + + if (t_success) + { + MCLog(" - created <%p>", t_buffer); + r_buffer = t_buffer; + return true; + } + + if (t_buffer != nil) + MCGLHardwareBufferDestroy(t_buffer); + + return false; +} + +void MCGLHardwareBufferDestroy(MCGLHardwareBufferRef p_buffer) +{ + MCLog("MCGLHardwareBufferDestroy <%p>", p_buffer); + if (p_buffer == nil) + return; + + MCAndroidHardwareBufferUnbindEGLImage(p_buffer); + + if (p_buffer->handle != nil) + s_AHardwareBuffer_release(p_buffer->handle); + + MCMemoryDelete(p_buffer); +} + +bool MCGLHardwareBufferBindToGLTexture(MCGLHardwareBufferRef p_buffer, GLuint p_texture) +{ + MCLog("MCGLHardwareBufferBindToGLTexture"); + if (!MCAndroidHardwareBufferBindToEGLImage(p_buffer)) + return false; + + glBindTexture(GL_TEXTURE_EXTERNAL_OES, p_texture); + + glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, p_buffer->egl_image); + + return MCGLCheckError("Binding hardware buffer to GL texture"); +} + +bool MCGLHardwareBufferGetStride(MCGLHardwareBufferRef p_buffer, uindex_t &r_stride) +{ + MCLog("MCGLHardwareBufferGetStride"); + if (p_buffer == nil) + return false; + + r_stride = p_buffer->stride; + return true; +} + +bool MCGLHardwareBufferLock(MCGLHardwareBufferRef p_buffer, void *&r_ptr) +{ + MCLog("MCGLHardwareBufferLock <%p>", p_buffer); + if (p_buffer == nil) + return false; + + if (p_buffer->lock == nil) + { + if (0 != s_AHardwareBuffer_lock(p_buffer->handle, AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN, -1, nil, &p_buffer->lock)) + return false; + } + + r_ptr = p_buffer->lock; + return true; +} + +void MCGLHardwareBufferUnlock(MCGLHardwareBufferRef p_buffer) +{ + MCLog("MCGLHardwareBufferUnlock <%p>", p_buffer); + if (p_buffer == nil || p_buffer->lock == nil) + return; + + int t_error; + t_error = s_AHardwareBuffer_unlock(p_buffer->handle, nil); + if (t_error != 0) + MCLog("error unlocking hardwarebuffer: %d", t_error); + p_buffer->lock = nil; +} + +#else // defined(MCANDROIDUSEHARDWAREBUFFER) + +#include +#include +#include "glhardwarebuffer.h" + +bool MCGLHardwareBufferIsSupported(void) +{ + return false; +} + +bool MCGLHardwareBufferCreate(uindex_t p_width, uindex_t p_height, MCGLHardwareBufferRef &r_buffer) +{ + return false; +} + +void MCGLHardwareBufferDestroy(MCGLHardwareBufferRef p_buffer) +{ +} + +bool MCGLHardwareBufferBindToGLTexture(MCGLHardwareBufferRef p_buffer, GLuint p_texture) +{ + return false; +} + +bool MCGLHardwareBufferGetStride(MCGLHardwareBufferRef p_buffer, uindex_t &r_stride) +{ + return false; +} + +bool MCGLHardwareBufferLock(MCGLHardwareBufferRef p_buffer, void *&r_ptr) +{ + return false; +} + +void MCGLHardwareBufferUnlock(MCGLHardwareBufferRef p_buffer) +{ +} + +#endif // defined(MCANDROIDUSEHARDWAREBUFFER) diff --git a/engine/src/glhardwarebuffer.h b/engine/src/glhardwarebuffer.h new file mode 100644 index 00000000000..f74147777a8 --- /dev/null +++ b/engine/src/glhardwarebuffer.h @@ -0,0 +1,28 @@ +/* Copyright (C) 2020 LiveCode Ltd. + +This file is part of LiveCode. + +LiveCode is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License v3 as published by the Free +Software Foundation. + +LiveCode is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with LiveCode. If not see . */ + +typedef struct __MCGLHardwareBuffer *MCGLHardwareBufferRef; + +bool MCGLHardwareBufferIsSupported(void); + +bool MCGLHardwareBufferCreate(uindex_t p_width, uindex_t p_height, MCGLHardwareBufferRef &r_buffer); +void MCGLHardwareBufferDestroy(MCGLHardwareBufferRef p_buffer); +bool MCGLHardwareBufferGetStride(MCGLHardwareBufferRef p_buffer, uindex_t &r_stride); + +bool MCGLHardwareBufferLock(MCGLHardwareBufferRef p_buffer, void *&r_ptr); +void MCGLHardwareBufferUnlock(MCGLHardwareBufferRef p_buffer); + +bool MCGLHardwareBufferBindToGLTexture(MCGLHardwareBufferRef p_buffer, GLuint p_texture); diff --git a/engine/src/tilecachegl3.x.cpp b/engine/src/tilecachegl3.x.cpp index dddc2057bc4..89229aa51cf 100644 --- a/engine/src/tilecachegl3.x.cpp +++ b/engine/src/tilecachegl3.x.cpp @@ -37,16 +37,27 @@ along with LiveCode. If not see . */ #elif defined(TARGET_SUBPLATFORM_ANDROID) +/* TODO - Don't attempt to build on 32bit armv7 - needs ndk update */ +#if !defined(__arm__) +#define USE_MCGLHARDWAREBUFFER +#endif + #define GL_GLEXT_PROTOTYPES +#define EGL_EGLEXT_PROTOTYPES #include #include + #include +// required for GL_TEXTURE_EXTERNAL_OES definition +#include + #else #error tilecachegl3.x.cpp not supported on this platform #endif #include "glcontext.h" +#include "glhardwarebuffer.h" //////////////////////////////////////////////////////////////////////////////// @@ -63,6 +74,11 @@ struct vertex_buffer struct super_tile { GLuint texture; + GLenum texture_target; + MCGLProgramType texture_program_type; +#if defined(USE_MCGLHARDWAREBUFFER) + MCGLHardwareBufferRef hw_buffer; +#endif vertex_buffer vertex_buffer; uint32_t free_count; uint8_t free_list[1]; @@ -125,6 +141,15 @@ struct MCTileCacheOpenGLCompositorContext GLint origin_x, origin_y; }; +//////////////////////////////////////////////////////////////////////////////// + +bool MCTileCacheOpenGLCompositorHardwareBufferIsSupported(MCTileCacheOpenGLCompositorContext *self); +bool MCTileCacheOpenGLCompositorInitHardwareBuffer(MCTileCacheOpenGLCompositorContext *self, uindex_t p_size, super_tile *p_super_tile); +bool MCTileCacheOpenGLCompositorUpdateHardwareBuffer(super_tile *p_super_tile, int32_t p_tile_size, int32_t p_size, const void *p_bits, uint32_t p_stride, GLint p_x, GLint p_y); +void MCTileCacheOpenGLCompositorUnlockHardwareBuffer(super_tile *p_super_tile); +void MCTileCacheOpenGLCompositorFreeHardwareBuffer(super_tile *p_super_tile); + + //////////////////////////////////////////////////////////////////////////////// static inline void MCTileCacheOpenGLCompositorDecodeTile(MCTileCacheOpenGLCompositorContext *self, uintptr_t p_tile, uint32_t& r_super_tile, uint32_t& r_sub_tile) @@ -181,18 +206,28 @@ static bool MCTileCacheOpenGLCompositorCreateTile(MCTileCacheOpenGLCompositorCon if (!MCMemoryAllocate(offsetof(super_tile, free_list) + self -> super_tile_arity, t_super_tile)) return false; - - // Generate a texture id, and bind an empty texture. + // Generate a texture id. glGenTextures(1, &t_super_tile -> texture); - glBindTexture(GL_TEXTURE_2D, t_super_tile -> texture); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - - // IM_2013-08-21: [[ RefactorGraphics ]] set iOS pixel format to RGBA - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kSuperTileSize, kSuperTileSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); - + + if (!MCTileCacheOpenGLCompositorInitHardwareBuffer(self, kSuperTileSize, t_super_tile)) + { + // Fall back to super_tile based on standard GL texture + MCLog("failed to allocate hardware buffer - will fallback to standard GL texture"); + + // Bind an empty texture. + glBindTexture(GL_TEXTURE_2D, t_super_tile -> texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // IM_2013-08-21: [[ RefactorGraphics ]] set iOS pixel format to RGBA + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, kSuperTileSize, kSuperTileSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, nil); + + t_super_tile->texture_target = GL_TEXTURE_2D; + t_super_tile->texture_program_type = kMCGLProgramTypeTexture; + } + // Fill in the free list - subtile 0 is the one we will allocate. for(uint32_t i = 1; i < self -> super_tile_arity; i++) t_super_tile -> free_list[i - 1] = i; @@ -237,12 +272,17 @@ static void MCTileCacheOpenGLCompositorFlushSuperTiles(MCTileCacheOpenGLComposit if (self -> super_tiles[i] == nil) continue; + MCTileCacheOpenGLCompositorUnlockHardwareBuffer(self->super_tiles[i]); + if (p_force || self -> super_tiles[i] -> free_count == self -> super_tile_arity) { // deleting the currently bound texture reverts the binding to zero if (self->current_texture == self->super_tiles[i]->texture) self->current_texture = 0; glDeleteTextures(1, &self -> super_tiles[i] -> texture); + + MCTileCacheOpenGLCompositorFreeHardwareBuffer(self->super_tiles[i]); + MCMemoryDelete(self -> super_tiles[i]); self -> super_tiles[i] = nil; @@ -288,7 +328,7 @@ bool MCTileCacheOpenGLCompositor_EndTiling(void *p_context) { MCTileCacheOpenGLCompositorContext *self; self = (MCTileCacheOpenGLCompositorContext *)p_context; - + // Flush any empty textures. MCTileCacheOpenGLCompositorFlushSuperTiles(self, false); @@ -301,44 +341,57 @@ bool MCTileCacheOpenGLCompositor_AllocateTile(void *p_context, int32_t p_size, c MCTileCacheOpenGLCompositorContext *self; self = (MCTileCacheOpenGLCompositorContext *)p_context; - // If the stride is exactly one tile wide, we don't need a copy. - void *t_data; - t_data = nil; - if (p_stride == p_size * sizeof(uint32_t)) - t_data = (void *)p_bits; - else if (MCMemoryAllocate(p_size * p_size * sizeof(uint32_t), t_data)) - { - // Copy across each scanline of the tile into the buffer. - for(int32_t y = 0; y < p_size; y++) - memcpy((uint8_t *)t_data + y * p_size * sizeof(uint32_t), (uint8_t *)p_bits + p_stride * y, p_size * sizeof(uint32_t)); - } - - void *t_tile; uint32_t t_tile_id; - t_tile = nil; - if (t_data != nil && MCTileCacheOpenGLCompositorCreateTile(self, t_tile_id)) + if (!MCTileCacheOpenGLCompositorCreateTile(self, t_tile_id)) + return false; + + // Fetch the super/sub indices of the tile. + uint32_t t_super_tile_index, t_sub_tile_index; + MCTileCacheOpenGLCompositorDecodeTile(self, t_tile_id, t_super_tile_index, t_sub_tile_index); + + uint32_t t_tile_count = kSuperTileSize / self->tile_size; + + // Calculate its location. + GLint t_x, t_y; + t_x = t_sub_tile_index % t_tile_count; + t_y = t_sub_tile_index / t_tile_count; + + super_tile *t_super_tile; + t_super_tile = self->super_tiles[t_super_tile_index]; + + if (!MCTileCacheOpenGLCompositorUpdateHardwareBuffer(t_super_tile, self->tile_size, p_size, p_bits, p_stride, t_x, t_y)) { - // Fetch the super/sub indices of the tile. - uint32_t t_super_tile_index, t_sub_tile_index; - MCTileCacheOpenGLCompositorDecodeTile(self, t_tile_id, t_super_tile_index, t_sub_tile_index); - + // If the stride is exactly one tile wide, we don't need a copy. + void *t_data; + t_data = nil; + if (p_stride == p_size * sizeof(uint32_t)) + t_data = (void *)p_bits; + else + { + if (!MCMemoryAllocate(p_size * p_size * sizeof(uint32_t), t_data)) + { + // failed to allocate memory - destroy tile & return + MCTileCacheOpenGLCompositorDestroyTile(self, t_tile_id); + return false; + } + + // Copy across each scanline of the tile into the buffer. + for(int32_t y = 0; y < p_size; y++) + memcpy((uint8_t *)t_data + y * p_size * sizeof(uint32_t), (uint8_t *)p_bits + p_stride * y, p_size * sizeof(uint32_t)); + } + // Now fetch the texture and bind it if necessary. GLuint t_texture; t_texture = self -> super_tiles[t_super_tile_index] -> texture; if (t_texture != self -> current_texture) { - glBindTexture(GL_TEXTURE_2D, t_texture); + glBindTexture(t_super_tile->texture_target, t_texture); self -> current_texture = t_texture; } - // Calculate its location. - GLint t_x, t_y; - t_x = t_sub_tile_index % (kSuperTileSize / self -> tile_size); - t_y = t_sub_tile_index / (kSuperTileSize / self -> tile_size); - // Fill the texture. // IM_2013-08-21: [[ RefactorGraphics ]] set iOS pixel format to RGBA - glTexSubImage2D(GL_TEXTURE_2D, 0, t_x * self -> tile_size, t_y * self -> tile_size, self -> tile_size, self -> tile_size, GL_RGBA, GL_UNSIGNED_BYTE, t_data); + glTexSubImage2D(t_super_tile->texture_target, 0, t_x * self -> tile_size, t_y * self -> tile_size, self -> tile_size, self -> tile_size, GL_RGBA, GL_UNSIGNED_BYTE, t_data); // SN-2015-04-13: [[ Bug 14879 ]] This function seems to fail sometimes, // and we want to get the error here, not in // MCTileCacheOpenGLCompositorFlushSuperTiles as it happens in the @@ -347,17 +400,12 @@ bool MCTileCacheOpenGLCompositor_AllocateTile(void *p_context, int32_t p_size, c if (t_error != GL_NO_ERROR) MCLog("glTextSubImage2D(x,x,%d,%d,%d,%d,...) returned error 0x%X", t_x * self -> tile_size, t_y * self -> tile_size, self -> tile_size, self -> tile_size, t_error); - // Set the tile id. - t_tile = (void *)t_tile_id; + if (t_data != p_bits) + MCMemoryDeallocate(t_data); } - if (t_data != p_bits) - MCMemoryDeallocate(t_data); - - if (t_tile == nil) - return false; - - r_tile = t_tile; + // Set the tile id. + r_tile = (void *)t_tile_id; return true; } @@ -377,14 +425,14 @@ static bool MCTileCacheOpenGLCompositorFlushTextureVertexBuffer(MCTileCacheOpenG if (p_super_tile->texture != self->current_texture) { - glBindTexture(GL_TEXTURE_2D, p_super_tile->texture); + glBindTexture(p_super_tile->texture_target, p_super_tile->texture); self -> current_texture = p_super_tile->texture; } if (self->is_filling) { self->is_filling = false; - MCGLContextSelectProgram(self->gl_context, kMCGLProgramTypeTexture); + MCGLContextSelectProgram(self->gl_context, p_super_tile->texture_program_type); } glBufferData(GL_ARRAY_BUFFER, sizeof(MCGLTextureVertex) * p_super_tile->vertex_buffer.count, p_super_tile->vertex_buffer.vertices, GL_STREAM_DRAW); @@ -480,6 +528,13 @@ static void MCTileCacheOpenGLCompositor_PrepareFrame(MCTileCacheOpenGLCompositor MCGLContextSetWorldTransform(self->gl_context, t_world_transform); MCGLContextSetTextureTransform(self->gl_context, t_texture_transform); + if (MCTileCacheOpenGLCompositorHardwareBufferIsSupported(self)) + { + MCGLContextSelectProgram(self->gl_context, kMCGLProgramTypeExternalTexture); + MCGLContextSetWorldTransform(self->gl_context, t_world_transform); + MCGLContextSetTextureTransform(self->gl_context, t_texture_transform); + } + // Initialize the blend function we would use. glDisable(GL_BLEND); glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); @@ -755,3 +810,127 @@ bool MCTileCacheOpenGLCompositorConfigure(MCTileCacheRef p_tilecache, MCTileCach } //////////////////////////////////////////////////////////////////////////////// + +#if defined(USE_MCGLHARDWAREBUFFER) + +bool MCTileCacheOpenGLCompositorHardwareBufferIsSupported(MCTileCacheOpenGLCompositorContext *self) +{ + return MCGLHardwareBufferIsSupported() && + MCGLContextGetProgramSupported(self->gl_context, kMCGLProgramTypeExternalTexture); +} + +bool MCTileCacheOpenGLCompositorInitHardwareBuffer(MCTileCacheOpenGLCompositorContext *self, uindex_t p_size, super_tile *x_super_tile) +{ + bool t_success = true; + + bool t_hw_buffer_support = MCGLHardwareBufferIsSupported(); + bool t_external_texture_support = MCGLContextGetProgramSupported(self->gl_context, kMCGLProgramTypeExternalTexture); + MCLog("have hardware buffer: %s, have external textures: %s", t_hw_buffer_support?"true":"false", t_external_texture_support?"true":"false"); + + t_success = t_hw_buffer_support && t_external_texture_support; + + MCGLHardwareBufferRef t_hw_buffer = nil; + if (t_success) + { + // create hardware buffer + t_success = MCGLHardwareBufferCreate(kSuperTileSize, kSuperTileSize, t_hw_buffer); + } + + if (t_success) + { + // Bind buffer to GL texture + t_success = MCGLHardwareBufferBindToGLTexture(t_hw_buffer, x_super_tile->texture); + } + + if (t_success) + { + x_super_tile->hw_buffer = t_hw_buffer; + x_super_tile->texture_target = GL_TEXTURE_EXTERNAL_OES; + x_super_tile->texture_program_type = kMCGLProgramTypeExternalTexture; + } + else + { + if (t_hw_buffer != nil) + MCGLHardwareBufferDestroy(t_hw_buffer); + + x_super_tile->hw_buffer = nil; + } + + return t_success; +} + +bool MCTileCacheOpenGLCompositorUpdateHardwareBuffer(super_tile *p_super_tile, int32_t p_tile_size, int32_t p_size, const void *p_bits, uint32_t p_stride, GLint p_x, GLint p_y) +{ + if (p_super_tile == nil || p_super_tile->hw_buffer == nil) + return false; + + // Attempt to lock the buffer - fail if lock fails + void *t_buff_ptr = nil; + if (!MCGLHardwareBufferLock(p_super_tile->hw_buffer, t_buff_ptr)) + { + MCLog("failed to lock hardware buffer"); + return false; + } + + uindex_t t_stride; + if (!MCGLHardwareBufferGetStride(p_super_tile->hw_buffer, t_stride)) + { + MCLog("failed to get hardware buffer stride"); + return false; + } + + // Adjust buffer ptr to top-left of target rect + t_buff_ptr = (uint8_t*)t_buff_ptr + p_tile_size * p_y * t_stride + p_tile_size * p_x * sizeof(uint32_t); + + // Copy pixel data to hardware buffer + for (int32_t y = 0; y < p_size; y++) + memcpy((uint8_t*)t_buff_ptr + y * t_stride, (uint8_t*)p_bits + y * p_stride, p_size * sizeof(uint32_t)); + + return true; +} + +void MCTileCacheOpenGLCompositorUnlockHardwareBuffer(super_tile *p_super_tile) +{ + if (p_super_tile == nil || p_super_tile->hw_buffer == nil) + return; + + MCGLHardwareBufferUnlock(p_super_tile->hw_buffer); +} + +void MCTileCacheOpenGLCompositorFreeHardwareBuffer(super_tile *p_super_tile) +{ + if (p_super_tile == nil || p_super_tile->hw_buffer == nil) + return; + + MCGLHardwareBufferDestroy(p_super_tile->hw_buffer); + p_super_tile->hw_buffer = nil; +} + +#else + +bool MCTileCacheOpenGLCompositorHardwareBufferIsSupported(MCTileCacheOpenGLCompositorContext *self) +{ + return false; +} + +bool MCTileCacheOpenGLCompositorInitHardwareBuffer(MCTileCacheOpenGLCompositorContext *self, uindex_t p_size, super_tile *x_super_tile) +{ + return false; +} + +bool MCTileCacheOpenGLCompositorUpdateHardwareBuffer(super_tile *p_super_tile, int32_t p_tile_size, int32_t p_size, const void *p_bits, uint32_t p_stride, GLint p_x, GLint p_y) +{ + return false; +} + +void MCTileCacheOpenGLCompositorUnlockHardwareBuffer(super_tile *p_super_tile) +{ +} + +void MCTileCacheOpenGLCompositorFreeHardwareBuffer(super_tile *p_super_tile) +{ +} + +#endif + +////////////////////////////////////////////////////////////////////////////////