Skip to content

Commit 358d594

Browse files
authored
gl: implement readTexture (#9652)
This implementation is just a refactor of readPixels.
1 parent b06b6b5 commit 358d594

File tree

3 files changed

+78
-14
lines changed

3 files changed

+78
-14
lines changed

filament/backend/src/opengl/OpenGLDriver.cpp

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3985,10 +3985,8 @@ void OpenGLDriver::stopCapture(int) {
39853985
// Read-back ops
39863986
// ------------------------------------------------------------------------------------------------
39873987

3988-
void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
3989-
uint32_t const x, uint32_t const y, uint32_t width, uint32_t height,
3990-
PixelBufferDescriptor&& p) {
3991-
DEBUG_MARKER()
3988+
void OpenGLDriver::readPixelsFromBoundFramebuffer(uint32_t const x, uint32_t const y,
3989+
uint32_t width, uint32_t height, PixelBufferDescriptor&& p) {
39923990
auto& gl = mContext;
39933991

39943992
GLenum const glFormat = getFormat(p.format);
@@ -4021,8 +4019,6 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
40214019
* of the buffer.
40224020
*/
40234021

4024-
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
4025-
40264022
using PBD = PixelBufferDescriptor;
40274023

40284024
// The PBO only needs to accommodate the area we're reading, with alignment.
@@ -4032,7 +4028,6 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
40324028
if (UTILS_UNLIKELY(gl.isES2())) {
40334029
void* buffer = malloc(pboSize);
40344030
if (buffer) {
4035-
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
40364031
glReadPixels(GLint(x), GLint(y), GLint(width), GLint(height), glFormat, glType, buffer);
40374032
CHECK_GL_ERROR()
40384033

@@ -4056,10 +4051,6 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
40564051
}
40574052

40584053
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
4059-
// glReadPixel doesn't resolve automatically, but it does with the auto-resolve extension,
4060-
// which we're always emulating. So if we have a resolved fbo (fbo_read), use that instead.
4061-
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
4062-
40634054
GLuint pbo;
40644055
glGenBuffers(1, &pbo);
40654056
gl.bindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
@@ -4111,10 +4102,83 @@ void OpenGLDriver::readPixels(Handle<HwRenderTarget> src,
41114102
#endif
41124103
}
41134104

4105+
void OpenGLDriver::readPixels(Handle<HwRenderTarget> src, uint32_t const x, uint32_t const y,
4106+
uint32_t width, uint32_t height, PixelBufferDescriptor&& p) {
4107+
DEBUG_MARKER()
4108+
auto& gl = mContext;
4109+
4110+
GLRenderTarget const* s = handle_cast<GLRenderTarget const*>(src);
4111+
4112+
if (UTILS_UNLIKELY(gl.isES2())) {
4113+
gl.bindFramebuffer(GL_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
4114+
readPixelsFromBoundFramebuffer(x, y, width, height, std::move(p));
4115+
return;
4116+
}
4117+
4118+
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
4119+
// glReadPixel doesn't resolve automatically, but it does with the auto-resolve extension,
4120+
// which we're always emulating. So if we have a resolved fbo (fbo_read), use that instead.
4121+
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, s->gl.fbo_read ? s->gl.fbo_read : s->gl.fbo);
4122+
readPixelsFromBoundFramebuffer(x, y, width, height, std::move(p));
4123+
#endif
4124+
}
4125+
41144126
void OpenGLDriver::readTexture(Handle<HwTexture> src, uint8_t level, uint16_t layer, uint32_t x,
41154127
uint32_t y, uint32_t width, uint32_t height, PixelBufferDescriptor&& p) {
4116-
// TODO: implement readTexture
4128+
DEBUG_MARKER()
4129+
auto& gl = mContext;
4130+
4131+
// readTexture() requires GLES 3.0+ features such as GL_READ_FRAMEBUFFER and
4132+
// glFramebufferTextureLayer, so we wrap it in FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2.
4133+
#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2
4134+
if (UTILS_UNLIKELY(gl.isES2())) {
4135+
// This is not supported on ES2 (at least not in this driver)
4136+
scheduleDestroy(std::move(p));
4137+
return;
4138+
}
4139+
4140+
GLTexture const* s = handle_cast<GLTexture*>(src);
4141+
assert_invariant(s);
4142+
assert_invariant(s->target != SamplerType::SAMPLER_3D);
4143+
4144+
GLuint fbo = 0;
4145+
glGenFramebuffers(1, &fbo);
4146+
gl.bindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
4147+
4148+
GLenum const attachment = GL_COLOR_ATTACHMENT0;
4149+
4150+
switch (s->target) {
4151+
case SamplerType::SAMPLER_2D:
4152+
if (any(s->usage & TextureUsage::SAMPLEABLE)) {
4153+
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment, GL_TEXTURE_2D, s->gl.id,
4154+
level);
4155+
} else {
4156+
glFramebufferRenderbuffer(GL_READ_FRAMEBUFFER, attachment, GL_RENDERBUFFER,
4157+
s->gl.id);
4158+
}
4159+
break;
4160+
case SamplerType::SAMPLER_CUBEMAP:
4161+
glFramebufferTexture2D(GL_READ_FRAMEBUFFER, attachment,
4162+
GL_TEXTURE_CUBE_MAP_POSITIVE_X + layer, s->gl.id, level);
4163+
break;
4164+
case SamplerType::SAMPLER_2D_ARRAY:
4165+
case SamplerType::SAMPLER_CUBEMAP_ARRAY:
4166+
glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, attachment, s->gl.id, level, layer);
4167+
break;
4168+
default:
4169+
// This should not happen (e.g. 3D textures are excluded)
4170+
break;
4171+
}
4172+
4173+
CHECK_GL_FRAMEBUFFER_STATUS(GL_READ_FRAMEBUFFER)
4174+
4175+
readPixelsFromBoundFramebuffer(x, y, width, height, std::move(p));
4176+
4177+
gl.unbindFramebuffer(GL_READ_FRAMEBUFFER);
4178+
glDeleteFramebuffers(1, &fbo);
4179+
#else
41174180
scheduleDestroy(std::move(p));
4181+
#endif
41184182
}
41194183

41204184
void OpenGLDriver::readBufferSubData(BufferObjectHandle boh,

filament/backend/src/opengl/OpenGLDriver.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ class OpenGLDriver final : public OpenGLDriverBase {
363363
bool isDefaultFramebuffer) noexcept;
364364

365365
// Common methods
366+
void readPixelsFromBoundFramebuffer(uint32_t x, uint32_t y, uint32_t width, uint32_t height,
367+
PixelBufferDescriptor&& p);
366368
void createTextureCommon(Handle<HwTexture> th, SamplerType target, uint8_t levels,
367369
TextureFormat format, uint8_t samples, uint32_t width, uint32_t height, uint32_t depth,
368370
TextureUsage usage, utils::ImmutableCString&& tag);

filament/backend/test/test_ReadTexture.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ class ReadTextureTest : public BackendTest {
5151
};
5252

5353
TEST_F(ReadTextureTest, ReadTexture2D) {
54-
SKIP_IF(Backend::OPENGL, "readTexture not implemented for OpenGL");
5554
SKIP_IF(Backend::METAL, "readTexture not implemented for Metal");
5655

5756
DriverApi& api = getDriverApi();
@@ -147,7 +146,6 @@ TEST_F(ReadTextureTest, ReadTexture2D) {
147146
}
148147

149148
TEST_F(ReadTextureTest, ReadTextureArray) {
150-
SKIP_IF(Backend::OPENGL, "readTexture not implemented for OpenGL");
151149
SKIP_IF(Backend::METAL, "readTexture not implemented for Metal");
152150

153151
DriverApi& api = getDriverApi();

0 commit comments

Comments
 (0)