Skip to content

Commit

Permalink
Add external texture support (GLES3)
Browse files Browse the repository at this point in the history
Co-authored-by: Fredia Huya-Kouadio <[email protected]>
Co-authored-by: Mauricio Narvaez <[email protected]>
  • Loading branch information
3 people committed Sep 18, 2024
1 parent 20c6a48 commit 6ed9781
Show file tree
Hide file tree
Showing 24 changed files with 354 additions and 5 deletions.
36 changes: 36 additions & 0 deletions doc/classes/ExternalTexture.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8" ?>
<class name="ExternalTexture" inherits="Texture2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd">
<brief_description>
Texture which displays the content of an external buffer.
</brief_description>
<description>
Displays the content of an external buffer provided by the platform.
Requires the [url=https://registry.khronos.org/OpenGL/extensions/OES/OES_EGL_image_external.txt]OES_EGL_image_external[/url] extension (OpenGL) or [url=https://registry.khronos.org/vulkan/specs/1.1-extensions/html/vkspec.html#VK_ANDROID_external_memory_android_hardware_buffer]VK_ANDROID_external_memory_android_hardware_buffer[/url] extension (Vulkan).
[b]Note:[/b] This is currently only supported in Android builds.
</description>
<tutorials>
</tutorials>
<methods>
<method name="get_external_texture_id" qualifiers="const">
<return type="int" />
<description>
Returns the external texture ID.
Depending on your use case, you may need to pass this to platform APIs, for example, when creating an [code]android.graphics.SurfaceTexture[/code] on Android.
</description>
</method>
<method name="set_external_buffer_id">
<return type="void" />
<param index="0" name="external_buffer_id" type="int" />
<description>
Sets the external buffer ID.
Depending on your use case, you may need to call this with data received from a platform API, for example, [code]SurfaceTexture.getHardwareBuffer()[/code] on Android.
</description>
</method>
</methods>
<members>
<member name="resource_local_to_scene" type="bool" setter="set_local_to_scene" getter="is_local_to_scene" overrides="Resource" default="false" />
<member name="size" type="Vector2" setter="set_size" getter="get_size" default="Vector2(256, 256)">
External texture size.
</member>
</members>
</class>
5 changes: 4 additions & 1 deletion doc/classes/RenderingServer.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5660,7 +5660,10 @@
<constant name="GLOBAL_VAR_TYPE_SAMPLERCUBE" value="27" enum="GlobalShaderParameterType">
Cubemap sampler global shader parameter ([code]global uniform samplerCube ...[/code]). Exposed as a [Cubemap] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAX" value="28" enum="GlobalShaderParameterType">
<constant name="GLOBAL_VAR_TYPE_SAMPLEREXT" value="28" enum="GlobalShaderParameterType">
External sampler global shader parameter ([code]global uniform samplerExternalOES ...[/code]). Exposed as a [ExternalTexture] in the editor UI.
</constant>
<constant name="GLOBAL_VAR_TYPE_MAX" value="29" enum="GlobalShaderParameterType">
Represents the size of the [enum GlobalShaderParameterType] enum.
</constant>
<constant name="RENDERING_INFO_TOTAL_OBJECTS_IN_FRAME" value="0" enum="RenderingInfo">
Expand Down
8 changes: 8 additions & 0 deletions drivers/gles3/shader_gles3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,14 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant
}
builder.append("\n"); //make sure defines begin at newline

// Optional support for external textures.
if (GLES3::Config::get_singleton()->external_texture_supported) {
builder.append("#extension GL_OES_EGL_image_external : enable\n");
builder.append("#extension GL_OES_EGL_image_external_essl3 : enable\n");
} else {
builder.append("#define samplerExternalOES sampler2D\n");
}

