renderer: simplify async readback API; use glMapBufferRange

glGetBufferSubData is not available on GLES
This commit is contained in:
Andrei Alexeyev 2024-05-01 00:19:07 +02:00
parent d03b2eb38c
commit 783172861a
No known key found for this signature in database
GPG key ID: 72D26128040B9690
7 changed files with 41 additions and 46 deletions

View file

@ -644,21 +644,17 @@ void r_framebuffer_read_async(
Framebuffer *framebuffer,
FramebufferAttachment attachment,
IntRect region,
Allocator *allocator,
Pixmap *out_pixmap,
void *userdata,
FramebufferReadAsyncCallback callback
) {
B.framebuffer_read_async(
framebuffer, attachment, region, allocator, out_pixmap, userdata, callback
framebuffer, attachment, region, userdata, callback
);
}
void r_framebuffer_read_viewport_async(
Framebuffer *framebuffer,
FramebufferAttachment attachment,
Allocator *allocator,
Pixmap *out_pixmap,
void *userdata,
FramebufferReadAsyncCallback callback
) {
@ -673,7 +669,7 @@ void r_framebuffer_read_viewport_async(
};
r_framebuffer_read_async(
framebuffer, attachment, region, allocator, out_pixmap, userdata, callback);
framebuffer, attachment, region, userdata, callback);
}
VertexBuffer* r_vertex_buffer_create(size_t capacity, void *data) {

View file

@ -219,7 +219,7 @@ enum {
FRAMEBUFFER_MAX_OUTPUTS = FRAMEBUFFER_MAX_COLOR_ATTACHMENTS,
};
typedef void (*FramebufferReadAsyncCallback)(Allocator *allocator, Pixmap *pixmap, void *userdata);
typedef void (*FramebufferReadAsyncCallback)(const Pixmap *pixmap, void *userdata);
typedef enum Primitive {
PRIM_POINTS,
@ -775,8 +775,8 @@ void r_framebuffer_destroy(Framebuffer *fb) attr_nonnull(1);
void r_framebuffer_clear(Framebuffer *fb, BufferKindFlags flags, const Color *colorval, float depthval);
void r_framebuffer_copy(Framebuffer *dst, Framebuffer *src, BufferKindFlags flags) attr_nonnull_all;
IntExtent r_framebuffer_get_size(Framebuffer *fb);
void r_framebuffer_read_async(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, Allocator *allocator, Pixmap *out_pixmap, void *userdata, FramebufferReadAsyncCallback callback);
void r_framebuffer_read_viewport_async(Framebuffer *framebuffer, FramebufferAttachment attachment, Allocator *allocator, Pixmap *out_pixmap, void *userdata, FramebufferReadAsyncCallback callback);
void r_framebuffer_read_async(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, void *userdata, FramebufferReadAsyncCallback callback);
void r_framebuffer_read_viewport_async(Framebuffer *framebuffer, FramebufferAttachment attachment, void *userdata, FramebufferReadAsyncCallback callback);
void r_framebuffer(Framebuffer *fb);
Framebuffer* r_framebuffer_current(void);

View file

@ -92,7 +92,7 @@ typedef struct RendererFuncs {
void (*framebuffer_clear)(Framebuffer *framebuffer, BufferKindFlags flags, const Color *colorval, float depthval);
void (*framebuffer_copy)(Framebuffer *dst, Framebuffer *src, BufferKindFlags flags);
IntExtent (*framebuffer_get_size)(Framebuffer *framebuffer);
void (*framebuffer_read_async)(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, Allocator *allocator, Pixmap *out_pixmap, void *userdata, FramebufferReadAsyncCallback callback);
void (*framebuffer_read_async)(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, void *userdata, FramebufferReadAsyncCallback callback);
void (*framebuffer)(Framebuffer *framebuffer);
Framebuffer* (*framebuffer_current)(void);

View file

@ -250,14 +250,17 @@ IntExtent gl33_framebuffer_get_effective_size(Framebuffer *framebuffer) {
}
typedef struct FramebufferReadRequest {
Pixmap *target;
Allocator *allocator;
void *userdata;
FramebufferReadAsyncCallback callback;
void *userdata;
GLuint pbo;
GLsync sync;
GLenum sync_result;
GLbitfield sync_flags;
struct {
uint width;
uint height;
PixmapFormat format;
} transfer;
} FramebufferReadRequest;
static FramebufferReadRequest read_requests[4];
@ -267,23 +270,27 @@ static void handle_read_request(FramebufferReadRequest *rq, bool ok) {
rq->sync = NULL;
rq->sync_result = GL_NONE;
auto pxm = rq->target;
if(ok) {
// data_size already specified
pxm->data.untyped = allocator_alloc(rq->allocator, pxm->data_size);
assert(rq->pbo != 0);
auto prev_pbo = gl33_buffer_current(GL33_BUFFER_BINDING_PIXEL_PACK);
gl33_bind_buffer(GL33_BUFFER_BINDING_PIXEL_PACK, rq->pbo);
gl33_sync_buffer(GL33_BUFFER_BINDING_PIXEL_PACK);
glGetBufferSubData(GL_PIXEL_PACK_BUFFER, 0, pxm->data_size, pxm->data.untyped);
auto data_size = pixmap_data_size(rq->transfer.format, rq->transfer.width, rq->transfer.height);
rq->callback(&(Pixmap) {
.width = rq->transfer.width,
.height = rq->transfer.height,
.format = rq->transfer.format,
.origin = PIXMAP_ORIGIN_BOTTOMLEFT,
.data_size = data_size,
.data.untyped = glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, data_size, GL_MAP_READ_BIT)
}, rq->userdata);
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
gl33_bind_buffer(GL33_BUFFER_BINDING_PIXEL_PACK, prev_pbo);
} else {
pxm->data_size = 0;
pxm->data.untyped = NULL;
rq->callback(NULL, rq->userdata);
}
rq->callback(rq->allocator, rq->target, rq->userdata);
}
static GLenum sync_read_request(FramebufferReadRequest *rq, GLuint64 timeout) {
@ -363,8 +370,6 @@ void gl33_framebuffer_read_async(
Framebuffer *framebuffer,
FramebufferAttachment attachment,
IntRect region,
Allocator *allocator,
Pixmap *pxm,
void *userdata,
FramebufferReadAsyncCallback callback
) {
@ -394,16 +399,10 @@ void gl33_framebuffer_read_async(
assume(fmtinfo != NULL);
}
*pxm = (Pixmap) {
.width = region.w,
.height = region.h,
.format = fmtinfo->transfer_format.pixmap_format,
.origin = PIXMAP_ORIGIN_BOTTOMLEFT,
};
auto rq = alloc_read_request();
rq->allocator = allocator;
rq->target = pxm;
rq->transfer.width = region.w;
rq->transfer.height = region.h;
rq->transfer.format = fmtinfo->transfer_format.pixmap_format;
rq->userdata = userdata;
rq->callback = callback;
rq->sync_flags = GL_SYNC_FLUSH_COMMANDS_BIT;
@ -412,12 +411,11 @@ void gl33_framebuffer_read_async(
glGenBuffers(1, &rq->pbo);
}
pxm->data_size = pixmap_data_size(pxm->format, pxm->width, pxm->height);
auto prev_pbo = gl33_buffer_current(GL33_BUFFER_BINDING_PIXEL_PACK);
gl33_bind_buffer(GL33_BUFFER_BINDING_PIXEL_PACK, rq->pbo);
gl33_sync_buffer(GL33_BUFFER_BINDING_PIXEL_PACK);
glBufferData(GL_PIXEL_PACK_BUFFER, pxm->data_size, NULL, GL_STREAM_READ);
auto data_size = pixmap_data_size(rq->transfer.format, rq->transfer.width, rq->transfer.height);
glBufferData(GL_PIXEL_PACK_BUFFER, data_size, NULL, GL_STREAM_READ);
glReadPixels(
region.x, region.y, region.w, region.h,

View file

@ -41,8 +41,6 @@ void gl33_framebuffer_read_async(
Framebuffer *framebuffer,
FramebufferAttachment attachment,
IntRect region,
Allocator *allocator,
Pixmap *pxm,
void *userdata,
FramebufferReadAsyncCallback callback
);

View file

@ -133,10 +133,8 @@ static void null_framebuffer_clear(Framebuffer *framebuffer, BufferKindFlags fla
static void null_framebuffer_copy(Framebuffer *dst, Framebuffer *src, BufferKindFlags flags) { }
static IntExtent null_framebuffer_get_size(Framebuffer *framebuffer) { return (IntExtent) { 64, 64 }; }
static void null_framebuffer_read_async(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, Allocator *allocator, Pixmap *out_pixmap, void *userdata, FramebufferReadAsyncCallback callback) {
out_pixmap->data.untyped = NULL;
out_pixmap->data_size = 0;
callback(allocator, out_pixmap, userdata);
static void null_framebuffer_read_async(Framebuffer *framebuffer, FramebufferAttachment attachment, IntRect region, void *userdata, FramebufferReadAsyncCallback callback) {
callback(NULL, userdata);
}
static int64_t null_vertex_buffer_stream_seek(SDL_RWops *rw, int64_t offset, int whence) { return 0; }

View file

@ -690,12 +690,17 @@ static void video_screenshot_free_task_data(void *arg) {
mem_free(tdata);
}
static void video_take_screenshot_callback(Allocator *allocator, Pixmap *px, void *userdata) {
if(!px->data.untyped) {
static void video_take_screenshot_callback(const Pixmap *px, void *userdata) {
if(!px) {
log_error("Failed to capture image");
return;
}
ScreenshotTaskData *tdata = userdata;
// NOTE: could convert formats here to avoid a double copy,
// but it's faster to do it on the task thread.
pixmap_copy_alloc(px, &tdata->image);
task_detach(taskmgr_global_submit((TaskParams) {
.callback = video_screenshot_task,
.userdata = userdata,
@ -721,7 +726,7 @@ void video_take_screenshot(bool viewport_only) {
}
r_framebuffer_read_viewport_async(
fb, attachment, &default_allocator, &tdata->image, tdata, video_take_screenshot_callback);
fb, attachment, tdata, video_take_screenshot_callback);
}
static void video_take_framedump(void) {
@ -741,7 +746,7 @@ static void video_take_framedump(void) {
tdata->frame_num = video.framedump.frame_count++;
r_framebuffer_read_viewport_async(
fb, attachment, &default_allocator, &tdata->image, tdata, video_take_screenshot_callback);
fb, attachment, tdata, video_take_screenshot_callback);
}
bool video_is_resizable(void) {