моя цель - иметь возможность использовать графический процессор в фоновом потоке, чтобы создать изображение за кадром, которое отображается с использованием класса Skia Canvas. В идеале мне бы хотелось использовать Compose Multiplatform и ее библиотеку Skiko для этого, но из -за ограничений своего класса DirectContext невозможно написать поверхность EGL в свой холст.
Как следствие, мне пришлось использовать собственную функциональность для реализации этого. Я работаю на рабочем столе с помощью библиотеки LWJGL. Я построил нативную реализацию Android, но я не уверен в том, как пропустить инструкции по рисованию холста к графическому графику, чтобы текстура, прикрепленная к кадрированию. Шаги, выполняющие очередь событий, которая работает в фоновом потоке: < /p>
[*] Я использую EGL, чтобы получить текущий дисплей < /li>
< li> Я использую этот дисплей для инициализации библиотеки EGL
[*] Я создаю конфигурацию, определяющую цветовой буфер, который соответствует OpenGL ES 2 и основан на Pbuffer
Я использую функцию eglchooseconfig, используя атрибуты дисплея и конфигурации, чтобы найти ближайшую конфигурацию к той, которую я просил < /li>
Я создаю Pbuffer с помощью дисплея и возвращался config, указав высоту и ширину, которые я хочу в буфере < /li>
Я создаю контекст рендеринга EGL, используя дисплей и конфигурацию. < /li>
Я делаю это Контекст ток, передавая на дисплее, поверхность Pbuffer и контекст. Если я использую команды GLES, я могу убедиться, что Pbuffer обновляется, и я могу сбросить его пиксели в растровое изображение, чтобы проверить его правильные цвета. Но я хочу использовать внедрение Android Class Class's Canvas, чтобы избежать необходимости писать шейдеры и т. Д. С Surfacetexture. Чтобы получить доступ к текстуре и иметь возможность использовать GlReadPixels для доступа к ее значениям, текстура должна быть связана с кадром. Таким образом, следующим этапом в моей обработке является создание кадрового буфера и подключить к нему текстуру в качестве цветового буфера. Вот шаги, за которыми я выполнил: < /p>
1.i Создаю кадр -буфер, используя Gles GenFrameBuffers.
2. Я связываю сгенерированный фреймбафер с GLES.GL_FRAMEBUFFER
3. Я проверяю, что кадр -буфер был успешно построен.
4. Затем я делаю GLES20's GL_TEXTURE0 Active.
5. Я использую функцию GLGENTEXTURES GLES20 для создания текстуры
6. Я связываю идентификатор текстуры с GL_TEXTURE_2D TARGET
7. Я использую метод GLTEximage2D GLES20, чтобы распределить текстуру, поставляющую его ширину, высоту и формат изображения.
8. Я проверяю, что текстура была построена успешно.
9. Затем я связываю текстуру с цветной точкой прикрепления кадра с использованием GLES20.GlframeBufferTexture2D. В звонке я устанавливаю параметры таким образом:
a. Цель установлен в GL_FRAMEBUFFER,
B. Приложение установлено на GL_COLOR_ATTACHMENT0,
C. TextArget установлен в GL_TEXTURE_2D
d. Текстура установлена на идентификатор текстуры (uint), который был создан при создании текстуры.
e. Уровень установлен на 0, так как необходимо только базовое изображение < /p>
, чтобы убедиться, что проводка текстуры в рамке сработал, работала, я использую вызовы GLES, чтобы привлечь к текстуре и и и и Затем сбросьте пиксели в PNG. Изображение PNG окрашено правильно, поэтому подключение к текстуре через кадр -буфера выглядит твердое. Я последовал совету, который нашел в Интернете (см. Сам код для URL -адреса, дающего советы): < /p>
val surfaceTexture = SurfaceTexture(_texId, true)
surfaceTexture.setDefaultBufferSize(width, height)
val surface = Surface(surfaceTexture)
val canvas = surface.lockHardwareCanvas()
< /code>
Я прикрепил изображение того, что я вижу в отладчике с точки зрения статуса этих вызовов и типов сгенерированных объектов. < /p>
Я призываю Canvas.ishardwareaccelerated (), чтобы убедиться, что холст жив и что он настроен на работу на GPU. Это возвращается как правда. Моя первая удивление заключается в том, что генерируемый холст - это записи. Я бы подумал, что построил бы стандартный холст. Из того, что я понимаю, записывает, что Canvas просто добавляет инструкции по рисованию в список и не выпустит их в графический процессор, который будет выведен в текстуру, пока они не будут выпущены. Я не могу найти никаких примеров того, как использовать этот тип холста, когда он не прикреплен к rendernode, так что отсюда я предполагаю. < /P>
Вот моя лучшая попытка догадываться в текстуре, используя холст, синий круг на коричневом фоне: < /p>
canvas.drawRGB(168, 133, 56)
var paint = Paint()
paint.setARGB(255, 55, 111, 168)
paint.setAntiAlias(true)
paint.setStyle(Paint.Style.FILL)
canvas.drawCircle(500.0f, 500.0f, 200.0f, paint)
GLES20.glFlush()
GLES20.glFinish()
surfaceTexture.updateTexImage()
surface.unlockCanvasAndPost(canvas)
< /code>
Нет никаких исключений ни в одном из этих вызовов. Я пытался добавить и удалить последние 4 вызовов в различных комбинациях. Я думал, что они выпустит инструкции по рисованию в GPU, возможно, это то, что не так. />
writebuffertobitmap (ширина, высота, "testsurfacetexture") < /p>
< /blockquote>
Когда я смотрю на сгенерированное изображение, оно чистый черный. Как будто написание с холстом не имеет никакого эффекта. Если я вместо этого использую команды GLES, чтобы привлечь к текстуре, это отдает твердый цвет, который я установил. , скорее всего, будет делать мой холст либо записи, либо мои предположения о выпуске инструкций по рисованию в GPU. Я полностью приложил исходный код для более подробной информации. Чтобы иметь возможность построить мое приложение. Если бы кто -то мог бы помочь мне преодолеть то, что я думаю, что, вероятно, является последним препятствием, я был бы наиболее благодарным, и я опубликую полученный код, чтобы он мог служить ресурсом для других, которые хотят оптимизировать рендеринг.
Ура,
tom cuthill < /p>
Вот весь код: < /p>
package org.example.project.rendering
import android.graphics.Paint
import android.graphics.SurfaceTexture
import android.opengl.EGL14
import android.opengl.GLES20
import android.opengl.GLU
import android.view.Surface
import androidx.compose.ui.graphics.Canvas
import java.io.BufferedOutputStream
import java.io.FileOutputStream
import java.nio.ByteBuffer
import java.nio.ByteOrder
import android.graphics.Bitmap;
import org.example.project.Log
import java.io.File
class CanvasGenerator {
companion object {
private var _display: android.opengl.EGLDisplay? = null
private var _surface: android.opengl.EGLSurface? = null
private var _context: android.opengl.EGLContext? = null
private var _texId : Int = 0
private var _frameId : Int = 0
/**
*
* get Canvas
*
* This is the launching point for the test of rendering
* to a texture using OpenGL ES and Android's Canvas
* class, and then saving the image to a png file.
*
*/
public fun getCanvas(width: Int, height: Int) : Canvas? {
var success = initGraphicsLibrary()
renderUsingGPUandSaveToBitmap(width, height)
return null
}
/**
* check GL Error
*
* Helper method to check if the last GLES20 call resulted
* in a error. If so, it logs a message and returns false.
*
*/
private fun checkGlError(operation: String) : Boolean {
val error = GLES20.glGetError()
if (error != GLES20.GL_NO_ERROR) {
val msg: String = operation + ": glError 0x" + Integer.toHexString(error)
Log.e("Attempt to render", msg)
return false
} else {
return true
}
}
/**
* write Buffer To Bitmap
*
* This method allocated a byte buffer, uses the GLES20.glReadPixels to fill it
* with the pixels in the current framebuffer, and then saves the pixels in a
* temp png file.
*/
private fun writeBufferToBitmap(width: Int, height: Int, prefix: String) {
val buf = ByteBuffer.allocateDirect(width * height * 4)
buf.order(ByteOrder.LITTLE_ENDIAN)
GLES20.glReadPixels(
0, 0, width, height,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, buf
)
val ableToRead = checkGlError("glReadPixels")
if(ableToRead) {
buf.rewind()
var bos: BufferedOutputStream? = null
try {
val file = File.createTempFile(prefix, "png")
bos = BufferedOutputStream(FileOutputStream(file))
val bmp: Bitmap =
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
bmp.copyPixelsFromBuffer(buf)
bmp.compress(Bitmap.CompressFormat.PNG, 90, bos)
bmp.recycle()
} catch(e : Exception) {
Log.i("Android Egl 2.0:CanvasGenerator", "Unable to save image to file ${e.message}")
} finally {
bos?.close()
}
}
}
/**
* Init Graphics Library
*
* Initialize the display and initialize EGL.
*/
private fun initGraphicsLibrary() : Boolean {
_display = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
if (_display === EGL14.EGL_NO_DISPLAY) {
Log.e("SimpleGLView", "Unable to get access to Native Display")
return false
}
val version = IntArray(2)
var success = EGL14.eglInitialize(_display, version, 0, version, 1)
if (!success) {
val error: Int = EGL14.eglGetError()
when (error) {
EGL14.EGL_NOT_INITIALIZED -> Log.e(
"SimpleGLView",
"Unable to initialize the EGL sub-system"
)
EGL14.EGL_BAD_DISPLAY -> Log.e("SimpleGLView", "Display not valid")
}
return false
}
return true
}
/**
* create Texture
*
* Create a texture of the specified dimensions which is suitable to
* render RGBA pixels.
*
*/
private fun createTexture(width: Int, height: Int): Int {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
val textures = IntArray(1)
GLES20.glGenTextures(1, textures, 0)
val texId = textures[0]
val textureTarget = GLES20.GL_TEXTURE_2D
GLES20.glBindTexture(textureTarget, texId)
GLES20.glTexImage2D(textureTarget, 0, GLES20.GL_RGBA,width, height, 0,
GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)
GLES20.glTexParameteri(textureTarget, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)
if (!GLES20.glIsTexture(texId)) {
throw RuntimeException("OpenGL error: \$result is an invalid texture.")
}
val error = GLES20.glGetError()
if (error != GLES20.GL_NO_ERROR) {
val errorString = GLU.gluErrorString(error)
throw RuntimeException("OpenGL error: \$errorString!")
}
return texId
}
/**
* create Frame Buffer
*
* Create a framebuffer and bind it.
*/
private fun createFrameBuffer() : Int {
val frameBuffers = IntArray(1)
GLES20.glGenFramebuffers(1, frameBuffers, 0)
val frameBufferId = frameBuffers[0]
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBufferId)
if(!GLES20.glIsFramebuffer(frameBufferId)) {
throw RuntimeException("OpenGL error: \$result is an invalid framebuffer.")
}
val error = GLES20.glGetError()
if(error != GLES20.GL_NO_ERROR) {
val errorString = GLU.gluErrorString(error)
throw RuntimeException("OpenGL error: \$errorString!")
}
return frameBufferId
}
/**
* bind Texture to FrameBuffer
*
* Attach the bound framebuffer to the texture.
*
*/
private fun bindTextureToFrameBuffer() {
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, _texId, 0);
val status = GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER)
if(status != GLES20.GL_FRAMEBUFFER_COMPLETE) {
throw RuntimeException("OpenGL error: Can't bind texture to the framebuffer.")
}
}
/**
* render Using GPU and Save To Bitmap
*/
private fun renderUsingGPUandSaveToBitmap(width: Int, height: Int) {
// Configure EGL with details about its buffers.
// This worked previously
val configAttr = intArrayOf(
// Attribute type first, then attribute value
// ie. the attribute EGL_COLOR_BUFFER_TYPE has value EGL_RGB_BUFFER
// See https://registry.khronos.org/EGL/sdk/do ... nfig.xhtml
EGL14.EGL_COLOR_BUFFER_TYPE, EGL14.EGL_RGB_BUFFER,
// zero is the default buffer
EGL14.EGL_LEVEL, 0,
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL14.EGL_SURFACE_TYPE, EGL14.EGL_PBUFFER_BIT,
// Ends the list
EGL14.EGL_NONE
)
val configs = arrayOfNulls(1)
val numConfig = IntArray(1)
// This method returns in configs, all the EGL framebuffer configurations
// that match the attributes in the configAttr.
EGL14.eglChooseConfig(
_display, configAttr, 0,
configs, 0, 1, numConfig, 0
)
if (numConfig[0] == 0) {
// TROUBLE! No config found.
Log.e("Android Egl 2.0: GraphicsContext",
"Unable to get access to Native Display")
return
}
val config = configs[0]
// Create a small pBuffer rendering surface.
val surfAttr = intArrayOf(
EGL14.EGL_WIDTH, width,
EGL14.EGL_HEIGHT, height,
EGL14.EGL_NONE
)
_surface = EGL14.eglCreatePbufferSurface(_display, config, surfAttr, 0)
if (_surface === EGL14.EGL_NO_SURFACE) {
Log.e("SimpleGLView", "Unable to create surface")
val error: Int = EGL14.eglGetError()
when (error) {
EGL14.EGL_BAD_CONFIG -> Log.e("Android Egl 2.0: GraphicsContext",
"Invalid configuration selected")
EGL14.EGL_BAD_NATIVE_WINDOW -> Log.e("Android Egl 2.0: GraphicsContext",
"Bad native window used")
EGL14.EGL_BAD_ALLOC -> Log.e(
"Android Egl 2.0: GraphicsContext",
"Not enough resources to create a surface"
)
}
return
}
//*SIGH* this still doesn't work
// var success1 = EGL14.eglBindAPI(EGL14.EGL_OPENGL_API)
// if(!success1) {
// Log.e("EGL surfaceGenerator", "Unable to set API to OpenGL")
// return
// }
// Create the context.
val ctxAttrib = intArrayOf(
EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
EGL14.EGL_NONE
)
_context = EGL14.eglCreateContext(_display, config, EGL14.EGL_NO_CONTEXT, ctxAttrib, 0)
if (_context == null) {
Log.i("Android Egl 2.0: GraphicsContext", "Create Context failed")
return
}
// Make the context current in the current thread.
var success = EGL14.eglMakeCurrent(_display, _surface, _surface, _context);
if(!success) {
Log.i("Android Egl 2.0: GraphicsContext", "Unable to make context current.")
return
}
// Test if we can write to the pBuffer -> this works
// GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f)
// GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
// GLES20.glFinish()
// writeBufferToBitmap(width, height, "testPBuffer")
// According to this link, the only way to read texture bytes using glReadPixels
// is via a framebuffer that has the texture attached to it. see:
// https://stackoverflow.com/questions/126 ... ture-image
// Here is an example:
// https://stackoverflow.com/questions/118 ... readpixels
// Create a framebuffer
_frameId = createFrameBuffer()
// Create a texture
_texId = createTexture(width, height)
// Bind the texture with the framebuffer.
bindTextureToFrameBuffer()
// Test if we can write to the framebuffer -> this works
// GLES20.glClearColor(1.0f, 0.0f, 0.0f, 1.0f)
// GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
// GLES20.glFinish()
// writeBufferToBitmap(width, height, "testFBuffer")
// Create a surface texture from the texture, and generate a surface and a canvas
// to draw upon. Based on code from here:
// https://slack-chats.kotlinlang.org/t/16 ... -api-and-f
val surfaceTexture = SurfaceTexture(_texId, true)
surfaceTexture.setDefaultBufferSize(width, height)
val surface = Surface(surfaceTexture)
val canvas = surface.lockHardwareCanvas()
// Just verifying that the canvas is indeed using the GPU -> it is true
val isHardwareAccelerated = canvas.isHardwareAccelerated()
// Draw a blue circle on a tan background.
// surfaceTexture.releaseTexImage()
canvas.drawRGB(168,133,56)
var paint = Paint ()
paint.setARGB(255, 55, 111, 168)
paint.setAntiAlias(true)
paint.setStyle(Paint.Style.FILL)
canvas.drawCircle(500.0f, 500.0f, 200.0f, paint)
GLES20.glFlush()
GLES20.glFinish()
surfaceTexture.updateTexImage()
surface.unlockCanvasAndPost(canvas)
writeBufferToBitmap(width, height, "testSurfaceTexture")
}
}
}
Подробнее здесь: https://stackoverflow.com/questions/793 ... ramebuffer
Android Canvas не рендеринг на текстурной поверхности, связанной с кадром ⇐ Android
-
- Похожие темы
- Ответы
- Просмотры
- Последнее сообщение
-
-
Android Canvas не рендеринг на текстурной поверхности, связанной с кадром
Anonymous » » в форуме Android - 0 Ответы
- 15 Просмотры
-
Последнее сообщение Anonymous
-