graphicsdriver/opengl: Use PBO when retrieving pixels

This commit is contained in:
Hajime Hoshi 2020-11-11 22:43:54 +09:00
parent 0366103b2e
commit 86a0c7aa82
7 changed files with 66 additions and 2 deletions

View File

@ -470,3 +470,11 @@ func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, he
gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil) gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gl.RGBA, gl.UNSIGNED_BYTE, nil)
gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0) gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0)
} }
func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, uint32(buffer))
pixels := make([]byte, 4*width*height)
gl.GetBufferSubData(gl.PIXEL_UNPACK_BUFFER, 0, 4*width*height, gl.Ptr(pixels))
gl.BindBuffer(gl.PIXEL_UNPACK_BUFFER, 0)
return pixels
}

View File

@ -565,3 +565,12 @@ func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, he
gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0) gl.Call("texSubImage2D", gles.TEXTURE_2D, 0, 0, 0, width, height, gles.RGBA, gles.UNSIGNED_BYTE, 0)
gl.Call("bindBuffer", int(pixelUnpackBuffer), nil) gl.Call("bindBuffer", int(pixelUnpackBuffer), nil)
} }
func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
gl := c.gl
gl.Call("bindBuffer", int(pixelUnpackBuffer), buffer)
arr := jsutil.TemporaryUint8Array(4 * width * height)
gl.Call("getBufferSubData", int(pixelUnpackBuffer), 0, arr)
gl.Call("bindBuffer", int(pixelUnpackBuffer), 0)
return jsutil.Uint8ArrayToSlice(arr)
}

View File

@ -436,3 +436,9 @@ func (c *context) replacePixelsWithPBO(buffer buffer, t textureNative, width, he
c.ctx.TexSubImage2D(gles.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil) c.ctx.TexSubImage2D(gles.TEXTURE_2D, 0, 0, 0, int32(width), int32(height), gles.RGBA, gles.UNSIGNED_BYTE, nil)
c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0) c.ctx.BindBuffer(gles.PIXEL_UNPACK_BUFFER, 0)
} }
func (c *context) getBufferSubData(buffer buffer, width, height int) []byte {
// gl.GetBufferSubData doesn't exist on OpenGL ES 2 and 3.
// As PBO is not used in mobiles, leave this unimplemented so far.
panic("opengl: getBufferSubData is not implemented for mobiles")
}

View File

