gpt4 book ai didi

java - 尽管确认传递的数据缓冲区足够长,但 glTexSubImage2D() 导致 EXCEPTION_ACCESS_VIOLATION (0xc0000005)

转载 作者:行者123 更新时间:2023-12-02 03:33:35 25 4
gpt4 key购买 nike

我正在尝试将 JCEF 提供的图像数据作为 ByteBuffer 复制到 OpenGL 纹理。当我使用适当的参数将提供的 ByteBuffer 传递给 glTexSubImage2D() 调用时,应用程序因 EXCEPTION_ACCESS_VIOLATION (0xc0000005) 崩溃。

我正在尝试做的事情:

JCEF 是 CEF(Chromium 嵌入式框架)的 Java 包装器,我目前正在使用离屏渲染模型(因为我想使用 opengl 在 3D 游戏中渲染浏览器)。

JCEF 通过 onPaint 方法从浏览器提供更新的屏幕数据,该方法包含脏矩形列表和包含更新的屏幕数据的 ByteBuffer。

我想使用 glTexSubImage2D 或类似工具将该 ByteBuffer 复制到 OpenGL 纹理上。

我已经确认传递给函数调用的所有值都是有效的,并且 JCEF 提供给我的缓冲区足够长。每次 OpenGL 库调用后调用 glGetError() 都会导致崩溃返回 0。

我的 opengl 库来自 LightWeight Java Game Library 3 (LWJGL3),我在所有其他代码中都使用了它。

在这里和其他各种论坛上对其他线程和问题的搜索都指出传递给 OpenGL glTexSubImage2D() 的 ByteBuffer 太短。我对此进行了两次和三次检查,但情况并非如此。 (据我对 OpenGL 的理解及其对数据格式和类型的解释告诉我。)

首先是一些基础类(class)。该代码是渲染引擎的一部分,因此我只会包含我认为相关的内容,但如果需要,我很乐意附加更多内容。

第一个类是 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 implementation that made use of the rectangles. It follows a similar 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 太短了,但是,正如我所说的,我已经仔细检查了这一点。我对正在发生的事情的理解是否有缺陷或者是别的什么?

最佳答案

在查看错误日志并查看库源代码之后,我追踪到错误是如何分配 ByteBuffer 的。 OpenGL 库使用了内存偏移访问器(原因很明显),但获取偏移量的方法要求 ByteBuffer 是直接的。将 BrowserWindow 类更改为使用直接缓冲区已修复该错误。

下面列出了更新的部分,供遇到类似问题的其他人使用。

//This is the updated constructor from the BrowserWindow class
public BrowserWindow(int _w, int _h){
width = _w;
height = _h;

//imgData = ByteBuffer.allocate(width * height * 4 * Integer.BYTES); This is the old line that created a non-direct ByteBuffer.
imgData = ByteBuffer.allocateDirect(width * height * 4);//This is the new line. Note the allocateDirect() call and the removal of the extra space in the buffer in the form of Integer.BYTES.
texture = new PreloadedTexture(width, height);
}

关于java - 尽管确认传递的数据缓冲区足够长,但 glTexSubImage2D() 导致 EXCEPTION_ACCESS_VIOLATION (0xc0000005),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56872469/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com