Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 1 | /* |
kjellander | b24317b | 2016-02-10 07:54:43 -0800 | [diff] [blame] | 2 | * Copyright 2015 The WebRTC project authors. All Rights Reserved. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 3 | * |
kjellander | b24317b | 2016-02-10 07:54:43 -0800 | [diff] [blame] | 4 | * Use of this source code is governed by a BSD-style license |
| 5 | * that can be found in the LICENSE file in the root of the source |
| 6 | * tree. An additional intellectual property rights grant can be found |
| 7 | * in the file PATENTS. All contributing project authors may |
| 8 | * be found in the AUTHORS file in the root of the source tree. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 9 | */ |
| 10 | |
| 11 | package org.webrtc; |
| 12 | |
| 13 | import android.opengl.GLES20; |
| 14 | |
| 15 | /** |
| 16 | * Helper class for handling OpenGL framebuffer with only color attachment and no depth or stencil |
| 17 | * buffer. Intended for simple tasks such as texture copy, texture downscaling, and texture color |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 18 | * conversion. This class is not thread safe and must be used by a thread with an active GL context. |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 19 | */ |
| 20 | // TODO(magjed): Add unittests for this class. |
| 21 | public class GlTextureFrameBuffer { |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 22 | private final int pixelFormat; |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 23 | private int frameBufferId; |
| 24 | private int textureId; |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 25 | private int width; |
| 26 | private int height; |
| 27 | |
| 28 | /** |
| 29 | * Generate texture and framebuffer resources. An EGLContext must be bound on the current thread |
| 30 | * when calling this function. The framebuffer is not complete until setSize() is called. |
| 31 | */ |
| 32 | public GlTextureFrameBuffer(int pixelFormat) { |
| 33 | switch (pixelFormat) { |
| 34 | case GLES20.GL_LUMINANCE: |
| 35 | case GLES20.GL_RGB: |
| 36 | case GLES20.GL_RGBA: |
| 37 | this.pixelFormat = pixelFormat; |
| 38 | break; |
| 39 | default: |
| 40 | throw new IllegalArgumentException("Invalid pixel format: " + pixelFormat); |
| 41 | } |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 42 | this.width = 0; |
| 43 | this.height = 0; |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 44 | } |
| 45 | |
| 46 | /** |
| 47 | * (Re)allocate texture. Will do nothing if the requested size equals the current size. An |
| 48 | * EGLContext must be bound on the current thread when calling this function. Must be called at |
| 49 | * least once before using the framebuffer. May be called multiple times to change size. |
| 50 | */ |
| 51 | public void setSize(int width, int height) { |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 52 | if (width <= 0 || height <= 0) { |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 53 | throw new IllegalArgumentException("Invalid size: " + width + "x" + height); |
| 54 | } |
| 55 | if (width == this.width && height == this.height) { |
| 56 | return; |
| 57 | } |
| 58 | this.width = width; |
| 59 | this.height = height; |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 60 | // Lazy allocation the first time setSize() is called. |
| 61 | if (textureId == 0) { |
| 62 | textureId = GlUtil.generateTexture(GLES20.GL_TEXTURE_2D); |
| 63 | } |
| 64 | if (frameBufferId == 0) { |
| 65 | final int frameBuffers[] = new int[1]; |
| 66 | GLES20.glGenFramebuffers(1, frameBuffers, 0); |
| 67 | frameBufferId = frameBuffers[0]; |
| 68 | } |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 69 | |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 70 | // Allocate texture. |
| 71 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
| 72 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); |
| 73 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, pixelFormat, width, height, 0, pixelFormat, |
| 74 | GLES20.GL_UNSIGNED_BYTE, null); |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 75 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); |
sakal | 1c82884 | 2016-11-23 06:12:26 -0800 | [diff] [blame] | 76 | GlUtil.checkNoGLES2Error("GlTextureFrameBuffer setSize"); |
magjed | 9b96a17 | 2017-01-17 02:51:06 -0800 | [diff] [blame] | 77 | |
| 78 | // Attach the texture to the framebuffer as color attachment. |
| 79 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId); |
| 80 | GLES20.glFramebufferTexture2D( |
| 81 | GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureId, 0); |
| 82 | |
| 83 | // Check that the framebuffer is in a good state. |
| 84 | final int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER); |
| 85 | if (status != GLES20.GL_FRAMEBUFFER_COMPLETE) { |
| 86 | throw new IllegalStateException("Framebuffer not complete, status: " + status); |
| 87 | } |
| 88 | |
| 89 | GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0); |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 90 | } |
| 91 | |
| 92 | public int getWidth() { |
| 93 | return width; |
| 94 | } |
| 95 | |
| 96 | public int getHeight() { |
| 97 | return height; |
| 98 | } |
| 99 | |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 100 | /** Gets the OpenGL frame buffer id. This value is only valid after setSize() has been called. */ |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 101 | public int getFrameBufferId() { |
| 102 | return frameBufferId; |
| 103 | } |
| 104 | |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 105 | /** Gets the OpenGL texture id. This value is only valid after setSize() has been called. */ |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 106 | public int getTextureId() { |
| 107 | return textureId; |
| 108 | } |
| 109 | |
| 110 | /** |
| 111 | * Release texture and framebuffer. An EGLContext must be bound on the current thread when calling |
| 112 | * this function. This object should not be used after this call. |
| 113 | */ |
| 114 | public void release() { |
| 115 | GLES20.glDeleteTextures(1, new int[] {textureId}, 0); |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 116 | textureId = 0; |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 117 | GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0); |
Magnus Jedvert | 2ed62b3 | 2018-04-11 14:25:14 +0200 | [diff] [blame^] | 118 | frameBufferId = 0; |
Magnus Jedvert | 4a78308 | 2015-09-18 16:32:18 +0200 | [diff] [blame] | 119 | width = 0; |
| 120 | height = 0; |
| 121 | } |
| 122 | } |