diff options
Diffstat (limited to 'video/out/opengl/ra_gl.c')
-rw-r--r-- | video/out/opengl/ra_gl.c | 290 |
1 files changed, 290 insertions, 0 deletions
diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c new file mode 100644 index 0000000000..bf926a197e --- /dev/null +++ b/video/out/opengl/ra_gl.c @@ -0,0 +1,290 @@ +#include "formats.h" + +#include "ra_gl.h" + +static struct ra_fns ra_fns_gl; + +int ra_init_gl(struct ra *ra, GL *gl) +{ + if (gl->version < 210 && gl->es < 200) { + MP_ERR(ra, "At least OpenGL 2.1 or OpenGL ES 2.0 required.\n"); + return -1; + } + + struct ra_gl *p = ra->priv = talloc_zero(NULL, struct ra_gl); + p->gl = gl; + + ra->fns = &ra_fns_gl; + ra->caps = 0; + if (gl->mpgl_caps & MPGL_CAP_1D_TEX) + ra->caps |= RA_CAP_TEX_1D; + if (gl->mpgl_caps & MPGL_CAP_3D_TEX) + ra->caps |= RA_CAP_TEX_3D; + + int gl_fmt_features = gl_format_feature_flags(gl); + int depth16 = gl_determine_16bit_tex_depth(gl); + + for (int n = 0; gl_formats[n].internal_format; n++) { + const struct gl_format *gl_fmt = &gl_formats[n]; + + if (!(gl_fmt->flags & gl_fmt_features)) + continue; + + struct ra_format *fmt = talloc_zero(ra, struct ra_format); + *fmt = (struct ra_format){ + .name = gl_fmt->name, + .priv = (void *)gl_fmt, + .ctype = gl_format_type(gl_fmt), + .num_components = gl_format_components(gl_fmt->format), + .pixel_size = gl_bytes_per_pixel(gl_fmt->format, gl_fmt->type), + .luminance_alpha = gl_fmt->format == GL_LUMINANCE_ALPHA, + .linear_filter = gl_fmt->flags & F_TF, + .renderable = gl_fmt->flags & F_CR, + }; + + int csize = gl_component_size(gl_fmt->type) * 8; + int depth = csize; + if (fmt->ctype == RA_CTYPE_UNORM) + depth = MPMIN(csize, depth16); // naive/approximate + if (gl_fmt->flags & F_F16) { + depth = 16; + csize = 32; // always upload as GL_FLOAT (simpler for us) + } + + for (int i = 0; i < fmt->num_components; i++) { + fmt->component_size[i] = csize; + fmt->component_depth[i] = depth; + } + + // Special formats for which OpenGL happens to have direct support. + if (strcmp(fmt->name, "rgb565") == 0) { + fmt->special_imgfmt = IMGFMT_RGB565; + struct ra_imgfmt_desc *desc = talloc_zero(fmt, struct ra_imgfmt_desc); + fmt->special_imgfmt_desc = desc; + desc->num_planes = 1; + desc->planes[0] = fmt; + for (int i = 0; i < 3; i++) + desc->components[0][i] = i + 1; + desc->chroma_w = desc->chroma_h = 1; + } + if (strcmp(fmt->name, "ashit") == 0) { + fmt->special_imgfmt = IMGFMT_UYVY; + struct ra_imgfmt_desc *desc = talloc_zero(fmt, struct ra_imgfmt_desc); + fmt->special_imgfmt_desc = desc; + desc->num_planes = 1; + desc->planes[0] = fmt; + desc->components[0][0] = 3; + desc->components[0][1] = 1; + desc->components[0][2] = 2; + desc->chroma_w = desc->chroma_h = 1; + } + + MP_TARRAY_APPEND(ra, ra->formats, ra->num_formats, fmt); + } + + gl->Disable(GL_DITHER); + + return 0; +} + +static void gl_destroy(struct ra *ra) +{ + talloc_free(ra->priv); +} + +static void gl_tex_destroy(struct ra *ra, struct ra_tex *tex) +{ + struct ra_gl *p = ra->priv; + struct ra_tex_gl *tex_gl = tex->priv; + + p->gl->DeleteTextures(1, &tex_gl->texture); + gl_pbo_upload_uninit(&tex_gl->pbo); + talloc_free(tex_gl); + talloc_free(tex); +} + +static struct ra_tex *gl_tex_create(struct ra *ra, + const struct ra_tex_params *params) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + struct ra_tex *tex = talloc_zero(NULL, struct ra_tex); + tex->params = *params; + struct ra_tex_gl *tex_gl = tex->priv = talloc_zero(NULL, struct ra_tex_gl); + + const struct gl_format *fmt = params->format->priv; + tex_gl->internal_format = fmt->internal_format; + tex_gl->format = fmt->format; + tex_gl->type = fmt->type; + switch (params->dimensions) { + case 1: tex_gl->target = GL_TEXTURE_1D; break; + case 2: tex_gl->target = GL_TEXTURE_2D; break; + case 3: tex_gl->target = GL_TEXTURE_3D; break; + default: abort(); + } + if (params->non_normalized) { + assert(params->dimensions == 2); + tex_gl->target = GL_TEXTURE_RECTANGLE; + } + + gl->GenTextures(1, &tex_gl->texture); + gl->BindTexture(tex_gl->target, tex_gl->texture); + + GLint filter = params->src_linear ? GL_LINEAR : GL_NEAREST; + GLint wrap = params->src_repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE; + gl->TexParameteri(tex_gl->target, GL_TEXTURE_MIN_FILTER, filter); + gl->TexParameteri(tex_gl->target, GL_TEXTURE_MAG_FILTER, filter); + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_S, wrap); + if (params->dimensions > 1) + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_T, wrap); + if (params->dimensions > 2) + gl->TexParameteri(tex_gl->target, GL_TEXTURE_WRAP_R, wrap); + + switch (params->dimensions) { + case 1: + gl->TexImage1D(tex_gl->target, 0, tex_gl->internal_format, params->w, + 0, tex_gl->format, tex_gl->type, NULL); + break; + case 2: + gl->TexImage2D(tex_gl->target, 0, tex_gl->internal_format, params->w, + params->h, 0, tex_gl->format, tex_gl->type, NULL); + break; + case 3: + gl->TexImage3D(tex_gl->target, 0, tex_gl->internal_format, params->w, + params->h, params->d, 0, tex_gl->format, tex_gl->type, + NULL); + break; + } + + gl->BindTexture(tex_gl->target, 0); + + return tex; +} + +static void gl_tex_upload(struct ra *ra, struct ra_tex *tex, + const void *src, ptrdiff_t stride, + struct ra_mapped_buffer *buf) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + struct ra_tex_gl *tex_gl = tex->priv; + struct ra_mapped_buffer_gl *buf_gl = NULL; + + if (buf) { + buf_gl = buf->priv; + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + src = (void *)((uintptr_t)src - (uintptr_t)buf->data); + } + + gl->BindTexture(tex_gl->target, tex_gl->texture); + + switch (tex->params.dimensions) { + case 1: + gl->TexImage1D(tex_gl->target, 0, tex_gl->internal_format, + tex->params.w, 0, tex_gl->format, tex_gl->type, src); + break; + case 2: + gl_pbo_upload_tex(&tex_gl->pbo, gl, tex->use_pbo && !buf, + tex_gl->target, tex_gl->format, tex_gl->type, + tex->params.w, tex->params.h, src, stride, + 0, 0, tex->params.w, tex->params.h); + break; + case 3: + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 1); + gl->TexImage3D(GL_TEXTURE_3D, 0, tex_gl->internal_format, tex->params.w, + tex->params.h, tex->params.d, 0, tex_gl->format, + tex_gl->type, src); + gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4); + break; + } + + gl->BindTexture(tex_gl->target, 0); + + if (buf) { + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + // Make sure the PBO is not reused until GL is done with it. If a + // previous operation is pending, "update" it by creating a new + // fence that will cover the previous operation as well. + gl->DeleteSync(buf_gl->fence); + buf_gl->fence = gl->FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0); + } +} + +static void gl_destroy_mapped_buffer(struct ra *ra, struct ra_mapped_buffer *buf) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + struct ra_mapped_buffer_gl *buf_gl = buf->priv; + + gl->DeleteSync(buf_gl->fence); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + if (buf->data) + gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + gl->DeleteBuffers(1, &buf_gl->pbo); + + talloc_free(buf_gl); + talloc_free(buf); +} + +static struct ra_mapped_buffer *gl_create_mapped_buffer(struct ra *ra, + size_t size) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + if (gl->version < 440) + return NULL; + + struct ra_mapped_buffer *buf = talloc_zero(NULL, struct ra_mapped_buffer); + buf->size = size; + buf->preferred_align = 1; + + struct ra_mapped_buffer_gl *buf_gl = buf->priv = + talloc_zero(NULL, struct ra_mapped_buffer_gl); + + unsigned flags = GL_MAP_READ_BIT | GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | + GL_MAP_COHERENT_BIT; + + gl->GenBuffers(1, &buf_gl->pbo); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, buf_gl->pbo); + gl->BufferStorage(GL_PIXEL_UNPACK_BUFFER, size, NULL, flags); + buf->data = gl->MapBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, buf->size, flags); + gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + if (!buf->data) { + gl_check_error(gl, ra->log, "mapping buffer"); + gl_destroy_mapped_buffer(ra, buf); + return NULL; + } + + return buf; +} + +static bool gl_poll_mapped_buffer(struct ra *ra, struct ra_mapped_buffer *buf) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + struct ra_mapped_buffer_gl *buf_gl = buf->priv; + + if (buf_gl->fence) { + GLenum res = gl->ClientWaitSync(buf_gl->fence, 0, 0); // non-blocking + if (res == GL_ALREADY_SIGNALED) { + gl->DeleteSync(buf_gl->fence); + buf_gl->fence = NULL; + } + } + + return !buf_gl->fence; +} + +static struct ra_fns ra_fns_gl = { + .destroy = gl_destroy, + .tex_create = gl_tex_create, + .tex_destroy = gl_tex_destroy, + .tex_upload = gl_tex_upload, + .create_mapped_buffer = gl_create_mapped_buffer, + .destroy_mapped_buffer = gl_destroy_mapped_buffer, + .poll_mapped_buffer = gl_poll_mapped_buffer, +}; + |