blob: 1916eb24b084ad81b6098c57419ce1df14d12071 [file] [log] [blame]
/*
* Copyright © 2018, VideoLAN and dav1d authors
* Copyright © 2018, Two Orioles, LLC
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "config.h"
#include <errno.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common/intops.h"
#include "common/mem.h"
#include "common/validate.h"
#include "src/internal.h"
#include "src/log.h"
#include "src/picture.h"
#include "src/ref.h"
#include "src/thread.h"
#include "src/thread_task.h"
#include "common/starboard_memory.h"
int dav1d_default_picture_alloc(Dav1dPicture *const p, void *const cookie) {
assert(cookie == NULL);
const int hbd = p->p.bpc > 8;
const int aligned_w = (p->p.w + 127) & ~127;
const int aligned_h = (p->p.h + 127) & ~127;
const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400;
const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420;
const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444;
ptrdiff_t y_stride = aligned_w << hbd;
ptrdiff_t uv_stride = has_chroma ? y_stride >> ss_hor : 0;
/* Due to how mapping of addresses to sets works in most L1 and L2 cache
* implementations, strides of multiples of certain power-of-two numbers
* may cause multiple rows of the same superblock to map to the same set,
* causing evictions of previous rows resulting in a reduction in cache
* hit rate. Avoid that by slightly padding the stride when necessary. */
if (!(y_stride & 1023))
y_stride += DAV1D_PICTURE_ALIGNMENT;
if (!(uv_stride & 1023) && has_chroma)
uv_stride += DAV1D_PICTURE_ALIGNMENT;
p->stride[0] = y_stride;
p->stride[1] = uv_stride;
const size_t y_sz = y_stride * aligned_h;
const size_t uv_sz = uv_stride * (aligned_h >> ss_ver);
const size_t pic_size = y_sz + 2 * uv_sz + DAV1D_PICTURE_ALIGNMENT;
uint8_t *data = dav1d_alloc_aligned(pic_size, DAV1D_PICTURE_ALIGNMENT);
if (!data) return DAV1D_ERR(ENOMEM);
p->data[0] = data;
p->data[1] = has_chroma ? data + y_sz : NULL;
p->data[2] = has_chroma ? data + y_sz + uv_sz : NULL;
#ifndef NDEBUG /* safety check */
p->allocator_data = data;
#endif
return 0;
}
void dav1d_default_picture_release(Dav1dPicture *const p, void *const cookie) {
assert(cookie == NULL);
#ifndef NDEBUG /* safety check */
assert(p->allocator_data == p->data[0]);
#endif
dav1d_free_aligned(p->data[0]);
}
struct pic_ctx_context {
Dav1dPicAllocator allocator;
Dav1dPicture pic;
void *extra_ptr; /* MUST BE AT THE END */
};
static void free_buffer(const uint8_t *const data, void *const user_data) {
struct pic_ctx_context *pic_ctx = user_data;
pic_ctx->allocator.release_picture_callback(&pic_ctx->pic,
pic_ctx->allocator.cookie);
free(pic_ctx);
}
static int picture_alloc_with_edges(Dav1dContext *const c, Dav1dPicture *const p,
const int w, const int h,
Dav1dSequenceHeader *seq_hdr, Dav1dRef *seq_hdr_ref,
Dav1dFrameHeader *frame_hdr, Dav1dRef *frame_hdr_ref,
Dav1dContentLightLevel *content_light, Dav1dRef *content_light_ref,
Dav1dMasteringDisplay *mastering_display, Dav1dRef *mastering_display_ref,
Dav1dITUTT35 *itut_t35, Dav1dRef *itut_t35_ref,
const int bpc, const Dav1dDataProps *props,
Dav1dPicAllocator *const p_allocator,
const size_t extra, void **const extra_ptr)
{
if (p->data[0]) {
dav1d_log(c, "Picture already allocated!\n");
return -1;
}
assert(bpc > 0 && bpc <= 16);
struct pic_ctx_context *pic_ctx = malloc(extra + sizeof(struct pic_ctx_context));
if (pic_ctx == NULL) {
return DAV1D_ERR(ENOMEM);
}
p->p.w = w;
p->p.h = h;
p->seq_hdr = seq_hdr;
p->frame_hdr = frame_hdr;
p->content_light = content_light;
p->mastering_display = mastering_display;
p->itut_t35 = itut_t35;
p->p.layout = seq_hdr->layout;
p->p.bpc = bpc;
dav1d_data_props_set_defaults(&p->m);
int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie);
if (res < 0) {
free(pic_ctx);
return res;
}
pic_ctx->allocator = *p_allocator;
pic_ctx->pic = *p;
if (!(p->ref = dav1d_ref_wrap(p->data[0], free_buffer, pic_ctx))) {
p_allocator->release_picture_callback(p, p_allocator->cookie);
free(pic_ctx);
dav1d_log(c, "Failed to wrap picture: %s\n", strerror(errno));
return DAV1D_ERR(ENOMEM);
}
p->seq_hdr_ref = seq_hdr_ref;
if (seq_hdr_ref) dav1d_ref_inc(seq_hdr_ref);
p->frame_hdr_ref = frame_hdr_ref;
if (frame_hdr_ref) dav1d_ref_inc(frame_hdr_ref);
dav1d_data_props_copy(&p->m, props);
if (extra && extra_ptr)
*extra_ptr = &pic_ctx->extra_ptr;
p->content_light_ref = content_light_ref;
if (content_light_ref) dav1d_ref_inc(content_light_ref);
p->mastering_display_ref = mastering_display_ref;
if (mastering_display_ref) dav1d_ref_inc(mastering_display_ref);
p->itut_t35_ref = itut_t35_ref;
if (itut_t35_ref) dav1d_ref_inc(itut_t35_ref);
return 0;
}
int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f,
const int bpc)
{
Dav1dThreadPicture *const p = &f->sr_cur;
p->t = c->n_fc > 1 ? &f->frame_thread.td : NULL;
const int res =
picture_alloc_with_edges(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height,
f->seq_hdr, f->seq_hdr_ref,
f->frame_hdr, f->frame_hdr_ref,
c->content_light, c->content_light_ref,
c->mastering_display, c->mastering_display_ref,
c->itut_t35, c->itut_t35_ref,
bpc, &f->tile[0].data.m, &c->allocator,
p->t != NULL ? sizeof(atomic_int) * 2 : 0,
(void **) &p->progress);
if (res) return res;
// Must be removed from the context after being attached to the frame
dav1d_ref_dec(&c->itut_t35_ref);
c->itut_t35 = NULL;
p->visible = f->frame_hdr->show_frame;
if (p->t) {
atomic_init(&p->progress[0], 0);
atomic_init(&p->progress[1], 0);
}
return res;
}
int dav1d_picture_alloc_copy(Dav1dContext *const c, Dav1dPicture *const dst, const int w,
const Dav1dPicture *const src)
{
struct pic_ctx_context *const pic_ctx = src->ref->user_data;
const int res = picture_alloc_with_edges(c, dst, w, src->p.h,
src->seq_hdr, src->seq_hdr_ref,
src->frame_hdr, src->frame_hdr_ref,
src->content_light, src->content_light_ref,
src->mastering_display, src->mastering_display_ref,
src->itut_t35, src->itut_t35_ref,
src->p.bpc, &src->m, &pic_ctx->allocator,
0, NULL);
return res;
}
void dav1d_picture_ref(Dav1dPicture *const dst, const Dav1dPicture *const src) {
validate_input(dst != NULL);
validate_input(dst->data[0] == NULL);
validate_input(src != NULL);
if (src->ref) {
validate_input(src->data[0] != NULL);
dav1d_ref_inc(src->ref);
if (src->frame_hdr_ref) dav1d_ref_inc(src->frame_hdr_ref);
if (src->seq_hdr_ref) dav1d_ref_inc(src->seq_hdr_ref);
if (src->m.user_data.ref) dav1d_ref_inc(src->m.user_data.ref);
if (src->content_light_ref) dav1d_ref_inc(src->content_light_ref);
if (src->mastering_display_ref) dav1d_ref_inc(src->mastering_display_ref);
if (src->itut_t35_ref) dav1d_ref_inc(src->itut_t35_ref);
}
*dst = *src;
}
void dav1d_picture_move_ref(Dav1dPicture *const dst, Dav1dPicture *const src) {
validate_input(dst != NULL);
validate_input(dst->data[0] == NULL);
validate_input(src != NULL);
if (src->ref)
validate_input(src->data[0] != NULL);
*dst = *src;
memset(src, 0, sizeof(*src));
}
void dav1d_thread_picture_ref(Dav1dThreadPicture *dst,
const Dav1dThreadPicture *src)
{
dav1d_picture_ref(&dst->p, &src->p);
dst->t = src->t;
dst->visible = src->visible;
dst->progress = src->progress;
}
void dav1d_picture_unref_internal(Dav1dPicture *const p) {
validate_input(p != NULL);
if (p->ref) {
validate_input(p->data[0] != NULL);
dav1d_ref_dec(&p->ref);
dav1d_ref_dec(&p->seq_hdr_ref);
dav1d_ref_dec(&p->frame_hdr_ref);
dav1d_ref_dec(&p->m.user_data.ref);
dav1d_ref_dec(&p->content_light_ref);
dav1d_ref_dec(&p->mastering_display_ref);
dav1d_ref_dec(&p->itut_t35_ref);
}
memset(p, 0, sizeof(*p));
}
void dav1d_thread_picture_unref(Dav1dThreadPicture *const p) {
dav1d_picture_unref_internal(&p->p);
p->t = NULL;
p->progress = NULL;
}
int dav1d_thread_picture_wait(const Dav1dThreadPicture *const p,
int y_unclipped, const enum PlaneType plane_type)
{
assert(plane_type != PLANE_TYPE_ALL);
if (!p->t)
return 0;
// convert to luma units; include plane delay from loopfilters; clip
const int ss_ver = p->p.p.layout == DAV1D_PIXEL_LAYOUT_I420;
y_unclipped *= 1 << (plane_type & ss_ver); // we rely here on PLANE_TYPE_UV being 1
y_unclipped += (plane_type != PLANE_TYPE_BLOCK) * 8; // delay imposed by loopfilter
const unsigned y = iclip(y_unclipped, 1, p->p.p.h);
atomic_uint *const progress = &p->progress[plane_type != PLANE_TYPE_BLOCK];
unsigned state;
if ((state = atomic_load_explicit(progress, memory_order_acquire)) >= y)
return state == FRAME_ERROR;
dav1d_pthread_mutex_lock(&p->t->lock);
while ((state = atomic_load_explicit(progress, memory_order_relaxed)) < y)
dav1d_pthread_cond_wait(&p->t->cond, &p->t->lock);
dav1d_pthread_mutex_unlock(&p->t->lock);
return state == FRAME_ERROR;
}
void dav1d_thread_picture_signal(const Dav1dThreadPicture *const p,
const int y, // in pixel units
const enum PlaneType plane_type)
{
assert(plane_type != PLANE_TYPE_UV);
if (!p->t)
return;
dav1d_pthread_mutex_lock(&p->t->lock);
if (plane_type != PLANE_TYPE_Y)
atomic_store(&p->progress[0], y);
if (plane_type != PLANE_TYPE_BLOCK)
atomic_store(&p->progress[1], y);
dav1d_pthread_cond_broadcast(&p->t->cond);
dav1d_pthread_mutex_unlock(&p->t->lock);
}