Я пытаюсь скопировать данные изображения, предоставленные JCEF, как ByteBuffer в текстуру OpenGL.Когда я передаю предоставленный ByteBuffer в вызов glTexSubImage2D () с соответствующими параметрами, происходит сбой приложения с EXCEPTION_ACCESS_VIOLATION (0xc0000005).
Что я пытаюсь сделать:
JCEF - это оболочка Javaдля CEF (Chromium Embedded Framework), и в настоящее время я использую модель закадрового рендеринга (поскольку я хочу рендерить браузер в 3d-игре с использованием opengl).
JCEF предоставляет обновленные данные экрана из браузера через метод onPaintсо списком грязных прямоугольников и ByteBuffer с обновленными данными экрана.
Я хочу скопировать этот ByteBuffer в текстуру OpenGL, используя glTexSubImage2D или аналогичный.
Я подтвердил, что все значения переданы ввызов функции действителен, и буфер, предоставленный мне JCEF, достаточно длинный.Вызовы glGetError () после каждого вызова библиотеки OpenGL, приводящего к сбою, возвращают 0.
Моя библиотека opengl взята из Java-библиотеки LightWeight 3 (LWJGL3), и я использовал ее во всем своем другом коде.
Поиск по другим темам и вопросам, как здесь, так и на различных форумах, все указывает на то, что ByteBuffer, переданный OpenGL glTexSubImage2D (), слишком короткий.Я дважды и трижды проверил это, и это не тот случай.(Насколько мне известно об OpenGL и его интерпретации форматов и типов данных.)
Сначала несколько базовых классов.Этот код является частью механизма рендеринга, поэтому я буду включать только то, что мне кажется уместным, но я с удовольствием добавлю больше, если необходимо.
Первый класс - это класс PreloadedTexture.Это представляет текстуру, которая не пришла непосредственно из файла, а была изменена или сгенерирована каким-либо образом.Это моя оболочка для OpenGL Texture2D.
package com.base.shader;
import java.nio.ByteBuffer;
import static org.lwjgl.opengl.GL45.*;
public class PreloadedTexture {
int width;//The width of the texture in pixels
int height;//The height of the texture in pixels
int handle;//The OpenGL handle for the texture. This is used to reference this texture in other OpenGL calls
/*
This constructor creates a blank texture with the dimensions specified.
*/
public PreloadedTexture(int width, int height) {
super(Math.random() + "");
this.width = width;
this.height = height;
this.handle = glGenTextures();
glBindTexture(GL_TEXTURE_2D, handle);
glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
}
/*EDIT: This method was renamed and had the internals changed to not bind the texture to a uniform sampler as mentioned in my comment below. It now just does a call to glBindTexture() as the uniform binding would occasionally return errors.
*/
public void bind(){
glBindTexture(GL_TEXTURE_2D, handle);
}
}
В классе BrowserWindow происходит большинство действий, но я удалил все, что не связано с этой проблемой.
package com.base.game;
import com.base.shader.*;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.awt.*;
import java.util.concurrent.LinkedTransferQueue;
import static org.lwjgl.opengl.GL46.*;
public class BrowserWindow {
private PreloadedTexture texture;//The texture that should be copied to.
private ByteBuffer imgData;//The BytBuffer provided by CEF with the updated screen data.
private LinkedTransferQueue<Rectangle> dirtyRects = new LinkedTransferQueue<>();//The list of dirty rects. The reasoning behind a LinkedTransferQueue is explained below.
int width;//The width of the browser view in pixels.
int height;//The height of the browser view in pixels.
/*
* Creates a browser window with the specified width and height. Initialises a ByteBuffer with image data to draw and a texture to draw to.
*/
public BrowserWindow(int _w, int _h){
width = _w;
height = _h;
imgData = ByteBuffer.allocate(width * height * 4 * Integer.BYTES);//This is later filled by the JCEF code but I have removed that as I know it works.
//The ByteBuffer is filled with bytes representing the screen data in BGRA format. Each Byte is a separate component of a pixel. The pixels are grouped together.
//This results in a structure like so: [B,G,R,A,B,G,R,A,etc.] I have maually checked that this data looks valid by going through portions of it and seeing understandable variances in the values.
texture = new PreloadedTexture(width, height); // An instance of the above PreloadedTexture class. The final destination for the imgData above.
}
//Called when the engine whats to render a frame. This should update the texture of the browser and then render the mesh with this texture. The actual mesh rendering code is ommited because again I know that works and it is not where the issue lies.
public void render(ShaderProgram shaderProgram, Camera camera) {
synchronized (imgData) {
//This block is to allow communication with the CEF thread.
//CEF runs the browser in a seperate thread and the onPaint method is called from there.
//OpenGL is not initialised in that thread however so I have used a LinkedTransferQueue<Rectangle> to transfer the dirty portions that need to be redrawn and the updated ByteBuffer is transferred byt directly setting it on the class.
//Again the actual code that does this has been ommitted as I know by checking the values manually that the data that is transfered is correct.
if (dirtyRects.size() > 0) {
//This just checks that there are dirty portions to update otherwise the call would waste both CPU and GPU time.
ArrayList<Rectangle> rects = new ArrayList<>();
//This provides a list to drop the Rectangles into for ease of access. These rectangles aren;t used at the moment but their implementation can be seen in the below commented out code.
dirtyRects.drainTo(rects);
imgData.rewind();
//This ensures that the imgData buffer is at the beggining.
texture.bind();
//Here I bind the texture to GL_TEXTURE_2D. The actual implementation is shown above in PreloadedTexture class.
System.out.println("Pre Render Error: " + glGetError());
//This checks to see if OpenGL was in an invalid state before and also clears the error buffer to ensure any errors reported from now on actually occur in this method.
System.out.println(glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH) + ", " + glGetTexLevelParameteri(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT));
//This is a sanity check to ensure that the texture was initialised correctly in the PreloadedTexture constructor.
System.out.println(imgData.limit() + ", " + width * height * 4);
//This is another sanity check to ensure that the ByteBuffer is actually large enough. At the moment I have given it extra data to work with to see if it was the problem, hence the much larger limit of the ByteBuffer compared to the computed nescesary amount.
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
//These two calls reset the OpenGL state to ensure that rows and pixels aren't being skipped.
glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, width, height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, imgData);//This line results in the EXCEPTION_ACCESS_VIOLATION (0xc0000005)
System.out.println("Render Error: " + glGetError()); //This is a sanity check from before with another problem that I fixed.
//Below is the implemtnation that made use of the rectangles. It follows a simmilar pattern to the above code but only updates the specified rectangles.
// for (Rectangle rect : rects) {
// System.out.println(rect.x + ", " + rect.y + ", " + rect.width + ", " + rect.height);
// glPixelStorei(GL_UNPACK_SKIP_PIXELS, rect.x);
// glPixelStorei(GL_UNPACK_SKIP_ROWS, rect.y);
// glTexSubImage2D(GL_TEXTURE_2D, 0, rect.x, rect.y, rect.width, rect.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV,imgData);
// }
}
imgData.notifyAll();//This notifies the CEF thread that the Rectangles and imgData objects are now free.
}
//Mesh rendering would happen here but again it has been omitted for conciseness this is not where the problem lies.
}
}
Как я уже упоминал в комментариях к коду, я дважды проверил, что данные из CEF имеют правильный формат.Далее следует 4-байтовая повторяющаяся последовательность значений BGRA.ByteBuffer достаточно длинный, как показывают проверки работоспособности перед вызовом glTexSubImage2D ();
Извините за объем кода, но, как я говорил ранее, он является частью механизма рендеринга и большей части структурыпод влиянием этого факта.
Я ожидаю, что вызов glTexSubImage2D () скопирует данные ByteBuffer в OpenGL Texture2D, однако в результате вызова получится EXCEPTION_ACCESS_VIOLATION (0xc0000005).ByteBuffer достаточно длинный, и данные внутри него находятся в допустимом формате.Я также попытался заменить GL_UNSIGNED_INT_8_8_8_8_REV на GL_UNSIGNED_INT_8_8_8_8 и GL_UNSIGNED_BYTE, но они не влияют на результат.При просмотре журналов возникает исключение, потому что он пытался получить доступ к адресу 0x0000000000000010.
Кто-нибудь знает, что может быть причиной этой ошибки.Все мои исследования показали, что ByteBuffer слишком короткий, однако, как я уже сказал, я дважды проверил это.Мое понимание того, что происходит, ошибочно или это что-то еще?