implemented texture_get_data() for TextureLayered

This commit is contained in:
thomas.herzog 2019-01-23 14:17:42 +01:00
parent 4f4e46edd5
commit dddfe9a2df
2 changed files with 144 additions and 4 deletions

View file

@ -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);

View file

@ -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