// Insert multiview extension loading, because it needs to appear before
// any non-preprocessor code (like the "precision highp..." lines below).
builder.append("#ifdef USE_MULTIVIEW\n");
Expand Down
8 changes: 8 additions & 0 deletions drivers/gles3/storage/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ Config::Config() {
// These are GLES only
rt_msaa_supported = extensions.has("GL_EXT_multisampled_render_to_texture");
rt_msaa_multiview_supported = extensions.has("GL_OVR_multiview_multisampled_render_to_texture");
external_texture_supported = extensions.has("GL_OES_EGL_image_external_essl3");

if (multiview_supported) {
eglFramebufferTextureMultiviewOVR = (PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)eglGetProcAddress("glFramebufferTextureMultiviewOVR");
Expand Down Expand Up @@ -166,6 +167,13 @@ Config::Config() {
rt_msaa_multiview_supported = false;
}
}

if (external_texture_supported) {
eglEGLImageTargetTexture2DOES = (PFNEGLIMAGETARGETTEXTURE2DOESPROC)eglGetProcAddress("glEGLImageTargetTexture2DOES");
if (eglEGLImageTargetTexture2DOES == nullptr) {
external_texture_supported = false;
}
}
#endif

force_vertex_shading = false; //GLOBAL_GET("rendering/quality/shading/force_vertex_shading");
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/storage/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint,
typedef void (*PFNGLTEXSTORAGE3DMULTISAMPLEPROC)(GLenum, GLsizei, GLenum, GLsizei, GLsizei, GLsizei, GLboolean);
typedef void (*PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC)(GLenum, GLenum, GLenum, GLuint, GLint, GLsizei);
typedef void (*PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC)(GLenum, GLenum, GLuint, GLint, GLsizei, GLint, GLsizei);
typedef void (*PFNEGLIMAGETARGETTEXTURE2DOESPROC)(GLenum, void *);
#endif

namespace GLES3 {
Expand Down Expand Up @@ -91,6 +92,7 @@ class Config {
bool rt_msaa_supported = false;
bool rt_msaa_multiview_supported = false;
bool multiview_supported = false;
bool external_texture_supported = false;

// Adreno 3XX compatibility
bool disable_particles_workaround = false; // set to 'true' to disable 'GPUParticles'
Expand All @@ -104,6 +106,7 @@ class Config {
PFNGLTEXSTORAGE3DMULTISAMPLEPROC eglTexStorage3DMultisample = nullptr;
PFNGLFRAMEBUFFERTEXTURE2DMULTISAMPLEEXTPROC eglFramebufferTexture2DMultisampleEXT = nullptr;
PFNGLFRAMEBUFFERTEXTUREMULTISAMPLEMULTIVIEWOVRPROC eglFramebufferTextureMultisampleMultiviewOVR = nullptr;
PFNEGLIMAGETARGETTEXTURE2DOESPROC eglEGLImageTargetTexture2DOES = nullptr;
#endif

static Config *get_singleton() { return singleton; };
Expand Down
11 changes: 10 additions & 1 deletion drivers/gles3/storage/material_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -675,6 +675,7 @@ static const GLenum target_from_type[ShaderLanguage::TYPE_MAX] = {
GL_TEXTURE_3D, // TYPE_USAMPLER3D,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBE,
GL_TEXTURE_CUBE_MAP, // TYPE_SAMPLERCUBEARRAY,
_GL_TEXTURE_EXTERNAL_OES, // TYPE_SAMPLEREXT
GL_TEXTURE_2D, // TYPE_STRUCT
};

Expand Down Expand Up @@ -946,6 +947,9 @@ void MaterialData::update_textures(const HashMap<StringName, Variant> &p_paramet
case ShaderLanguage::TYPE_SAMPLERCUBEARRAY: {
ERR_PRINT_ONCE("Type: SamplerCubeArray not supported in GL Compatibility rendering backend, please use another type.");
} break;
case ShaderLanguage::TYPE_SAMPLEREXT: {
gl_texture = texture_storage->texture_gl_get_default(DEFAULT_GL_TEXTURE_EXT);
} break;

case ShaderLanguage::TYPE_ISAMPLER3D:
case ShaderLanguage::TYPE_USAMPLER3D:
Expand Down Expand Up @@ -1949,6 +1953,7 @@ void MaterialStorage::global_shader_parameters_load_settings(bool p_load_texture
"sampler2DArray",
"sampler3D",
"samplerCube",
"samplerExternalOES"
};

RS::GlobalShaderParameterType gvtype = RS::GLOBAL_VAR_TYPE_MAX;
Expand Down Expand Up @@ -2661,7 +2666,11 @@ static void bind_uniforms_generic(const Vector<RID> &p_textures, const Vector<Sh
const ShaderCompiler::GeneratedCode::Texture &texture_uniform = texture_uniforms[texture_uniform_index];
if (texture) {
glActiveTexture(GL_TEXTURE0 + texture_offset + ti);
glBindTexture(target_from_type[texture_uniform.type], texture->tex_id);
GLenum target = target_from_type[texture_uniform.type];
if (target == _GL_TEXTURE_EXTERNAL_OES && !GLES3::Config::get_singleton()->external_texture_supported) {
target = GL_TEXTURE_2D;
}
glBindTexture(target, texture->tex_id);
if (texture->render_target) {
texture->render_target->used_in_frame = true;
}
Expand Down
63 changes: 63 additions & 0 deletions drivers/gles3/storage/texture_storage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,11 @@ TextureStorage::TextureStorage() {
texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_ANISO], image);
}

{
default_gl_textures[DEFAULT_GL_TEXTURE_EXT] = texture_allocate();
texture_external_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_EXT], 1, 1, 0);
}

{
unsigned char pixel_data[4 * 4 * 4];
for (int i = 0; i < 16; i++) {
Expand Down Expand Up @@ -769,6 +774,48 @@ void TextureStorage::texture_2d_initialize(RID p_texture, const Ref<Image> &p_im
texture_set_data(p_texture, p_image);
}

void TextureStorage::texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) {
Texture texture;
texture.active = true;
texture.alloc_width = texture.width = p_width;
texture.alloc_height = texture.height = p_height;
texture.real_format = texture.format = Image::FORMAT_RGB8;
texture.type = Texture::TYPE_2D;

if (GLES3::Config::get_singleton()->external_texture_supported) {
texture.target = _GL_TEXTURE_EXTERNAL_OES;
} else {
texture.target = GL_TEXTURE_2D;
}

glGenTextures(1, &texture.tex_id);
glBindTexture(texture.target, texture.tex_id);

#ifdef ANDROID_ENABLED
if (texture.target == _GL_TEXTURE_EXTERNAL_OES) {
if (p_external_buffer) {
GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer));
}
texture.total_data_size = 0;
} else
#endif
{
// If external textures aren't supported, allocate an empty 1x1 texture.
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
texture.total_data_size = 3;
}

glTexParameteri(texture.target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texture.target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(texture.target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

GLES3::Utilities::get_singleton()->texture_allocated_data(texture.tex_id, texture.total_data_size, "Texture External");
texture_owner.initialize_rid(p_texture, texture);

glBindTexture(texture.target, 0);
}

void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) {
ERR_FAIL_COND(p_layers.is_empty());

Expand Down Expand Up @@ -930,6 +977,22 @@ void TextureStorage::texture_3d_update(RID p_texture, const Vector<Ref<Image>> &
GLES3::Utilities::get_singleton()->texture_resize_data(tex->tex_id, tex->total_data_size);
}

void TextureStorage::texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex);

tex->alloc_width = tex->width = p_width;
tex->alloc_height = tex->height = p_height;

#ifdef ANDROID_ENABLED
if (tex->target == _GL_TEXTURE_EXTERNAL_OES && p_external_buffer) {
glBindTexture(_GL_TEXTURE_EXTERNAL_OES, tex->tex_id);
GLES3::Config::get_singleton()->eglEGLImageTargetTexture2DOES(_GL_TEXTURE_EXTERNAL_OES, reinterpret_cast<void *>(p_external_buffer));
glBindTexture(_GL_TEXTURE_EXTERNAL_OES, 0);
}
#endif
}

void TextureStorage::texture_proxy_update(RID p_texture, RID p_proxy_to) {
Texture *tex = texture_owner.get_or_null(p_texture);
ERR_FAIL_NULL(tex);
Expand Down
3 changes: 3 additions & 0 deletions drivers/gles3/storage/texture_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ enum DefaultGLTexture {
DEFAULT_GL_TEXTURE_3D_BLACK,
DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE,
DEFAULT_GL_TEXTURE_2D_UINT,
DEFAULT_GL_TEXTURE_EXT,
DEFAULT_GL_TEXTURE_MAX
};

Expand Down Expand Up @@ -512,12 +513,14 @@ class TextureStorage : public RendererTextureStorage {
virtual void texture_2d_initialize(RID p_texture, const Ref<Image> &p_image) override;
virtual void texture_2d_layered_initialize(RID p_texture, const Vector<Ref<Image>> &p_layers, RS::TextureLayeredType p_layered_type) override;
virtual void texture_3d_initialize(RID p_texture, Image::Format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_initialize(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_initialize(RID p_texture, RID p_base) override; //all slices, then all the mipmaps, must be coherent

virtual RID texture_create_from_native_handle(RS::TextureType p_type, Image::Format p_format, uint64_t p_native_handle, int p_width, int p_height, int p_depth, int p_layers = 1, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY) override;

virtual void texture_2d_update(RID p_texture, const Ref<Image> &p_image, int p_layer = 0) override;
virtual void texture_3d_update(RID p_texture, const Vector<Ref<Image>> &p_data) override;
virtual void texture_external_update(RID p_texture, int p_width, int p_height, uint64_t p_external_buffer) override;
virtual void texture_proxy_update(RID p_proxy, RID p_base) override;

//these two APIs can be used together or in combination with the others.
Expand Down
1 change: 0 additions & 1 deletion editor/renames_map_3_to_4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,6 @@ const char *RenamesMap3To4::class_renames[][2] = {
{ "EditorSceneImporterGLTF", "EditorSceneFormatImporterGLTF" },
{ "EditorSpatialGizmo", "EditorNode3DGizmo" },
{ "EditorSpatialGizmoPlugin", "EditorNode3DGizmoPlugin" },
{ "ExternalTexture", "ImageTexture" },
{ "GIProbe", "VoxelGI" },
{ "GIProbeData", "VoxelGIData" },
{ "Generic6DOFJoint", "Generic6DOFJoint3D" },
Expand Down
2 changes: 2 additions & 0 deletions scene/register_scene_types.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@
#include "scene/resources/compressed_texture.h"
#include "scene/resources/curve_texture.h"
#include "scene/resources/environment.h"
#include "scene/resources/external_texture.h"
#include "scene/resources/font.h"
#include "scene/resources/gradient.h"
#include "scene/resources/gradient_texture.h"
Expand Down Expand Up @@ -926,6 +927,7 @@ void register_scene_types() {
GDREGISTER_CLASS(GradientTexture2D);
GDREGISTER_CLASS(AnimatedTexture);
GDREGISTER_CLASS(CameraTexture);
GDREGISTER_CLASS(ExternalTexture);
GDREGISTER_VIRTUAL_CLASS(TextureLayered);
GDREGISTER_ABSTRACT_CLASS(ImageTextureLayered);
GDREGISTER_VIRTUAL_CLASS(Texture3D);
Expand Down
91 changes: 91 additions & 0 deletions scene/resources/external_texture.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/**************************************************************************/
/* external_texture.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "external_texture.h"
#include "drivers/gles3/storage/texture_storage.h"
#include "servers/rendering/rendering_server_globals.h"

void ExternalTexture::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_size", "size"), &ExternalTexture::set_size);
ClassDB::bind_method(D_METHOD("get_external_texture_id"), &ExternalTexture::get_external_texture_id);
ClassDB::bind_method(D_METHOD("set_external_buffer_id", "external_buffer_id"), &ExternalTexture::set_external_buffer_id);

ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size");
}

uint64_t ExternalTexture::get_external_texture_id() const {
return RenderingServer::get_singleton()->texture_get_native_handle(texture);
}

void ExternalTexture::set_size(const Size2 &p_size) {
if (p_size.width > 0 && p_size.height > 0 && p_size != size) {
size = p_size;
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
emit_changed();
}
}

Size2 ExternalTexture::get_size() const {
return size;
}

void ExternalTexture::set_external_buffer_id(uint64_t p_external_buffer) {
if (p_external_buffer != external_buffer) {
external_buffer = p_external_buffer;
RenderingServer::get_singleton()->texture_external_update(texture, size.width, size.height, external_buffer);
}
}

int ExternalTexture::get_width() const {
return size.width;
}

int ExternalTexture::get_height() const {
return size.height;
}

bool ExternalTexture::has_alpha() const {
return false;
}

RID ExternalTexture::get_rid() const {
return texture;
}

ExternalTexture::ExternalTexture() {
texture = RenderingServer::get_singleton()->texture_external_create(size.width, size.height);
}

ExternalTexture::~ExternalTexture() {
if (texture.is_valid()) {
ERR_FAIL_NULL(RenderingServer::get_singleton());
RenderingServer::get_singleton()->free(texture);
}
}
Loading

0 comments on commit 6ed9781

Please sign in to comment.