I've created an opengl surface and everything works fine, however when I try to draw text onto it using the following method:
public void loadFPSTexture(GL10 gl){
Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.RGB_565);
bitmap.eraseColor(Color.BLACK);
Canvas canvas = new Canvas(bitmap);
Paint textPaint = new Paint();
textPaint.setTextSize(35);
textPaint.setFakeBoldText(true);
textPaint.setAntiAlias(true);
textPaint.setARGB(255, 255, 255, 255);
canvas.drawText("FPS "+reportedFramerate, 10,35, textPaint);
gl.glGenTextures(1, texturesFPS, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesFPS[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
Then used in my onDraw function with:
gl.glPushMatrix();
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glBindTexture(GL10.GL_TEXTURE_2D, texturesFPS[0]);
gl.glTranslatef(-surfaceSize.x/1.5f, surfaceSize.y/1.5f, 0.0f);
gl.glScalef(10, 10, 1.0f);
gl.glColor4f(1.0f, 1.0f, 1.0f, saturation_head);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0,vertexBuffer);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureFPSBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,GL10.GL_UNSIGNED_SHORT, indexBuffer);
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
gl.glDisable(GL10.GL_TEXTURE_2D);
gl.glPopMatrix();
I get a weird crash and the surface is unable to be drawn after this point. Logcat shows a constant stream of the following:
E/Adreno200-EGL( 2578): eglLockWindowSurface: failed to map the memory for fd=32 offs=1536000
E/SurfaceFlinger( 2578): GL error 0x0505
E/Adreno200-EGL( 2578): eglLockWindowSurface: failed to map the memory for fd=32 offs=1536000
E/Adreno200-EGL( 2578): egliSwapWindowSurface: oglSwapBuffer failed
E/SurfaceFlinger( 2578): eglSwapBuffers: EGL error 0x3003 (EGL_BAD_ALLOC)
I'm not sure why this is happening? any help would be much appreciated!
The trouble is that I was generating textures repeatedly without ever deleting them. Simply adding one line before generation is enough to prevent a memory leak (it seems there is no need to check that a texture has already been generated):
gl.glDeleteTextures(1, texturesFPS, 1);
gl.glGenTextures(1, texturesFPS, 1);
Simple as that :)
Looks like you're creating a new texture each time you call loadFPSTexture(), and never release it. After some time this will cause you to run out of memory, which could explain the EGL_BAD_ALLOC in your logs.
It would be better to create your bitmap, canvas and texturesFPS variables just once, and reuse them in the loadFPSTexture() function. In that case, you should probably use GLUtils.texSubImage2D() instead of GLUtils.texImage2D(), to upload new bitmap data to the existing texture.
I don't have comment access yet (frequent other 'help' sites, just getting up on SO), but wanted to echo (and upvoted) svdree's note, and add more details -- because other people starting with GLES will certainly hit similar issues.
Your bitmap, canvas, and texture (and paint!) should be created once, wherever you set up your gles resources to begin with. They should be deleted when you clean up resources for the app. Unless you are resizing the bitmap/texture, recreating is thrashing memory (cpu and gpu) unnecessarily.
The initial creation of the texture, you'd use the GLUtils.texImage2D function to prepare the texture (can just upload the bitmap as-is, don't care about the data). That ensures the texture is allocated by the driver, proper width/height buffer ready for later updates.
Rendering the fps might then look more like:
s_bitmap.eraseColor(Color.BLACK);
s_canvas.drawText("FPS "+reportedFramerate, 10, 35, s_textPaint);
gl.glBindTexture(GL10.GL_TEXTURE_2D, s_texturesFPS[0]);
GLUtils.texSubImage2D(GL10.GL_TEXTURE_2D, 0, 0, 0, s_bitmap);
... and that's it. An order of magnitude faster, and obviously much cleaner. :-)
You can make it even faster beyond that, for example only erase/fill the bitmap rectangle where the fps is being drawn, and then use gl.glTexSubImage2D directly to upload only those rows the text is rendered to (saving you clearing, and uploading, say 220 extra rows of data that isn't changing...).
Hope that helps!
Related
My goal is to add some text info in the video output file obtained after recording a video with the Camera2 API (e. g. date/time, user id etc.). I have checked some references about how to do this using camera API but I didn't find any information about how to do achieve this with Camera2 API. Can anyone help me?
This is what I found for camera API
The link that you provided about how to achieve your solution using Camera API will work also for Camera2 API. You should generate a GLSurfaceView with the information that you want to achieve together with a GLSurfaceView.Renderer to process each frame of your camera with OpenGL.
After configure your surface you should generate a new Surface from your SurfaceTexture:
Surface videoSurface = new Surface(surfaceGLTexture);
After that you can use createCaptureSession together with your Surface and a CameraCaptureSession.StateCallback()to generate a video preview using CameraDevice.TEMPLATE_RECORD in your CaptureRequest.Builder.
Take a look at grafica. Take a look specifically at TextureMovieEncoder.java. Replace the code in private void drawBox(int posn) with the code found in the best answer to this question:
Draw text in OpenGL ES
copied here for reference:
// Create an empty, mutable bitmap
Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444);
// get a canvas to paint over the bitmap
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
// get a background image from resources
// note the image format must match the bitmap format
Drawable background = context.getResources().getDrawable(R.drawable.background);
background.setBounds(0, 0, 256, 256);
background.draw(canvas); // draw the background to our bitmap
// Draw the text
Paint textPaint = new Paint();
textPaint.setTextSize(32);
textPaint.setAntiAlias(true);
textPaint.setARGB(0xff, 0x00, 0x00, 0x00);
// draw the text centered
canvas.drawText("Hello World", 16,112, textPaint);
//Generate one texture pointer...
gl.glGenTextures(1, textures, 0);
//...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
//Create Nearest Filtered Texture
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//Clean up
bitmap.recycle();
The same approach works, pretty much - the camera2 API wants a Surface to draw to, but you can create one from a SurfaceTexture:
Surface s = new Surface(mSurfaceTexture);
Then you can pass this Surface to the camera2 CameraDevice.createCaptureSession() call; see for example Camera2Video for a basic recording app to modify.
From your GL rendering, you have to then send the data to a video encoder.
FFMPEG provides too many thing that you can do what you want. Please try this tool. May be it will help you. I suggest you. Please check this link:
https://stackoverflow.com/a/38299320/3992798
I am developing a game where similar objects are rendered on the screen. The problem is that every time a new object is added, more vertices needs to be handled, so FPS decrease.
Since I am a beginner with openGl, I have some general ideas about improving performance, I just don't know what openGl allows me to do.
Since most of my objects are identical(same number of vertices, same colors, same uvs and same normals), but only the position and rotation of the objects are different, is it possible to send the vertices for similar objects only once, on each frame render , and then call drawElements for all objects and modify only the objects positions?
Is it possible to go even further, and upload those similar vertices, only once when the scene is 1st created, and on each frame render just call drawElements for each object?
Currently, I am sending the vertices for each object like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
I am thinking I should do someting like this:
.......
gl.glNormalPointer(GL10.GL_FLOAT, 0, normals);
gl.glColorPointer(4, GL10.GL_UNSIGNED_BYTE, 0, colors);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
for(eachObject)
{
gl.glPushMatrix();
gl.glTranslatef(position);
gl.glRotatef(rotation);
gl.glScalef(scale);
gl.glDrawElements(renderType,nr,GL10.GL_UNSIGNED_SHORT, faces);
gl.glPopMatrix();
........
}
Yes
Yes (even better)
Your second code example looks exactly like what you should be doing.
There's no reason you'd need to upload the vertices more than once, doing it on initialization should be sufficient. After that just set the pointers any time you want to render. You can call glDrawElements as many times as you want with different translations/rotations.
I am programming an Android 2d game using opengl es 2.0. After I draw my sprites to the backbuffer I draw lights to a FBO and try to blend it to the back buffer again.
When I draw the FBO to the framebuffer, even trasparent without any color, the framerates drops from 60 to 30 on a Samsung Galaxy w (it has an adreno 205 as gpu). I searched everywhere and tried everything, even if I draw a single sprite on the scene and blend a trasparent FBO texture to the screen the framerate drops. I tried other games with lighting effects on that phone and they run fine, almost every game is fine on that phone, I believe they use the framebuffer as well.
On the Galaxy SII (mali 400 gpu) runs fine, I am quite new to opengl so I believe I am making a mistake somewhere, I share my code.
// Create a framebuffer and renderbuffer
GLES20.glGenFramebuffers(1, fb, offset);
GLES20.glGenRenderbuffers(1, depthRb, offset);
// Create a texture to hold the frame buffer
GLES20.glGenTextures(1, renderTex, offset);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, renderTex[offset]);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA,
screenWidth, screenHeight, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE,
null);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
//bind renderbuffer
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, depthRb[offset]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_DEPTH_COMPONENT16,
screenWidth, screenHeight);
// bind the framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fb[offset]);
// specify texture as color attachment
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, renderTex[offset], 0);
// specify depth_renderbufer as depth attachment
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT,
GLES20.GL_RENDERBUFFER, depthRb[0]);
// Check FBO status.
int status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER);
if ( status == GLES20.GL_FRAMEBUFFER_COMPLETE )
{
Log.d("GLGame framebuffer creation", "Framebuffer complete");
}
// set default framebuffer
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
I do this once on surface creation. Not sure if is correct. I keep the texture and framebuffer ids to switch to them when I need.
My drawing code:
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
ShaderProgram program = glgame.getProgram();
//put vertices in the floatbuffer
mTriangleVertices.put(vertices, 0, len);
mTriangleVertices.flip();
GLES20.glVertexAttribPointer(program.POSITION_LOCATION, 2, GLES20.GL_FLOAT, false,
TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
//preparing parameter for texture position
mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
GLES20.glEnableVertexAttribArray(program.POSITION_LOCATION);
//preparing parameter for texture coords
GLES20.glVertexAttribPointer(program.TEXTURECOORD_LOCATION, 2, GLES20.GL_FLOAT,
false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES,
mTriangleVertices);
//set projection matrix
GLES20.glEnableVertexAttribArray(program.TEXTURECOORD_LOCATION);
GLES20.glUniformMatrix4fv(program.MATRIX_LOCATION, 1, false, matrix, 0);
//draw triangle with indices to form a rectangle
GLES20.glDrawElements(GLES20.GL_TRIANGLES, numSprites * 6, GLES20.GL_UNSIGNED_SHORT,
indicesbuf);
//clear buffers
mTriangleVertices.clear();
mVertexColors.clear();
Everything is rendered on screen correctly, but the performance are ruined just when I draw the FBO texture.
Thank you very much for your help. I worked very hard on this and didn't find a solution.
According to qualcomm docs, you need to glclear after every glbindframebuffer, this is a problem related to tiled architecture, if you are switching framebuffers, data need to get copied from fastmem to normal memory to save current framebuffer and from slowmem to fast mem to get contents of newly binded frame, in case you are clearing just after glbind no data is copied from slowmem to fastmem and you are saving time, but you need to redesign your render pipeline often, so it will avoid reading data back and forth between slow and fast memory, so try to do glclear after each bind and it should help, you can also use adreno profiler to get additional information about problematic calls, but i doubt it will help with adreno200 i am trying to get two buffers for blur and i am ending with 10fps, bindframebuffer call can take up to 20msec if its not cleared, if it is it should end at 2ms.
I have an Android app which uses a bit of OpenGL running on a Galaxy Nexus with ICS. After turning on hardware acceleration and using my OpenGL activity some of those textures are stolen by the system and now in my listviews and other UI elements. Its as if my GL pointers obtained via GLES20.glGenTextures are not actually fresh pointers but rather overwriting ones used by the window renderer.
In any case there should be some sort of firewall or sandbox between the OS screen drawing system and my app, no?
Turning off hardwareAcceleration entirely displays fine, but the UI is choppy (and buttery smooth on 2.3 and lower either way). Turning it off/on activity by activity doesn't help either.
Screenshot (before/after)
Normally a repeating bitmap drawable, now an image (from camera in this case) I loaded into OpenGL in a different activity - http://a.yfrog.com/img532/9245/t81k.png
Gen/Load texture
int[] image = new int[1];
GLES20.glGenTextures(1, image, 0);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,GLES20.GL_LINEAR);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bmp, 0);
Basic draw
GLES20.glViewport(0, 0, width, height);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
GLES20.glUseProgram(shader.program);
GLES20.glUniform1i(GLES20.glGetUniformLocation(shader.program, "imageTexture"), 0);
GLES20.glVertexAttribPointer(Attributes.VERTEX, 2, GLES20.GL_FLOAT, true, 0, squareVertices);
GLES20.glEnableVertexAttribArray(Attributes.VERTEX);
GLES20.glVertexAttribPointer(Attributes.TEXTUREPOSITON, 2, GLES20.GL_FLOAT, true, 0, textureVertices);
GLES20.glEnableVertexAttribArray(Attributes.TEXTUREPOSITON);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, image[0]);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
Turns out I was calling GL commands on the UI thread at certain times. This interfered with the apps hardware accelerated display as suggested by Romain Guy at this post.
I am now able to load a texture to the project I have made but it gave me results that I was not expecting. Instead of loading my 91 tiles and having them have crate textures, and having a picture of a puppy be at 10x and 10 y, everything is the puppy.
This is my draw code
#Override
public void onDrawFrame(GL10 gl) {
// TODO Auto-generated method stub
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glLoadIdentity();
for(int i =0;i<91;i++)
{
myfloortiles[i].draw(gl);
}
gl.glLoadIdentity();
gl.glOrthof(0, width, height, 0, 0, 1);
gl.glDisable(GL10.GL_DEPTH_TEST);
// gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glTranslatef(10, 10, 1);
bitmap=BitmapFactory.decodeResource(context.getResources(), R.drawable.puppy);
int textureID;
int[] temp = new int[1];
gl.glGenTextures(1, temp, 0);
textureID = temp[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, textureID);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
GL10.GL_NEAREST); // GL_LINEAR for quality.
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
GL10.GL_CLAMP_TO_EDGE);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
GL10.GL_CLAMP_TO_EDGE);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();
}
Of course I have to be doing something wrong. Is it that I have to make a 3d object and then render the texture to it? Or is there another way? Also how would I have an animation if i chose the 3d object with texture path? Would I swap in new images when I want it to draw a different image for the interface? Is my place the image in a specific location logic correct? Or is that also terribly incorrect also?
Technically not a mistake, but a problem is generating texture names and creating a texture object in the drawing function. Texture creating is a one-time thing:
You generate a texture name, which is a handle by which you refer to the texture later (glGenTextures)
You bind that name (glBindTexture)
You upload the image (glTexImage and/or glTexSubImage)
For drawing the actual textured meshes you
enable texturing (*glEnable(GL_TEXTURE_…)*)
bind the texture(s) (glBindTexture)
draw the mesh(es)
In your code you're going through the whole texture creation phase for every redraw. The last created texture is also the bound texture and thus will be subsequently applied to the following drawing calls. Which are about to happen in the next call of onDrawFrame
On a side note: You clearly have not understood how the matrix stack works. You mixed calls of glOrtho, glLoadIdentity, glTranslate and so on without any sensible structure. You need to get the understanding of those straigt, too. I'd say even before you tackle texturing.