mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
video: retry hardware decoder backpressure without waiting
Poll decoder and output availability instead of sleeping or waiting for a held texture. Preserve pending pictures, input packets and the presentation clock until a later player poll can proceed. Cover busy decode and output retries, inline and threaded playback, and native D3D11 texture saturation, recovery and cancellation.
This commit is contained in:
1 parent
45cede3586
commit
bffe92cae8
16 files changed
+488
-100
No files matched your search
@@ -64,8 +64,6 @@
|
||||
#define D3D11VD_MAX_SLOTS 32
|
||||
#define D3D11VD_MAX_SLICES 64 /* rh264 gathers no more */
|
||||
#define D3D11VD_OUTS 4 /* shared output textures per decoder */
|
||||
#define D3D11VD_WAIT_MS 1000
|
||||
#define D3D11VD_BEGIN_TRIES 500 /* DecoderBeginFrame's E_PENDING, 1 ms */
|
||||
/* The scaling lists in coded (zig-zag) order; a sample sets it to try
|
||||
* raster order on a GPU */
|
||||
#ifndef D3D11VD_QM_RASTER
|
||||
@@ -129,7 +127,6 @@ typedef struct d3d11vd_dec
|
||||
ID3D11Fence *ready; /* raised behind each copy */
|
||||
HANDLE ready_handle;
|
||||
uint64_t ready_value;
|
||||
HANDLE event;
|
||||
|
||||
uint8_t pp[DXVA_H264_PP_SIZE];
|
||||
uint8_t qm[DXVA_H264_QM_SIZE];
|
||||
@@ -164,8 +161,6 @@ static void d3d11vd_destroy(d3d11vd_dec_t *d)
|
||||
}
|
||||
if (d->ready_handle)
|
||||
CloseHandle(d->ready_handle);
|
||||
if (d->event)
|
||||
CloseHandle(d->event);
|
||||
D3D11VD_RELEASE(d->ready);
|
||||
for (i = 0; i < D3D11VD_MAX_SLOTS; i++)
|
||||
D3D11VD_RELEASE(d->view[i]);
|
||||
@@ -245,27 +240,6 @@ static void d3d11vd_abandon(void *owner, uint64_t done, uint64_t value)
|
||||
}
|
||||
}
|
||||
|
||||
/* Output texture @o free to be copied into: waits, on the decoding
|
||||
* thread, for its reader. */
|
||||
static bool d3d11vd_out_wait(d3d11vd_dec_t *d, unsigned o)
|
||||
{
|
||||
unsigned waited = 0;
|
||||
if (d3d11vd_out_settled(d, o))
|
||||
return true;
|
||||
if (FAILED(d->out_done[o]->lpVtbl->SetEventOnCompletion(d->out_done[o],
|
||||
d->out_value[o], d->event)))
|
||||
return false;
|
||||
/* in slices: an abandon raises no event */
|
||||
while (waited < D3D11VD_WAIT_MS)
|
||||
{
|
||||
WaitForSingleObject(d->event, 10);
|
||||
waited += 10;
|
||||
if (d3d11vd_out_settled(d, o))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* ---- open ---- */
|
||||
|
||||
static bool d3d11vd_make_device(d3d11vd_dec_t *d)
|
||||
@@ -409,8 +383,7 @@ static bool d3d11vd_make_outputs(d3d11vd_dec_t *d)
|
||||
goto end;
|
||||
retro_atomic_store_release_int(&d->out_abandoned[o], 0);
|
||||
}
|
||||
ok = d3d11vd_shared_fence(dev5, &d->ready, &d->ready_handle)
|
||||
&& (d->event = CreateEventA(NULL, FALSE, FALSE, NULL)) != NULL;
|
||||
ok = d3d11vd_shared_fence(dev5, &d->ready, &d->ready_handle);
|
||||
end:
|
||||
D3D11VD_RELEASE(dev5);
|
||||
return ok;
|
||||
@@ -523,14 +496,10 @@ static int d3d11vd_decode(void *dec, const rh264_hw_pic_t *pic)
|
||||
if (pic->refs[i].slot < 0 || (unsigned)pic->refs[i].slot >= d->slots)
|
||||
return -1;
|
||||
|
||||
for (i = 0; ; i++)
|
||||
{
|
||||
hr = d->vctx->lpVtbl->DecoderBeginFrame(d->vctx, d->decoder,
|
||||
d->view[pic->slot], 0, NULL);
|
||||
if (hr != E_PENDING || i >= D3D11VD_BEGIN_TRIES)
|
||||
break;
|
||||
Sleep(1);
|
||||
}
|
||||
hr = d->vctx->lpVtbl->DecoderBeginFrame(d->vctx, d->decoder,
|
||||
d->view[pic->slot], 0, NULL);
|
||||
if (hr == E_PENDING)
|
||||
return 1;
|
||||
if (FAILED(hr))
|
||||
return -1;
|
||||
|
||||
@@ -597,17 +566,16 @@ static int d3d11vd_output(void *dec, unsigned slot, video_hw_frame_t *out)
|
||||
{
|
||||
d3d11vd_dec_t *d = (d3d11vd_dec_t*)dec;
|
||||
D3D11_BOX box;
|
||||
unsigned o;
|
||||
unsigned o, n;
|
||||
|
||||
if (slot >= d->slots)
|
||||
return -1;
|
||||
/* the oldest texture, once its last reader is done */
|
||||
o = d->out_next++ % D3D11VD_OUTS;
|
||||
if (!d3d11vd_out_wait(d, o))
|
||||
{
|
||||
RARCH_WARN("[D3D11] Video decode: an output frame was never given back.\n");
|
||||
return -1;
|
||||
}
|
||||
for (n = 0; n < D3D11VD_OUTS; n++)
|
||||
if (d3d11vd_out_settled(d, (d->out_next + n) % D3D11VD_OUTS))
|
||||
break;
|
||||
if (n == D3D11VD_OUTS)
|
||||
return 1;
|
||||
o = (d->out_next + n) % D3D11VD_OUTS;
|
||||
box.left = d->crop_x;
|
||||
box.top = d->crop_y;
|
||||
box.front = 0;
|
||||
@@ -621,6 +589,7 @@ static int d3d11vd_output(void *dec, unsigned slot, video_hw_frame_t *out)
|
||||
return -1;
|
||||
d->ready_value++;
|
||||
d->ctx->lpVtbl->Flush(d->ctx);
|
||||
d->out_next = (o + 1) % D3D11VD_OUTS;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
out->image = (uint64_t)(uintptr_t)d->out_handle[o];
|
||||
|
||||
@@ -224,6 +224,16 @@ static bool vvd_wait(VkSemaphore sem, uint64_t value, uint64_t timeout_ns)
|
||||
return vvd.wait_sems(vvd.device, &info, timeout_ns) == VK_SUCCESS;
|
||||
}
|
||||
|
||||
static int vvd_try_wait(VkSemaphore sem, uint64_t value)
|
||||
{
|
||||
uint64_t now;
|
||||
VkResult r;
|
||||
if (!value)
|
||||
return 0;
|
||||
r = vvd.counter(vvd.device, sem, &now);
|
||||
return r == VK_SUCCESS ? (now < value ? 1 : 0) : -1;
|
||||
}
|
||||
|
||||
static bool vvd_reached(VkSemaphore sem, uint64_t value)
|
||||
{
|
||||
uint64_t now = 0;
|
||||
@@ -897,7 +907,7 @@ static bool vvd_make_params(vvd_dec_t *d, const rh264_hw_pic_t *pic)
|
||||
* it is done */
|
||||
if (d->params)
|
||||
{
|
||||
if (!vvd_wait(d->tl, d->tl_value, VVD_WAIT_NS))
|
||||
if (!vvd_reached(d->tl, d->tl_value))
|
||||
return false;
|
||||
vvd.destroy_params(vvd.device, d->params, NULL);
|
||||
d->params = VK_NULL_HANDLE;
|
||||
@@ -1105,18 +1115,26 @@ static int vvd_decode(void *dec, const rh264_hw_pic_t *pic)
|
||||
VkCommandBuffer cb;
|
||||
VkDeviceSize size;
|
||||
unsigned i, n, k;
|
||||
int ready;
|
||||
unsigned setup = (unsigned)pic->slot;
|
||||
|
||||
if (setup >= d->slots || pic->num_refs > 16 || !pic->num_slices)
|
||||
return -1;
|
||||
if (d->have_params && pic->params_gen != d->params_gen)
|
||||
{
|
||||
ready = vvd_try_wait(d->tl, d->tl_value);
|
||||
if (ready != 0)
|
||||
return ready;
|
||||
}
|
||||
if ( (!d->have_params || pic->params_gen != d->params_gen)
|
||||
&& !vvd_make_params(d, pic))
|
||||
return -1;
|
||||
|
||||
/* the ring entry, once the work it last held is done */
|
||||
k = d->dring++ % VVD_RING;
|
||||
if (!vvd_wait(d->tl, d->dcb_value[k], VVD_WAIT_NS))
|
||||
return -1;
|
||||
k = d->dring % VVD_RING;
|
||||
ready = vvd_try_wait(d->tl, d->dcb_value[k]);
|
||||
if (ready != 0)
|
||||
return ready;
|
||||
size = (pic->size + d->bs_size_align - 1) / d->bs_size_align
|
||||
* d->bs_size_align;
|
||||
if (!vvd_bitstream(d, k, size))
|
||||
@@ -1245,6 +1263,7 @@ static int vvd_decode(void *dec, const rh264_hw_pic_t *pic)
|
||||
return -1;
|
||||
d->reset = true;
|
||||
d->dcb_value[k] = d->tl_value;
|
||||
d->dring++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1256,20 +1275,26 @@ static int vvd_output(void *dec, unsigned slot, video_hw_frame_t *out)
|
||||
VkCommandBuffer cb;
|
||||
VkImage src;
|
||||
uint32_t layer;
|
||||
unsigned o, k;
|
||||
unsigned o, k, n;
|
||||
int ready;
|
||||
|
||||
if (slot >= d->slots)
|
||||
return -1;
|
||||
/* the oldest output image, once its last reader is done */
|
||||
o = d->out_next++ % VVD_OUTS;
|
||||
if (!vvd_wait(d->out_done[o], d->out_value[o], VVD_WAIT_NS))
|
||||
for (n = 0; n < VVD_OUTS; n++)
|
||||
{
|
||||
RARCH_WARN("[Vulkan] Video decode: an output frame was never given back.\n");
|
||||
return -1;
|
||||
o = (d->out_next + n) % VVD_OUTS;
|
||||
ready = vvd_try_wait(d->out_done[o], d->out_value[o]);
|
||||
if (ready < 0)
|
||||
return -1;
|
||||
if (!ready)
|
||||
break;
|
||||
}
|
||||
k = d->xring++ % VVD_RING;
|
||||
if (!vvd_wait(d->tl, d->xcb_value[k], VVD_WAIT_NS))
|
||||
return -1;
|
||||
if (n == VVD_OUTS)
|
||||
return 1;
|
||||
k = d->xring % VVD_RING;
|
||||
ready = vvd_try_wait(d->tl, d->xcb_value[k]);
|
||||
if (ready != 0)
|
||||
return ready;
|
||||
|
||||
src = d->dpb[d->dpb_array ? 0 : slot];
|
||||
layer = d->dpb_array ? slot : 0;
|
||||
@@ -1322,6 +1347,8 @@ static int vvd_output(void *dec, unsigned slot, video_hw_frame_t *out)
|
||||
|| !vvd_submit(d, d->xq, cb))
|
||||
return -1;
|
||||
d->xcb_value[k] = d->tl_value;
|
||||
d->out_next = (o + 1) % VVD_OUTS;
|
||||
d->xring++;
|
||||
|
||||
memset(out, 0, sizeof(*out));
|
||||
memcpy(&out->image, &d->out[o], sizeof(d->out[o]));
|
||||
|
||||
@@ -10484,6 +10484,10 @@ typedef struct rh264_hw
|
||||
int out_gen[RH264_OUT_SLOTS];
|
||||
uint8_t out_used[RH264_OUT_SLOTS];
|
||||
int out_len;
|
||||
int pending_out;
|
||||
const uint8_t *retry_data;
|
||||
size_t retry_len, retry_pos;
|
||||
int retrying, retry_got;
|
||||
rh264_hw_ref_t refs[RH264_MAX_REFS];
|
||||
video_hw_frame_t shown;
|
||||
int have_shown;
|
||||
@@ -10683,8 +10687,8 @@ static int rh264_hw_out_pick(const rh264_hw *hw)
|
||||
static int rh264_hw_show(rh264_video *v, int i)
|
||||
{
|
||||
rh264_hw *hw = v->hw;
|
||||
hw->out_used[i] = 0;
|
||||
hw->out_len--;
|
||||
video_hw_frame_t shown;
|
||||
int r;
|
||||
if (v->hw_readback)
|
||||
{
|
||||
int w = hw->sps.width, h = hw->sps.height;
|
||||
@@ -10710,17 +10714,24 @@ static int rh264_hw_show(rh264_video *v, int i)
|
||||
}
|
||||
hw->have_cpu = 1;
|
||||
hw->fresh = 1;
|
||||
hw->out_used[i] = 0;
|
||||
hw->out_len--;
|
||||
return 0;
|
||||
}
|
||||
/* two pictures in one call: the first is passed over */
|
||||
if (hw->fresh)
|
||||
video_hw_frame_abandon(&hw->shown);
|
||||
hw->have_shown = 0;
|
||||
if (hw->ops->output(hw->dec, (unsigned)hw->out_slot[i], &hw->shown) != 0)
|
||||
r = hw->ops->output(hw->dec, (unsigned)hw->out_slot[i], &shown);
|
||||
if (r > 0)
|
||||
return 2;
|
||||
if (r < 0)
|
||||
{
|
||||
hw->broken = 1;
|
||||
return -1;
|
||||
}
|
||||
if (hw->fresh)
|
||||
video_hw_frame_abandon(&hw->shown);
|
||||
hw->shown = shown;
|
||||
hw->out_used[i] = 0;
|
||||
hw->out_len--;
|
||||
hw->have_shown = 1;
|
||||
hw->fresh = 1;
|
||||
return 0;
|
||||
@@ -10730,7 +10741,7 @@ static int rh264_hw_show(rh264_video *v, int i)
|
||||
static int rh264_hw_out_push(rh264_video *v, int poc, int is_idr)
|
||||
{
|
||||
rh264_hw *hw = v->hw;
|
||||
int i, slot = -1;
|
||||
int i, r, slot = -1;
|
||||
for (i = 0; i < RH264_OUT_SLOTS; i++)
|
||||
if (!hw->out_used[i]) { slot = i; break; }
|
||||
if (slot < 0)
|
||||
@@ -10742,8 +10753,12 @@ static int rh264_hw_out_push(rh264_video *v, int poc, int is_idr)
|
||||
hw->out_len++;
|
||||
if (hw->out_len <= v->reorder_delay && !(is_idr && hw->out_len == 1))
|
||||
return 0;
|
||||
if (rh264_hw_show(v, rh264_hw_out_pick(hw)) != 0)
|
||||
return -1;
|
||||
i = rh264_hw_out_pick(hw);
|
||||
r = rh264_hw_show(v, i);
|
||||
if (r == 2)
|
||||
hw->pending_out = i + 1;
|
||||
if (r != 0)
|
||||
return r;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -10756,11 +10771,22 @@ static int rh264_hw_finish(rh264_video *v, int *got_pic)
|
||||
int j, r;
|
||||
unsigned n = 0;
|
||||
|
||||
if (hw->pending_out)
|
||||
{
|
||||
r = rh264_hw_show(v, hw->pending_out - 1);
|
||||
if (r != 0)
|
||||
return r;
|
||||
hw->pending_out = 0;
|
||||
*got_pic = 1;
|
||||
return 0;
|
||||
}
|
||||
if (!hw->pic_open)
|
||||
return 0;
|
||||
hw->pic_open = 0;
|
||||
if (hw->skip)
|
||||
{
|
||||
hw->pic_open = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* an IDR predicts from nothing: the DPB empties under it */
|
||||
for (j = 0; j < v->dpb_len && !hw->idr; j++)
|
||||
@@ -10801,11 +10827,15 @@ static int rh264_hw_finish(rh264_video *v, int *got_pic)
|
||||
pic.long_term = 1;
|
||||
pic.long_term_idx = v->pend_mmco_a[j];
|
||||
}
|
||||
if (hw->ops->decode(hw->dec, &pic) != 0)
|
||||
r = hw->ops->decode(hw->dec, &pic);
|
||||
if (r > 0)
|
||||
return 2;
|
||||
if (r < 0)
|
||||
{
|
||||
hw->broken = 1;
|
||||
return -1;
|
||||
}
|
||||
hw->pic_open = 0;
|
||||
|
||||
if (hw->idr)
|
||||
{
|
||||
@@ -10840,8 +10870,8 @@ static int rh264_hw_finish(rh264_video *v, int *got_pic)
|
||||
}
|
||||
r = rh264_hw_out_push(v, v->last_poc, epoch);
|
||||
}
|
||||
if (r < 0)
|
||||
return -1;
|
||||
if (r < 0 || r == 2)
|
||||
return r;
|
||||
if (r > 0)
|
||||
*got_pic = 1;
|
||||
return 0;
|
||||
@@ -10876,8 +10906,11 @@ static int rh264_hw_slice(rh264_video *v, const uint8_t *nal, size_t len,
|
||||
if (sh->first_mb_in_slice == 0)
|
||||
{
|
||||
/* a picture the last access unit left open ends here */
|
||||
if (rh264_hw_finish(v, got_pic) != 0)
|
||||
return -1;
|
||||
{
|
||||
int r = rh264_hw_finish(v, got_pic);
|
||||
if (r != 0)
|
||||
return r;
|
||||
}
|
||||
hw->skip = 0;
|
||||
if (nri == 0 && retro_atomic_load_acquire_int(&v->skip_nonref))
|
||||
{
|
||||
@@ -11083,7 +11116,7 @@ static int rh264_video_handle_slice_nal(rh264_video *v, const uint8_t *nal,
|
||||
if (type == 7 || type == 8)
|
||||
return rh264_hw_take_ps(v, nal, nl) != 0 ? -1 : 0;
|
||||
if (type == 5 || type == 1)
|
||||
return rh264_hw_slice(v, nal, nl, got_pic) != 0 ? -1 : 0;
|
||||
return rh264_hw_slice(v, nal, nl, got_pic);
|
||||
return 0;
|
||||
}
|
||||
/* a switching slice was refused earlier: everything after it would
|
||||
@@ -11112,6 +11145,17 @@ static int rh264_video_handle_slice_nal(rh264_video *v, const uint8_t *nal,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rh264_hw_retry(rh264_video *v, const uint8_t *data,
|
||||
size_t len, size_t pos, int got)
|
||||
{
|
||||
v->hw->retry_data = data;
|
||||
v->hw->retry_len = len;
|
||||
v->hw->retry_pos = pos;
|
||||
v->hw->retry_got = got;
|
||||
v->hw->retrying = 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len)
|
||||
{
|
||||
size_t p = 0;
|
||||
@@ -11119,7 +11163,17 @@ int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len)
|
||||
if (!v || !data) return -1;
|
||||
v->dropped = 0;
|
||||
if (v->hw)
|
||||
v->hw->fresh = 0;
|
||||
{
|
||||
if (v->hw->retrying)
|
||||
{
|
||||
if (data != v->hw->retry_data || len != v->hw->retry_len)
|
||||
return -1;
|
||||
p = v->hw->retry_pos;
|
||||
got_pic = v->hw->retry_got;
|
||||
}
|
||||
else
|
||||
v->hw->fresh = 0;
|
||||
}
|
||||
|
||||
if (v->nal_length_size > 0 && len >= (size_t)v->nal_length_size)
|
||||
{
|
||||
@@ -11132,12 +11186,14 @@ int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len)
|
||||
int ls = v->nal_length_size, i;
|
||||
while (p + ls <= len)
|
||||
{
|
||||
size_t nl = 0;
|
||||
size_t nl = 0, at = p;
|
||||
for (i = 0; i < ls; i++) nl = (nl << 8) | data[p + i];
|
||||
p += ls;
|
||||
if (nl == 0 || p + nl > len) break;
|
||||
{
|
||||
int r = rh264_video_handle_slice_nal(v, data + p, nl, &got_pic);
|
||||
if (r == 2)
|
||||
return rh264_hw_retry(v, data, len, at, got_pic);
|
||||
if (r < 0) return -1;
|
||||
/* One coded picture per call: sequentially the picture
|
||||
* shown is this sample's, complete at its last slice, and
|
||||
@@ -11166,6 +11222,8 @@ int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len)
|
||||
if (s < e)
|
||||
{
|
||||
int r = rh264_video_handle_slice_nal(v, data + s, e - s, &got_pic);
|
||||
if (r == 2)
|
||||
return rh264_hw_retry(v, data, len, p, got_pic);
|
||||
if (r < 0) return -1;
|
||||
/* One coded picture per call: sequentially the picture
|
||||
* shown is this sample's, complete at its last slice, and
|
||||
@@ -11179,8 +11237,15 @@ int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len)
|
||||
}
|
||||
}
|
||||
/* on the backend a picture is whole at the end of its access unit */
|
||||
if (v->hw && rh264_hw_finish(v, &got_pic) != 0)
|
||||
return -1;
|
||||
if (v->hw)
|
||||
{
|
||||
int r = rh264_hw_finish(v, &got_pic);
|
||||
if (r == 2)
|
||||
return rh264_hw_retry(v, data, len, len, got_pic);
|
||||
v->hw->retrying = 0;
|
||||
if (r < 0)
|
||||
return -1;
|
||||
}
|
||||
return got_pic ? 1 : 0;
|
||||
}
|
||||
|
||||
@@ -11338,9 +11403,15 @@ int rh264_video_drain(rh264_video *v)
|
||||
if (v->hw)
|
||||
{
|
||||
int got = 0;
|
||||
int r;
|
||||
if (v->hw->retrying)
|
||||
return 2;
|
||||
v->hw->fresh = 0;
|
||||
if (v->hw->broken || rh264_hw_finish(v, &got) != 0)
|
||||
if (v->hw->broken)
|
||||
return -1;
|
||||
r = rh264_hw_finish(v, &got);
|
||||
if (r != 0)
|
||||
return r;
|
||||
if (got)
|
||||
return 0;
|
||||
if ((i = rh264_hw_out_pick(v->hw)) < 0)
|
||||
|
||||
@@ -1386,6 +1386,19 @@ void *image_transfer_anim_stream_new_avail(void *buf, size_t len,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool image_transfer_anim_stream_waiting(const void *stream,
|
||||
enum image_type_enum type)
|
||||
{
|
||||
#ifdef HAVE_RMP4
|
||||
if (type == IMAGE_TYPE_MP4)
|
||||
return rmp4_video_stream_waiting((const rmp4_video_stream_t*)stream) != 0;
|
||||
#else
|
||||
(void)stream;
|
||||
(void)type;
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
void image_transfer_anim_stream_free(void *stream,
|
||||
enum image_type_enum type){
|
||||
switch (type)
|
||||
|
||||
@@ -68,6 +68,9 @@
|
||||
struct rmp4_video_stream
|
||||
{
|
||||
rmp4_t *demux;
|
||||
rmp4_packet pending_packet;
|
||||
size_t pending_floor;
|
||||
int pending, waiting;
|
||||
rvp8_video *vp8;
|
||||
#ifdef HAVE_RVP9
|
||||
rvp9_dec *vp9;
|
||||
@@ -934,6 +937,8 @@ static int rmp4_video_decode_packet(rmp4_video_stream_t *s,
|
||||
return 0;
|
||||
}
|
||||
dec = rh264_video_decode(s->h264, pkt->data, pkt->size);
|
||||
if (dec == 2)
|
||||
return 2;
|
||||
if (dec < 0)
|
||||
{
|
||||
if (pkt->keyframe)
|
||||
@@ -1010,10 +1015,21 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
|
||||
int *duration_ms)
|
||||
{
|
||||
rmp4_packet pkt;
|
||||
int drained;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
int r = rmp4_read_packet(s->demux, &pkt);
|
||||
int r;
|
||||
if (s->pending)
|
||||
{
|
||||
pkt = s->pending_packet;
|
||||
r = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
s->pending_floor = rmp4_consumed(s->demux);
|
||||
r = rmp4_read_packet(s->demux, &pkt);
|
||||
}
|
||||
if (r == RMP4_READ_AGAIN)
|
||||
/* The sample's bytes are not buffered yet. Do NOT fall
|
||||
* through to the drain below: packets remain, and flushing
|
||||
@@ -1025,6 +1041,13 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
|
||||
if (pkt.track != s->track)
|
||||
continue;
|
||||
r = rmp4_video_decode_packet(s, &pkt);
|
||||
if (r == 2)
|
||||
{
|
||||
s->pending_packet = pkt;
|
||||
s->pending = 1;
|
||||
return 2;
|
||||
}
|
||||
s->pending = 0;
|
||||
if (r < 0)
|
||||
return -1; /* decode error: end the animation */
|
||||
if (r == 0)
|
||||
@@ -1037,7 +1060,9 @@ static int rmp4_video_stream_step(rmp4_video_stream_t *s,
|
||||
/* Out of packets. Display reordering can leave the last few pictures
|
||||
* queued inside the H.264 decoder; hand them out before ending the
|
||||
* pass, one per call, on the same presentation clock. */
|
||||
if (s->h264 && rh264_video_drain(s->h264) == 0)
|
||||
if (s->h264 && (drained = rh264_video_drain(s->h264)) == 2)
|
||||
return 2;
|
||||
if (s->h264 && drained == 0)
|
||||
{
|
||||
s->rndr_kind = 0; /* the drain replaced the current picture */
|
||||
if (rh264_video_is_hw(s->h264))
|
||||
@@ -1079,15 +1104,22 @@ int rmp4_video_stream_skip(rmp4_video_stream_t *s, int *duration_ms)
|
||||
|
||||
if (!s)
|
||||
return -1;
|
||||
s->waiting = 0;
|
||||
|
||||
/* 2 (not yet resident) also ends the loop: nothing was consumed,
|
||||
* and the caller retries after feeding more bytes. Fully-resident
|
||||
* streams never see it. */
|
||||
while ((r = rmp4_video_stream_step(s, duration_ms)) == 0)
|
||||
; /* non-shown frame: keep going */
|
||||
s->waiting = r == 2;
|
||||
return r;
|
||||
}
|
||||
|
||||
int rmp4_video_stream_waiting(const rmp4_video_stream_t *s)
|
||||
{
|
||||
return s && s->waiting;
|
||||
}
|
||||
|
||||
int rmp4_video_stream_set_hw(rmp4_video_stream_t *s, int enable)
|
||||
{
|
||||
if (!s || s->codec != RMP4_CODEC_H264)
|
||||
@@ -1318,7 +1350,7 @@ size_t rmp4_video_stream_media_floor(rmp4_video_stream_t *s)
|
||||
|
||||
size_t rmp4_video_stream_consumed(rmp4_video_stream_t *s)
|
||||
{
|
||||
return s ? rmp4_consumed(s->demux) : 0;
|
||||
return s ? (s->pending ? s->pending_floor : rmp4_consumed(s->demux)) : 0;
|
||||
}
|
||||
|
||||
int64_t rmp4_video_stream_duration_ns(rmp4_video_stream_t *s)
|
||||
@@ -1530,6 +1562,7 @@ void rmp4_video_stream_rewind(rmp4_video_stream_t *s)
|
||||
{
|
||||
if (!s)
|
||||
return;
|
||||
s->pending = s->waiting = 0;
|
||||
rmp4_video_hwf_drop(s);
|
||||
s->rndr_kind = 0; /* decoder reset invalidates the planes */
|
||||
rmp4_rewind(s->demux);
|
||||
|
||||
@@ -64,6 +64,7 @@ enum vp_job_status
|
||||
VP_JOB_FINISHED, /* loops done, or the stream failed */
|
||||
VP_JOB_HELD, /* presented as QUEUED: the slot is the sink's */
|
||||
VP_JOB_IDLE, /* neither the worker's nor holding a frame */
|
||||
VP_JOB_WAITING, /* hardware busy; the next poll retries */
|
||||
VP_JOB_CANCELLED, /* released while queued: the worker still
|
||||
holds its place in the queue */
|
||||
VP_JOB_DROPPED /* the worker came to it and let it go */
|
||||
@@ -124,6 +125,8 @@ static const uint32_t *vp_next(void *stream, enum image_type_enum type,
|
||||
duration_ms);
|
||||
if (frame)
|
||||
return frame;
|
||||
if (image_transfer_anim_stream_waiting(stream, type))
|
||||
return NULL;
|
||||
if (*loops_left > 0)
|
||||
(*loops_left)--;
|
||||
if (*loops_left == 0)
|
||||
@@ -177,13 +180,13 @@ static void vp_schedule(video_player_t *p, int duration_ms, int64_t now)
|
||||
}
|
||||
|
||||
/* Worker or polling thread: a GPU frame into hw[@idx]. */
|
||||
static bool vp_step_hw(video_player_t *p, unsigned idx, int32_t *loops_left,
|
||||
static int vp_step_hw(video_player_t *p, unsigned idx, int32_t *loops_left,
|
||||
int *duration_ms)
|
||||
{
|
||||
if (p->sink.feed && !p->sink.feed(p->sink.user))
|
||||
return false;
|
||||
if (!vp_next(p->stream, p->type, loops_left, duration_ms))
|
||||
return false;
|
||||
return image_transfer_anim_stream_waiting(p->stream, p->type) ? 2 : 0;
|
||||
/* a frame that came in software, which a backend that took the
|
||||
* stream once and not after a rewind gives, ends the playback */
|
||||
if (!image_transfer_anim_stream_hw_frame(p->stream, p->type,
|
||||
@@ -245,7 +248,7 @@ static void vp_pool_sync(void)
|
||||
/* Worker: one shown frame into job->frame in its final order. false
|
||||
* when the playback is over. The job is RUNNING: the stream is the
|
||||
* worker's alone. */
|
||||
static bool vp_job_step(vp_job_t *job)
|
||||
static int vp_job_step(vp_job_t *job)
|
||||
{
|
||||
video_player_t *p = job->owner;
|
||||
void *stream = p->stream;
|
||||
@@ -257,10 +260,11 @@ static bool vp_job_step(vp_job_t *job)
|
||||
|
||||
if (job->gpu)
|
||||
{
|
||||
job->hw = vp_step_hw(p, (job == p->job[1]) ? 1 : 0,
|
||||
int r = vp_step_hw(p, (job == p->job[1]) ? 1 : 0,
|
||||
&job->loops_left, &duration_ms);
|
||||
job->hw = r == 1;
|
||||
job->duration_ms = duration_ms;
|
||||
return job->hw;
|
||||
return r;
|
||||
}
|
||||
/* Every stream emits the order asked for; one that cannot is
|
||||
* swizzled below. Asked a frame at a time, and a repeat ask of the
|
||||
@@ -334,7 +338,7 @@ static void vp_worker(void *unused)
|
||||
(void)unused;
|
||||
for (;;)
|
||||
{
|
||||
bool alive;
|
||||
int alive;
|
||||
vp_job_t *job = vp_worker_local;
|
||||
|
||||
if (!job)
|
||||
@@ -380,7 +384,7 @@ static void vp_worker(void *unused)
|
||||
/* Publishes the frame to the poll, and wakes a release waiting
|
||||
* the decode out. */
|
||||
retro_atomic_store_release_int(&job->status,
|
||||
alive ? VP_JOB_READY : VP_JOB_FINISHED);
|
||||
alive == 2 ? VP_JOB_WAITING : (alive ? VP_JOB_READY : VP_JOB_FINISHED));
|
||||
retro_eventcount_notify(&vp_worker_done);
|
||||
}
|
||||
}
|
||||
@@ -537,6 +541,11 @@ static enum video_player_state vp_poll_threaded(video_player_t *p,
|
||||
* READY one is done; the pair needs no joint snapshot */
|
||||
su = retro_atomic_load_acquire_int(&ju->status);
|
||||
so = retro_atomic_load_acquire_int(&jo->status);
|
||||
if (su == VP_JOB_WAITING)
|
||||
{
|
||||
vp_job_enqueue(ju);
|
||||
return VIDEO_PLAYER_PLAYING;
|
||||
}
|
||||
/* a job that writes its slot no more lets the sink keep it no more */
|
||||
if (p->sink.slot_end && !(p->flags & VP_HW))
|
||||
{
|
||||
@@ -637,7 +646,10 @@ static enum video_player_state vp_poll_inline(video_player_t *p,
|
||||
fr.hw = NULL;
|
||||
if (p->flags & VP_HW)
|
||||
{
|
||||
if (!vp_step_hw(p, 0, &p->loops_left, &duration_ms))
|
||||
int r = vp_step_hw(p, 0, &p->loops_left, &duration_ms);
|
||||
if (r == 2)
|
||||
return VIDEO_PLAYER_PLAYING;
|
||||
if (!r)
|
||||
return VIDEO_PLAYER_ENDED;
|
||||
fr.pixels = NULL;
|
||||
fr.slot = 0;
|
||||
|
||||
@@ -620,6 +620,8 @@ bool image_transfer_anim_stream_set_output(void *stream,
|
||||
* frame came - and read or abandoned by the caller. */
|
||||
bool image_transfer_anim_stream_set_hw(void *stream,
|
||||
enum image_type_enum type, bool enable);
|
||||
bool image_transfer_anim_stream_waiting(const void *stream,
|
||||
enum image_type_enum type);
|
||||
bool image_transfer_anim_stream_hw_frame(void *stream,
|
||||
enum image_type_enum type, struct video_hw_frame *out);
|
||||
|
||||
|
||||
@@ -88,11 +88,13 @@ int rh264_video_set_extradata(rh264_video *v, const uint8_t *avcc, size_t len);
|
||||
* decode order: returns 1 when a picture is ready through rh264_video_plane,
|
||||
* 0 when the data was consumed but the picture is still held for reordering,
|
||||
* and negative on malformed input or an unsupported (high-profile) stream. */
|
||||
/* 2 on hardware backpressure: retry with the same live data and length. */
|
||||
int rh264_video_decode(rh264_video *v, const uint8_t *data, size_t len);
|
||||
|
||||
/* Hand out the next pending picture in display order without feeding more
|
||||
* data, for end of stream. Returns 0 when a picture became available through
|
||||
* rh264_video_plane, -1 when nothing is pending. */
|
||||
/* 2 when hardware output is busy; retry later. */
|
||||
int rh264_video_drain(rh264_video *v);
|
||||
|
||||
/* Bits per sample of the decoded pictures (8..14). Above 8 the planes
|
||||
@@ -255,10 +257,11 @@ typedef struct rh264_hw_ops
|
||||
* NULL when the backend cannot take the stream. On the decoding
|
||||
* thread, as are the rest. */
|
||||
void *(*open)(void *user, const rh264_hw_sps_t *sps, unsigned slots);
|
||||
/* Decode @pic into its slot; 0, or negative on failure. */
|
||||
/* 0 done, 1 busy without submitting, negative on failure. */
|
||||
int (*decode)(void *dec, const rh264_hw_pic_t *pic);
|
||||
/* The picture in @slot is to be shown: a frame of it into @out that
|
||||
* stays good while the slot is decoded into again. 0, or negative. */
|
||||
* stays good while the slot is decoded into again. 0 done, 1 busy
|
||||
* without taking a lease, negative on failure. */
|
||||
int (*output)(void *dec, unsigned slot, video_hw_frame_t *out);
|
||||
void (*close)(void *dec);
|
||||
/* Optional: copy the visible picture in slot to packed I420 planes.
|
||||
|
||||
@@ -125,6 +125,8 @@ void rmp4_video_stream_get_info(const rmp4_video_stream_t *stream,
|
||||
* memory order R,G,B,A by default; see rmp4_video_stream_set_argb. */
|
||||
const uint32_t *rmp4_video_stream_next(rmp4_video_stream_t *stream,
|
||||
int *duration_ms);
|
||||
/* NULL from next with waiting set is backpressure, not end of pass. */
|
||||
int rmp4_video_stream_waiting(const rmp4_video_stream_t *stream);
|
||||
|
||||
/* Select the channel order of subsequent frames: non-zero emits ARGB
|
||||
* words (memory order B,G,R,A on little-endian), zero restores the
|
||||
|
||||
@@ -107,6 +107,8 @@ typedef struct
|
||||
} fake_t;
|
||||
|
||||
static fake_t fake;
|
||||
static int inject_busy;
|
||||
static int decode_busy, output_busy;
|
||||
/* the sample the next decode call is for, as the test feeds them */
|
||||
static int cur_sample;
|
||||
|
||||
@@ -139,6 +141,12 @@ static int fake_decode(void *dec, const rh264_hw_pic_t *pic)
|
||||
unsigned i, j;
|
||||
fake_t *f = (fake_t*)dec;
|
||||
(void)f;
|
||||
if (inject_busy && !decode_busy)
|
||||
{
|
||||
decode_busy = 1;
|
||||
return 1;
|
||||
}
|
||||
decode_busy = 0;
|
||||
if (pic->slot < 0 || (unsigned)pic->slot >= fake.slots)
|
||||
{
|
||||
fake_err("picture %d: slot %d out of range", cur_sample, pic->slot);
|
||||
@@ -211,6 +219,12 @@ static void fake_abandon(void *owner, uint64_t done, uint64_t value)
|
||||
static int fake_output(void *dec, unsigned slot, video_hw_frame_t *out)
|
||||
{
|
||||
(void)dec;
|
||||
if (inject_busy && !output_busy)
|
||||
{
|
||||
output_busy = 1;
|
||||
return 1;
|
||||
}
|
||||
output_busy = 0;
|
||||
if (slot >= fake.slots || !fake.pending[slot])
|
||||
{
|
||||
fake_err("slot %u shown without a picture waiting", slot);
|
||||
@@ -316,7 +330,14 @@ static int decode(const char *mp4, int hw, int skip_lo, int skip_hi,
|
||||
cur_sample = res->n;
|
||||
rh264_video_set_skip_nonref(v,
|
||||
res->n >= skip_lo && res->n < skip_hi);
|
||||
r = rh264_video_decode(v, pkt.data, pkt.size);
|
||||
{
|
||||
unsigned tries = 0;
|
||||
do
|
||||
r = rh264_video_decode(v, pkt.data, pkt.size);
|
||||
while (r == 2 && ++tries < 100);
|
||||
if (r == 2)
|
||||
r = -1;
|
||||
}
|
||||
res->n++;
|
||||
if (rh264_video_dropped(v))
|
||||
res->dropped++;
|
||||
@@ -328,8 +349,15 @@ static int decode(const char *mp4, int hw, int skip_lo, int skip_hi,
|
||||
if (r == 1)
|
||||
res->shown++;
|
||||
}
|
||||
while (!res->failed && rh264_video_drain(v) == 0)
|
||||
while (!res->failed)
|
||||
{
|
||||
int r = rh264_video_drain(v);
|
||||
if (r == 2)
|
||||
continue;
|
||||
if (r != 0)
|
||||
break;
|
||||
res->shown++;
|
||||
}
|
||||
if (hw == 2)
|
||||
res->hw = rh264_video_is_hw(v);
|
||||
if (res->hw)
|
||||
@@ -494,6 +522,16 @@ int main(void)
|
||||
decode(mp4, 1, -1, -1, 1, &r);
|
||||
check("no backend: stays in software", !r.hw && !r.failed && r.shown == r.n);
|
||||
|
||||
inject_busy = 1;
|
||||
rh264_set_hw_backend(&fake_ops);
|
||||
decode_busy = output_busy = 0;
|
||||
hw_case("busy_baseline", "testsrc2=s=176x144:r=25", 24,
|
||||
"-profile:v baseline -preset medium -x264-params bframes=0", 1);
|
||||
hw_case("busy_bpyr", "testsrc2=s=176x144:r=25", 48,
|
||||
"-profile:v main -preset slow -x264-params bframes=3:b-pyramid=normal:ref=4", 1);
|
||||
hw_case("busy_slices", "testsrc2=s=192x160:r=25", 24,
|
||||
"-profile:v high -preset medium -x264-params slices=4:bframes=2", 1);
|
||||
inject_busy = 0;
|
||||
run("rm -rf '%s'", dir);
|
||||
printf("rh264_hw_test: %s (%d failure%s)\n", fails ? "FAIL" : "PASS",
|
||||
fails, fails == 1 ? "" : "s");
|
||||
|
||||
@@ -67,6 +67,9 @@ typedef struct
|
||||
uint32_t *out;
|
||||
int frames;
|
||||
int pos;
|
||||
int wait_left;
|
||||
int wait_at;
|
||||
bool waiting;
|
||||
int duration_ms;
|
||||
int catchup; /* last value set */
|
||||
int catchup_sets;
|
||||
@@ -111,6 +114,12 @@ const uint32_t *image_transfer_anim_stream_next(void *stream,
|
||||
uint32_t *dst;
|
||||
unsigned i;
|
||||
(void)type;
|
||||
f->waiting = f->wait_left > 0 && f->pos == f->wait_at;
|
||||
if (f->waiting)
|
||||
{
|
||||
f->wait_left--;
|
||||
return NULL;
|
||||
}
|
||||
if (f->pos >= f->frames)
|
||||
return NULL;
|
||||
if (f->hw_on)
|
||||
@@ -137,6 +146,13 @@ static void fake_abandon(void *owner, uint64_t done, uint64_t value)
|
||||
f->hw_abandoned++;
|
||||
}
|
||||
|
||||
bool image_transfer_anim_stream_waiting(const void *stream,
|
||||
enum image_type_enum type)
|
||||
{
|
||||
(void)type;
|
||||
return ((const fake_t*)stream)->waiting;
|
||||
}
|
||||
|
||||
bool image_transfer_anim_stream_set_hw(void *stream,
|
||||
enum image_type_enum type, bool enable)
|
||||
{
|
||||
@@ -525,6 +541,8 @@ static void lane_hw_inline(void)
|
||||
|
||||
fake_init(&f, 6, 10);
|
||||
f.hw_can = true;
|
||||
f.wait_left = 3;
|
||||
f.wait_at = 2;
|
||||
sink_init_hw(&k, &s);
|
||||
k.hw_busy_every = 4;
|
||||
p = video_player_new(&f, IMAGE_TYPE_MP4, 2, &s, VIDEO_PLAYER_INLINE);
|
||||
@@ -589,6 +607,8 @@ static void lane_hw_threaded(void)
|
||||
|
||||
fake_init(&f, 7, 5);
|
||||
f.hw_can = true;
|
||||
f.wait_left = 3;
|
||||
f.wait_at = 2;
|
||||
sink_init_hw(&k, &s);
|
||||
k.queue_every = 3;
|
||||
k.hw_busy_every = 5;
|
||||
|
||||
@@ -30,6 +30,7 @@ BACKEND := $(RA)/gfx/common/d3d11_video_decode.c \
|
||||
TARGETS := dxva_h264_test$(EXE)
|
||||
ifeq ($(EXE),.exe)
|
||||
TARGETS += d3d11_video_decode_mock_test$(EXE) d3d11_video_decode_test$(EXE)
|
||||
TARGETS += d3d11_backpressure_test$(EXE)
|
||||
LDFLAGS += -lwinmm
|
||||
WINFLAGS := -isystem $(RA)/gfx/include/dxsdk -Wno-cast-function-type
|
||||
endif
|
||||
@@ -49,15 +50,35 @@ d3d11_video_decode_test$(EXE): d3d11_video_decode_test.c \
|
||||
$(CC) $(CFLAGS) $(WINFLAGS) -o $@ d3d11_video_decode_test.c \
|
||||
$(DECODERS) $(LDFLAGS) -ld3d11 -lm
|
||||
|
||||
PLAYER_SOURCES := $(RA)/libretro-common/formats/video_player.c \
|
||||
$(RA)/libretro-common/formats/image_transfer.c \
|
||||
$(RA)/libretro-common/formats/mp4/rmp4_video.c \
|
||||
$(RA)/libretro-common/formats/h265/rh265.c \
|
||||
$(RA)/libretro-common/formats/vp8/rvp8.c \
|
||||
$(RA)/libretro-common/formats/webm/rwebm_video.c \
|
||||
$(RA)/libretro-common/formats/webm/rwebm.c \
|
||||
$(RA)/libretro-common/formats/image/image_blit_bands.c \
|
||||
$(RA)/libretro-common/formats/image/image_hdr_blit.c \
|
||||
$(RA)/libretro-common/features/features_cpu.c \
|
||||
$(RA)/libretro-common/time/rtime.c
|
||||
|
||||
d3d11_backpressure_test$(EXE): d3d11_backpressure_test.c \
|
||||
d3d11_video_decode_test.c $(DECODERS) $(DEPS) $(BACKEND) $(PLAYER_SOURCES)
|
||||
$(CC) $(CFLAGS) $(WINFLAGS) -ffunction-sections -fdata-sections -o $@ \
|
||||
d3d11_backpressure_test.c $(DECODERS) $(PLAYER_SOURCES) \
|
||||
$(LDFLAGS) -Wl,--gc-sections $(if $(filter .exe,$(EXE)),-ld3d11) -lm
|
||||
|
||||
check: all
|
||||
$(RUNNER) ./dxva_h264_test$(EXE)
|
||||
ifeq ($(EXE),.exe)
|
||||
$(RUNNER) ./d3d11_video_decode_mock_test$(EXE)
|
||||
$(RUNNER) ./d3d11_video_decode_test$(EXE)
|
||||
$(RUNNER) ./d3d11_backpressure_test$(EXE)
|
||||
endif
|
||||
|
||||
clean:
|
||||
rm -f dxva_h264_test dxva_h264_test.exe \
|
||||
d3d11_video_decode_mock_test.exe d3d11_video_decode_test.exe
|
||||
rm -f d3d11_backpressure_test d3d11_backpressure_test.exe
|
||||
|
||||
.PHONY: all check clean
|
||||
@@ -0,0 +1,150 @@
|
||||
#ifdef _WIN32
|
||||
#define main reference_main
|
||||
#include "d3d11_video_decode_test.c"
|
||||
#undef main
|
||||
#include <formats/image.h>
|
||||
#include <formats/video_player.h>
|
||||
|
||||
static video_hw_frame_t held_frames[8];
|
||||
static unsigned held_count;
|
||||
|
||||
static int player_start(void *user, unsigned width, unsigned height, unsigned jobs)
|
||||
{
|
||||
(void)user; (void)width; (void)height; (void)jobs;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static enum video_player_present player_present(void *user,
|
||||
const video_player_frame_t *frame)
|
||||
{
|
||||
(void)user;
|
||||
if (!frame->hw || held_count >= 8)
|
||||
return VIDEO_PLAYER_PRESENT_FAILED;
|
||||
held_frames[held_count++] = *frame->hw;
|
||||
return VIDEO_PLAYER_PRESENT_DONE;
|
||||
}
|
||||
|
||||
static void test_backpressure(int free_later, int cancel)
|
||||
{
|
||||
video_player_sink_t sink = {0};
|
||||
video_player_t *player;
|
||||
void *stream;
|
||||
unsigned i, before_count;
|
||||
size_t anchor;
|
||||
int64_t now = 0;
|
||||
int64_t due;
|
||||
DWORD started;
|
||||
enum video_player_state state = VIDEO_PLAYER_PLAYING;
|
||||
|
||||
held_count = 0;
|
||||
stream = image_transfer_anim_stream_new((void*)stream_baseline,
|
||||
sizeof(stream_baseline), IMAGE_TYPE_MP4);
|
||||
if (!stream || !image_transfer_anim_stream_set_hw(stream, IMAGE_TYPE_MP4, true))
|
||||
{
|
||||
puts("d3d11_backpressure_test: SKIP (H.264 decoder unavailable)");
|
||||
image_transfer_anim_stream_free(stream, IMAGE_TYPE_MP4);
|
||||
return;
|
||||
}
|
||||
sink.start = player_start;
|
||||
sink.start_hw = player_start;
|
||||
sink.present = player_present;
|
||||
player = video_player_new(stream, IMAGE_TYPE_MP4, 1, &sink, VIDEO_PLAYER_INLINE);
|
||||
check("inline player opened", stream && player);
|
||||
if (!player)
|
||||
{
|
||||
image_transfer_anim_stream_free(stream, IMAGE_TYPE_MP4);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < 1000 && held_count < 4 && state == VIDEO_PLAYER_PLAYING; i++)
|
||||
{
|
||||
state = video_player_poll(player, now += 100000);
|
||||
if (held_count < 4)
|
||||
Sleep(1);
|
||||
}
|
||||
check("four output leases held", held_count == 4);
|
||||
if (free_later && held_count == 4)
|
||||
video_hw_frame_abandon(&held_frames[1]);
|
||||
anchor = image_transfer_anim_stream_consumed(stream, IMAGE_TYPE_MP4);
|
||||
due = video_player_due(player);
|
||||
started = GetTickCount();
|
||||
state = video_player_poll(player, now += 100000);
|
||||
check("busy poll returns without ending", state == VIDEO_PLAYER_PLAYING
|
||||
&& GetTickCount() - started < 100);
|
||||
if (free_later)
|
||||
check("returned non-oldest texture is selected", held_count == 5
|
||||
&& held_frames[4].image == held_frames[1].image);
|
||||
if (!free_later)
|
||||
{
|
||||
check("pending packet stays inside the window", anchor
|
||||
== image_transfer_anim_stream_consumed(stream, IMAGE_TYPE_MP4));
|
||||
check("busy output is not published", held_count == 4);
|
||||
before_count = held_count;
|
||||
state = video_player_poll(player, now += 100000);
|
||||
check("repeated busy poll preserves the frame", state == VIDEO_PLAYER_PLAYING
|
||||
&& held_count == before_count);
|
||||
check("busy poll does not advance the clock", video_player_due(player)
|
||||
== due);
|
||||
}
|
||||
if (cancel)
|
||||
{
|
||||
video_player_free(player);
|
||||
image_transfer_anim_stream_free(stream, IMAGE_TYPE_MP4);
|
||||
}
|
||||
for (i = 0; i < held_count; i++)
|
||||
video_hw_frame_abandon(&held_frames[i]);
|
||||
if (!cancel)
|
||||
{
|
||||
before_count = held_count;
|
||||
for (i = 0; i < 1000 && held_count == before_count; i++)
|
||||
{
|
||||
state = video_player_poll(player, now += 100000);
|
||||
if (held_count == before_count)
|
||||
Sleep(1);
|
||||
}
|
||||
check("release resumes with one frame", state == VIDEO_PLAYER_PLAYING
|
||||
&& held_count == before_count + 1);
|
||||
if (held_count)
|
||||
video_hw_frame_abandon(&held_frames[held_count - 1]);
|
||||
video_player_free(player);
|
||||
image_transfer_anim_stream_free(stream, IMAGE_TYPE_MP4);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
D3D_FEATURE_LEVEL level = D3D_FEATURE_LEVEL_11_0;
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
if (FAILED(D3D11CreateDevice(NULL, D3D_DRIVER_TYPE_HARDWARE, NULL, 0,
|
||||
&level, 1, D3D11_SDK_VERSION, &dev, NULL, &ctx))
|
||||
|| FAILED(dev->lpVtbl->QueryInterface(dev, &d3d11vd_iid_device5,
|
||||
(void**)&dev5))
|
||||
|| FAILED(ctx->lpVtbl->QueryInterface(ctx, &d3d11vd_iid_context4,
|
||||
(void**)&ctx4))
|
||||
|| !d3d11_video_decode_init(dev))
|
||||
{
|
||||
D3D11VD_RELEASE(ctx4);
|
||||
D3D11VD_RELEASE(dev5);
|
||||
D3D11VD_RELEASE(ctx);
|
||||
D3D11VD_RELEASE(dev);
|
||||
puts("d3d11_backpressure_test: SKIP (no hardware decoder)");
|
||||
return 0;
|
||||
}
|
||||
test_backpressure(0, 0);
|
||||
test_backpressure(1, 0);
|
||||
test_backpressure(0, 1);
|
||||
d3d11_video_decode_deinit();
|
||||
D3D11VD_RELEASE(ctx4);
|
||||
D3D11VD_RELEASE(dev5);
|
||||
D3D11VD_RELEASE(ctx);
|
||||
D3D11VD_RELEASE(dev);
|
||||
printf("d3d11_backpressure_test: %s (%d failures)\n", fails ? "FAIL" : "PASS", fails);
|
||||
return fails ? 1 : 0;
|
||||
}
|
||||
#else
|
||||
#include <stdio.h>
|
||||
int main(void)
|
||||
{
|
||||
puts("d3d11_backpressure_test: SKIP (Windows GPU test)");
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
@@ -10,7 +10,7 @@
|
||||
* slot's view, the four buffers submitted with their sizes, the
|
||||
* bitstream padded to 128 bytes with zeros the last slice takes, a
|
||||
* slice entry for each NAL unit's start code;
|
||||
* - DecoderBeginFrame's E_PENDING waited out, and given up on;
|
||||
* - DecoderBeginFrame's E_PENDING returned for a later retry;
|
||||
* - a bitstream buffer too small refused, one with no room for the
|
||||
* padding taking none;
|
||||
* - each frame shown copied from its slot's visible window, the
|
||||
@@ -727,6 +727,8 @@ static void test_direct(void)
|
||||
|
||||
subs_free();
|
||||
pending_left = 3;
|
||||
for (i = 0; i < 3; i++)
|
||||
check("E_PENDING: no submission", ops->decode(dec, &pic) == 1 && !n_subs);
|
||||
r = ops->decode(dec, &pic);
|
||||
check("E_PENDING three times, then decoded", !r && n_subs == 1
|
||||
&& !pending_left);
|
||||
@@ -741,8 +743,8 @@ static void test_direct(void)
|
||||
stub_quiet = 1;
|
||||
r = ops->decode(dec, &pic);
|
||||
stub_quiet = 0;
|
||||
check("E_PENDING for good: given up on", r < 0
|
||||
&& GetTickCount() - t0 < 5000);
|
||||
check("E_PENDING for good: retryable at once", r == 1
|
||||
&& pending_left == 99999 && GetTickCount() - t0 < 100);
|
||||
pending_left = 0;
|
||||
|
||||
subs_free();
|
||||
@@ -770,8 +772,8 @@ static void test_direct(void)
|
||||
stub_quiet = 1;
|
||||
r = ops->output(dec, 0, &fr[4]);
|
||||
stub_quiet = 0;
|
||||
check("a fifth with none read: waited for, then given up",
|
||||
r < 0 && GetTickCount() - t0 >= 900);
|
||||
check("a fifth with none read: retryable at once",
|
||||
r == 1 && GetTickCount() - t0 < 100);
|
||||
video_hw_frame_abandon(&fr[1]);
|
||||
t0 = GetTickCount();
|
||||
r = ops->output(dec, 2, &fr[4]);
|
||||
|
||||
@@ -514,7 +514,18 @@ static int decode(const unsigned char *mp4, size_t len, int hw,
|
||||
{
|
||||
if (pkt.track != trk)
|
||||
continue;
|
||||
if ((r = rh264_video_decode(v, pkt.data, pkt.size)) < 0)
|
||||
{
|
||||
unsigned tries = 0;
|
||||
do
|
||||
{
|
||||
r = rh264_video_decode(v, pkt.data, pkt.size);
|
||||
if (r == 2)
|
||||
Sleep(1);
|
||||
} while (r == 2 && ++tries < 1000);
|
||||
if (r == 2)
|
||||
r = -1;
|
||||
}
|
||||
if (r < 0)
|
||||
ok = 0;
|
||||
else if (r == 1)
|
||||
{
|
||||
@@ -525,8 +536,16 @@ static int decode(const unsigned char *mp4, size_t len, int hw,
|
||||
ok = 0;
|
||||
}
|
||||
}
|
||||
while (ok && rh264_video_drain(v) == 0)
|
||||
while (ok)
|
||||
{
|
||||
r = rh264_video_drain(v);
|
||||
if (r == 2)
|
||||
{
|
||||
Sleep(1);
|
||||
continue;
|
||||
}
|
||||
if (r != 0)
|
||||
break;
|
||||
if (!on_hw || hw == 2)
|
||||
take_sw(f, v);
|
||||
else if (rh264_video_hw_frame(v, &fr) != 0
|
||||
|
||||
@@ -199,6 +199,12 @@ const uint32_t *image_transfer_anim_stream_next(void *stream,
|
||||
return gfx_anim_preview_next((gfx_anim_preview_t*)stream, duration_ms,
|
||||
&native);
|
||||
}
|
||||
bool image_transfer_anim_stream_waiting(const void *stream,
|
||||
enum image_type_enum type)
|
||||
{
|
||||
(void)stream; (void)type;
|
||||
return false;
|
||||
}
|
||||
void image_transfer_anim_stream_rewind(void *stream,
|
||||
enum image_type_enum type)
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user