@ -122,6 +122,7 @@ package gl
// typedef void (APIENTRYP GPGENBUFFERS)(GLsizei n, GLuint * buffers); // typedef void (APIENTRYP GPGENBUFFERS)(GLsizei n, GLuint * buffers);
// typedef void (APIENTRYP GPGENFRAMEBUFFERSEXT)(GLsizei n, GLuint * framebuffers); // typedef void (APIENTRYP GPGENFRAMEBUFFERSEXT)(GLsizei n, GLuint * framebuffers);
// typedef void (APIENTRYP GPGENTEXTURES)(GLsizei n, GLuint * textures); // typedef void (APIENTRYP GPGENTEXTURES)(GLsizei n, GLuint * textures);
// typedef void (APIENTRYP GPGETBUFFERSUBDATA)(GLenum target, GLintptr offset, GLsizeiptr size, void * data);
// typedef void (APIENTRYP GPGETDOUBLEI_V)(GLenum target, GLuint index, GLdouble * data); // typedef void (APIENTRYP GPGETDOUBLEI_V)(GLenum target, GLuint index, GLdouble * data);
// typedef void (APIENTRYP GPGETDOUBLEI_VEXT)(GLenum pname, GLuint index, GLdouble * params); // typedef void (APIENTRYP GPGETDOUBLEI_VEXT)(GLenum pname, GLuint index, GLdouble * params);
// typedef GLenum (APIENTRYP GPGETERROR)(); // typedef GLenum (APIENTRYP GPGETERROR)();
@ -246,6 +247,9 @@ package gl
// static void glowGenTextures(GPGENTEXTURES fnptr, GLsizei n, GLuint * textures) { // static void glowGenTextures(GPGENTEXTURES fnptr, GLsizei n, GLuint * textures) {
// (*fnptr)(n, textures); // (*fnptr)(n, textures);
// } // }
// static void glowGetBufferSubData(GPGETBUFFERSUBDATA fnptr, GLenum target, GLintptr offset, GLsizeiptr size, void * data) {
// (*fnptr)(target, offset, size, data);
// }
// static void glowGetDoublei_v(GPGETDOUBLEI_V fnptr, GLenum target, GLuint index, GLdouble * data) { // static void glowGetDoublei_v(GPGETDOUBLEI_V fnptr, GLenum target, GLuint index, GLdouble * data) {
// (*fnptr)(target, index, data); // (*fnptr)(target, index, data);
// } // }
@ -407,6 +411,7 @@ var (
gpGenBuffers C.GPGENBUFFERS gpGenBuffers C.GPGENBUFFERS
gpGenFramebuffersEXT C.GPGENFRAMEBUFFERSEXT gpGenFramebuffersEXT C.GPGENFRAMEBUFFERSEXT
gpGenTextures C.GPGENTEXTURES gpGenTextures C.GPGENTEXTURES
gpGetBufferSubData C.GPGETBUFFERSUBDATA
gpGetDoublei_v C.GPGETDOUBLEI_V gpGetDoublei_v C.GPGETDOUBLEI_V
gpGetDoublei_vEXT C.GPGETDOUBLEI_VEXT gpGetDoublei_vEXT C.GPGETDOUBLEI_VEXT
gpGetError C.GPGETERROR gpGetError C.GPGETERROR
@ -569,6 +574,10 @@ func GenTextures(n int32, textures *uint32) {
C.glowGenTextures(gpGenTextures, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(textures))) C.glowGenTextures(gpGenTextures, (C.GLsizei)(n), (*C.GLuint)(unsafe.Pointer(textures)))
} }
func GetBufferSubData(target uint32, offset int, size int, data unsafe.Pointer) {
C.glowGetBufferSubData(gpGetBufferSubData, (C.GLenum)(target), (C.GLintptr)(offset), (C.GLsizeiptr)(size), data)
}
func GetDoublei_v(target uint32, index uint32, data *float64) { func GetDoublei_v(target uint32, index uint32, data *float64) {
C.glowGetDoublei_v(gpGetDoublei_v, (C.GLenum)(target), (C.GLuint)(index), (*C.GLdouble)(unsafe.Pointer(data))) C.glowGetDoublei_v(gpGetDoublei_v, (C.GLenum)(target), (C.GLuint)(index), (*C.GLdouble)(unsafe.Pointer(data)))
} }
@ -832,6 +841,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) unsafe.Pointer) error {
if gpGenTextures == nil { if gpGenTextures == nil {
return errors.New("glGenTextures") return errors.New("glGenTextures")
} }
gpGetBufferSubData = (C.GPGETBUFFERSUBDATA)(getProcAddr("glGetBufferSubData"))
if gpGetBufferSubData == nil {
return errors.New("glGetBufferSubData")
}
gpGetDoublei_v = (C.GPGETDOUBLEI_V)(getProcAddr("glGetDoublei_v")) gpGetDoublei_v = (C.GPGETDOUBLEI_V)(getProcAddr("glGetDoublei_v"))
gpGetDoublei_vEXT = (C.GPGETDOUBLEI_VEXT)(getProcAddr("glGetDoublei_vEXT")) gpGetDoublei_vEXT = (C.GPGETDOUBLEI_VEXT)(getProcAddr("glGetDoublei_vEXT"))
gpGetError = (C.GPGETERROR)(getProcAddr("glGetError")) gpGetError = (C.GPGETERROR)(getProcAddr("glGetError"))

View File

@ -37,6 +37,7 @@ var (
gpGenBuffers uintptr gpGenBuffers uintptr
gpGenFramebuffersEXT uintptr gpGenFramebuffersEXT uintptr
gpGenTextures uintptr gpGenTextures uintptr
gpGetBufferSubData uintptr
gpGetDoublei_v uintptr gpGetDoublei_v uintptr
gpGetDoublei_vEXT uintptr gpGetDoublei_vEXT uintptr
gpGetError uintptr gpGetError uintptr
@ -199,6 +200,10 @@ func GenTextures(n int32, textures *uint32) {
syscall.Syscall(gpGenTextures, 2, uintptr(n), uintptr(unsafe.Pointer(textures)), 0) syscall.Syscall(gpGenTextures, 2, uintptr(n), uintptr(unsafe.Pointer(textures)), 0)
} }
func GetBufferSubData(target uint32, offset int, size int, data unsafe.Pointer) {
syscall.Syscall6(gpGetBufferSubData, 4, uintptr(target), uintptr(offset), uintptr(size), uintptr(data), 0, 0)
}
func GetDoublei_v(target uint32, index uint32, data *float64) { func GetDoublei_v(target uint32, index uint32, data *float64) {
syscall.Syscall(gpGetDoublei_v, 3, uintptr(target), uintptr(index), uintptr(unsafe.Pointer(data))) syscall.Syscall(gpGetDoublei_v, 3, uintptr(target), uintptr(index), uintptr(unsafe.Pointer(data)))
} }
@ -462,6 +467,10 @@ func InitWithProcAddrFunc(getProcAddr func(name string) uintptr) error {
if gpGenTextures == 0 { if gpGenTextures == 0 {
return errors.New("glGenTextures") return errors.New("glGenTextures")
} }
gpGetBufferSubData = getProcAddr("glGetBufferSubData")
if gpGetBufferSubData == 0 {
return errors.New("glGetBufferSubData")
}
gpGetDoublei_v = getProcAddr("glGetDoublei_v") gpGetDoublei_v = getProcAddr("glGetDoublei_v")
gpGetDoublei_vEXT = getProcAddr("glGetDoublei_vEXT") gpGetDoublei_vEXT = getProcAddr("glGetDoublei_vEXT")
gpGetError = getProcAddr("glGetError") gpGetError = getProcAddr("glGetError")

View File

@ -152,6 +152,11 @@ func (g *Graphics) Draw(dst, src driver.ImageID, indexLen int, indexOffset int,
destination := g.images[dst] destination := g.images[dst]
source := g.images[src] source := g.images[src]
if !destination.pbo.equal(*new(buffer)) {
g.context.deleteBuffer(destination.pbo)
destination.pbo = *new(buffer)
}
g.drawCalled = true g.drawCalled = true
if err := destination.setViewport(); err != nil { if err := destination.setViewport(); err != nil {
@ -294,6 +299,11 @@ func (g *Graphics) DrawShader(dst driver.ImageID, srcs [graphics.ShaderImageNum]
d := g.images[dst] d := g.images[dst]
s := g.shaders[shader] s := g.shaders[shader]
if !d.pbo.equal(*new(buffer)) {
g.context.deleteBuffer(d.pbo)
d.pbo = *new(buffer)
}
g.drawCalled = true g.drawCalled = true
if err := d.setViewport(); err != nil { if err := d.setViewport(); err != nil {

View File

@ -71,7 +71,16 @@ func (i *Image) Pixels() ([]byte, error) {
if err := i.ensureFramebuffer(); err != nil { if err := i.ensureFramebuffer(); err != nil {
return nil, err return nil, err
} }
p := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height)
// PBO is created only when PBO is enabled AND ReplacePixels is called.
// If PBO is enabled but the buffer doesn't exist, this means either ReplacePixels is not called or
// different draw calls than ReplacePixels were called.
if !i.graphics.context.canUsePBO() || i.pbo.equal(*new(buffer)) {
p := i.graphics.context.framebufferPixels(i.framebuffer, i.width, i.height)
return p, nil
}
p := i.graphics.context.getBufferSubData(i.pbo, i.width, i.height)
return p, nil return p, nil
} }
@ -123,12 +132,12 @@ func (i *Image) ReplacePixels(args []*driver.ReplacePixelsArgs) {
i.graphics.context.texSubImage2D(i.textureNative, w, h, args) i.graphics.context.texSubImage2D(i.textureNative, w, h, args)
return return
} }
if i.pbo.equal(*new(buffer)) { if i.pbo.equal(*new(buffer)) {
i.pbo = i.graphics.context.newPixelBufferObject(w, h) i.pbo = i.graphics.context.newPixelBufferObject(w, h)
} }
if i.pbo.equal(*new(buffer)) { if i.pbo.equal(*new(buffer)) {
panic("opengl: newPixelBufferObject failed") panic("opengl: newPixelBufferObject failed")
} }
i.graphics.context.replacePixelsWithPBO(i.pbo, i.textureNative, w, h, args) i.graphics.context.replacePixelsWithPBO(i.pbo, i.textureNative, w, h, args)
} }