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
+
+////////////////////////////////////////////////////////////////////////////////