blob: b906fe56e03bc7396fcb6051ed28c6f65ef8f7c0 [file] [log] [blame]
Magnus Jedvert4a783082015-09-18 16:32:18 +02001/*
kjellanderb24317b2016-02-10 07:54:43 -08002 * Copyright 2015 The WebRTC project authors. All Rights Reserved.
Magnus Jedvert4a783082015-09-18 16:32:18 +02003 *
kjellanderb24317b2016-02-10 07:54:43 -08004 * 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 Jedvert4a783082015-09-18 16:32:18 +02009 */
10
11package org.webrtc;
12
13import 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 Jedvert2ed62b32018-04-11 14:25:14 +020018 * conversion. This class is not thread safe and must be used by a thread with an active GL context.
Magnus Jedvert4a783082015-09-18 16:32:18 +020019 */
20// TODO(magjed): Add unittests for this class.
21public class GlTextureFrameBuffer {
Magnus Jedvert4a783082015-09-18 16:32:18 +020022 private final int pixelFormat;
Magnus Jedvert2ed62b32018-04-11 14:25:14 +020023 private int frameBufferId;
24 private int textureId;
Magnus Jedvert4a783082015-09-18 16:32:18 +020025 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 Jedvert4a783082015-09-18 16:32:18 +020042 this.width = 0;
43 this.height = 0;
Magnus Jedvert4a783082015-09-18 16:32:18 +020044 }
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 Jedvert2ed62b32018-04-11 14:25:14 +020052 if (width <= 0 || height <= 0) {
Magnus Jedvert4a783082015-09-18 16:32:18 +020053 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 Jedvert2ed62b32018-04-11 14:25:14 +020060 // 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 Jedvert4a783082015-09-18 16:32:18 +020069
Magnus Jedvert4a783082015-09-18 16:32:18 +020070 // 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 Jedvert4a783082015-09-18 16:32:18 +020075 GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
sakal1c828842016-11-23 06:12:26 -080076 GlUtil.checkNoGLES2Error("GlTextureFrameBuffer setSize");
magjed9b96a172017-01-17 02:51:06 -080077
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 Jedvert4a783082015-09-18 16:32:18 +020090 }
91
92 public int getWidth() {
93 return width;
94 }
95
96 public int getHeight() {
97 return height;
98 }
99
Magnus Jedvert2ed62b32018-04-11 14:25:14 +0200100 /** Gets the OpenGL frame buffer id. This value is only valid after setSize() has been called. */
Magnus Jedvert4a783082015-09-18 16:32:18 +0200101 public int getFrameBufferId() {
102 return frameBufferId;
103 }
104
Magnus Jedvert2ed62b32018-04-11 14:25:14 +0200105 /** Gets the OpenGL texture id. This value is only valid after setSize() has been called. */
Magnus Jedvert4a783082015-09-18 16:32:18 +0200106 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 Jedvert2ed62b32018-04-11 14:25:14 +0200116 textureId = 0;
Magnus Jedvert4a783082015-09-18 16:32:18 +0200117 GLES20.glDeleteFramebuffers(1, new int[] {frameBufferId}, 0);
Magnus Jedvert2ed62b32018-04-11 14:25:14 +0200118 frameBufferId = 0;
Magnus Jedvert4a783082015-09-18 16:32:18 +0200119 width = 0;
120 height = 0;
121 }
122}