From fbf08b94aa9ac13de3cbc5fc3a4f0c51010f0fe6 Mon Sep 17 00:00:00 2001 From: Geoffrey McRae Date: Sun, 31 Dec 2017 00:27:26 +1100 Subject: [PATCH] [client] initial vaapi h264 decode support (unfinished) --- client/Makefile | 4 +- client/decoders/h264.c | 844 ++++++++++++++++++++++++++++++++++++-- client/decoders/null.c | 62 ++- client/lg-decoder.h | 53 ++- client/renderers/opengl.c | 328 +++++++++------ 5 files changed, 1095 insertions(+), 196 deletions(-) diff --git a/client/Makefile b/client/Makefile index 9f593c83..a6c79151 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,12 +1,12 @@ BINARY = looking-glass-client -CFLAGS = -g -Og -std=gnu99 -march=native -Wall -Werror -I./ -I../common -DDEBUG -DATOMIC_LOCKING +CFLAGS = -g -O3 -std=gnu99 -march=native -Wall -Werror -I./ -I../common -DDEBUG -DATOMIC_LOCKING LDFLAGS = -lrt CFLAGS += -ffast-math CFLAGS += -fdata-sections -ffunction-sections LDFLAGS += -Wl,--gc-sections -LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 libconfig libva +LIBS = sdl2 SDL2_ttf gl glu libssl openssl spice-protocol fontconfig x11 libconfig libva libva-glx CFLAGS += $(shell pkg-config --cflags $(LIBS)) LDFLAGS += $(shell pkg-config --libs $(LIBS)) BUILD ?= .build diff --git a/client/decoders/h264.c b/client/decoders/h264.c index 7df9e376..58c71d2f 100644 --- a/client/decoders/h264.c +++ b/client/decoders/h264.c @@ -20,24 +20,107 @@ Place, Suite 330, Boston, MA 02111-1307 USA #include "lg-decoder.h" #include "debug.h" +#include "memcpySSE.h" #include #include +#include +#include + +#define SURFACE_NUM 3 + +#define NALU_AUD 9 +#define SLICE_TYPE_P 0 +#define SLICE_TYPE_B 1 +#define SLICE_TYPE_I 2 struct Inst { - bool initialized; - - LG_RendererFormat format; + LG_RendererFormat format; + SDL_Window * window; + VADisplay vaDisplay; + int vaMajorVer, vaMinorVer; + VASurfaceID vaSurfaceID[SURFACE_NUM]; + VAConfigID vaConfigID; + VAContextID vaContextID; + int lastSID; + int currentSID; + VAPictureH264 curPic; + VAPictureH264 oldPic; + int frameNum; + int fieldCount; + VABufferID picBufferID[SURFACE_NUM]; + VABufferID matBufferID[SURFACE_NUM]; + VABufferID sliBufferID[SURFACE_NUM]; + VABufferID datBufferID[SURFACE_NUM]; + bool t2First; + int sliceType; }; -static bool lgd_h264_create (void ** opaque); -static void lgd_h264_destroy (void * opaque); -static bool lgd_h264_initialize (void * opaque, const LG_RendererFormat format); -static void lgd_h264_deinitialize (void * opaque); -static LG_OutFormat lgd_h264_get_out_format (void * opaque); -static unsigned int lgd_h264_get_frame_pitch(void * opaque); -static bool lgd_h264_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); +static const unsigned char MatrixBufferH264[] = { + //ScalingList4x4[6][16] + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + 0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10,0x10, + //ScalingList8x8[2][64] + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 +}; + +static bool lgd_h264_create (void ** opaque); +static void lgd_h264_destroy (void * opaque); +static bool lgd_h264_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window); +static void lgd_h264_deinitialize (void * opaque); +static LG_OutFormat lgd_h264_get_out_format (void * opaque); +static unsigned int lgd_h264_get_frame_pitch (void * opaque); +static unsigned int lgd_h264_get_frame_stride(void * opaque); +static bool lgd_h264_decode (void * opaque, const uint8_t * src, size_t srcSize); +static bool lgd_h264_get_buffer (void * opaque, uint8_t * dst, size_t dstSize); + +static bool lgd_h264_init_gl_texture (void * opaque, GLenum target, GLuint texture, void ** ref); +static void lgd_h264_free_gl_texture (void * opaque, void * ref); +static bool lgd_h264_update_gl_texture(void * opaque, void * ref); + +#define check_surface(x, y, z) _check_surface(__LINE__, x, y, z) +static bool _check_surface(const unsigned int line, struct Inst * this, unsigned int sid, VASurfaceStatus *out) +{ + VASurfaceStatus surfStatus; + VAStatus status = vaQuerySurfaceStatus( + this->vaDisplay, + this->vaSurfaceID[sid], + &surfStatus + ); + + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaQuerySurfaceStatus: %s", vaErrorStr(status)); + return false; + } + +#if 0 + DEBUG_INFO("L%d: surface %u status: %d", line, sid, surfStatus); +#endif + if (out) + *out = surfStatus; + return true; +} static bool lgd_h264_create(void ** opaque) { @@ -49,41 +132,201 @@ static bool lgd_h264_create(void ** opaque) return false; } memset(*opaque, 0, sizeof(struct Inst)); + struct Inst * this = (struct Inst *)*opaque; - //struct Inst * this = (struct Inst *)*opaque; + this->vaSurfaceID[0] = VA_INVALID_ID; + this->vaConfigID = VA_INVALID_ID; + this->vaContextID = VA_INVALID_ID; + for(int i = 0; i < SURFACE_NUM; ++i) + this->picBufferID[i] = + this->matBufferID[i] = + this->sliBufferID[i] = + this->datBufferID[i] = VA_INVALID_ID; + lgd_h264_deinitialize(this); return true; } static void lgd_h264_destroy(void * opaque) { struct Inst * this = (struct Inst *)opaque; - lgd_h264_deinitialize(opaque); + lgd_h264_deinitialize(this); free(this); } -static bool lgd_h264_initialize(void * opaque, const LG_RendererFormat format) +static bool lgd_h264_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window) { struct Inst * this = (struct Inst *)opaque; - if (this->initialized) - lgd_h264_deinitialize(opaque); + lgd_h264_deinitialize(this); memcpy(&this->format, &format, sizeof(LG_RendererFormat)); + this->window = window; + + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + if (!SDL_GetWindowWMInfo(window, &wminfo)) + { + DEBUG_ERROR("Failed to get SDL window WM Info"); + return false; + } + + switch(wminfo.subsystem) + { + case SDL_SYSWM_X11: + this->vaDisplay = vaGetDisplayGLX(wminfo.info.x11.display); + break; + + default: + DEBUG_ERROR("Unsupported window subsystem"); + return false; + } + + VAStatus status; + status = vaInitialize(this->vaDisplay, &this->vaMajorVer, &this->vaMinorVer); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaInitialize Failed"); + return false; + } + + DEBUG_INFO("Vendor: %s", vaQueryVendorString(this->vaDisplay)); + + VAEntrypoint entryPoints[5]; + int entryPointCount; + + status = vaQueryConfigEntrypoints( + this->vaDisplay, + VAProfileH264Baseline, + entryPoints, + &entryPointCount + ); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaQueryConfigEntrypoints Failed"); + return false; + } + + int ep; + for(ep = 0; ep < entryPointCount; ++ep) + if (entryPoints[ep] == VAEntrypointVLD) + break; + + if (ep == entryPointCount) + { + DEBUG_ERROR("Failed to find VAEntrypointVLD index"); + return false; + } + + VAConfigAttrib attrib; + attrib.type = VAConfigAttribRTFormat; + vaGetConfigAttributes(this->vaDisplay, VAProfileH264Baseline, VAEntrypointVLD, &attrib, 1); + if (!(attrib.value & VA_RT_FORMAT_YUV420)) + { + DEBUG_ERROR("Failed to find desired YUV420 RT format"); + return false; + } + + status = vaCreateConfig(this->vaDisplay, VAProfileH264Baseline, VAEntrypointVLD, &attrib, 1, &this->vaConfigID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaCreateConfig"); + return false; + } + + status = vaCreateSurfaces( + this->vaDisplay, + VA_RT_FORMAT_YUV420, + this->format.width, + this->format.height, + this->vaSurfaceID, + SURFACE_NUM, + NULL, + 0 + ); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaCreateSurfaces"); + return false; + } + + for(int i = 0; i < SURFACE_NUM; ++i) + if (!check_surface(this, i, NULL)) + return false; + + status = vaCreateContext( + this->vaDisplay, + this->vaConfigID, + this->format.width, + this->format.height, + VA_PROGRESSIVE, + this->vaSurfaceID, + SURFACE_NUM, + &this->vaContextID + ); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaCreateContext"); + return false; + } + + this->currentSID = 0; + this->sliceType = 2; + this->t2First = true; + + status = vaBeginPicture(this->vaDisplay, this->vaContextID, this->vaSurfaceID[0]); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaBeginPicture"); + return false; + } + return true; } static void lgd_h264_deinitialize(void * opaque) { struct Inst * this = (struct Inst *)opaque; - if (!this->initialized) - return; - memset(this, 0, sizeof(struct Inst)); + for(int i = 0; i < SURFACE_NUM; ++i) + { + if (this->picBufferID[i] != VA_INVALID_ID) + vaDestroyBuffer(this->vaDisplay, this->picBufferID[i]); + + if (this->matBufferID[i] != VA_INVALID_ID) + vaDestroyBuffer(this->vaDisplay, this->matBufferID[i]); + + if (this->sliBufferID[i] != VA_INVALID_ID) + vaDestroyBuffer(this->vaDisplay, this->sliBufferID[i]); + + if (this->datBufferID[i] != VA_INVALID_ID) + vaDestroyBuffer(this->vaDisplay, this->datBufferID[i]); + + this->picBufferID[i] = + this->matBufferID[i] = + this->sliBufferID[i] = + this->datBufferID[i] = VA_INVALID_ID; + } + + if (this->vaSurfaceID[0] != VA_INVALID_ID) + vaDestroySurfaces(this->vaDisplay, this->vaSurfaceID, SURFACE_NUM); + this->vaSurfaceID[0] = VA_INVALID_ID; + + if (this->vaContextID != VA_INVALID_ID) + vaDestroyContext(this->vaDisplay, this->vaContextID); + this->vaContextID = VA_INVALID_ID; + + if (this->vaConfigID != VA_INVALID_ID) + vaDestroyConfig(this->vaDisplay, this->vaConfigID); + this->vaConfigID = VA_INVALID_ID; + + if (this->vaDisplay) + vaTerminate(this->vaDisplay); + this->vaDisplay = NULL; } static LG_OutFormat lgd_h264_get_out_format(void * opaque) { - return LG_OUTPUT_BGRA; + return LG_OUTPUT_YUV420; } static unsigned int lgd_h264_get_frame_pitch(void * opaque) @@ -92,19 +335,564 @@ static unsigned int lgd_h264_get_frame_pitch(void * opaque) return this->format.width * 4; } -static bool lgd_h264_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize) +static unsigned int lgd_h264_get_frame_stride(void * opaque) { + struct Inst * this = (struct Inst *)opaque; + return this->format.width; +} + +static bool get_buffer(struct Inst * this, const VABufferType type, const unsigned int size, VABufferID * buf_id) +{ + if (*buf_id != VA_INVALID_ID) + return true; + + VAStatus status = vaCreateBuffer(this->vaDisplay, this->vaContextID, type, size, 1, NULL, buf_id); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("Failed to create buffer: %s", vaErrorStr(status)); + return false; + } + + if (!check_surface(this, this->currentSID, NULL)) + return false; + + return true; +} + +static void set_slice_parameter_buffer(VASliceParameterBufferH264 *p) +{ + memset(p, 0, sizeof(VASliceParameterBufferH264)); + p->slice_data_size = 0; + p->slice_data_bit_offset = 64; + p->slice_alpha_c0_offset_div2 = 2; + p->slice_beta_offset_div2 = 2; + p->chroma_weight_l0_flag = 1; + p->chroma_weight_l0[0][0] = 1; + p->chroma_offset_l0[0][0] = 0; + p->chroma_weight_l0[0][1] = 1; + p->chroma_offset_l0[0][1] = 0; + p->luma_weight_l1_flag = 1; + p->chroma_weight_l1_flag = 1; + p->luma_weight_l0[0] = 0x01; + + for (int i = 0; i < 32; i++) + p->RefPicList0[i].flags = + p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID; + + p->RefPicList1[0].picture_id = 0xffffffff; +} + +static void set_slice_parameter_buffer_t2(VASliceParameterBufferH264 *p, const bool first) +{ + memset(p, 0, sizeof(VASliceParameterBufferH264)); + p->slice_data_size = 0; + p->slice_data_bit_offset = 64; + p->slice_alpha_c0_offset_div2 = 2; + p->slice_beta_offset_div2 = 2; + p->slice_type = 2; + + if (first) + { + p->luma_weight_l0_flag = 1; + p->chroma_weight_l0_flag = 1; + p->luma_weight_l1_flag = 1; + p->chroma_weight_l1_flag = 1; + } + else + { + p->chroma_weight_l0_flag = 1; + p->chroma_weight_l0[0][0] = 1; + p->chroma_offset_l0[0][0] = 0; + p->chroma_weight_l0[0][1] = 1; + p->chroma_offset_l0[0][1] = 0; + p->luma_weight_l1_flag = 1; + p->chroma_weight_l1_flag = 1; + p->luma_weight_l0[0] = 0x01; + } + + for (int i = 0; i < 32; i++) + p->RefPicList0[i].flags = + p->RefPicList1[i].flags = VA_PICTURE_H264_INVALID; + + p->RefPicList1[0].picture_id = + p->RefPicList0[0].picture_id = 0xffffffff; +} + +static bool setup_pic_buffer(struct Inst * this) +{ + VAStatus status; + + VABufferID * picBufferID = &this->picBufferID[this->currentSID]; + if (!get_buffer(this, VAPictureParameterBufferType, sizeof(VAPictureParameterBufferH264), picBufferID)) + { + DEBUG_ERROR("get picBuffer failed"); + return false; + } + + VAPictureParameterBufferH264 *p; + status = vaMapBuffer(this->vaDisplay, *picBufferID, (void **)&p); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status)); + return false; + } + + memset(p, 0, sizeof(VAPictureParameterBufferH264)); + p->picture_width_in_mbs_minus1 = (this->format.width + 15) / 16; + p->picture_width_in_mbs_minus1 = (this->format.height + 15) / 16; + p->num_ref_frames = 1; + p->seq_fields.value = 145; + p->pic_fields.value = 0x501; + p->frame_num = this->frameNum % 16; + for(int i = 0; i < 16; ++i) + { + p->ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + p->ReferenceFrames[i].picture_id = 0xFFFFFFFF; + } + + this->curPic.picture_id = this->vaSurfaceID[this->currentSID]; + this->curPic.frame_idx = p->frame_num; + this->curPic.flags = 0; + this->curPic.BottomFieldOrderCnt = this->fieldCount; + this->curPic.TopFieldOrderCnt = this->fieldCount; + memcpy(&p->CurrPic, &this->curPic, sizeof(VAPictureH264)); + + if (this->sliceType != 2) + { + memcpy(&p->ReferenceFrames[0], &this->oldPic, sizeof(VAPictureH264)); + p->ReferenceFrames[0].flags = 0; + } + + status = vaUnmapBuffer(this->vaDisplay, *picBufferID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool setup_mat_buffer(struct Inst * this) +{ + VAStatus status; + + VABufferID * matBufferID = &this->matBufferID[this->currentSID]; + if (!get_buffer(this, VAIQMatrixBufferType, sizeof(VAIQMatrixBufferH264), matBufferID)) + { + DEBUG_ERROR("get matBuffer failed"); + return false; + } + + VAIQMatrixBufferH264 * m; + status = vaMapBuffer(this->vaDisplay, *matBufferID, (void **)&m); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status)); + return false; + } + + memcpy(m, MatrixBufferH264, sizeof(MatrixBufferH264)); + + status = vaUnmapBuffer(this->vaDisplay, *matBufferID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool setup_sli_buffer(struct Inst * this, size_t srcSize) +{ + VAStatus status; + + VABufferID * sliBufferID = &this->sliBufferID[this->currentSID]; + if (!get_buffer(this, VASliceParameterBufferType, sizeof(VASliceParameterBufferH264), sliBufferID)) + { + DEBUG_ERROR("get sliBuffer failed"); + return false; + } + + VASliceParameterBufferH264 * s; + status = vaMapBuffer(this->vaDisplay, *sliBufferID, (void **)&s); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status)); + return false; + } + + if (this->sliceType == 2) + { + set_slice_parameter_buffer_t2(s, this->t2First); + this->t2First = false; + } + else + { + set_slice_parameter_buffer(s); + memcpy(&s->RefPicList0[0], &this->oldPic, sizeof(VAPictureH264)); + s->RefPicList0[0].flags = 0; + } + s->slice_data_bit_offset = 0; + s->slice_data_size = srcSize; + + status = vaUnmapBuffer(this->vaDisplay, *sliBufferID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool setup_dat_buffer(struct Inst * this, const uint8_t * src, size_t srcSize) +{ + VAStatus status; + + VABufferID * datBufferID = &this->datBufferID[this->currentSID]; + + if (!get_buffer(this, VASliceDataBufferType, srcSize, datBufferID)) + { + DEBUG_ERROR("get datBuffer failed"); + return false; + } + + uint8_t * d; + status = vaMapBuffer(this->vaDisplay, *datBufferID, (void **)&d); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status)); + return false; + } + + memcpySSE(d, src, srcSize); + + status = vaUnmapBuffer(this->vaDisplay, *datBufferID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool parse_nalu(struct Inst * this, const uint8_t * src, size_t size) +{ + static const uint8_t startCode[4] = {0x00, 0x00, 0x00, 0x01}; + + if (memcmp(src, startCode, sizeof(startCode)) != 0) + { + DEBUG_ERROR("Missing start code"); + return false; + } + src += 4; + + if (*src & 0x80) + { + DEBUG_ERROR("forbidden_zero_bit is set"); + return false; + } + +// uint8_t nal_ref_idc = (*src & 0x60) >> 5; + uint8_t nal_ref_unit_type = (*src & 0x1F); + ++src; + + if (nal_ref_unit_type == NALU_AUD) + { + static const int pic_type_to_slice_type[3] = + { + SLICE_TYPE_I, + SLICE_TYPE_P, + SLICE_TYPE_B + }; + + const uint8_t primary_pic_type = (*src & 0xE0) >> 5; + this->sliceType = pic_type_to_slice_type[primary_pic_type]; + return true; + } + + return false; +} + +static bool lgd_h264_decode(void * opaque, const uint8_t * src, size_t srcSize) +{ + VAStatus status; + struct Inst * this = (struct Inst *)opaque; + + if (!parse_nalu(this, src, srcSize)) + { + DEBUG_ERROR("Failed to parse required information"); + return false; + } + + // don't start until we have an I-FRAME + if (this->frameNum == 0 && this->sliceType != SLICE_TYPE_I) + return true; + + { + if (!setup_pic_buffer(this)) return false; + if (!setup_mat_buffer(this)) return false; + + VABufferID bufferIDs[] = + { + this->picBufferID[this->currentSID], + this->matBufferID[this->currentSID] + }; + + status = vaRenderPicture(this->vaDisplay, this->vaContextID, bufferIDs, 2); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaRenderPicture: %s", vaErrorStr(status)); + return false; + } + + // intel broke the ABI here, see: + // https://github.com/01org/libva/commit/3eb038aa13bdd785808286c0a4995bd7a1ef07e9 + // the buffers are released by vaRenderPicture in old versions + if (this->vaMajorVer == 0 && this->vaMinorVer < 40) + { + this->picBufferID[this->currentSID] = + this->matBufferID[this->currentSID] = VA_INVALID_ID; + } + } + + { + if (!setup_sli_buffer(this, srcSize )) return false; + if (!setup_dat_buffer(this, src, srcSize)) return false; + VABufferID bufferIDs[] = + { + this->sliBufferID[this->currentSID], + this->datBufferID[this->currentSID] + }; + + status = vaRenderPicture(this->vaDisplay, this->vaContextID, bufferIDs, 2); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaRenderPicture: %s", vaErrorStr(status)); + return false; + } + + // intel broke the ABI here, see: + // https://github.com/01org/libva/commit/3eb038aa13bdd785808286c0a4995bd7a1ef07e9 + // the buffers are released by vaRenderPicture in old versions + if (this->vaMajorVer == 0 && this->vaMinorVer < 40) + { + this->sliBufferID[this->currentSID] = + this->datBufferID[this->currentSID] = VA_INVALID_ID; + } + } + + status = vaEndPicture(this->vaDisplay, this->vaContextID); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaEndPicture: %s", vaErrorStr(status)); + return false; + } + + // advance to the next surface and save the old picture info + this->lastSID = this->currentSID; + if (++this->currentSID == SURFACE_NUM) + this->currentSID = 0; + this->frameNum += 1; + this->fieldCount += 2; + memcpy(&this->oldPic, &this->curPic, sizeof(VAPictureH264)); + + // prepare the next surface + status = vaBeginPicture(this->vaDisplay, this->vaContextID, this->vaSurfaceID[this->currentSID]); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaBeginPicture: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool lgd_h264_get_buffer(void * opaque, uint8_t * dst, size_t dstSize) +{ + struct Inst * this = (struct Inst *)opaque; + VAStatus status; + + // don't return anything until we have some data + if (this->frameNum == 0) + return true; + + // ensure the surface is ready + status = vaSyncSurface(this->vaDisplay, this->vaSurfaceID[this->lastSID]); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaSyncSurface: %s", vaErrorStr(status)); + return false; + } + +#if 0 + // this doesn't work on my system, seems the vdpau va driver is bugged + VASurfaceStatus surfStatus; + if (!check_surface(this, this->lastSID, &surfStatus)) + return false; + + if (surfStatus != VASurfaceReady) + { + DEBUG_ERROR("vaSyncSurface didn't block, the surface is not ready!"); + return false; + } +#endif + + // get the decoded data + VAImage decoded = + { + .image_id = VA_INVALID_ID, + .buf = VA_INVALID_ID + }; + + status = vaDeriveImage(this->vaDisplay, this->vaSurfaceID[this->lastSID], &decoded); + if (status == VA_STATUS_ERROR_OPERATION_FAILED) + { + VAImageFormat format = + { + .fourcc = VA_FOURCC_NV12, + .byte_order = VA_LSB_FIRST, + .bits_per_pixel = 12 + }; + + status = vaCreateImage( + this->vaDisplay, + &format, + this->format.width, + this->format.height, + &decoded + ); + + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaCreateImage: %s", vaErrorStr(status)); + return false; + } + + status = vaPutImage( + this->vaDisplay, + this->vaSurfaceID[this->lastSID], + decoded.image_id, + 0 , 0 , + this->format.width, this->format.height, + 0 , 0 , + this->format.width, this->format.height + ); + + if (status != VA_STATUS_SUCCESS) + { + vaDestroyImage(this->vaDisplay, decoded.image_id); + DEBUG_ERROR("vaPutImage: %s", vaErrorStr(status)); + return false; + } + } + else + { + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaDeriveImage: %s", vaErrorStr(status)); + return false; + } + } + + uint8_t * d; + status = vaMapBuffer(this->vaDisplay, decoded.buf, (void **)&d); + if (status != VA_STATUS_SUCCESS) + { + vaDestroyImage(this->vaDisplay, decoded.image_id); + DEBUG_ERROR("vaMapBuffer: %s", vaErrorStr(status)); + return false; + } + + memcpySSE(dst, d, decoded.data_size); + + status = vaUnmapBuffer(this->vaDisplay, decoded.buf); + if (status != VA_STATUS_SUCCESS) + { + vaDestroyImage(this->vaDisplay, decoded.image_id); + DEBUG_ERROR("vaUnmapBuffer: %s", vaErrorStr(status)); + return false; + } + + status = vaDestroyImage(this->vaDisplay, decoded.image_id); + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaDestroyImage: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static bool lgd_h264_init_gl_texture(void * opaque, GLenum target, GLuint texture, void ** ref) +{ + struct Inst * this = (struct Inst *)opaque; + VAStatus status; + + status = vaCreateSurfaceGLX(this->vaDisplay, target, texture, ref); + if (status != VA_STATUS_SUCCESS) + { + *ref = NULL; + DEBUG_ERROR("vaCreateSurfaceGLX: %s", vaErrorStr(status)); + return false; + } + + return true; +} + +static void lgd_h264_free_gl_texture(void * opaque, void * ref) +{ + struct Inst * this = (struct Inst *)opaque; + VAStatus status; + + status = vaDestroySurfaceGLX(this->vaDisplay, ref); + if (status != VA_STATUS_SUCCESS) + DEBUG_ERROR("vaDestroySurfaceGLX: %s", vaErrorStr(status)); +} + +static bool lgd_h264_update_gl_texture(void * opaque, void * ref) +{ + struct Inst * this = (struct Inst *)opaque; + VAStatus status; + + // don't return anything until we have some data + if (this->frameNum == 0) + return true; + + status = vaCopySurfaceGLX( + this->vaDisplay, + ref, + this->vaSurfaceID[this->lastSID], + 0 + ); + + if (status != VA_STATUS_SUCCESS) + { + DEBUG_ERROR("vaCopySurfaceGLX: %s", vaErrorStr(status)); + return false; + } + return true; } const LG_Decoder LGD_H264 = { - .name = "H.264", - .create = lgd_h264_create, - .destroy = lgd_h264_destroy, - .initialize = lgd_h264_initialize, - .deinitialize = lgd_h264_deinitialize, - .get_out_format = lgd_h264_get_out_format, - .get_frame_pitch = lgd_h264_get_frame_pitch, - .decode = lgd_h264_decode + .name = "H.264", + .create = lgd_h264_create, + .destroy = lgd_h264_destroy, + .initialize = lgd_h264_initialize, + .deinitialize = lgd_h264_deinitialize, + .get_out_format = lgd_h264_get_out_format, + .get_frame_pitch = lgd_h264_get_frame_pitch, + .get_frame_stride = lgd_h264_get_frame_stride, + .decode = lgd_h264_decode, + .get_buffer = lgd_h264_get_buffer, + + .has_gl = true, + .init_gl_texture = lgd_h264_init_gl_texture, + .free_gl_texture = lgd_h264_free_gl_texture, + .update_gl_texture = lgd_h264_update_gl_texture }; \ No newline at end of file diff --git a/client/decoders/null.c b/client/decoders/null.c index 6834d709..24beaaf3 100644 --- a/client/decoders/null.c +++ b/client/decoders/null.c @@ -27,16 +27,19 @@ Place, Suite 330, Boston, MA 02111-1307 USA struct Inst { - LG_RendererFormat format; + LG_RendererFormat format; + const uint8_t * src; }; -static bool lgd_null_create (void ** opaque); -static void lgd_null_destroy (void * opaque); -static bool lgd_null_initialize (void * opaque, const LG_RendererFormat format); -static void lgd_null_deinitialize (void * opaque); -static LG_OutFormat lgd_null_get_out_format (void * opaque); -static unsigned int lgd_null_get_frame_pitch(void * opaque); -static bool lgd_null_decode (void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); +static bool lgd_null_create (void ** opaque); +static void lgd_null_destroy (void * opaque); +static bool lgd_null_initialize (void * opaque, const LG_RendererFormat format, SDL_Window * window); +static void lgd_null_deinitialize (void * opaque); +static LG_OutFormat lgd_null_get_out_format (void * opaque); +static unsigned int lgd_null_get_frame_pitch (void * opaque); +static unsigned int lgd_null_get_frame_stride(void * opaque); +static bool lgd_null_decode (void * opaque, const uint8_t * src, size_t srcSize); +static bool lgd_null_get_buffer (void * opaque, uint8_t * dst, size_t dstSize); static bool lgd_null_create(void ** opaque) { @@ -56,7 +59,7 @@ static void lgd_null_destroy(void * opaque) free(opaque); } -static bool lgd_null_initialize(void * opaque, const LG_RendererFormat format) +static bool lgd_null_initialize(void * opaque, const LG_RendererFormat format, SDL_Window * window) { struct Inst * this = (struct Inst *)opaque; memcpy(&this->format, &format, sizeof(LG_RendererFormat)); @@ -65,6 +68,8 @@ static bool lgd_null_initialize(void * opaque, const LG_RendererFormat format) static void lgd_null_deinitialize(void * opaque) { + struct Inst * this = (struct Inst *)opaque; + memset(this, 0, sizeof(struct Inst)); } static LG_OutFormat lgd_null_get_out_format(void * opaque) @@ -78,20 +83,39 @@ static unsigned int lgd_null_get_frame_pitch(void * opaque) return this->format.pitch; } -static bool lgd_null_decode(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize) +static unsigned int lgd_null_get_frame_stride(void * opaque) { - memcpySSE(dst, src, dstSize); + struct Inst * this = (struct Inst *)opaque; + return this->format.stride; +} + +static bool lgd_null_decode(void * opaque, const uint8_t * src, size_t srcSize) +{ + struct Inst * this = (struct Inst *)opaque; + this->src = src; + return true; +} + +static bool lgd_null_get_buffer(void * opaque, uint8_t * dst, size_t dstSize) +{ + struct Inst * this = (struct Inst *)opaque; + if (!this->src) + return false; + + memcpySSE(dst, this->src, dstSize); return true; } const LG_Decoder LGD_NULL = { - .name = "NULL", - .create = lgd_null_create, - .destroy = lgd_null_destroy, - .initialize = lgd_null_initialize, - .deinitialize = lgd_null_deinitialize, - .get_out_format = lgd_null_get_out_format, - .get_frame_pitch = lgd_null_get_frame_pitch, - .decode = lgd_null_decode + .name = "NULL", + .create = lgd_null_create, + .destroy = lgd_null_destroy, + .initialize = lgd_null_initialize, + .deinitialize = lgd_null_deinitialize, + .get_out_format = lgd_null_get_out_format, + .get_frame_pitch = lgd_null_get_frame_pitch, + .get_frame_stride = lgd_null_get_frame_stride, + .decode = lgd_null_decode, + .get_buffer = lgd_null_get_buffer }; \ No newline at end of file diff --git a/client/lg-decoder.h b/client/lg-decoder.h index 7cefeb84..add0f259 100644 --- a/client/lg-decoder.h +++ b/client/lg-decoder.h @@ -19,34 +19,53 @@ Place, Suite 330, Boston, MA 02111-1307 USA #pragma once +#include "lg-renderer.h" + #include #include +#include -#include "lg-renderer.h" +#include typedef enum LG_OutFormat { - LG_OUTPUT_BGRA + LG_OUTPUT_BGRA, + LG_OUTPUT_YUV420 } LG_OutFormat; -typedef bool (* LG_DecoderCreate )(void ** opaque); -typedef void (* LG_DecoderDestroy )(void * opaque); -typedef bool (* LG_DecoderInitialize )(void * opaque, const LG_RendererFormat format); -typedef void (* LG_DecoderDeInitialize )(void * opaque); -typedef LG_OutFormat (* LG_DecoderGetOutFormat )(void * opaque); -typedef unsigned int (* LG_DecoderGetFramePitch)(void * opaque); -typedef bool (* LG_DecoderDecode )(void * opaque, uint8_t * dst, size_t dstSize, const uint8_t * src, size_t srcSize); +typedef bool (* LG_DecoderCreate )(void ** opaque); +typedef void (* LG_DecoderDestroy )(void * opaque); +typedef bool (* LG_DecoderInitialize )(void * opaque, const LG_RendererFormat format, SDL_Window * window); +typedef void (* LG_DecoderDeInitialize )(void * opaque); +typedef LG_OutFormat (* LG_DecoderGetOutFormat )(void * opaque); +typedef unsigned int (* LG_DecoderGetFramePitch )(void * opaque); +typedef unsigned int (* LG_DecoderGetFrameStride)(void * opaque); +typedef bool (* LG_DecoderDecode )(void * opaque, const uint8_t * src, size_t srcSize); +typedef bool (* LG_DecoderGetBuffer )(void * opaque, uint8_t * dst, size_t dstSize); + +typedef bool (* LG_DecoderInitGLTexture )(void * opaque, GLenum target, GLuint texture, void ** ref); +typedef void (* LG_DecoderFreeGLTexture )(void * opaque, void * ref); +typedef bool (* LG_DecoderUpdateGLTexture)(void * opaque, void * ref); typedef struct LG_Decoder { - const char * name; - LG_DecoderCreate create; - LG_DecoderDestroy destroy; - LG_DecoderInitialize initialize; - LG_DecoderDeInitialize deinitialize; - LG_DecoderGetOutFormat get_out_format; - LG_DecoderGetFramePitch get_frame_pitch; - LG_DecoderDecode decode; + // mandatory support + const char * name; + LG_DecoderCreate create; + LG_DecoderDestroy destroy; + LG_DecoderInitialize initialize; + LG_DecoderDeInitialize deinitialize; + LG_DecoderGetOutFormat get_out_format; + LG_DecoderGetFramePitch get_frame_pitch; + LG_DecoderGetFrameStride get_frame_stride; + LG_DecoderDecode decode; + LG_DecoderGetBuffer get_buffer; + + // optional support + const bool has_gl; + LG_DecoderInitGLTexture init_gl_texture; + LG_DecoderFreeGLTexture free_gl_texture; + LG_DecoderUpdateGLTexture update_gl_texture; } LG_Decoder; \ No newline at end of file diff --git a/client/renderers/opengl.c b/client/renderers/opengl.c index 233d4462..fa05e8d9 100644 --- a/client/renderers/opengl.c +++ b/client/renderers/opengl.c @@ -36,10 +36,9 @@ Place, Suite 330, Boston, MA 02111-1307 USA #define BUFFER_COUNT 2 -#define FRAME_TEXTURE 0 -#define FPS_TEXTURE 1 -#define MOUSE_TEXTURE 2 -#define TEXTURE_COUNT 3 +#define FPS_TEXTURE 0 +#define MOUSE_TEXTURE 1 +#define TEXTURE_COUNT 2 static PFNGLXGETVIDEOSYNCSGIPROC glXGetVideoSyncSGI = NULL; static PFNGLXWAITVIDEOSYNCSGIPROC glXWaitVideoSyncSGI = NULL; @@ -82,16 +81,18 @@ struct Inst uint64_t drawStart; bool hasBuffers; - GLuint vboID[1]; + GLuint vboID[BUFFER_COUNT]; uint8_t * texPixels[BUFFER_COUNT]; LG_Lock syncLock; - int texIndex, wTexIndex; + int texIndex; int texList; int fpsList; int mouseList; LG_RendererRect destRect; - bool hasTextures; + bool hasTextures, hasFrames; + GLuint frames[BUFFER_COUNT]; + void * decoderFrames[BUFFER_COUNT]; GLuint textures[TEXTURE_COUNT]; uint gpuFrameCount; @@ -283,15 +284,8 @@ bool opengl_on_frame_event(void * opaque, const LG_RendererFormat format, const } LG_UNLOCK(this->formatLock); - // lock, perform the update, then unlock LG_LOCK(this->syncLock); - if (!this->decoder->decode( - this->decoderData, - this->texPixels[this->wTexIndex], - this->texSize, - data, - this->format.pitch - )) + if (!this->decoder->decode(this->decoderData, data, format.pitch)) { DEBUG_ERROR("decode returned failure"); LG_UNLOCK(this->syncLock); @@ -430,7 +424,7 @@ bool opengl_render(void * opaque, SDL_Window * window) glXGetVideoSyncSGI(&before); SDL_GL_SwapWindow(window); - // wait for the swap to happen to ensure we dont buffer frames // + // wait for the swap to happen to ensure we dont buffer frames glXGetVideoSyncSGI(&after); if (before == after) glXWaitVideoSyncSGI(1, 0, &before); @@ -588,8 +582,8 @@ static bool configure(struct Inst * this, SDL_Window *window) if (!this->decoder->initialize( this->decoderData, - this->format - )) + this->format, + window)) { DEBUG_ERROR("Failed to initialize decoder"); return false; @@ -602,6 +596,12 @@ static bool configure(struct Inst * this, SDL_Window *window) this->vboFormat = GL_BGRA; break; + case LG_OUTPUT_YUV420: + // fixme + this->intFormat = GL_RGBA8; + this->vboFormat = GL_BGRA; + break; + default: DEBUG_ERROR("Format not supported"); LG_UNLOCK(this->formatLock); @@ -618,54 +618,57 @@ static bool configure(struct Inst * this, SDL_Window *window) this->fpsList = glGenLists(1); this->mouseList = glGenLists(1); - // generate the pixel unpack buffers - glGenBuffers(1, this->vboID); - if (check_gl_error("glGenBuffers")) + // generate the pixel unpack buffers if the decoder isn't going to do it for us + if (!this->decoder->has_gl) { - LG_UNLOCK(this->formatLock); - return false; - } - this->hasBuffers = true; + glGenBuffers(BUFFER_COUNT, this->vboID); + if (check_gl_error("glGenBuffers")) + { + LG_UNLOCK(this->formatLock); + return false; + } + this->hasBuffers = true; - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0]); - if (check_gl_error("glBindBuffer")) - { - LG_UNLOCK(this->formatLock); - return false; + for(int i = 0; i < BUFFER_COUNT; ++i) + { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[i]); + if (check_gl_error("glBindBuffer")) + { + LG_UNLOCK(this->formatLock); + return false; + } + + glBufferStorage( + GL_PIXEL_UNPACK_BUFFER, + this->texSize, + NULL, + GL_MAP_WRITE_BIT | + GL_MAP_PERSISTENT_BIT + ); + if (check_gl_error("glBufferStorage")) + { + LG_UNLOCK(this->formatLock); + return false; + } + + this->texPixels[i] = glMapBufferRange( + GL_PIXEL_UNPACK_BUFFER, + 0, + this->texSize, + GL_MAP_WRITE_BIT | + GL_MAP_PERSISTENT_BIT | + GL_MAP_FLUSH_EXPLICIT_BIT + ); + + if (check_gl_error("glMapBufferRange")) + { + LG_UNLOCK(this->formatLock); + return false; + } + } } - glBufferStorage( - GL_PIXEL_UNPACK_BUFFER, - this->texSize * BUFFER_COUNT, - NULL, - GL_MAP_WRITE_BIT | - GL_MAP_PERSISTENT_BIT - ); - if (check_gl_error("glBufferStorage")) - { - LG_UNLOCK(this->formatLock); - return false; - } - - this->texPixels[0] = glMapBufferRange( - GL_PIXEL_UNPACK_BUFFER, - 0, - this->texSize * BUFFER_COUNT, - GL_MAP_WRITE_BIT | - GL_MAP_PERSISTENT_BIT | - GL_MAP_FLUSH_EXPLICIT_BIT - ); - - if (check_gl_error("glMapBufferRange")) - { - LG_UNLOCK(this->formatLock); - return false; - } - - for(int i = 1; i < BUFFER_COUNT; ++i) - this->texPixels[i] = this->texPixels[i-1] + this->texSize; - - // create the textures + // create the overlay textures glGenTextures(TEXTURE_COUNT, this->textures); if (check_gl_error("glGenTextures")) { @@ -674,51 +677,72 @@ static bool configure(struct Inst * this, SDL_Window *window) } this->hasTextures = true; - // create the frame texture - glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]); - if (check_gl_error("glBindTexture")) + // create the frame textures + glGenTextures(BUFFER_COUNT, this->frames); + if (check_gl_error("glGenTextures")) { LG_UNLOCK(this->formatLock); return false; } + this->hasFrames = true; - glTexImage2D( - GL_TEXTURE_2D, - 0, - this->intFormat, - this->format.width, - this->format.height * BUFFER_COUNT, - 0, - this->vboFormat, - GL_UNSIGNED_BYTE, - (void*)0 - ); - if (check_gl_error("glTexImage2D")) + for(int i = 0; i < BUFFER_COUNT; ++i) { - LG_UNLOCK(this->formatLock); - return false; - } + // bind and create the new texture + glBindTexture(GL_TEXTURE_2D, this->frames[i]); + if (check_gl_error("glBindTexture")) + { + LG_UNLOCK(this->formatLock); + return false; + } - // configure the texture - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D( + GL_TEXTURE_2D, + 0, + this->intFormat, + this->format.width, + this->format.height, + 0, + this->vboFormat, + GL_UNSIGNED_BYTE, + (void*)0 + ); + if (check_gl_error("glTexImage2D")) + { + LG_UNLOCK(this->formatLock); + return false; + } - for (int i = 0; i < BUFFER_COUNT; ++i) - { - const float ts = (1.0f / BUFFER_COUNT) * i; - const float te = (1.0f / BUFFER_COUNT) + ts; + if (this->decoder->has_gl) + { + if (!this->decoder->init_gl_texture( + this->decoderData, + GL_TEXTURE_2D, + this->frames[i], + &this->decoderFrames[i])) + { + LG_UNLOCK(this->formatLock); + return false; + } + } + else + { + // configure the texture + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + } // create the display lists glNewList(this->texList + i, GL_COMPILE); - glBindTexture(GL_TEXTURE_2D, this->textures[FRAME_TEXTURE]); + glBindTexture(GL_TEXTURE_2D, this->frames[i]); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glBegin(GL_TRIANGLE_STRIP); - glTexCoord2f(0.0f, ts); glVertex2i(0 , 0 ); - glTexCoord2f(1.0f, ts); glVertex2i(this->format.width, 0 ); - glTexCoord2f(0.0f, te); glVertex2i(0 , this->format.height); - glTexCoord2f(1.0f, te); glVertex2i(this->format.width, this->format.height); + glTexCoord2f(0.0f, 0.0f); glVertex2i(0 , 0 ); + glTexCoord2f(1.0f, 0.0f); glVertex2i(this->format.width, 0 ); + glTexCoord2f(0.0f, 1.0f); glVertex2i(0 , this->format.height); + glTexCoord2f(1.0f, 1.0f); glVertex2i(this->format.width, this->format.height); glEnd(); glEndList(); } @@ -753,9 +777,28 @@ static void deconfigure(struct Inst * this) this->hasTextures = false; } + if (this->hasFrames) + { + if (this->decoder->has_gl) + { + for(int i = 0; i < BUFFER_COUNT; ++i) + { + if (this->decoderFrames[i]) + this->decoder->free_gl_texture( + this->decoderData, + this->decoderFrames[i] + ); + this->decoderFrames[i] = NULL; + } + } + + glDeleteTextures(BUFFER_COUNT, this->frames); + this->hasFrames = false; + } + if (this->hasBuffers) { - glDeleteBuffers(1, this->vboID); + glDeleteBuffers(BUFFER_COUNT, this->vboID); this->hasBuffers = false; } @@ -765,7 +808,7 @@ static void deconfigure(struct Inst * this) this->glContext = NULL; } - if (this->decoder) + if (this->decoderData) { this->decoder->destroy(this->decoderData); this->decoderData = NULL; @@ -918,51 +961,76 @@ static bool draw_frame(struct Inst * this) return true; } - this->texIndex = this->wTexIndex; - if (++this->wTexIndex == BUFFER_COUNT) - this->wTexIndex = 0; + if (++this->texIndex == BUFFER_COUNT) + this->texIndex = 0; + this->frameUpdate = false; LG_UNLOCK(this->syncLock); LG_LOCK(this->formatLock); + if (this->decoder->has_gl) + { + if (!this->decoder->update_gl_texture( + this->decoderData, + this->decoderFrames[this->texIndex] + )) + { + LG_UNLOCK(this->formatLock); + DEBUG_ERROR("Failed to update the texture from the decoder"); + return false; + } + } + else + { + glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]); + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[this->texIndex]); - // bind the texture and update it - glBindTexture(GL_TEXTURE_2D , this->textures[FRAME_TEXTURE]); - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, this->vboID[0] ); - glPixelStorei(GL_UNPACK_ALIGNMENT , 4 ); - glPixelStorei(GL_UNPACK_ROW_LENGTH , this->format.stride ); - - // copy the buffer to the texture - glFlushMappedBufferRange( - GL_PIXEL_UNPACK_BUFFER, - this->texSize * this->texIndex, - this->texSize - ); - - // update the texture - glTexSubImage2D( - GL_TEXTURE_2D, - 0, - 0, - this->texIndex * this->format.height, - this->format.width , - this->format.height, - this->vboFormat, - GL_UNSIGNED_BYTE, - (void*)(this->texIndex * this->texSize) - ); - if (check_gl_error("glTexSubImage2D")) - DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu", - this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize + glPixelStorei(GL_UNPACK_ALIGNMENT , 4); + glPixelStorei(GL_UNPACK_ROW_LENGTH , + this->decoder->get_frame_stride(this->decoderData) ); - // unbind the buffer - glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + if (!this->decoder->get_buffer( + this->decoderData, + this->texPixels[this->texIndex], + this->texSize + )) + { + LG_UNLOCK(this->formatLock); + DEBUG_ERROR("Failed to get the buffer from the decoder"); + return false; + } + + glFlushMappedBufferRange(GL_PIXEL_UNPACK_BUFFER, 0, this->texSize); + + // update the texture + glTexSubImage2D( + GL_TEXTURE_2D, + 0, + 0, + 0, + this->format.width , + this->format.height, + this->vboFormat, + GL_UNSIGNED_BYTE, + (void*)0 + ); + if (check_gl_error("glTexSubImage2D")) + { + DEBUG_ERROR("texIndex: %u, width: %u, height: %u, vboFormat: %x, texSize: %lu", + this->texIndex, this->format.width, this->format.height, this->vboFormat, this->texSize + ); + } + + // unbind the buffer + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + } const bool mipmap = this->opt.mipmap && ( (this->format.width > this->destRect.w) || (this->format.height > this->destRect.h)); + glBindTexture(GL_TEXTURE_2D, this->frames[this->texIndex]); if (mipmap) { glGenerateMipmap(GL_TEXTURE_2D); @@ -974,8 +1042,8 @@ static bool draw_frame(struct Inst * this) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); } - glBindTexture(GL_TEXTURE_2D, 0); + LG_UNLOCK(this->formatLock); return true; }