summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gles3/rasterizer_storage_gles3.cpp125
-rw-r--r--drivers/gles3/shaders/copy.glsl23
2 files changed, 144 insertions, 4 deletions
diff --git a/drivers/gles3/rasterizer_storage_gles3.cpp b/drivers/gles3/rasterizer_storage_gles3.cpp
index 24673c8755..8b3f1a1b77 100644
--- a/drivers/gles3/rasterizer_storage_gles3.cpp
+++ b/drivers/gles3/rasterizer_storage_gles3.cpp
@@ -1056,6 +1056,128 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
return texture->images[p_layer];
}
+ // 3D textures and 2D texture arrays need special treatment, as the glGetTexImage reads **the whole**
+ // texture to host-memory. 3D textures and 2D texture arrays are potentially very big, so reading
+ // everything just to throw everything but one layer away is A Bad Idea.
+ //
+ // Unfortunately, to solve this, the copy shader has to read the data out via a shader and store it
+ // in a temporary framebuffer. The data from the framebuffer can then be read using glReadPixels.
+ if (texture->type == VS::TEXTURE_TYPE_2D_ARRAY || texture->type == VS::TEXTURE_TYPE_3D) {
+ // can't read a layer that doesn't exist
+ ERR_FAIL_INDEX_V(p_layer, texture->alloc_depth, Ref<Image>());
+
+ // get some information about the texture
+ Image::Format real_format;
+ GLenum gl_format;
+ GLenum gl_internal_format;
+ GLenum gl_type;
+
+ bool compressed;
+ bool srgb;
+
+ _get_gl_image_and_format(
+ Ref<Image>(),
+ texture->format,
+ texture->flags,
+ real_format,
+ gl_format,
+ gl_internal_format,
+ gl_type,
+ compressed,
+ srgb);
+
+ PoolVector<uint8_t> data;
+
+ // TODO need to decide between RgbaUnorm and RgbaFloat32 for output
+ 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 more memory at the end, just in case for buggy drivers
+ PoolVector<uint8_t>::Write wb = data.write();
+
+ // generate temporary resources
+ GLuint tmp_fbo;
+ glGenFramebuffers(1, &tmp_fbo);
+
+ GLuint tmp_color_attachment;
+ glGenTextures(1, &tmp_color_attachment);
+
+ // now bring the OpenGL context into the correct state
+ {
+ glBindFramebuffer(GL_FRAMEBUFFER, tmp_fbo);
+
+ // back color attachment with memory, then set properties
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, tmp_color_attachment);
+ // TODO support HDR properly
+ 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);
+
+ // use the color texture as color attachment for this render pass
+ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tmp_color_attachment, 0);
+
+ // more GL state, wheeeey
+ glDepthMask(GL_FALSE);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glDisable(GL_BLEND);
+ glDepthFunc(GL_LEQUAL);
+ glColorMask(1, 1, 1, 1);
+
+ // use volume tex for reading
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(texture->target, texture->tex_id);
+
+ glViewport(0, 0, texture->alloc_width, texture->alloc_height);
+
+ // set up copy shader for proper use
+ shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, !srgb);
+ shaders.copy.set_conditional(CopyShaderGLES3::USE_TEXTURE3D, texture->type == VS::TEXTURE_TYPE_3D);
+ shaders.copy.set_conditional(CopyShaderGLES3::USE_TEXTURE2DARRAY, texture->type == VS::TEXTURE_TYPE_2D_ARRAY);
+ shaders.copy.bind();
+
+ // calculate the normalized z coordinate for the layer
+ float layer = (float)p_layer / (float)texture->alloc_depth;
+
+ shaders.copy.set_uniform(CopyShaderGLES3::LAYER, layer);
+
+ glBindVertexArray(resources.quadie_array);
+ }
+
+ // clear color attachment, then perform copy
+ glClearColor(0.0, 0.0, 0.0, 0.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+
+ // read the image into the host buffer
+ glReadPixels(0, 0, texture->alloc_width, texture->alloc_height, GL_RGBA, GL_UNSIGNED_BYTE, &wb[0]);
+
+ // remove temp resources and unset some GL state
+ {
+ shaders.copy.set_conditional(CopyShaderGLES3::USE_TEXTURE3D, false);
+ shaders.copy.set_conditional(CopyShaderGLES3::USE_TEXTURE2DARRAY, false);
+ shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, false);
+
+ glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
+ glDeleteTextures(1, &tmp_color_attachment);
+ glDeleteFramebuffers(1, &tmp_fbo);
+ }
+
+ wb = PoolVector<uint8_t>::Write();
+
+ data.resize(data_size);
+
+ Image *img = memnew(Image(texture->alloc_width, texture->alloc_height, false, Image::FORMAT_RGBA8, data));
+ if (!texture->compressed) {
+ img->convert(real_format);
+ }
+
+ return Ref<Image>(img);
+ }
+
#ifdef GLES_OVER_GL
Image::Format real_format;
@@ -1172,9 +1294,8 @@ Ref<Image> RasterizerStorageGLES3::texture_get_data(RID p_texture, int p_layer)
glViewport(0, 0, texture->alloc_width, texture->alloc_height);
- shaders.copy.bind();
-
shaders.copy.set_conditional(CopyShaderGLES3::LINEAR_TO_SRGB, !srgb);
+ shaders.copy.bind();
glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl
index 3b36bc7cc1..e1a0813efc 100644
--- a/drivers/gles3/shaders/copy.glsl
+++ b/drivers/gles3/shaders/copy.glsl
@@ -61,19 +61,35 @@ in vec3 cube_interp;
#else
in vec2 uv_interp;
#endif
-/* clang-format on */
#ifdef USE_ASYM_PANO
uniform highp mat4 pano_transform;
uniform highp vec4 asym_proj;
#endif
+// These definitions are here because the shader-wrapper builder does
+// not understand `#elif defined()`
+#ifdef USE_TEXTURE3D
+#endif
+#ifdef USE_TEXTURE2DARRAY
+#endif
+
#ifdef USE_CUBEMAP
uniform samplerCube source_cube; //texunit:0
+#elif defined(USE_TEXTURE3D)
+uniform sampler3D source_3d; //texunit:0
+#elif defined(USE_TEXTURE2DARRAY)
+uniform sampler2DArray source_2d_array; //texunit:0
#else
uniform sampler2D source; //texunit:0
#endif
+/* clang-format on */
+
+#if defined(USE_TEXTURE3D) || defined(USE_TEXTURE2DARRAY)
+uniform float layer;
+#endif
+
#ifdef USE_MULTIPLIER
uniform float multiplier;
#endif
@@ -97,7 +113,6 @@ vec4 texturePanorama(vec3 normal, sampler2D pano) {
#endif
-uniform float stuff;
uniform vec2 pixel_size;
in vec2 uv2_interp;
@@ -147,6 +162,10 @@ void main() {
#elif defined(USE_CUBEMAP)
vec4 color = texture(source_cube, normalize(cube_interp));
+#elif defined(USE_TEXTURE3D)
+ vec4 color = textureLod(source_3d, vec3(uv_interp, layer), 0.0);
+#elif defined(USE_TEXTURE2DARRAY)
+ vec4 color = textureLod(source_2d_array, vec3(uv_interp, layer), 0.0);
#else
vec4 color = textureLod(source, uv_interp, 0.0);
#endif