From ea229f51488bd70732690fd1691f6a6f22f4ab91 Mon Sep 17 00:00:00 2001 From: Alula <6276139+alula@users.noreply.github.com> Date: Mon, 7 Aug 2023 10:41:09 +0200 Subject: [PATCH] OpenGL: Implement 3D Texture support --- drivers/gles3/effects/copy_effects.cpp | 18 + drivers/gles3/effects/copy_effects.h | 1 + drivers/gles3/shader_gles3.cpp | 1 + drivers/gles3/shaders/copy.glsl | 22 ++ drivers/gles3/storage/texture_storage.cpp | 446 ++++++++++++++++++---- drivers/gles3/storage/texture_storage.h | 12 +- 6 files changed, 423 insertions(+), 77 deletions(-) diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 7b350d8da68..29e7de873b6 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -125,6 +125,24 @@ void CopyEffects::copy_to_rect(const Rect2 &p_rect) { draw_screen_quad(); } +void CopyEffects::copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod) { + ERR_FAIL_COND(p_type != Texture::TYPE_LAYERED && p_type != Texture::TYPE_3D); + + CopyShaderGLES3::ShaderVariant variant = p_type == Texture::TYPE_LAYERED + ? CopyShaderGLES3::MODE_COPY_SECTION_2D_ARRAY + : CopyShaderGLES3::MODE_COPY_SECTION_3D; + + bool success = copy.shader.version_bind_shader(copy.shader_version, variant); + if (!success) { + return; + } + + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, variant); + copy.shader.version_set_uniform(CopyShaderGLES3::LAYER, p_layer, copy.shader_version, variant); + copy.shader.version_set_uniform(CopyShaderGLES3::LOD, p_lod, copy.shader_version, variant); + draw_screen_quad(); +} + void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) { bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); if (!success) { diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index 62707ebe993..e65ebbce03f 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -62,6 +62,7 @@ public: // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. void copy_to_rect(const Rect2 &p_rect); + void copy_to_rect_3d(const Rect2 &p_rect, float p_layer, int p_type, float p_lod = 0.0f); void copy_to_and_from_rect(const Rect2 &p_rect); void copy_screen(); void copy_cube_to_rect(const Rect2 &p_rect); diff --git a/drivers/gles3/shader_gles3.cpp b/drivers/gles3/shader_gles3.cpp index 688d32de0b6..551136ce36f 100644 --- a/drivers/gles3/shader_gles3.cpp +++ b/drivers/gles3/shader_gles3.cpp @@ -213,6 +213,7 @@ void ShaderGLES3::_build_variant_code(StringBuilder &builder, uint32_t p_variant builder.append("precision highp sampler2D;\n"); builder.append("precision highp samplerCube;\n"); builder.append("precision highp sampler2DArray;\n"); + builder.append("precision highp sampler3D;\n"); } const StageTemplate &stage_template = stage_templates[p_stage_type]; diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index b5ab15309c8..db63b5d3484 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -4,6 +4,8 @@ mode_default = #define MODE_SIMPLE_COPY mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM +mode_copy_section_3d = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_3D +mode_copy_section_2d_array = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define USE_TEXTURE_2D_ARRAY mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -44,6 +46,11 @@ void main() { in vec2 uv_interp; /* clang-format on */ +#if defined(USE_TEXTURE_3D) || defined(USE_TEXTURE_2D_ARRAY) +uniform float layer; +uniform float lod; +#endif + #ifdef MODE_SIMPLE_COLOR uniform vec4 color_in; #endif @@ -70,7 +77,14 @@ uniform lowp float mip_level; uniform samplerCube source_cube; // texunit:0 #else // ~(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA)) + +#if defined(USE_TEXTURE_3D) +uniform sampler3D source_3d; // texunit:0 +#elif defined(USE_TEXTURE_2D_ARRAY) +uniform sampler2DArray source_2d_array; // texunit:0 +#else uniform sampler2D source; // texunit:0 +#endif #endif // !(defined(CUBE_TO_OCTAHEDRAL) || defined(CUBE_TO_PANORAMA)) @@ -84,7 +98,15 @@ vec3 srgb_to_linear(vec3 color) { void main() { #ifdef MODE_SIMPLE_COPY + +#ifdef USE_TEXTURE_3D + vec4 color = textureLod(source_3d, vec3(uv_interp, layer), lod); +#elif defined(USE_TEXTURE_2D_ARRAY) + vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), lod); +#else vec4 color = texture(source, uv_interp); +#endif + frag_color = color; #endif diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index 5a59f6c7721..bd4793f4dc4 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -77,14 +77,7 @@ TextureStorage::TextureStorage() { default_gl_textures[DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE] = texture_allocate(); texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_2D_ARRAY_WHITE], images, RS::TEXTURE_LAYERED_2D_ARRAY); - for (int i = 0; i < 3; i++) { - images.push_back(image); - } - - default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE] = texture_allocate(); - texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE], image->get_format(), 4, 4, 4, false, images); - - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 5; i++) { images.push_back(image); } @@ -92,6 +85,18 @@ TextureStorage::TextureStorage() { texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_WHITE], images, RS::TEXTURE_LAYERED_CUBEMAP); } + { + Ref image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); + image->fill(Color(1, 1, 1, 1)); + + Vector> images; + for (int i = 0; i < 4; i++) { + images.push_back(image); + } + default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE] = texture_allocate(); + texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_WHITE], image->get_format(), 4, 4, 4, false, images); + } + { // black Ref image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 1)); @@ -101,21 +106,25 @@ TextureStorage::TextureStorage() { texture_2d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_BLACK], image); Vector> images; - - for (int i = 0; i < 4; i++) { - images.push_back(image); - } - - default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK] = texture_allocate(); - texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK], image->get_format(), 4, 4, 4, false, images); - - for (int i = 0; i < 2; i++) { + for (int i = 0; i < 6; i++) { images.push_back(image); } default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_BLACK] = texture_allocate(); texture_2d_layered_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_CUBEMAP_BLACK], images, RS::TEXTURE_LAYERED_CUBEMAP); } + { + Ref image = Image::create_empty(4, 4, false, Image::FORMAT_RGBA8); + image->fill(Color()); + + Vector> images; + for (int i = 0; i < 4; i++) { + images.push_back(image); + } + default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK] = texture_allocate(); + texture_3d_initialize(default_gl_textures[DEFAULT_GL_TEXTURE_3D_BLACK], image->get_format(), 4, 4, 4, false, images); + } + { // transparent black Ref image = Image::create_empty(4, 4, true, Image::FORMAT_RGBA8); image->fill(Color(0, 0, 0, 0)); @@ -812,8 +821,42 @@ void TextureStorage::texture_2d_layered_initialize(RID p_texture, const Vector> &p_data) { - texture_owner.initialize_rid(p_texture, Texture()); +void TextureStorage::texture_3d_initialize(RID p_texture, Image::Format p_format, int p_width, int p_height, int p_depth, bool p_mipmaps, const Vector> &p_data) { + ERR_FAIL_COND(p_data.is_empty()); + + Image::Image3DValidateError verr = Image::validate_3d_image(p_format, p_width, p_height, p_depth, p_mipmaps, p_data); + ERR_FAIL_COND_MSG(verr != Image::VALIDATE_3D_OK, Image::get_3d_image_validation_error_text(verr)); + + Ref image = p_data[0]; + int mipmap_count = 0; + { + Size2i prev_size; + for (int i = 0; i < p_data.size(); i++) { + Size2i img_size(p_data[i]->get_width(), p_data[i]->get_height()); + if (img_size != prev_size) { + mipmap_count++; + } + prev_size = img_size; + } + } + + Texture texture; + texture.width = p_width; + texture.height = p_height; + texture.depth = p_depth; + texture.alloc_width = texture.width; + texture.alloc_height = texture.height; + texture.mipmaps = mipmap_count; + texture.format = image->get_format(); + texture.type = Texture::TYPE_3D; + texture.target = GL_TEXTURE_3D; + _get_gl_image_and_format(Ref(), texture.format, texture.real_format, texture.gl_format_cache, texture.gl_internal_format_cache, texture.gl_type_cache, texture.compressed, false); + texture.total_data_size = p_data[0]->get_image_data_size(texture.width, texture.height, texture.format, texture.mipmaps) * texture.depth; + texture.active = true; + glGenTextures(1, &texture.tex_id); + GLES3::Utilities::get_singleton()->texture_allocated_data(texture.tex_id, texture.total_data_size, "Texture 3D"); + texture_owner.initialize_rid(p_texture, texture); + _texture_set_3d_data(p_texture, p_data, true); } // Called internally when texture_proxy_create(p_base) is called. @@ -872,6 +915,19 @@ void TextureStorage::texture_2d_update(RID p_texture, const Ref &p_image, #endif } +void TextureStorage::texture_3d_update(RID p_texture, const Vector> &p_data) { + Texture *tex = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL(tex); + ERR_FAIL_COND(tex->type != Texture::TYPE_3D); + + Image::Image3DValidateError verr = Image::validate_3d_image(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps > 1, p_data); + ERR_FAIL_COND_MSG(verr != Image::VALIDATE_3D_OK, Image::get_3d_image_validation_error_text(verr)); + + _texture_set_3d_data(p_texture, p_data, false); + + GLES3::Utilities::get_singleton()->texture_resize_data(tex->tex_id, tex->total_data_size); +} + 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); @@ -984,7 +1040,7 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); - ERR_FAIL_COND_V(data.size() == 0, Ref()); + ERR_FAIL_COND_V(data.is_empty(), Ref()); image = Image::create_from_data(texture->width, texture->height, texture->mipmaps > 1, texture->real_format, data); ERR_FAIL_COND_V(image->is_empty(), Ref()); if (texture->format != texture->real_format) { @@ -1040,7 +1096,7 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { data.resize(data_size); - ERR_FAIL_COND_V(data.size() == 0, Ref()); + ERR_FAIL_COND_V(data.is_empty(), Ref()); image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data); ERR_FAIL_COND_V(image->is_empty(), Ref()); @@ -1063,6 +1119,165 @@ Ref TextureStorage::texture_2d_get(RID p_texture) const { return image; } +Ref TextureStorage::texture_2d_layer_get(RID p_texture, int p_layer) const { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(texture, Ref()); + + Vector data; + + int data_size = Image::get_image_data_size(texture->alloc_width, texture->alloc_height, Image::FORMAT_RGBA8, false); + + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + uint8_t *w = data.ptrw(); + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, texture->tex_id); + + glViewport(0, 0, texture->alloc_width, texture->alloc_height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + CopyEffects::get_singleton()->copy_to_rect_3d(Rect2i(0, 0, 1, 1), p_layer, Texture::TYPE_LAYERED); + + glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &w[0]); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &temp_color_texture); + glDeleteFramebuffers(1, &temp_framebuffer); + + data.resize(data_size); + + ERR_FAIL_COND_V(data.is_empty(), Ref()); + Ref image = Image::create_from_data(texture->width, texture->height, false, Image::FORMAT_RGBA8, data); + ERR_FAIL_COND_V(image->is_empty(), Ref()); + + if (texture->format != Image::FORMAT_RGBA8) { + image->convert(texture->format); + } + + if (texture->mipmaps > 1) { + image->generate_mipmaps(); + } + + return image; +} + +Vector> TextureStorage::_texture_3d_read_framebuffer(Texture *p_texture) const { + ERR_FAIL_NULL_V(p_texture, Vector>()); + + Vector> ret; + Vector data; + + int width = p_texture->width; + int height = p_texture->height; + int depth = p_texture->depth; + + for (int mipmap_level = 0; mipmap_level < p_texture->mipmaps; mipmap_level++) { + int data_size = Image::get_image_data_size(width, height, Image::FORMAT_RGBA8, false); + glViewport(0, 0, width, height); + glClearColor(0.0, 0.0, 0.0, 0.0); + glClear(GL_COLOR_BUFFER_BIT); + + for (int layer = 0; layer < depth; layer++) { + data.resize(data_size * 2); //add some memory at the end, just in case for buggy drivers + uint8_t *w = data.ptrw(); + + float layer_f = layer / float(depth); + CopyEffects::get_singleton()->copy_to_rect_3d(Rect2i(0, 0, 1, 1), layer_f, Texture::TYPE_3D, mipmap_level); + glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &w[0]); + + data.resize(data_size); + ERR_FAIL_COND_V(data.is_empty(), Vector>()); + + Ref img = Image::create_from_data(width, height, false, Image::FORMAT_RGBA8, data); + ERR_FAIL_COND_V(img->is_empty(), Vector>()); + + if (p_texture->format != Image::FORMAT_RGBA8) { + img->convert(p_texture->format); + } + + ret.push_back(img); + } + + width = MAX(1, width >> 1); + height = MAX(1, height >> 1); + depth = MAX(1, depth >> 1); + } + + return ret; +} + +Vector> TextureStorage::texture_3d_get(RID p_texture) const { + Texture *texture = texture_owner.get_or_null(p_texture); + ERR_FAIL_NULL_V(texture, Vector>()); + ERR_FAIL_COND_V(texture->type != Texture::TYPE_3D, Vector>()); + +#ifdef TOOLS_ENABLED + if (!texture->image_cache_3d.is_empty() && !texture->is_render_target) { + return texture->image_cache_3d; + } +#endif + + GLuint temp_framebuffer; + glGenFramebuffers(1, &temp_framebuffer); + + GLuint temp_color_texture; + glGenTextures(1, &temp_color_texture); + + glBindFramebuffer(GL_FRAMEBUFFER, temp_framebuffer); + + glBindTexture(GL_TEXTURE_2D, temp_color_texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture->alloc_width, texture->alloc_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, temp_color_texture, 0); + + glDepthMask(GL_FALSE); + glDisable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); + glDepthFunc(GL_LEQUAL); + glColorMask(1, 1, 1, 1); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_3D, texture->tex_id); + + Vector> ret = _texture_3d_read_framebuffer(texture); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glDeleteTextures(1, &temp_color_texture); + glDeleteFramebuffers(1, &temp_framebuffer); + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() && !texture->is_render_target) { + texture->image_cache_3d = ret; + } +#endif + + return ret; +} + void TextureStorage::texture_replace(RID p_texture, RID p_by_texture) { Texture *tex_to = texture_owner.get_or_null(p_texture); ERR_FAIL_NULL(tex_to); @@ -1218,7 +1433,7 @@ void TextureStorage::texture_set_data(RID p_texture, const Ref &p_image, _texture_set_data(p_texture, p_image, p_layer, false); } -void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, int p_layer, bool initialize) { +void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, int p_layer, bool p_initialize) { Texture *texture = texture_owner.get_or_null(p_texture); ERR_FAIL_NULL(texture); @@ -1259,56 +1474,7 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, glActiveTexture(GL_TEXTURE0); glBindTexture(texture->target, texture->tex_id); - -#ifndef WEB_ENABLED - switch (texture->format) { - case Image::FORMAT_L8: { - if (RasterizerGLES3::is_gles_over_gl()) { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); - } else { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); - } - } break; - case Image::FORMAT_LA8: { - if (RasterizerGLES3::is_gles_over_gl()) { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); - } else { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); - } - } break; - case Image::FORMAT_ETC2_RA_AS_RG: - case Image::FORMAT_DXT5_RA_AS_RG: { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - if (texture->format == real_format) { - // Swizzle RA from compressed texture into RG - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_ALPHA); - } else { - // Converted textures are already in RG, leave as-is - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - } - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_ZERO); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); - } break; - default: { - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); - glTexParameteri(texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); - } break; - } -#endif // WEB_ENABLED + _texture_set_swizzle(texture, real_format); int mipmaps = img->has_mipmaps() ? img->get_mipmap_count() + 1 : 1; @@ -1340,7 +1506,7 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, } else { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); if (texture->target == GL_TEXTURE_2D_ARRAY) { - if (initialize) { + if (p_initialize) { glTexImage3D(GL_TEXTURE_2D_ARRAY, i, internal_format, w, h, texture->layers, 0, format, type, nullptr); } glTexSubImage3D(GL_TEXTURE_2D_ARRAY, i, 0, 0, p_layer, w, h, 1, format, type, &read[ofs]); @@ -1362,6 +1528,140 @@ void TextureStorage::_texture_set_data(RID p_texture, const Ref &p_image, texture->mipmaps = mipmaps; } +void TextureStorage::_texture_set_3d_data(RID p_texture, const Vector> &p_data, bool p_initialize) { + Texture *texture = texture_owner.get_or_null(p_texture); + + ERR_FAIL_NULL(texture); + ERR_FAIL_COND(!texture->active); + ERR_FAIL_COND(texture->is_render_target); + ERR_FAIL_COND(texture->target != GL_TEXTURE_3D); + ERR_FAIL_COND(p_data.is_empty()); + + GLenum type; + GLenum format; + GLenum internal_format; + bool compressed = false; + + Image::Format real_format; + Ref img = _get_gl_image_and_format(p_data[0], p_data[0]->get_format(), real_format, format, internal_format, type, compressed, texture->resize_to_po2); + ERR_FAIL_COND(img.is_null()); + + ERR_FAIL_COND_MSG(compressed, "Compressed 3D textures are not supported in the GL Compatibility backend."); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->target, texture->tex_id); + _texture_set_swizzle(texture, texture->real_format); + + // Set filtering and repeat state to default. + if (texture->mipmaps > 1) { + texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST_WITH_MIPMAPS); + } else { + texture->gl_set_filter(RS::CANVAS_ITEM_TEXTURE_FILTER_NEAREST); + } + + texture->gl_set_repeat(RS::CANVAS_ITEM_TEXTURE_REPEAT_ENABLED); + + Vector> images; + images.resize(p_data.size()); + for (int i = 0; i < p_data.size(); i++) { + Ref image = p_data[i]; + if (image->get_format() != texture->format) { + image = image->duplicate(); + image->convert(texture->format); + } + images.write[i] = image; + } + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + int all_data_size = 0; + int mipmap_level = 0; + int layer = 0; + int depth = texture->depth; + Size2i prev_size(images[0]->get_width(), images[0]->get_height()); + for (int i = 0; i < images.size(); i++) { + Ref image = images[i]; + Size2i img_size(image->get_width(), image->get_height()); + + if (img_size != prev_size) { + mipmap_level++; + depth = MAX(1, depth >> 1); + layer = 0; + } + prev_size = img_size; + all_data_size += image->get_data().size(); + + if (layer == 0 && p_initialize) { + glTexImage3D(GL_TEXTURE_3D, mipmap_level, internal_format, img_size.width, img_size.height, depth, 0, format, type, nullptr); + } + + glTexSubImage3D(GL_TEXTURE_3D, mipmap_level, 0, 0, layer, img_size.width, img_size.height, 1, format, type, image->get_data().ptr()); + + layer++; + } + + texture->total_data_size = all_data_size; + texture->mipmaps = mipmap_level + 1; + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint() && !texture->is_render_target) { + texture->image_cache_3d = images; + } +#endif +} + +void TextureStorage::_texture_set_swizzle(Texture *p_texture, Image::Format p_real_format) { +#ifndef WEB_ENABLED + switch (p_texture->format) { + case Image::FORMAT_L8: { + if (RasterizerGLES3::is_gles_over_gl()) { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + } else { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + } + } break; + case Image::FORMAT_LA8: { + if (RasterizerGLES3::is_gles_over_gl()) { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_GREEN); + } else { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + } + } break; + case Image::FORMAT_ETC2_RA_AS_RG: + case Image::FORMAT_DXT5_RA_AS_RG: { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + if (p_texture->format == p_real_format) { + // Swizzle RA from compressed texture into RG. + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_ALPHA); + } else { + // Converted textures are already in RG, leave as-is. + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + } + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_ZERO); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_ONE); + } break; + default: { + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_R, GL_RED); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_G, GL_GREEN); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_B, GL_BLUE); + glTexParameteri(p_texture->target, GL_TEXTURE_SWIZZLE_A, GL_ALPHA); + } break; + } +#endif // WEB_ENABLED +} + Image::Format TextureStorage::texture_get_format(RID p_texture) const { Texture *texture = texture_owner.get_or_null(p_texture); diff --git a/drivers/gles3/storage/texture_storage.h b/drivers/gles3/storage/texture_storage.h index c0c8119dfe5..83f31d122c9 100644 --- a/drivers/gles3/storage/texture_storage.h +++ b/drivers/gles3/storage/texture_storage.h @@ -191,6 +191,7 @@ struct Texture { RenderTarget *render_target = nullptr; Ref image_cache_2d; + Vector> image_cache_3d; bool redraw_if_visible = false; @@ -451,7 +452,10 @@ private: void _render_target_clear_sdf(RenderTarget *rt); Rect2i _render_target_get_sdf_rect(const RenderTarget *rt) const; - void _texture_set_data(RID p_texture, const Ref &p_image, int p_layer, bool initialize); + void _texture_set_data(RID p_texture, const Ref &p_image, int p_layer, bool p_initialize); + void _texture_set_3d_data(RID p_texture, const Vector> &p_data, bool p_initialize); + void _texture_set_swizzle(Texture *p_texture, Image::Format p_real_format); + Vector> _texture_3d_read_framebuffer(Texture *p_texture) const; struct RenderTargetSDF { CanvasSdfShaderGLES3 shader; @@ -511,7 +515,7 @@ public: RID texture_create_external(Texture::Type p_type, Image::Format p_format, unsigned int p_image, int p_width, int p_height, int p_depth, int p_layers, RS::TextureLayeredType p_layered_type = RS::TEXTURE_LAYERED_2D_ARRAY); virtual void texture_2d_update(RID p_texture, const Ref &p_image, int p_layer = 0) override; - virtual void texture_3d_update(RID p_texture, const Vector> &p_data) override{}; + virtual void texture_3d_update(RID p_texture, const Vector> &p_data) 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. @@ -520,8 +524,8 @@ public: virtual void texture_3d_placeholder_initialize(RID p_texture) override; virtual Ref texture_2d_get(RID p_texture) const override; - virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const override { return Ref(); }; - virtual Vector> texture_3d_get(RID p_texture) const override { return Vector>(); }; + virtual Ref texture_2d_layer_get(RID p_texture, int p_layer) const override; + virtual Vector> texture_3d_get(RID p_texture) const override; virtual void texture_replace(RID p_texture, RID p_by_texture) override; virtual void texture_set_size_override(RID p_texture, int p_width, int p_height) override;