diff --git a/Makefile b/Makefile index 5422016..cf2189c 100644 --- a/Makefile +++ b/Makefile @@ -107,7 +107,13 @@ ifeq ($(OPENGL),1) CONFIG += -DUSE_GLX _CFLAGS += $(shell pkg-config --cflags gl glu glew) LIBS += $(shell pkg-config --libs gl glu glew) +CONFIG += -DUSE_OPENGLOSD +_CFLAGS += $(shell pkg-config --cflags glew) +LIBS += $(shell pkg-config --libs glew) +_CFLAGS += $(shell pkg-config --cflags freetype2) +LIBS += $(shell pkg-config --libs freetype2) endif + # # Test that libswresample is available # @@ -189,14 +195,14 @@ DEFINES += -DPLUGIN_NAME_I18N='"$(PLUGIN)"' -D_GNU_SOURCE $(CONFIG) \ ### Make it standard override CXXFLAGS += $(_CFLAGS) $(DEFINES) $(INCLUDES) \ - -g -Wextra -Winit-self -Werror=overloaded-virtual + -g -Wextra -Winit-self -Werror=overloaded-virtual -std=c++0x override CFLAGS += $(_CFLAGS) $(DEFINES) $(INCLUDES) \ -g -W -Wextra -Winit-self -Wdeclaration-after-statement ### The object files (add further files here): -OBJS = $(PLUGIN).o softhddev.o video.o audio.o codec.o ringbuffer.o +OBJS = $(PLUGIN).o softhddev.o video.o audio.o codec.o ringbuffer.o openglosd.o SRCS = $(wildcard $(OBJS:.o=.c)) $(PLUGIN).cpp diff --git a/openglosd.cpp b/openglosd.cpp new file mode 100644 index 0000000..f0d2b37 --- /dev/null +++ b/openglosd.cpp @@ -0,0 +1,2027 @@ +#define __STL_CONFIG_H +#include +#include "openglosd.h" + + +/**************************************************************************************** +* Helpers +****************************************************************************************/ + +void ConvertColor(const GLint &colARGB, glm::vec4 &col) { + col.a = ((colARGB & 0xFF000000) >> 24) / 255.0; + col.r = ((colARGB & 0x00FF0000) >> 16) / 255.0; + col.g = ((colARGB & 0x0000FF00) >> 8 ) / 255.0; + col.b = ((colARGB & 0x000000FF) ) / 255.0; +} + +/**************************************************************************************** +* cShader +****************************************************************************************/ + + +const char *rectVertexShader = +"#version 330 core \n\ +\ +layout (location = 0) in vec2 position; \ +out vec4 rectCol; \ +uniform vec4 inColor; \ +uniform mat4 projection; \ +\ +void main() \ +{ \ + gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \ + rectCol = inColor; \ +} \ +"; + +const char *rectFragmentShader = +"#version 330 core \n\ +\ +in vec4 rectCol; \ +out vec4 color; \ +\ +void main() \ +{ \ + color = rectCol; \ +} \ +"; + +const char *textureVertexShader = +"#version 330 core \n\ +\ +layout (location = 0) in vec2 position; \ +layout (location = 1) in vec2 texCoords; \ +\ +out vec2 TexCoords; \ +out vec4 alphaValue;\ +\ +uniform mat4 projection; \ +uniform vec4 alpha; \ +\ +void main() \ +{ \ + gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \ + TexCoords = texCoords; \ + alphaValue = alpha; \ +} \ +"; + +const char *textureFragmentShader = +"#version 330 core \n\ +in vec2 TexCoords; \ +in vec4 alphaValue; \ +out vec4 color; \ +\ +uniform sampler2D screenTexture; \ +\ +void main() \ +{ \ + color = texture(screenTexture, TexCoords) * alphaValue; \ +} \ +"; + +const char *textVertexShader = +"#version 330 core \n\ +\ +layout (location = 0) in vec2 position; \ +layout (location = 1) in vec2 texCoords; \ +\ +out vec2 TexCoords; \ +out vec4 textColor; \ +\ +uniform mat4 projection; \ +uniform vec4 inColor; \ +\ +void main() \ +{ \ + gl_Position = projection * vec4(position.x, position.y, 0.0, 1.0); \ + TexCoords = texCoords; \ + textColor = inColor; \ +} \ +"; + +const char *textFragmentShader = +"#version 330 core \n\ +in vec2 TexCoords; \ +in vec4 textColor; \ +\ +out vec4 color; \ +\ +uniform sampler2D glyphTexture; \ +\ +void main() \ +{ \ + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(glyphTexture, TexCoords).r); \ + color = textColor * sampled; \ +} \ +"; + +static cShader *Shaders[stCount]; + +void cShader::Use(void) { + glUseProgram(id); +} + +bool cShader::Load(eShaderType type) { + this->type = type; + + const char *vertexCode = NULL; + const char *fragmentCode = NULL; + + switch (type) { + case stRect: + vertexCode = rectVertexShader; + fragmentCode = rectFragmentShader; + break; + case stTexture: + vertexCode = textureVertexShader; + fragmentCode = textureFragmentShader; + break; + case stText: + vertexCode = textVertexShader; + fragmentCode = textFragmentShader; + break; + default: + esyslog("[softhddev]unknown shader type\n"); + break; + } + + if (vertexCode == NULL || fragmentCode == NULL) { + esyslog("[softhddev]ERROR reading shader\n"); + return false; + } + + if (!Compile(vertexCode, fragmentCode)) { + esyslog("[softhddev]ERROR compiling shader\n"); + return false; + } + return true; +} + +void cShader::SetFloat(const GLchar *name, GLfloat value) { + glUniform1f(glGetUniformLocation(id, name), value); +} + +void cShader::SetInteger(const GLchar *name, GLint value) { + glUniform1i(glGetUniformLocation(id, name), value); +} + +void cShader::SetVector2f(const GLchar *name, GLfloat x, GLfloat y) { + glUniform2f(glGetUniformLocation(id, name), x, y); +} + +void cShader::SetVector3f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z) { + glUniform3f(glGetUniformLocation(id, name), x, y, z); +} + +void cShader::SetVector4f(const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { + glUniform4f(glGetUniformLocation(id, name), x, y, z, w); +} + +void cShader::SetMatrix4(const GLchar *name, const glm::mat4 &matrix) { + glUniformMatrix4fv(glGetUniformLocation(id, name), 1, GL_FALSE, glm::value_ptr(matrix)); +} + +bool cShader::Compile(const char *vertexCode, const char *fragmentCode) { + GLuint sVertex, sFragment; + // Vertex Shader + sVertex = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(sVertex, 1, &vertexCode, NULL); + glCompileShader(sVertex); + if (!CheckCompileErrors(sVertex)) + return false; + // Fragment Shader + sFragment = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(sFragment, 1, &fragmentCode, NULL); + glCompileShader(sFragment); + if (!CheckCompileErrors(sFragment)) + return false; + // link Program + id = glCreateProgram(); + glAttachShader(id, sVertex); + glAttachShader(id, sFragment); + glLinkProgram(id); + if (!CheckCompileErrors(id, true)) + return false; + // Delete the shaders as they're linked into our program now and no longer necessery + glDeleteShader(sVertex); + glDeleteShader(sFragment); + return true; +} + +bool cShader::CheckCompileErrors(GLuint object, bool program) { + GLint success; + GLchar infoLog[1024]; + if (!program) { + glGetShaderiv(object, GL_COMPILE_STATUS, &success); + if (!success) { + glGetShaderInfoLog(object, 1024, NULL, infoLog); + esyslog("[softhddev]:SHADER: Compile-time error: Type: %d - %s", type, infoLog); + return false; + } + } else { + glGetProgramiv(object, GL_LINK_STATUS, &success); + if (!success) { + glGetProgramInfoLog(object, 1024, NULL, infoLog); + esyslog("[softhddev]:SHADER: Link-time error: Type: %d", type); + return false; + } + } + return true; +} + +#define KERNING_UNKNOWN (-10000) +/**************************************************************************************** +* cOglGlyph +****************************************************************************************/ +cOglGlyph::cOglGlyph(uint charCode, FT_BitmapGlyph ftGlyph) { + this->charCode = charCode; + bearingLeft = ftGlyph->left; + bearingTop = ftGlyph->top; + width = ftGlyph->bitmap.width; + height = ftGlyph->bitmap.rows; + advanceX = ftGlyph->root.advance.x >> 16; //value in 1/2^16 pixel + LoadTexture(ftGlyph); +} + +cOglGlyph::~cOglGlyph(void) { + +} + +int cOglGlyph::GetKerningCache(uint prevSym) { + for (int i = kerningCache.Size(); --i > 0; ) { + if (kerningCache[i].prevSym == prevSym) + return kerningCache[i].kerning; + } + return KERNING_UNKNOWN; +} + +void cOglGlyph::SetKerningCache(uint prevSym, int kerning) { + kerningCache.Append(tKerning(prevSym, kerning)); +} + +void cOglGlyph::BindTexture(void) { + glBindTexture(GL_TEXTURE_2D, texture); +} + +void cOglGlyph::LoadTexture(FT_BitmapGlyph ftGlyph) { + // Disable byte-alignment restriction + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RED, + ftGlyph->bitmap.width, + ftGlyph->bitmap.rows, + 0, + GL_RED, + GL_UNSIGNED_BYTE, + ftGlyph->bitmap.buffer + ); + // Set texture options + 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_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + glPixelStorei(GL_UNPACK_ALIGNMENT, 4); +} + + +/**************************************************************************************** +* cOglFont +****************************************************************************************/ +FT_Library cOglFont::ftLib = 0; +cList *cOglFont::fonts = 0; +bool cOglFont::initiated = false; + +cOglFont::cOglFont(const char *fontName, int charHeight) : name(fontName) { + size = charHeight; + height = 0; + bottom = 0; + + int error = FT_New_Face(ftLib, fontName, 0, &face); + if (error) + esyslog("[softhddev]ERROR: failed to open %s!", *name); + + FT_Set_Char_Size(face, 0, charHeight * 64, 0, 0); + height = (face->size->metrics.ascender - face->size->metrics.descender + 63) / 64; + bottom = abs((face->size->metrics.descender - 63) / 64); +} + +cOglFont::~cOglFont(void) { + FT_Done_Face(face); +} + +cOglFont *cOglFont::Get(const char *name, int charHeight) { + if (!fonts) + Init(); + + cOglFont *font; + for (font = fonts->First(); font; font = fonts->Next(font)) + if (!strcmp(font->Name(), name) && charHeight == font->Size()) { + return font; + } + font = new cOglFont(name, charHeight); + fonts->Add(font); + return font; +} + +void cOglFont::Init(void) { + fonts = new cList; + if (FT_Init_FreeType(&ftLib)) + esyslog("[softhddev]failed to initialize FreeType library!"); + initiated = true; +} + +void cOglFont::Cleanup(void) { + if (!initiated) + return; + delete fonts; + fonts = 0; + if (FT_Done_FreeType(ftLib)) + esyslog("failed to deinitialize FreeType library!"); +} + +cOglGlyph* cOglFont::Glyph(uint charCode) const { + // Non-breaking space: + if (charCode == 0xA0) + charCode = 0x20; + + // Lookup in cache: + for (cOglGlyph *g = glyphCache.First(); g; g = glyphCache.Next(g)) { + if (g->CharCode() == charCode) { + return g; + } + } + + FT_UInt glyph_index = FT_Get_Char_Index(face, charCode); + + FT_Int32 loadFlags = FT_LOAD_NO_BITMAP; + // Load glyph image into the slot (erase previous one): + int error = FT_Load_Glyph(face, glyph_index, loadFlags); + if (error) { + esyslog("[softhddev]FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); + return NULL; + } + + FT_Glyph ftGlyph; + FT_Stroker stroker; + error = FT_Stroker_New( ftLib, &stroker ); + if (error) { + esyslog("[softhddev]FT_Stroker_New FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); + return NULL; + } + float outlineWidth = 0.25f; + FT_Stroker_Set(stroker, + (int)(outlineWidth * 64), + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, + 0); + + + error = FT_Get_Glyph(face->glyph, &ftGlyph); + if (error) { + esyslog("[softhddev]FT_Get_Glyph FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); + return NULL; + } + + error = FT_Glyph_StrokeBorder( &ftGlyph, stroker, 0, 1 ); + if ( error ) { + esyslog("[softhddev]FT_Glyph_StrokeBorder FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); + return NULL; + } + FT_Stroker_Done(stroker); + + error = FT_Glyph_To_Bitmap( &ftGlyph, FT_RENDER_MODE_NORMAL, 0, 1); + if (error) { + esyslog("[softhddev]FT_Glyph_To_Bitmap FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); + return NULL; + } + + cOglGlyph *Glyph = new cOglGlyph(charCode, (FT_BitmapGlyph)ftGlyph); + glyphCache.Add(Glyph); + FT_Done_Glyph(ftGlyph); + + return Glyph; +} + +int cOglFont::Kerning(cOglGlyph *glyph, uint prevSym) const { + int kerning = 0; + if (glyph && prevSym) { + kerning = glyph->GetKerningCache(prevSym); + if (kerning == KERNING_UNKNOWN) { + FT_Vector delta; + FT_UInt glyph_index = FT_Get_Char_Index(face, glyph->CharCode()); + FT_UInt glyph_index_prev = FT_Get_Char_Index(face, prevSym); + FT_Get_Kerning(face, glyph_index_prev, glyph_index, FT_KERNING_DEFAULT, &delta); + kerning = delta.x / 64; + glyph->SetKerningCache(prevSym, kerning); + } + } + return kerning; +} + +/**************************************************************************************** +* cOglFb +****************************************************************************************/ +cOglFb::cOglFb(GLint width, GLint height, GLint viewPortWidth, GLint viewPortHeight) { + initiated = false; + fb = 0; + texture = 0; + this->width = width; + this->height = height; + this->viewPortWidth = viewPortWidth; + this->viewPortHeight = viewPortHeight; + if (width != viewPortWidth || height != viewPortHeight) + scrollable = true; + else + scrollable = false; +} + +cOglFb::~cOglFb(void) { + glDeleteTextures(1, &texture); + glDeleteFramebuffers(1, &fb); +} + + +bool cOglFb::Init(void) { + initiated = true; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); //GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + esyslog("[softhddev]ERROR: Framebuffer is not complete!\n"); + return false; + } + return true; +} + +void cOglFb::Bind(void) { + if (!initiated) + Init(); + glViewport(0, 0, width, height); + glBindFramebuffer(GL_FRAMEBUFFER, fb); +} + +void cOglFb::BindRead(void) { + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb); +} + +void cOglFb::BindWrite(void) { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); +} + +void cOglFb::Unbind(void) { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindTexture(GL_TEXTURE_2D, 0); +} + +bool cOglFb::BindTexture(void) { + if (!initiated) + return false; + glBindTexture(GL_TEXTURE_2D, texture); + return true; +} + +void cOglFb::Blit(GLint destX1, GLint destY1, GLint destX2, GLint destY2) { + glBlitFramebuffer(0, 0, width, height, destX1, destY1, destX2, destY2, GL_COLOR_BUFFER_BIT, GL_NEAREST); + glFlush(); +} + +/**************************************************************************************** +* cOglOutputFb +****************************************************************************************/ +cOglOutputFb::cOglOutputFb(GLint width, GLint height) : cOglFb(width, height, width, height) { +// surface = 0; +} + +cOglOutputFb::~cOglOutputFb(void) { +// glVDPAUUnregisterSurfaceNV(surface); +} + +bool cOglOutputFb::Init(void) { +#if 0 + //fetching osd vdpau output surface from softhddevice +// void *vdpauOutputSurface = GetVDPAUOutputSurface(); + glGenTextures(1, &texture); + //register surface for texture +// surface = glVDPAURegisterOutputSurfaceNV(vdpauOutputSurface, GL_TEXTURE_2D, 1, &texture); + //set write access to surface +// glVDPAUSurfaceAccessNV(surface, GL_WRITE_DISCARD_NV); + //create framebuffer +// glVDPAUMapSurfacesNV (1, &surface); + glBindTexture(GL_TEXTURE_2D, texture); + glGenFramebuffers(1, &fb); + glBindFramebuffer(GL_FRAMEBUFFER, fb); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + esyslog("[softhddev]ERROR::cOglOutputFb: Framebuffer is not complete!"); + return true; + return false; + } +#endif + return true; +} + +void cOglOutputFb::BindWrite(void) { +// glVDPAUMapSurfacesNV(1, &surface); +// glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fb); +} + +void cOglOutputFb::Unbind(void) { +// glVDPAUUnmapSurfacesNV(1, &surface); +// glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +/**************************************************************************************** +* cOglVb +****************************************************************************************/ +static cOglVb *VertexBuffers[vbCount]; + +cOglVb::cOglVb(int type) { + this->type = (eVertexBufferType)type; + vao = 0; + vbo = 0; + sizeVertex1 = 0; + sizeVertex2 = 0; + numVertices = 0; + drawMode = 0; +} + +cOglVb::~cOglVb(void) { +} + +bool cOglVb::Init(void) { + + if (type == vbTexture) { + //Texture VBO definition + sizeVertex1 = 2; + sizeVertex2 = 2; + numVertices = 6; + drawMode = GL_TRIANGLES; + shader = stTexture; + } else if (type == vbRect) { + //Rectangle VBO definition + sizeVertex1 = 2; + sizeVertex2 = 0; + numVertices = 4; + drawMode = GL_TRIANGLE_FAN; + shader = stRect; + } else if (type == vbEllipse) { + //Ellipse VBO definition + sizeVertex1 = 2; + sizeVertex2 = 0; + numVertices = 182; + drawMode = GL_TRIANGLE_FAN; + shader = stRect; + } else if (type == vbSlope) { + //Slope VBO definition + sizeVertex1 = 2; + sizeVertex2 = 0; + numVertices = 102; + drawMode = GL_TRIANGLE_FAN; + shader = stRect; + } else if (type == vbText) { + //Text VBO definition + sizeVertex1 = 2; + sizeVertex2 = 2; + numVertices = 6; + drawMode = GL_TRIANGLES; + shader = stText; + } + glGenVertexArrays(1, &vao); + glGenBuffers(1, &vbo); + glBindVertexArray(vao); + glBindBuffer(GL_ARRAY_BUFFER, vbo); + + glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * (sizeVertex1 + sizeVertex2) * numVertices, NULL, GL_DYNAMIC_DRAW); + + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, sizeVertex1, GL_FLOAT, GL_FALSE, (sizeVertex1 + sizeVertex2) * sizeof(GLfloat), (GLvoid*)0); + if (sizeVertex2 > 0) { + glEnableVertexAttribArray(1); + glVertexAttribPointer(1, sizeVertex2, GL_FLOAT, GL_FALSE, (sizeVertex1 + sizeVertex2) * sizeof(GLfloat), (GLvoid*)(sizeVertex1 * sizeof(GLfloat))); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + return true; +} + +void cOglVb::Bind(void) { + glBindVertexArray(vao); +} + +void cOglVb::Unbind(void) { + glBindVertexArray(0); +} + +void cOglVb::ActivateShader(void) { + Shaders[shader]->Use(); +} + +void cOglVb::EnableBlending(void) { + glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); +} + +void cOglVb::DisableBlending(void) { + glDisable(GL_BLEND); +} + +void cOglVb::SetShaderColor(GLint color) { + glm::vec4 col; + ConvertColor(color, col); + Shaders[shader]->SetVector4f("inColor", col.r, col.g, col.b, col.a); +} + +void cOglVb::SetShaderAlpha(GLint alpha) { + Shaders[shader]->SetVector4f("alpha", 1.0f, 1.0f, 1.0f, (GLfloat)(alpha) / 255.0f); +} + +void cOglVb::SetShaderProjectionMatrix(GLint width, GLint height) { + glm::mat4 projection = glm::ortho(0.0f, (GLfloat)width, (GLfloat)height, 0.0f, -1.0f, 1.0f); + Shaders[shader]->SetMatrix4("projection", projection); +} + +void cOglVb::SetVertexData(GLfloat *vertices, int count) { + if (count == 0) + count = numVertices; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * (sizeVertex1 + sizeVertex2) * count, vertices); + glBindBuffer(GL_ARRAY_BUFFER, 0); +} + +void cOglVb::DrawArrays(int count) { + if (count == 0) + count = numVertices; + glDrawArrays(drawMode, 0, count); + glFlush(); +} + + +/**************************************************************************************** +* cOpenGLCmd +****************************************************************************************/ +//------------------ cOglCmdInitOutputFb -------------------- +cOglCmdInitOutputFb::cOglCmdInitOutputFb(cOglOutputFb *oFb) : cOglCmd(NULL) { + this->oFb = oFb; +} + +bool cOglCmdInitOutputFb::Execute(void) { + bool ok = oFb->Init(); + oFb->Unbind(); + return ok; +} + +//------------------ cOglCmdInitFb -------------------- +cOglCmdInitFb::cOglCmdInitFb(cOglFb *fb, cCondWait *wait) : cOglCmd(fb) { + this->wait = wait; +} + +bool cOglCmdInitFb::Execute(void) { + bool ok = fb->Init(); + fb->Unbind(); + if (wait) + wait->Signal(); + return ok; +} + +//------------------ cOglCmdDeleteFb -------------------- +cOglCmdDeleteFb::cOglCmdDeleteFb(cOglFb *fb) : cOglCmd(fb) { +} + +bool cOglCmdDeleteFb::Execute(void) { + + return true; +} + +//------------------ cOglCmdRenderFbToBufferFb -------------------- +cOglCmdRenderFbToBufferFb::cOglCmdRenderFbToBufferFb(cOglFb *fb, cOglFb *buffer, GLint x, GLint y, GLint transparency, GLint drawPortX, GLint drawPortY) : cOglCmd(fb) { + this->buffer = buffer; + this->x = (GLfloat)x; + this->y = (GLfloat)y; + this->drawPortX = (GLfloat)drawPortX; + this->drawPortY = (GLfloat)drawPortY; + this->transparency = transparency; +} + +bool cOglCmdRenderFbToBufferFb::Execute(void) { + GLfloat x2 = x + fb->ViewportWidth(); //right + GLfloat y2 = y + fb->ViewportHeight(); //bottom + + GLfloat texX1 = drawPortX / (GLfloat)fb->Width(); + GLfloat texY1 = drawPortY / (GLfloat)fb->Height(); + GLfloat texX2 = texX1 + 1.0f; + GLfloat texY2 = texY1 + 1.0f; + + if (fb->Scrollable()) { + GLfloat pageHeight = (GLfloat)fb->ViewportHeight() / (GLfloat)fb->Height(); + texX1 = abs(drawPortX) / (GLfloat)fb->Width(); + texY1 = 1.0f - pageHeight - abs(drawPortY) / (GLfloat)fb->Height(); + texX2 = texX1 + (GLfloat)fb->ViewportWidth() / (GLfloat)fb->Width(); + texY2 = texY1 + pageHeight; + } + + GLfloat quadVertices[] = { + // Pos // TexCoords + x , y , texX1, texY2, //left top + x , y2, texX1, texY1, //left bottom + x2, y2, texX2, texY1, //right bottom + + x , y , texX1, texY2, //left top + x2, y2, texX2, texY1, //right bottom + x2, y , texX2, texY2 //right top + }; + + VertexBuffers[vbTexture]->ActivateShader(); + VertexBuffers[vbTexture]->SetShaderAlpha(transparency); + VertexBuffers[vbTexture]->SetShaderProjectionMatrix(buffer->Width(), buffer->Height()); + + buffer->Bind(); + if (!fb->BindTexture()) + return false; + VertexBuffers[vbTexture]->Bind(); + VertexBuffers[vbTexture]->SetVertexData(quadVertices); + VertexBuffers[vbTexture]->DrawArrays(); + VertexBuffers[vbTexture]->Unbind(); + buffer->Unbind(); + + return true; +} +#if 0 +//------------------ cOglCmdCopyBufferToOutputFb -------------------- +cOglCmdCopyBufferToOutputFb::cOglCmdCopyBufferToOutputFb(cOglFb *fb, cOglOutputFb *oFb, GLint x, GLint y) : cOglCmd(fb) { + this->oFb = oFb; + this->x = x; + this->y = y; +} + +bool cOglCmdCopyBufferToOutputFb::Execute(void) { + + fb->BindRead(); + oFb->BindWrite(); + fb->Blit(x, y + fb->Height(), x + fb->Width(), y); + oFb->Unbind(); + +// ActivateOsd(oFb->texture,oFb->fb,glXGetCurrentContext()); + return true; +} +#endif +//------------------ cOglCmdFill -------------------- +cOglCmdFill::cOglCmdFill(cOglFb *fb, GLint color) : cOglCmd(fb) { + this->color = color; +} + +bool cOglCmdFill::Execute(void) { + glm::vec4 col; + ConvertColor(color, col); + fb->Bind(); + glClearColor(col.r, col.g, col.b, col.a); + glClear(GL_COLOR_BUFFER_BIT); + fb->Unbind(); + return true; +} + +//------------------ cOglCmdDrawRectangle -------------------- +cOglCmdDrawRectangle::cOglCmdDrawRectangle( cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color) : cOglCmd(fb) { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->color = color; +} + +bool cOglCmdDrawRectangle::Execute(void) { + GLfloat x1 = x; + GLfloat y1 = y; + GLfloat x2 = x + width; + GLfloat y2 = y + height; + + GLfloat vertices[] = { + x1, y1, //left top + x2, y1, //right top + x2, y2, //right bottom + x1, y2 //left bottom + }; + + VertexBuffers[vbRect]->ActivateShader(); + VertexBuffers[vbRect]->SetShaderColor(color); + VertexBuffers[vbRect]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + fb->Bind(); + VertexBuffers[vbRect]->DisableBlending(); + VertexBuffers[vbRect]->Bind(); + VertexBuffers[vbRect]->SetVertexData(vertices); + VertexBuffers[vbRect]->DrawArrays(); + VertexBuffers[vbRect]->Unbind(); + VertexBuffers[vbRect]->EnableBlending(); + fb->Unbind(); + + return true; +} + +//------------------ cOglCmdDrawEllipse -------------------- +///quadrants: +///< 0 draws the entire ellipse +///< 1..4 draws only the first, second, third or fourth quadrant, respectively +///< 5..8 draws the right, top, left or bottom half, respectively +///< -1..-4 draws the inverted part of the given quadrant +cOglCmdDrawEllipse::cOglCmdDrawEllipse( cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color, GLint quadrants) : cOglCmd(fb) { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->color = color; + this->quadrants = quadrants; +} + +bool cOglCmdDrawEllipse::Execute(void) { + int numVertices = 0; + GLfloat *vertices = NULL; + + switch (quadrants) { + case 0: + vertices = CreateVerticesFull(numVertices); + break; + case 1: case 2: case 3: case 4: case -1: case -2: case -3: case -4: + vertices = CreateVerticesQuadrant(numVertices); + break; + case 5: case 6: case 7: case 8: + vertices = CreateVerticesHalf(numVertices); + break; + default: + break; + } + + VertexBuffers[vbEllipse]->ActivateShader(); + VertexBuffers[vbEllipse]->SetShaderColor(color); + VertexBuffers[vbEllipse]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + //not antialiased + fb->Bind(); + VertexBuffers[vbEllipse]->DisableBlending(); + VertexBuffers[vbEllipse]->Bind(); + VertexBuffers[vbEllipse]->SetVertexData(vertices, numVertices); + VertexBuffers[vbEllipse]->DrawArrays(numVertices); + VertexBuffers[vbEllipse]->Unbind(); + VertexBuffers[vbEllipse]->EnableBlending(); + fb->Unbind(); + + delete[] vertices; + return true; +} + +GLfloat *cOglCmdDrawEllipse::CreateVerticesFull(int &numVertices) { + int size = 364; + numVertices = size/2; + GLfloat radiusX = (GLfloat)width/2; + GLfloat radiusY = (GLfloat)height/2; + GLfloat *vertices = new GLfloat[size]; + vertices[0] = x + radiusX; + vertices[1] = y + radiusY; + for (int i=0; i <= 180; i++) { + vertices[2*i+2] = x + radiusX + (GLfloat)cos(2*i * M_PI / 180.0f)*radiusX; + vertices[2*i+3] = y + radiusY - (GLfloat)sin(2*i * M_PI / 180.0f)*radiusY; + } + return vertices; +} + +GLfloat *cOglCmdDrawEllipse::CreateVerticesQuadrant(int &numVertices) { + int size = 94; + numVertices = size/2; + GLfloat radiusX = (GLfloat)width; + GLfloat radiusY = (GLfloat)height; + GLint transX = 0; + GLint transY = 0; + GLint startAngle = 0; + GLfloat *vertices = new GLfloat[size]; + switch (quadrants) { + case 1: + vertices[0] = x; + vertices[1] = y + height; + transY = radiusY; + break; + case 2: + vertices[0] = x + width; + vertices[1] = y + height; + transX = radiusX; + transY = radiusY; + startAngle = 90; + break; + case 3: + vertices[0] = x + width; + vertices[1] = y; + transX = radiusX; + startAngle = 180; + break; + case 4: + vertices[0] = x; + vertices[1] = y; + startAngle = 270; + break; + case -1: + vertices[0] = x + width; + vertices[1] = y; + transY = radiusY; + break; + case -2: + vertices[0] = x; + vertices[1] = y; + transX = radiusX; + transY = radiusY; + startAngle = 90; + break; + case -3: + vertices[0] = x; + vertices[1] = y + height; + transX = radiusX; + startAngle = 180; + break; + case -4: + vertices[0] = x + width; + vertices[1] = y + height; + startAngle = 270; + break; + default: + break; + } + for (int i=0; i <= 45; i++) { + vertices[2*i+2] = x + transX + (GLfloat)cos((2*i + startAngle) * M_PI / 180.0f)*radiusX; + vertices[2*i+3] = y + transY - (GLfloat)sin((2*i + startAngle) * M_PI / 180.0f)*radiusY; + } + return vertices; +} + +GLfloat *cOglCmdDrawEllipse::CreateVerticesHalf(int &numVertices) { + int size = 184; + numVertices = size/2; + GLfloat radiusX = 0.0f; + GLfloat radiusY = 0.0f; + GLint transX = 0; + GLint transY = 0; + GLint startAngle = 0; + GLfloat *vertices = new GLfloat[size]; + switch (quadrants) { + case 5: + radiusX = (GLfloat)width; + radiusY = (GLfloat)height/2; + vertices[0] = x; + vertices[1] = y + radiusY; + startAngle = 270; + transY = radiusY; + break; + case 6: + radiusX = (GLfloat)width/2; + radiusY = (GLfloat)height; + vertices[0] = x + radiusX; + vertices[1] = y + radiusY; + startAngle = 0; + transX = radiusX; + transY = radiusY; + break; + case 7: + radiusX = (GLfloat)width; + radiusY = (GLfloat)height/2; + vertices[0] = x + radiusX; + vertices[1] = y + radiusY; + startAngle = 90; + transX = radiusX; + transY = radiusY; + break; + case 8: + radiusX = (GLfloat)width/2; + radiusY = (GLfloat)height; + vertices[0] = x + radiusX; + vertices[1] = y; + startAngle = 180; + transX = radiusX; + break; + default: + break; + } + for (int i=0; i <= 90; i++) { + vertices[2*i+2] = x + transX + (GLfloat)cos((2*i + startAngle) * M_PI / 180.0f)*radiusX; + vertices[2*i+3] = y + transY - (GLfloat)sin((2*i + startAngle) * M_PI / 180.0f)*radiusY; + } + return vertices; +} + +//------------------ cOglCmdDrawSlope -------------------- +///type: +///< 0: horizontal, rising, lower +///< 1: horizontal, rising, upper +///< 2: horizontal, falling, lower +///< 3: horizontal, falling, upper +///< 4: vertical, rising, lower +///< 5: vertical, rising, upper +///< 6: vertical, falling, lower +///< 7: vertical, falling, upper +cOglCmdDrawSlope::cOglCmdDrawSlope( cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color, GLint type) : cOglCmd(fb) { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->color = color; + this->type = type; +} + +bool cOglCmdDrawSlope::Execute(void) { + bool falling = type & 0x02; + bool vertical = type & 0x04; + + int steps = 100; + if (width < 100) + steps = 25; + int numVertices = steps + 2; + GLfloat *vertices = new GLfloat[numVertices*2]; + + switch (type) { + case 0: case 4: + vertices[0] = (GLfloat)(x + width); + vertices[1] = (GLfloat)(y + height); + break; + case 1: case 5: + vertices[0] = (GLfloat)x; + vertices[1] = (GLfloat)y; + break; + case 2: case 6: + vertices[0] = (GLfloat)x; + vertices[1] = (GLfloat)(y + height); + break; + case 3: case 7: + vertices[0] = (GLfloat)(x + width); + vertices[1] = (GLfloat)y; + break; + default: + vertices[0] = (GLfloat)(x); + vertices[1] = (GLfloat)(y); + break; + } + + for (int i = 0; i <= steps; i++) { + GLfloat c = cos(i * M_PI / steps); + if (falling) + c = -c; + if (vertical) { + vertices[2*i+2] = (GLfloat)x + (GLfloat)width / 2.0f + (GLfloat)width * c / 2.0f; + vertices[2*i+3] = (GLfloat)y + (GLfloat)i * ((GLfloat)height) / steps ; + } else { + vertices[2*i+2] = (GLfloat)x + (GLfloat)i * ((GLfloat)width) / steps ; + vertices[2*i+3] = (GLfloat)y + (GLfloat)height / 2.0f + (GLfloat)height * c / 2.0f; + } + } + + VertexBuffers[vbSlope]->ActivateShader(); + VertexBuffers[vbSlope]->SetShaderColor(color); + VertexBuffers[vbSlope]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + //not antialiased + fb->Bind(); + VertexBuffers[vbSlope]->DisableBlending(); + VertexBuffers[vbSlope]->Bind(); + VertexBuffers[vbSlope]->SetVertexData(vertices, numVertices); + VertexBuffers[vbSlope]->DrawArrays(numVertices); + VertexBuffers[vbSlope]->Unbind(); + VertexBuffers[vbSlope]->EnableBlending(); + fb->Unbind(); + + delete[] vertices; + return true; +} + +//------------------ cOglCmdDrawText -------------------- +cOglCmdDrawText::cOglCmdDrawText( cOglFb *fb, GLint x, GLint y, unsigned int *symbols, GLint limitX, + const char *name, int fontSize, tColor colorText) : cOglCmd(fb), fontName(name) { + this->x = x; + this->y = y; + this->limitX = limitX; + this->colorText = colorText; + this->fontSize = fontSize; + this->symbols = symbols; +} + +cOglCmdDrawText::~cOglCmdDrawText(void) { + free(symbols); +} + +bool cOglCmdDrawText::Execute(void) { + cOglFont *f = cOglFont::Get(*fontName, fontSize); + if (!f) + return false; + + VertexBuffers[vbText]->ActivateShader(); + VertexBuffers[vbText]->SetShaderColor(colorText); + VertexBuffers[vbText]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + fb->Bind(); + VertexBuffers[vbText]->Bind(); + + int xGlyph = x; + int fontHeight = f->Height(); + int bottom = f->Bottom(); + uint sym = 0; + uint prevSym = 0; + int kerning = 0; + + for (int i = 0; symbols[i]; i++) { + sym = symbols[i]; + cOglGlyph *g = f->Glyph(sym); + if (!g) { + esyslog("[softhddev]ERROR: could not load glyph %x", sym); + } + + if ( limitX && xGlyph + g->AdvanceX() > limitX ) + break; + + kerning = f->Kerning(g, prevSym); + prevSym = sym; + + GLfloat x1 = xGlyph + kerning + g->BearingLeft(); //left + GLfloat y1 = y + (fontHeight - bottom - g->BearingTop()); //top + GLfloat x2 = x1 + g->Width(); //right + GLfloat y2 = y1 + g->Height(); //bottom + + GLfloat vertices[] = { + x1, y2, 0.0, 1.0, // left bottom + x1, y1, 0.0, 0.0, // left top + x2, y1, 1.0, 0.0, // right top + + x1, y2, 0.0, 1.0, // left bottom + x2, y1, 1.0, 0.0, // right top + x2, y2, 1.0, 1.0 // right bottom + }; + + g->BindTexture(); + VertexBuffers[vbText]->SetVertexData(vertices); + VertexBuffers[vbText]->DrawArrays(); + + xGlyph += kerning + g->AdvanceX(); + + if ( xGlyph > fb->Width() - 1 ) + break; + } + + glBindTexture(GL_TEXTURE_2D, 0); + VertexBuffers[vbText]->Unbind(); + fb->Unbind(); + return true; +} + +//------------------ cOglCmdDrawImage -------------------- +cOglCmdDrawImage::cOglCmdDrawImage(cOglFb *fb, tColor *argb, GLint width, GLint height, GLint x, GLint y, bool overlay, double scaleX, double scaleY): cOglCmd(fb) { + this->argb = argb; + this->x = x; + this->y = y; + this->width = width; + this->height = height; + this->overlay = overlay; + this->scaleX = scaleX; + this->scaleY = scaleY; +} + +cOglCmdDrawImage::~cOglCmdDrawImage(void) { + free(argb); +} + +bool cOglCmdDrawImage::Execute(void) { + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA8, + width, + height, + 0, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + argb + ); + 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_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + + GLfloat x1 = x; //left + GLfloat y1 = y; //top + GLfloat x2 = x + width; //right + GLfloat y2 = y + height; //bottom + + GLfloat quadVertices[] = { + x1, y2, 0.0, 1.0, // left bottom + x1, y1, 0.0, 0.0, // left top + x2, y1, 1.0, 0.0, // right top + + x1, y2, 0.0, 1.0, // left bottom + x2, y1, 1.0, 0.0, // right top + x2, y2, 1.0, 1.0 // right bottom + }; + + VertexBuffers[vbTexture]->ActivateShader(); + VertexBuffers[vbTexture]->SetShaderAlpha(255); + VertexBuffers[vbTexture]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + + fb->Bind(); + glBindTexture(GL_TEXTURE_2D, texture); + if (overlay) + VertexBuffers[vbTexture]->DisableBlending(); + VertexBuffers[vbTexture]->Bind(); + VertexBuffers[vbTexture]->SetVertexData(quadVertices); + VertexBuffers[vbTexture]->DrawArrays(); + VertexBuffers[vbTexture]->Unbind(); + if (overlay) + VertexBuffers[vbTexture]->EnableBlending(); + fb->Unbind(); + glBindTexture(GL_TEXTURE_2D, 0); + glDeleteTextures(1, &texture); + + return true; +} + +//------------------ cOglCmdDrawTexture -------------------- +cOglCmdDrawTexture::cOglCmdDrawTexture(cOglFb *fb, sOglImage *imageRef, GLint x, GLint y): cOglCmd(fb) { + this->imageRef = imageRef; + this->x = x; + this->y = y; +} + +bool cOglCmdDrawTexture::Execute(void) { + GLfloat x1 = x; //top + GLfloat y1 = y; //left + GLfloat x2 = x + imageRef->width; //right + GLfloat y2 = y + imageRef->height; //bottom + + GLfloat quadVertices[] = { + // Pos // TexCoords + x1, y1, 0.0f, 0.0f, //left bottom + x1, y2, 0.0f, 1.0f, //left top + x2, y2, 1.0f, 1.0f, //right top + + x1, y1, 0.0f, 0.0f, //left bottom + x2, y2, 1.0f, 1.0f, //right top + x2, y1, 1.0f, 0.0f //right bottom + }; + + VertexBuffers[vbTexture]->ActivateShader(); + VertexBuffers[vbTexture]->SetShaderAlpha(128); + VertexBuffers[vbTexture]->SetShaderProjectionMatrix(fb->Width(), fb->Height()); + + fb->Bind(); + glBindTexture(GL_TEXTURE_2D, imageRef->texture); + VertexBuffers[vbTexture]->Bind(); + VertexBuffers[vbTexture]->SetVertexData(quadVertices); + VertexBuffers[vbTexture]->DrawArrays(); + VertexBuffers[vbTexture]->Unbind(); + fb->Unbind(); + + return true; +} + + +//------------------ cOglCmdStoreImage -------------------- +cOglCmdStoreImage::cOglCmdStoreImage(sOglImage *imageRef, tColor *argb) : cOglCmd(NULL) { + this->imageRef = imageRef; + data = argb; +} + +cOglCmdStoreImage::~cOglCmdStoreImage(void) { + free(data); +} + +bool cOglCmdStoreImage::Execute(void) { + glGenTextures(1, &imageRef->texture); + glBindTexture(GL_TEXTURE_2D, imageRef->texture); + glTexImage2D( + GL_TEXTURE_2D, + 0, + GL_RGBA8, + imageRef->width, + imageRef->height, + 0, + GL_BGRA, + GL_UNSIGNED_INT_8_8_8_8_REV, + data + ); + 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_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glBindTexture(GL_TEXTURE_2D, 0); + return true; +} + +//------------------ cOglCmdDropImage -------------------- +cOglCmdDropImage::cOglCmdDropImage(sOglImage *imageRef, cCondWait *wait) : cOglCmd(NULL) { + this->imageRef = imageRef; + this->wait = wait; +} + +bool cOglCmdDropImage::Execute(void) { + if (imageRef->texture != GL_NONE) + glDeleteTextures(1, &imageRef->texture); + wait->Signal(); + return true; +} + +/****************************************************************************** +* cOglThread +******************************************************************************/ +cOglThread::cOglThread(cCondWait *startWait, int maxCacheSize) : cThread("oglThread") { + stalled = false; + memCached = 0; + this->maxCacheSize = maxCacheSize * 1024 * 1024; + this->startWait = startWait; + wait = new cCondWait(); + maxTextureSize = 0; + for (int i = 0; i < OGL_MAX_OSDIMAGES; i++) { + imageCache[i].used = false; + imageCache[i].texture = GL_NONE; + imageCache[i].width = 0; + imageCache[i].height = 0; + } + + Start(); +} + +cOglThread::~cOglThread() { + delete wait; + wait = NULL; +} + +void cOglThread::Stop(void) { + for (int i = 0; i < OGL_MAX_OSDIMAGES; i++) { + if (imageCache[i].used) { + DropImageData(i); + } + } + Cancel(2); + stalled = false; +} + +void cOglThread::DoCmd(cOglCmd* cmd) { + while (stalled) + cCondWait::SleepMs(10); + + bool doSignal = false; + Lock(); + if (commands.size() == 0) + doSignal = true; + commands.push(cmd); + Unlock(); + + if (commands.size() > OGL_CMDQUEUE_SIZE) { + stalled = true; + } + + if (doSignal || stalled) + wait->Signal(); +} + +int cOglThread::StoreImage(const cImage &image) { + if (image.Width() > maxTextureSize || image.Height() > maxTextureSize) { + esyslog("[softhddev] cannot store image of %dpx x %dpx " + "(maximum size is %dpx x %dpx) - falling back to " + "cOsdProvider::StoreImageData()", + image.Width(), image.Height(), + maxTextureSize, maxTextureSize); + return 0; + } + + int imgSize = image.Width() * image.Height(); + int newMemUsed = imgSize * sizeof(tColor) + memCached; + if (newMemUsed > maxCacheSize) { + float cachedMB = memCached / 1024.0f / 1024.0f; + float maxMB = maxCacheSize / 1024.0f / 1024.0f; + esyslog("[softhddev]Maximum size for GPU cache reached. Used: %.2fMB Max: %.2fMB", cachedMB, maxMB); + return 0; + } + + int slot = GetFreeSlot(); + if (!slot) + return 0; + + tColor *argb = MALLOC(tColor, imgSize); + if (!argb) { + esyslog("[softhddev]memory allocation of %d kb for OSD image failed", imgSize * sizeof(tColor) / 1024); + ClearSlot(slot); + slot = 0; + return 0; + } + + memcpy(argb, image.Data(), sizeof(tColor) * imgSize); + + sOglImage *imageRef = GetImageRef(slot); + imageRef->width = image.Width(); + imageRef->height = image.Height(); + DoCmd(new cOglCmdStoreImage(imageRef, argb)); + + cTimeMs timer(5000); + while (imageRef->used && imageRef->texture == 0 && !timer.TimedOut()) + cCondWait::SleepMs(2); + + if (imageRef->texture == GL_NONE) { + esyslog("[softhddev]failed to store OSD image texture! (%s)", timer.TimedOut() ? "timed out" : "allocation failed"); + DropImageData(slot); + slot = 0; + } + + memCached += imgSize * sizeof(tColor); + return slot; +} + +int cOglThread::GetFreeSlot(void) { + Lock(); + int slot = 0; + for (int i = 0; i < OGL_MAX_OSDIMAGES && !slot; i++) { + if (!imageCache[i].used) { + imageCache[i].used = true; + slot = -i - 1; + } + } + Unlock(); + return slot; +} + +void cOglThread::ClearSlot(int slot) { + int i = -slot - 1; + if (i >= 0 && i < OGL_MAX_OSDIMAGES) { + Lock(); + imageCache[i].used = false; + imageCache[i].texture = GL_NONE; + imageCache[i].width = 0; + imageCache[i].height = 0; + Unlock(); + } +} + +sOglImage *cOglThread::GetImageRef(int slot) { + int i = -slot - 1; + if (0 <= i && i < OGL_MAX_OSDIMAGES) + return &imageCache[i]; + return 0; +} + +void cOglThread::DropImageData(int imageHandle) { + sOglImage *imageRef = GetImageRef(imageHandle); + if (!imageRef) + return; + int imgSize = imageRef->width * imageRef->height * sizeof(tColor); + memCached -= imgSize; + cCondWait dropWait; + DoCmd(new cOglCmdDropImage(imageRef, &dropWait)); + dropWait.Wait(); + ClearSlot(imageHandle); +} + + +void cOglThread::Action(void) { + if (!InitOpenGL()) { + esyslog("[softhddev]Could not initiate OpenGL Context"); + Cleanup(); + startWait->Signal(); + return; + } + dsyslog("[softhddev]OpenGL Context initialized"); + + if (!InitShaders()) { + esyslog("[softhddev]Could not initiate Shaders"); + Cleanup(); + startWait->Signal(); + return; + } + dsyslog("[softhddev]Shaders initialized"); + + if (!InitVdpauInterop()) { + esyslog("[softhddev]: vdpau interop NOT initialized"); + Cleanup(); + startWait->Signal(); + return; + } + dsyslog("[softhddev]vdpau interop initialized"); + + if (!InitVertexBuffers()) { + esyslog("[softhddev]: Vertex Buffers NOT initialized"); + Cleanup(); + startWait->Signal(); + return; + } + dsyslog("[softhddev]Vertex buffers initialized"); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize); + dsyslog("[softhddev]Maximum Pixmap size: %dx%dpx", maxTextureSize, maxTextureSize); + + //now Thread is ready to do his job + startWait->Signal(); + stalled = false; + + while(Running()) { + + if (commands.empty()) { + wait->Wait(20); + continue; + } + + Lock(); + cOglCmd* cmd = commands.front(); + commands.pop(); + Unlock(); + //uint64_t start = cTimeMs::Now(); + cmd->Execute(); + //esyslog("[softhddev]\"%s\", %dms, %d commands left, time %" PRIu64 "", cmd->Description(), (int)(cTimeMs::Now() - start), commands.size(), cTimeMs::Now()); + delete cmd; + if (stalled && commands.size() < OGL_CMDQUEUE_SIZE / 2) + stalled = false; + } + + dsyslog("[softhddev]Cleaning up OpenGL stuff"); + Cleanup(); + dsyslog("[softhddev]OpenGL Worker Thread Ended"); +} + +extern "C" int GlxInitopengl(); + + + +bool cOglThread::InitOpenGL(void) { + + +#if 0 + const char *displayName = X11DisplayName; + if (!displayName) { + displayName = getenv("DISPLAY"); + if (!displayName) { + displayName = ":0.0"; + } + } + dsyslog("[softhddev]OpenGL using display %s", displayName); + + int argc = 3; + char* buffer[3]; + buffer[0] = strdup("openglosd"); + buffer[1] = strdup("-display"); + buffer[2] = strdup(displayName); + char **argv = buffer; + glutInit(&argc, argv); + glutInitDisplayMode (GLUT_SINGLE | GLUT_RGBA | GLUT_ALPHA); + glutInitWindowSize (1, 1); + glutInitWindowPosition (0, 0); + glutCreateWindow("openglosd"); + glutHideWindow(); + free(buffer[0]); + free(buffer[1]); + free(buffer[2]); + + GLenum err = glewInit(); + if( err != GLEW_OK) { + esyslog("[softhddev]glewInit failed, aborting\n"); + return false; + } +#endif +#if 1 + if (!GlxInitopengl()) + return false; +#endif + VertexBuffers[vbText]->EnableBlending(); + glDisable(GL_DEPTH_TEST); + return true; + +} + +bool cOglThread::InitShaders(void) { + for (int i=0; i < stCount; i++) { + cShader *shader = new cShader(); + if (!shader->Load((eShaderType)i)) + return false; + Shaders[i] = shader; + } + return true; +} + +void cOglThread::DeleteShaders(void) { + for (int i=0; i < stCount; i++) + delete Shaders[i]; +} + +bool cOglThread::InitVdpauInterop(void) { +#if 0 + void *vdpDevice = GetVDPAUDevice(); + void *procAdress = GetVDPAUProcAdress(); + while (glGetError() != GL_NO_ERROR); + glVDPAUInitNV(vdpDevice, procAdress); + if (glGetError() != GL_NO_ERROR) + return false; +#endif + return true; +} + +bool cOglThread::InitVertexBuffers(void) { + for (int i=0; i < vbCount; i++) { + cOglVb *vb = new cOglVb(i); + if (!vb->Init()) + return false; + VertexBuffers[i] = vb; + } + return true; +} + +void cOglThread::DeleteVertexBuffers(void) { + for (int i=0; i < vbCount; i++) { + delete VertexBuffers[i]; + } +} + +void cOglThread::Cleanup(void) { + esyslog("[softhddev]OglThread cleanup\n"); + DeleteVertexBuffers(); + delete cOglOsd::oFb; + cOglOsd::oFb = NULL; + DeleteShaders(); +// glVDPAUFiniNV(); + cOglFont::Cleanup(); +// glutExit(); +} + +/**************************************************************************************** +* cOglPixmap +****************************************************************************************/ + +cOglPixmap::cOglPixmap(std::shared_ptr oglThread, int Layer, const cRect &ViewPort, const cRect &DrawPort) : cPixmap(Layer, ViewPort, DrawPort) { + this->oglThread = oglThread; + int width = DrawPort.IsEmpty() ? ViewPort.Width() : DrawPort.Width(); + int height = DrawPort.IsEmpty() ? ViewPort.Height() : DrawPort.Height(); + fb = new cOglFb(width, height, ViewPort.Width(), ViewPort.Height()); + dirty = true; +} + +cOglPixmap::~cOglPixmap(void) { + if (!oglThread->Active()) + return; + oglThread->DoCmd(new cOglCmdDeleteFb(fb)); +} + +void cOglPixmap::SetAlpha(int Alpha) { + Alpha = constrain(Alpha, ALPHA_TRANSPARENT, ALPHA_OPAQUE); + if (Alpha != cPixmap::Alpha()) { + cPixmap::SetAlpha(Alpha); + SetDirty(); + } +} + +void cOglPixmap::SetTile(bool Tile) { + cPixmap::SetTile(Tile); + SetDirty(); +} + +void cOglPixmap::SetViewPort(const cRect &Rect) { + cPixmap::SetViewPort(Rect); + SetDirty(); +} + +void cOglPixmap::SetDrawPortPoint(const cPoint &Point, bool Dirty) { + cPixmap::SetDrawPortPoint(Point, Dirty); + if (Dirty) + SetDirty(); +} + +void cOglPixmap::Clear(void) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + oglThread->DoCmd(new cOglCmdFill(fb, clrTransparent)); + SetDirty(); + MarkDrawPortDirty(DrawPort()); +} + +void cOglPixmap::Fill(tColor Color) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + oglThread->DoCmd(new cOglCmdFill(fb, Color)); + SetDirty(); + MarkDrawPortDirty(DrawPort()); +} + +void cOglPixmap::DrawImage(const cPoint &Point, const cImage &Image) { + if (!oglThread->Active()) + return; + tColor *argb = MALLOC(tColor, Image.Width() * Image.Height()); + if (!argb) + return; + memcpy(argb, Image.Data(), sizeof(tColor) * Image.Width() * Image.Height()); + + oglThread->DoCmd(new cOglCmdDrawImage(fb, argb, Image.Width(), Image.Height(), Point.X(), Point.Y())); + + SetDirty(); + MarkDrawPortDirty(cRect(Point, cSize(Image.Width(), Image.Height())).Intersected(DrawPort().Size())); +} + +void cOglPixmap::DrawImage(const cPoint &Point, int ImageHandle) { + if (!oglThread->Active()) + return; + if (ImageHandle < 0 && oglThread->GetImageRef(ImageHandle)) { + sOglImage *img = oglThread->GetImageRef(ImageHandle); + oglThread->DoCmd(new cOglCmdDrawTexture(fb, img, Point.X(), Point.Y())); + } + /* + Fallback to VDR implementation, needs to separate cSoftOsdProvider from softhddevice.cpp + else { + if (cSoftOsdProvider::GetImageData(ImageHandle)) + DrawImage(Point, *cSoftOsdProvider::GetImageData(ImageHandle)); + } + */ + SetDirty(); + MarkDrawPortDirty(DrawPort()); +} + +void cOglPixmap::DrawPixel(const cPoint &Point, tColor Color) { + esyslog("[softhddev] DrawPixel %d %d color %x not implemented in OpenGl OSD", Point.X(), Point.X(), Color); +} + +void cOglPixmap::DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg, tColor ColorBg, bool Overlay) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + bool specialColors = ColorFg || ColorBg; + tColor *argb = MALLOC(tColor, Bitmap.Width() * Bitmap.Height()); + if (!argb) + return; + + tColor *p = argb; + for (int py = 0; py < Bitmap.Height(); py++) + for (int px = 0; px < Bitmap.Width(); px++) { + tIndex index = *Bitmap.Data(px, py); + *p++ = (!index && Overlay) ? clrTransparent : (specialColors ? + (index == 0 ? ColorBg : index == 1 ? ColorFg : + Bitmap.Color(index)) : Bitmap.Color(index)); + } + oglThread->DoCmd(new cOglCmdDrawImage(fb, argb, Bitmap.Width(), Bitmap.Height(), Point.X(), Point.Y(), Overlay)); + SetDirty(); + MarkDrawPortDirty(cRect(Point, cSize(Bitmap.Width(), Bitmap.Height())).Intersected(DrawPort().Size())); +} + +void cOglPixmap::DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width, int Height, int Alignment) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + int len = s ? Utf8StrLen(s) : 0; + unsigned int *symbols = MALLOC(unsigned int, len + 1); + if (!symbols) + return; + + if (len) + Utf8ToArray(s, symbols, len + 1); + else + symbols[0] = 0; + + int x = Point.X(); + int y = Point.Y(); + int w = Font->Width(s); + int h = Font->Height(); + int limitX = 0; + int cw = Width ? Width : w; + int ch = Height ? Height : h; + cRect r(x, y, cw, ch); + + if (ColorBg != clrTransparent) + oglThread->DoCmd(new cOglCmdDrawRectangle(fb, r.X(), r.Y(), r.Width(), r.Height(), ColorBg)); + + if (Width || Height) { + limitX = x + cw; + if (Width) { + if ((Alignment & taLeft) != 0) { + if ((Alignment & taBorder) != 0) + x += std::max(h / TEXT_ALIGN_BORDER, 1); + } else if ((Alignment & taRight) != 0) { + if (w < Width) + x += Width - w; + if ((Alignment & taBorder) != 0) + x -= std::max(h / TEXT_ALIGN_BORDER, 1); + } else { // taCentered + if (w < Width) + x += (Width - w) / 2; + } + } + + if (Height) { + if ((Alignment & taTop) != 0) + ; + else if ((Alignment & taBottom) != 0) { + if (h < Height) + y += Height - h; + } else { // taCentered + if (h < Height) + y += (Height - h) / 2; + } + } + } + oglThread->DoCmd(new cOglCmdDrawText(fb, x, y, symbols, limitX, Font->FontName(), Font->Size(), ColorFg)); + + SetDirty(); + MarkDrawPortDirty(r); +} + +void cOglPixmap::DrawRectangle(const cRect &Rect, tColor Color) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + oglThread->DoCmd(new cOglCmdDrawRectangle(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color)); + SetDirty(); + MarkDrawPortDirty(Rect); +} + +void cOglPixmap::DrawEllipse(const cRect &Rect, tColor Color, int Quadrants) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + oglThread->DoCmd(new cOglCmdDrawEllipse(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color, Quadrants)); + SetDirty(); + MarkDrawPortDirty(Rect); +} + +void cOglPixmap::DrawSlope(const cRect &Rect, tColor Color, int Type) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + oglThread->DoCmd(new cOglCmdDrawSlope(fb, Rect.X(), Rect.Y(), Rect.Width(), Rect.Height(), Color, Type)); + SetDirty(); + MarkDrawPortDirty(Rect); +} + +void cOglPixmap::Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) { + esyslog("[softhddev] Render %d %d %d not implemented in OpenGl OSD", Pixmap->ViewPort().X(), Source.X(), Dest.X()); +} + +void cOglPixmap::Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest) { + esyslog("[softhddev] Copy %d %d %d not implemented in OpenGl OSD", Pixmap->ViewPort().X(), Source.X(), Dest.X()); +} + +void cOglPixmap::Scroll(const cPoint &Dest, const cRect &Source) { + esyslog("[softhddev] Scroll %d %d not implemented in OpenGl OSD", Source.X(), Dest.X()); +} + +void cOglPixmap::Pan(const cPoint &Dest, const cRect &Source) { + esyslog("[softhddev] Pan %d %d not implemented in OpenGl OSD", Source.X(), Dest.X()); +} + +/****************************************************************************** +* cOglOsd +******************************************************************************/ +cOglOutputFb *cOglOsd::oFb = NULL; + +cOglOsd::cOglOsd(int Left, int Top, uint Level, std::shared_ptr oglThread) : cOsd(Left, Top, Level) { + this->oglThread = oglThread; + bFb = NULL; + isSubtitleOsd = false; + int osdWidth = 0; + int osdHeight = 0; + + VideoGetOsdSize(&osdWidth, &osdHeight); + + dsyslog("[softhddev]cOglOsd osdLeft %d osdTop %d screenWidth %d screenHeight %d", Left, Top, osdWidth, osdHeight); + + //create vdpau output framebuffer + if (!oFb) { + oFb = new cOglOutputFb(osdWidth, osdHeight); + oglThread->DoCmd(new cOglCmdInitOutputFb(oFb)); + } +} + +cOglOsd::~cOglOsd() { + OsdClose(); + SetActive(false); + oglThread->DoCmd(new cOglCmdDeleteFb(bFb)); +} + +eOsdError cOglOsd::SetAreas(const tArea *Areas, int NumAreas) { + cRect r; + if (NumAreas > 1) + isSubtitleOsd = true; + for (int i = 0; i < NumAreas; i++) + r.Combine(cRect(Areas[i].x1, Areas[i].y1, Areas[i].Width(), Areas[i].Height())); + + tArea area = { r.Left(), r.Top(), r.Right(), r.Bottom(), 32 }; + + //now we know the actuaL osd size, create double buffer frame buffer + if (bFb) { + oglThread->DoCmd(new cOglCmdDeleteFb(bFb)); + DestroyPixmap(oglPixmaps[0]); + } + bFb = new cOglFb(r.Width(), r.Height(), r.Width(), r.Height()); + cCondWait initiated; + oglThread->DoCmd(new cOglCmdInitFb(bFb, &initiated)); + initiated.Wait(); + + return cOsd::SetAreas(&area, 1); +} + +cPixmap *cOglOsd::CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort) { + if (!oglThread->Active()) + return NULL; + LOCK_PIXMAPS; + int width = DrawPort.IsEmpty() ? ViewPort.Width() : DrawPort.Width(); + int height = DrawPort.IsEmpty() ? ViewPort.Height() : DrawPort.Height(); + + if (width > oglThread->MaxTextureSize() || height > oglThread->MaxTextureSize()) { + esyslog("[softhddev] cannot allocate pixmap of %dpx x %dpx, clipped to %dpx x %dpx!", + width, height, std::min(width, oglThread->MaxTextureSize()), std::min(height, oglThread->MaxTextureSize())); + width = std::min(width, oglThread->MaxTextureSize()); + height = std::min(height, oglThread->MaxTextureSize()); + } + + cOglPixmap *p = new cOglPixmap(oglThread, Layer, ViewPort, DrawPort); + + if (cOsd::AddPixmap(p)) { + //find free slot + for (int i = 0; i < oglPixmaps.Size(); i++) + if (!oglPixmaps[i]) + return oglPixmaps[i] = p; + //append at end + oglPixmaps.Append(p); + return p; + } + delete p; + return NULL; +} + +void cOglOsd::DestroyPixmap(cPixmap *Pixmap) { + if (!oglThread->Active()) + return; + if (!Pixmap) + return; + LOCK_PIXMAPS; + int start = 1; + if (isSubtitleOsd) + start = 0; + for (int i = start; i < oglPixmaps.Size(); i++) { + if (oglPixmaps[i] == Pixmap) { + if (Pixmap->Layer() >= 0) + oglPixmaps[0]->SetDirty(); + oglPixmaps[i] = NULL; + cOsd::DestroyPixmap(Pixmap); + return; + } + } +} + +void cOglOsd::Flush(void) { + if (!oglThread->Active()) + return; + LOCK_PIXMAPS; + //check if any pixmap is dirty + bool dirty = false; + for (int i = 0; i < oglPixmaps.Size() && !dirty; i++) + if (oglPixmaps[i] && oglPixmaps[i]->Layer() >= 0 && oglPixmaps[i]->IsDirty()) + dirty = true; + if (!dirty) + return; + //clear buffer + //uint64_t start = cTimeMs::Now(); + //dsyslog("[softhddev]Start Flush at %" PRIu64 "", cTimeMs::Now()); + oglThread->DoCmd(new cOglCmdFill(bFb, clrTransparent)); + + //render pixmap textures blended to buffer + for (int layer = 0; layer < MAXPIXMAPLAYERS; layer++) { + for (int i = 0; i < oglPixmaps.Size(); i++) { + if (oglPixmaps[i]) { + if (oglPixmaps[i]->Layer() == layer) { + oglThread->DoCmd(new cOglCmdRenderFbToBufferFb( oglPixmaps[i]->Fb(), + bFb, + oglPixmaps[i]->ViewPort().X(), + (!isSubtitleOsd) ? oglPixmaps[i]->ViewPort().Y() : 0, + oglPixmaps[i]->Alpha(), + oglPixmaps[i]->DrawPort().X(), + oglPixmaps[i]->DrawPort().Y())); + oglPixmaps[i]->SetDirty(false); + } + } + } + } + ActivateOsd(bFb->texture,Left(), Top() , bFb->Width(), bFb->Height()); +// oglThread->DoCmd(new cOglCmdCopyBufferToOutputFb(bFb, oFb, Left(), Top())); + //dsyslog("[softhddev]End Flush at %" PRIu64 ", duration %d", cTimeMs::Now(), (int)(cTimeMs::Now()-start)); +} + +void cOglOsd::DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias) { + (void)FactorX; + (void)FactorY; + (void)AntiAlias; + int yNew = y - oglPixmaps[0]->ViewPort().Y(); + oglPixmaps[0]->DrawBitmap(cPoint(x, yNew), Bitmap); +} diff --git a/openglosd.h b/openglosd.h new file mode 100644 index 0000000..aa56910 --- /dev/null +++ b/openglosd.h @@ -0,0 +1,514 @@ +#ifndef __SOFTHDDEVICE_OPENGLOSD_H +#define __SOFTHDDEVICE_OPENGLOSD_H + +#include +#include +#include + +#define GLM_FORCE_RADIANS +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H +#include FT_LCD_FILTER_H +#include FT_STROKER_H + +#undef __FTERRORS_H__ +#define FT_ERRORDEF( e, v, s ) { e, s }, +#define FT_ERROR_START_LIST { +#define FT_ERROR_END_LIST { 0, 0 } }; +const struct { + int code; + const char* message; +} FT_Errors[] = +#include FT_ERRORS_H + + +#include +#include + +#include +#include +#include + +#include "softhddev.h" + +extern "C" +{ +#include +#include + +#include "audio.h" +#include "video.h" +#include "codec.h" +} + +struct sOglImage { + GLuint texture; + GLint width; + GLint height; + bool used; +}; + +/**************************************************************************************** +* Helpers +****************************************************************************************/ + +void ConvertColor(const GLint &colARGB, glm::vec4 &col); + +/**************************************************************************************** +* cShader +****************************************************************************************/ +enum eShaderType { + stRect, + stTexture, + stText, + stCount +}; + +class cShader { +private: + eShaderType type; + GLuint id; + bool Compile(const char *vertexCode, const char *fragmentCode); + bool CheckCompileErrors(GLuint object, bool program = false); +public: + cShader(void) {}; + virtual ~cShader(void) {}; + bool Load(eShaderType type); + void Use(void); + void SetFloat (const GLchar *name, GLfloat value); + void SetInteger (const GLchar *name, GLint value); + void SetVector2f (const GLchar *name, GLfloat x, GLfloat y); + void SetVector3f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z); + void SetVector4f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w); + void SetMatrix4 (const GLchar *name, const glm::mat4 &matrix); +}; + +/**************************************************************************************** +* cOglGlyph +****************************************************************************************/ +class cOglGlyph : public cListObject { +private: + struct tKerning { + public: + tKerning(uint prevSym, GLfloat kerning = 0.0f) { + this->prevSym = prevSym; + this->kerning = kerning; + } + uint prevSym; + GLfloat kerning; + }; + uint charCode; + int bearingLeft; + int bearingTop; + int width; + int height; + int advanceX; + cVector kerningCache; + GLuint texture; + void LoadTexture(FT_BitmapGlyph ftGlyph); +public: + cOglGlyph(uint charCode, FT_BitmapGlyph ftGlyph); + virtual ~cOglGlyph(); + uint CharCode(void) { return charCode; } + int AdvanceX(void) { return advanceX; } + int BearingLeft(void) const { return bearingLeft; } + int BearingTop(void) const { return bearingTop; } + int Width(void) const { return width; } + int Height(void) const { return height; } + int GetKerningCache(uint prevSym); + void SetKerningCache(uint prevSym, int kerning); + void BindTexture(void); +}; + +/**************************************************************************************** +* cOglFont +****************************************************************************************/ +class cOglFont : public cListObject { +private: + static bool initiated; + cString name; + int size; + int height; + int bottom; + static FT_Library ftLib; + FT_Face face; + static cList *fonts; + mutable cList glyphCache; + cOglFont(const char *fontName, int charHeight); + static void Init(void); +public: + virtual ~cOglFont(void); + static cOglFont *Get(const char *name, int charHeight); + static void Cleanup(void); + const char *Name(void) { return *name; }; + int Size(void) { return size; }; + int Bottom(void) {return bottom; }; + int Height(void) {return height; }; + cOglGlyph* Glyph(uint charCode) const; + int Kerning(cOglGlyph *glyph, uint prevSym) const; +}; + +/**************************************************************************************** +* cOglFb +* Framebuffer Object - OpenGL part of a Pixmap +****************************************************************************************/ +class cOglFb { +protected: + bool initiated; +// GLuint fb; +// GLuint texture; + GLint width, height; + GLint viewPortWidth, viewPortHeight; + bool scrollable; +public: + GLuint fb; + GLuint texture; + cOglFb(GLint width, GLint height, GLint viewPortWidth, GLint viewPortHeight); + virtual ~cOglFb(void); + bool Initiated(void) { return initiated; } + virtual bool Init(void); + void Bind(void); + void BindRead(void); + virtual void BindWrite(void); + virtual void Unbind(void); + bool BindTexture(void); + void Blit(GLint destX1, GLint destY1, GLint destX2, GLint destY2); + GLint Width(void) { return width; }; + GLint Height(void) { return height; }; + bool Scrollable(void) { return scrollable; }; + GLint ViewportWidth(void) { return viewPortWidth; }; + GLint ViewportHeight(void) { return viewPortHeight; }; +}; + +/**************************************************************************************** +* cOglOutputFb +* Output Framebuffer Object - holds Vdpau Output Surface which is our "output framebuffer" +****************************************************************************************/ +class cOglOutputFb : public cOglFb { +private: + GLvdpauSurfaceNV surface; +public: + cOglOutputFb(GLint width, GLint height); + virtual ~cOglOutputFb(void); + virtual bool Init(void); + virtual void BindWrite(void); + virtual void Unbind(void); +}; + +/**************************************************************************************** +* cOglVb +* Vertex Buffer - OpenGl Vertices for the different drawing commands +****************************************************************************************/ +enum eVertexBufferType { + vbRect, + vbEllipse, + vbSlope, + vbTexture, + vbText, + vbCount +}; + +class cOglVb { +private: + eVertexBufferType type; + eShaderType shader; + GLuint vao; + GLuint vbo; + int sizeVertex1; + int sizeVertex2; + int numVertices; + GLuint drawMode; +public: + cOglVb(int type); + virtual ~cOglVb(void); + bool Init(void); + void Bind(void); + void Unbind(void); + void ActivateShader(void); + void EnableBlending(void); + void DisableBlending(void); + void SetShaderColor(GLint color); + void SetShaderAlpha(GLint alpha); + void SetShaderProjectionMatrix(GLint width, GLint height); + void SetVertexData(GLfloat *vertices, int count = 0); + void DrawArrays(int count = 0); +}; + +/**************************************************************************************** +* cOpenGLCmd +****************************************************************************************/ +class cOglCmd { +protected: + cOglFb *fb; +public: + cOglCmd(cOglFb *fb) { this->fb = fb; }; + virtual ~cOglCmd(void) {}; + virtual const char* Description(void) = 0; + virtual bool Execute(void) = 0; +}; + +class cOglCmdInitOutputFb : public cOglCmd { +private: + cOglOutputFb *oFb; +public: + cOglCmdInitOutputFb(cOglOutputFb *oFb); + virtual ~cOglCmdInitOutputFb(void) {}; + virtual const char* Description(void) { return "InitOutputFramebuffer"; } + virtual bool Execute(void); +}; + +class cOglCmdInitFb : public cOglCmd { +private: + cCondWait *wait; +public: + cOglCmdInitFb(cOglFb *fb, cCondWait *wait = NULL); + virtual ~cOglCmdInitFb(void) {}; + virtual const char* Description(void) { return "InitFramebuffer"; } + virtual bool Execute(void); +}; + +class cOglCmdDeleteFb : public cOglCmd { +public: + cOglCmdDeleteFb(cOglFb *fb); + virtual ~cOglCmdDeleteFb(void) {}; + virtual const char* Description(void) { return "DeleteFramebuffer"; } + virtual bool Execute(void); +}; + +class cOglCmdRenderFbToBufferFb : public cOglCmd { +private: + cOglFb *buffer; + GLfloat x, y; + GLfloat drawPortX, drawPortY; + GLint transparency; +public: + cOglCmdRenderFbToBufferFb(cOglFb *fb, cOglFb *buffer, GLint x, GLint y, GLint transparency, GLint drawPortX, GLint drawPortY); + virtual ~cOglCmdRenderFbToBufferFb(void) {}; + virtual const char* Description(void) { return "Render Framebuffer to Buffer"; } + virtual bool Execute(void); +}; + +class cOglCmdCopyBufferToOutputFb : public cOglCmd { +private: + cOglOutputFb *oFb; + GLint x, y; +public: + cOglCmdCopyBufferToOutputFb(cOglFb *fb, cOglOutputFb *oFb, GLint x, GLint y); + virtual ~cOglCmdCopyBufferToOutputFb(void) {}; + virtual const char* Description(void) { return "Copy buffer to OutputFramebuffer"; } + virtual bool Execute(void); +}; + +class cOglCmdFill : public cOglCmd { +private: + GLint color; +public: + cOglCmdFill(cOglFb *fb, GLint color); + virtual ~cOglCmdFill(void) {}; + virtual const char* Description(void) { return "Fill"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawRectangle : public cOglCmd { +private: + GLint x, y; + GLint width, height; + GLint color; +public: + cOglCmdDrawRectangle(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color); + virtual ~cOglCmdDrawRectangle(void) {}; + virtual const char* Description(void) { return "DrawRectangle"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawEllipse : public cOglCmd { +private: + GLint x, y; + GLint width, height; + GLint color; + GLint quadrants; + GLfloat *CreateVerticesFull(int &numVertices); + GLfloat *CreateVerticesQuadrant(int &numVertices); + GLfloat *CreateVerticesHalf(int &numVertices); +public: + cOglCmdDrawEllipse(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color, GLint quadrants); + virtual ~cOglCmdDrawEllipse(void) {}; + virtual const char* Description(void) { return "DrawEllipse"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawSlope : public cOglCmd { +private: + GLint x, y; + GLint width, height; + GLint color; + GLint type; +public: + cOglCmdDrawSlope(cOglFb *fb, GLint x, GLint y, GLint width, GLint height, GLint color, GLint type); + virtual ~cOglCmdDrawSlope(void) {}; + virtual const char* Description(void) { return "DrawSlope"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawText : public cOglCmd { +private: + GLint x, y; + GLint limitX; + GLint colorText; + cString fontName; + int fontSize; + unsigned int *symbols; +public: + cOglCmdDrawText(cOglFb *fb, GLint x, GLint y, unsigned int *symbols, GLint limitX, const char *name, int fontSize, tColor colorText); + virtual ~cOglCmdDrawText(void); + virtual const char* Description(void) { return "DrawText"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawImage : public cOglCmd { +private: + tColor *argb; + GLint x, y, width, height; + bool overlay; + GLfloat scaleX, scaleY; +public: + cOglCmdDrawImage(cOglFb *fb, tColor *argb, GLint width, GLint height, GLint x, GLint y, bool overlay = true, double scaleX = 1.0f, double scaleY = 1.0f); + virtual ~cOglCmdDrawImage(void); + virtual const char* Description(void) { return "Draw Image"; } + virtual bool Execute(void); +}; + +class cOglCmdDrawTexture : public cOglCmd { +private: + sOglImage *imageRef; + GLint x, y; +public: + cOglCmdDrawTexture(cOglFb *fb, sOglImage *imageRef, GLint x, GLint y); + virtual ~cOglCmdDrawTexture(void) {}; + virtual const char* Description(void) { return "Draw Texture"; } + virtual bool Execute(void); +}; + +class cOglCmdStoreImage : public cOglCmd { +private: + sOglImage *imageRef; + tColor *data; +public: + cOglCmdStoreImage(sOglImage *imageRef, tColor *argb); + virtual ~cOglCmdStoreImage(void); + virtual const char* Description(void) { return "Store Image"; } + virtual bool Execute(void); +}; + +class cOglCmdDropImage : public cOglCmd { +private: + sOglImage *imageRef; + cCondWait *wait; +public: + cOglCmdDropImage(sOglImage *imageRef, cCondWait *wait); + virtual ~cOglCmdDropImage(void) {}; + virtual const char* Description(void) { return "Drop Image"; } + virtual bool Execute(void); +}; + +/****************************************************************************** +* cOglThread +******************************************************************************/ +#define OGL_MAX_OSDIMAGES 256 +#define OGL_CMDQUEUE_SIZE 100 + +class cOglThread : public cThread { +private: + cCondWait *startWait; + cCondWait *wait; + bool stalled; + std::queue commands; + GLint maxTextureSize; + sOglImage imageCache[OGL_MAX_OSDIMAGES]; + long memCached; + long maxCacheSize; + bool InitOpenGL(void); + bool InitShaders(void); + void DeleteShaders(void); + bool InitVdpauInterop(void); + bool InitVertexBuffers(void); + void DeleteVertexBuffers(void); + void Cleanup(void); + int GetFreeSlot(void); + void ClearSlot(int slot); +protected: + virtual void Action(void); +public: + cOglThread(cCondWait *startWait, int maxCacheSize); + virtual ~cOglThread(); + void Stop(void); + void DoCmd(cOglCmd* cmd); + int StoreImage(const cImage &image); + void DropImageData(int imageHandle); + sOglImage *GetImageRef(int slot); + int MaxTextureSize(void) { return maxTextureSize; }; +}; + +/**************************************************************************************** +* cOglPixmap +****************************************************************************************/ +class cOglPixmap : public cPixmap { +private: + cOglFb *fb; + std::shared_ptr oglThread; + bool dirty; +public: + cOglPixmap(std::shared_ptr oglThread, int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null); + virtual ~cOglPixmap(void); + cOglFb *Fb(void) { return fb; }; + int X(void) { return ViewPort().X(); }; + int Y(void) { return ViewPort().Y(); }; + virtual bool IsDirty(void) { return dirty; } + virtual void SetDirty(bool dirty = true) { this->dirty = dirty; } + virtual void SetAlpha(int Alpha); + virtual void SetTile(bool Tile); + virtual void SetViewPort(const cRect &Rect); + virtual void SetDrawPortPoint(const cPoint &Point, bool Dirty = true); + virtual void Clear(void); + virtual void Fill(tColor Color); + virtual void DrawImage(const cPoint &Point, const cImage &Image); + virtual void DrawImage(const cPoint &Point, int ImageHandle); + virtual void DrawPixel(const cPoint &Point, tColor Color); + virtual void DrawBitmap(const cPoint &Point, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool Overlay = false); + virtual void DrawText(const cPoint &Point, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault); + virtual void DrawRectangle(const cRect &Rect, tColor Color); + virtual void DrawEllipse(const cRect &Rect, tColor Color, int Quadrants = 0); + virtual void DrawSlope(const cRect &Rect, tColor Color, int Type); + virtual void Render(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest); + virtual void Copy(const cPixmap *Pixmap, const cRect &Source, const cPoint &Dest); + virtual void Scroll(const cPoint &Dest, const cRect &Source = cRect::Null); + virtual void Pan(const cPoint &Dest, const cRect &Source = cRect::Null); +}; + +/****************************************************************************** +* cOglOsd +******************************************************************************/ +class cOglOsd : public cOsd { +private: + cOglFb *bFb; + std::shared_ptr oglThread; + cVector oglPixmaps; + bool isSubtitleOsd; +protected: +public: + cOglOsd(int Left, int Top, uint Level, std::shared_ptr oglThread); + virtual ~cOglOsd(); + virtual eOsdError SetAreas(const tArea *Areas, int NumAreas); + virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null); + virtual void DestroyPixmap(cPixmap *Pixmap); + virtual void Flush(void); + virtual void DrawScaledBitmap(int x, int y, const cBitmap &Bitmap, double FactorX, double FactorY, bool AntiAlias = false); + static cOglOutputFb *oFb; +}; + +#endif //__SOFTHDDEVICE_OPENGLOSD_H diff --git a/softhdcuvid.cpp b/softhdcuvid.cpp index dce1880..efcea32 100644 --- a/softhdcuvid.cpp +++ b/softhdcuvid.cpp @@ -17,7 +17,7 @@ /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU Affero General Public License for more details. /// -/// $Id: 70994d77440d1a19d3b6204a50e578c950008b8a $ +/// $Id: fa6a877682f47297580ff5f502425fc7948cb2fa $ ////////////////////////////////////////////////////////////////////////////// #define __STDC_CONSTANT_MACROS ///< needed for ffmpeg UINT64_C @@ -37,14 +37,19 @@ #include "softhddevice.h" #include "softhddevice_service.h" +#ifdef USE_OPENGLOSD +#include "openglosd.h" +#endif + extern "C" { #include #include - +#ifndef USE_OPENGLOSD #include "audio.h" #include "video.h" #include "codec.h" +#endif } #if APIVERSNUM >= 20301 @@ -67,7 +72,7 @@ static const char *const VERSION = "0.6.1rc1" /// vdr-plugin description. static const char *const DESCRIPTION = -trNOOP("A software and GPU emulated UHD device"); +trNOOP("A software and GPU emulated HD device"); /// vdr-plugin text of main menu entry static const char *MAINMENUENTRY = trNOOP("SoftUHD"); @@ -176,6 +181,10 @@ static int ConfigPipAltVideoHeight = 50; ///< config pip alt. video height in % static char ConfigEnableDPMSatBlackScreen; ///< Enable DPMS(Screensaver) while displaying black screen(radio) #endif +#ifdef USE_OPENGLOSD +static int ConfigMaxSizeGPUImageCache = 128; ///< maximum size of GPU mem to be used for image caching +#endif + static volatile int DoMakePrimary; ///< switch primary device to this #define SUSPEND_EXTERNAL -1 ///< play external suspend mode @@ -620,6 +629,44 @@ void cSoftOsd::Flush(void) Dirty = 0; } +#ifdef USE_OPENGLOSD +//Dummy OSD for OpenGL OSD if no X Server is available +class cDummyOsd : public cOsd { + public: + cDummyOsd(int Left, int Top, uint Level) : cOsd(Left, Top, Level) {} + virtual ~cDummyOsd() {} + virtual cPixmap *CreatePixmap(int Layer, const cRect &ViewPort, const cRect &DrawPort = cRect::Null) { + (void)Layer; (void)ViewPort; (void)DrawPort; + return NULL; + } + virtual void DestroyPixmap(cPixmap *Pixmap) { (void)Pixmap; } + virtual void DrawImage(const cPoint &Point, const cImage &Image) { (void)Point; (void)Image; } + virtual void DrawImage(const cPoint &Point, int ImageHandle) { (void) Point; (void)ImageHandle; } + virtual eOsdError CanHandleAreas(const tArea *Areas, int NumAreas) { (void)Areas; (void)NumAreas; return oeOk; } + virtual eOsdError SetAreas(const tArea *Areas, int NumAreas) { (void)Areas; (void)NumAreas; return oeOk; } + virtual void SaveRegion(int x1, int y1, int x2, int y2) { (void)x1; (void)y1; (void)x2; (void)y2; } + virtual void RestoreRegion(void) {} + virtual eOsdError SetPalette(const cPalette &Palette, int Area) { (void)Palette; (void)Area; return oeOk; } + virtual void DrawPixel(int x, int y, tColor Color) { (void)x; (void)y; (void)Color; } + virtual void DrawBitmap(int x, int y, const cBitmap &Bitmap, tColor ColorFg = 0, tColor ColorBg = 0, bool ReplacePalette = false, bool Overlay = false) { + (void)x; (void)y; (void)Bitmap; (void)ColorFg; (void)ColorBg; (void)ReplacePalette; (void)Overlay; + } + virtual void DrawText(int x, int y, const char *s, tColor ColorFg, tColor ColorBg, const cFont *Font, int Width = 0, int Height = 0, int Alignment = taDefault) { + (void)x; (void)y; (void)s; (void)ColorFg; (void)ColorBg; (void)Font; (void)Width; (void)Height; (void)Alignment; + } + virtual void DrawRectangle(int x1, int y1, int x2, int y2, tColor Color) { + (void)x1; (void)y1; (void)x2; (void)y2; (void)Color; + } + virtual void DrawEllipse(int x1, int y1, int x2, int y2, tColor Color, int Quadrants = 0) { + (void)x1; (void)y1; (void)x2; (void)y2; (void)Color; (void)Quadrants; + } + virtual void DrawSlope(int x1, int y1, int x2, int y2, tColor Color, int Type) { + (void)x1; (void)y1; (void)x2; (void)y2; (void)Color; (void)Type; + } + virtual void Flush(void) {} +}; +#endif + ////////////////////////////////////////////////////////////////////////////// // OSD provider ////////////////////////////////////////////////////////////////////////////// @@ -631,15 +678,45 @@ class cSoftOsdProvider:public cOsdProvider { private: static cOsd *Osd; ///< single OSD +#ifdef USE_OPENGLOSD + static std::shared_ptr oglThread; + static bool StartOpenGlThread(void); +protected: + virtual int StoreImageData(const cImage &Image); + virtual void DropImageData(int ImageHandle); +#endif public: virtual cOsd * CreateOsd(int, int, uint); virtual bool ProvidesTrueColor(void); +#ifdef USE_OPENGLOSD + static void StopOpenGlThread(void); + static const cImage *GetImageData(int ImageHandle); + static void OsdSizeChanged(void); +#endif cSoftOsdProvider(void); ///< OSD provider constructor - //virtual ~cSoftOsdProvider(); ///< OSD provider destructor + virtual ~cSoftOsdProvider(); ///< OSD provider destructor }; cOsd *cSoftOsdProvider::Osd; ///< single osd +#ifdef USE_OPENGLOSD +std::shared_ptr cSoftOsdProvider::oglThread; ///< openGL worker Thread + +int cSoftOsdProvider::StoreImageData(const cImage &Image) +{ + if (StartOpenGlThread()) { + int imgHandle = oglThread->StoreImage(Image); + return imgHandle; + } + return 0; +} + +void cSoftOsdProvider::DropImageData(int ImageHandle) +{ + if (StartOpenGlThread()) + oglThread->DropImageData(ImageHandle); +} +#endif /** ** Create a new OSD. ** @@ -649,11 +726,17 @@ cOsd *cSoftOsdProvider::Osd; ///< single osd */ cOsd *cSoftOsdProvider::CreateOsd(int left, int top, uint level) { -#ifdef OSD_DEBUG +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]%s: left %d, top %d, level %d, using OpenGL OSD support\n", __FUNCTION__, left, top, level); + if (StartOpenGlThread()) + return Osd = new cOglOsd(left, top, level, oglThread); + //return dummy osd if shd is detached + dsyslog("[softhddev]OpenGl Thread not started successfully, using Dummy OSD"); + return Osd = new cDummyOsd(left, top, 999); +#else dsyslog("[softhddev]%s: %d, %d, %d\n", __FUNCTION__, left, top, level); -#endif - return Osd = new cSoftOsd(left, top, level); +#endif } /** @@ -666,6 +749,53 @@ bool cSoftOsdProvider::ProvidesTrueColor(void) return true; } +#ifdef USE_OPENGLOSD +const cImage *cSoftOsdProvider::GetImageData(int ImageHandle) { + return cOsdProvider::GetImageData(ImageHandle); +} + +void cSoftOsdProvider::OsdSizeChanged(void) { + //cleanup OpenGl Context + cSoftOsdProvider::StopOpenGlThread(); + cOsdProvider::UpdateOsdSize(); +} + + +bool cSoftOsdProvider::StartOpenGlThread(void) { + //only try to start worker thread if shd is attached + //otherwise glutInit() crashes + if (SuspendMode != NOT_SUSPENDED) { + dsyslog("[softhddev]detached - OpenGl Worker Thread not tried to start"); + return false; + } + if (oglThread.get()) { + if (oglThread->Active()) { + return true; + } + oglThread.reset(); + } + cCondWait wait; + dsyslog("[softhddev]Trying to start OpenGL Worker Thread"); + oglThread.reset(new cOglThread(&wait, ConfigMaxSizeGPUImageCache)); + wait.Wait(); + if (oglThread->Active()) { + dsyslog("[softhddev]OpenGL Worker Thread successfully started"); + return true; + } + dsyslog("[softhddev]openGL Thread NOT successfully started"); + return false; +} + +void cSoftOsdProvider::StopOpenGlThread(void) { + dsyslog("[softhddev]stopping OpenGL Worker Thread "); + if (oglThread) { + oglThread->Stop(); + } + oglThread.reset(); + dsyslog("[softhddev]OpenGL Worker Thread stopped"); +} +#endif + /** ** Create cOsdProvider class. */ @@ -675,15 +805,25 @@ cSoftOsdProvider::cSoftOsdProvider(void) #ifdef OSD_DEBUG dsyslog("[softhddev]%s:\n", __FUNCTION__); #endif +#ifdef USE_OPENGLOSD + StopOpenGlThread(); + VideoSetVideoEventCallback(&OsdSizeChanged); +#endif + } /** ** Destroy cOsdProvider class. +*/ cSoftOsdProvider::~cSoftOsdProvider() { +#ifdef OSD_DEBUG dsyslog("[softhddev]%s:\n", __FUNCTION__); +#endif +#ifdef USE_OPENGLOSD + StopOpenGlThread(); +#endif } -*/ ////////////////////////////////////////////////////////////////////////////// // cMenuSetupPage @@ -778,6 +918,10 @@ class cMenuSetupSoft:public cMenuSetupPage #ifdef USE_SCREENSAVER int EnableDPMSatBlackScreen; #endif + +#ifdef USE_OPENGLOSD + int MaxSizeGPUImageCache; +#endif /// @} private: inline cOsdItem * CollapsedItem(const char *, int &, const char * = NULL); @@ -854,7 +998,7 @@ void cMenuSetupSoft::Create(void) "None", "PCM", "AC-3", "PCM + AC-3" }; static const char *const resolution[RESOLUTIONS] = { - "576i", "720p", "fake 1080i", "1080i", "UHD" + "576i", "720p", "fake 1080i", "1080i" ,"UHD" }; int current; int i; @@ -880,6 +1024,9 @@ void cMenuSetupSoft::Create(void) Add(new cMenuEditIntItem(tr("Osd width"), &OsdWidth, 0, 4096)); Add(new cMenuEditIntItem(tr("Osd height"), &OsdHeight, 0, 4096)); } +#ifdef USE_OPENGLOSD + Add(new cMenuEditIntItem(tr("GPU mem used for image caching (MB)"), &MaxSizeGPUImageCache, 0, 4000)); +#endif // // suspend // @@ -905,7 +1052,7 @@ void cMenuSetupSoft::Create(void) &Video4to3DisplayFormat, 3, video_display_formats_4_3)); Add(new cMenuEditStraItem(trVDR("16:9+other video display format"), &VideoOtherDisplayFormat, 3, video_display_formats_16_9)); -#if 0 + // FIXME: switch config gray/color configuration Add(new cMenuEditIntItem(tr("Video background color (RGB)"), (int *)&Background, 0, 0x00FFFFFF)); @@ -915,14 +1062,13 @@ void cMenuSetupSoft::Create(void) &StudioLevels, trVDR("no"), trVDR("yes"))); Add(new cMenuEditBoolItem(tr("60hz display mode"), &_60HzMode, trVDR("no"), trVDR("yes"))); -#endif Add(new cMenuEditBoolItem(tr("Soft start a/v sync"), &SoftStartSync, trVDR("no"), trVDR("yes"))); Add(new cMenuEditBoolItem(tr("Black during channel switch"), &BlackPicture, trVDR("no"), trVDR("yes"))); Add(new cMenuEditBoolItem(tr("Clear decoder on channel switch"), &ClearOnSwitch, trVDR("no"), trVDR("yes"))); -#if 0 + Add(new cMenuEditIntItem(tr("Brightness (-1000..1000) (vdpau)"), &Brightness, -1000, 1000, tr("min"), tr("max"))); Add(new cMenuEditIntItem(tr("Contrast (0..10000) (vdpau)"), &Contrast, @@ -974,7 +1120,6 @@ void cMenuSetupSoft::Create(void) &AutoCropDelay, 0, 200)); Add(new cMenuEditIntItem(tr("Autocrop tolerance (pixel)"), &AutoCropTolerance, 0, 32)); -#endif } // // audio @@ -1224,6 +1369,10 @@ cMenuSetupSoft::cMenuSetupSoft(void) EnableDPMSatBlackScreen = ConfigEnableDPMSatBlackScreen; #endif +#ifdef USE_OPENGLOSD + MaxSizeGPUImageCache = ConfigMaxSizeGPUImageCache; +#endif + Create(); } @@ -1400,6 +1549,11 @@ void cMenuSetupSoft::Store(void) EnableDPMSatBlackScreen); SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen); #endif + +#ifdef USE_OPENGLOSD + SetupStore("MaxSizeGPUImageCache", ConfigMaxSizeGPUImageCache = + MaxSizeGPUImageCache); +#endif } ////////////////////////////////////////////////////////////////////////////// @@ -2196,6 +2350,10 @@ eOSState cSoftHdMenu::ProcessKey(eKeys key) ConfigSuspendX11); SuspendMode = SUSPEND_NORMAL; } +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread osUser1"); + cSoftOsdProvider::StopOpenGlThread(); +#endif if (ShutdownHandler.GetUserInactiveTime()) { dsyslog("[softhddev]%s: set user inactive\n", __FUNCTION__); @@ -2241,7 +2399,7 @@ class cSoftHdDevice:public cDevice cSoftHdDevice(void); virtual ~ cSoftHdDevice(void); - virtual cString DeviceName(void) const { return "softhdcuvid"; } + virtual cString DeviceName(void) const { return "softhdcuvid"; } virtual bool HasDecoder(void) const; virtual bool CanReplay(void) const; virtual bool SetPlayMode(ePlayMode); @@ -2330,15 +2488,19 @@ void cSoftHdDevice::MakePrimaryDevice(bool on) cDevice::MakePrimaryDevice(on); if (on) { - new cSoftOsdProvider(); + new cSoftOsdProvider(); - if (SuspendMode == SUSPEND_DETACHED) { - Resume(); - SuspendMode = NOT_SUSPENDED; - } + if (SuspendMode == SUSPEND_DETACHED) { + Resume(); + SuspendMode = NOT_SUSPENDED; + } } else if (SuspendMode == NOT_SUSPENDED) { - Suspend(1, 1, 0); - SuspendMode = SUSPEND_DETACHED; + Suspend(1, 1, 0); + SuspendMode = SUSPEND_DETACHED; +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread MakePrimaryDevice"); + cSoftOsdProvider::StopOpenGlThread(); +#endif } } @@ -2402,18 +2564,22 @@ bool cSoftHdDevice::SetPlayMode(ePlayMode play_mode) // FIXME: what if already suspended? Suspend(1, 1, 0); SuspendMode = SUSPEND_EXTERNAL; - return true; +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread pmExtern_THIS_SHOULD_BE_AVOIDED"); + cSoftOsdProvider::StopOpenGlThread(); +#endif + return true; default: dsyslog("[softhddev] playmode not implemented... %d\n", play_mode); break; } if (SuspendMode != NOT_SUSPENDED) { - if (SuspendMode != SUSPEND_EXTERNAL) { - return false; - } - Resume(); - SuspendMode = NOT_SUSPENDED; + if (SuspendMode != SUSPEND_EXTERNAL) { + return false; + } + Resume(); + SuspendMode = NOT_SUSPENDED; } return::SetPlayMode(play_mode); @@ -2862,7 +3028,6 @@ const char *cPluginSoftHdDevice::CommandLineHelp(void) bool cPluginSoftHdDevice::ProcessArgs(int argc, char *argv[]) { //dsyslog("[softhddev]%s:\n", __FUNCTION__); - return::ProcessArgs(argc, argv); } @@ -2944,6 +3109,10 @@ void cPluginSoftHdDevice::Housekeeping(void) cControl::Attach(); Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11); SuspendMode = SUSPEND_NORMAL; +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread Housekeeping"); + cSoftOsdProvider::StopOpenGlThread(); +#endif } ::Housekeeping(); @@ -3295,10 +3464,17 @@ bool cPluginSoftHdDevice::SetupParse(const char *name, const char *value) #endif #ifdef USE_SCREENSAVER - if (!strcasecmp(name, "EnableDPMSatBlackScreen")) { - ConfigEnableDPMSatBlackScreen = atoi(value); - SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen); - return true; + if (!strcasecmp(name, "EnableDPMSatBlackScreen")) { + ConfigEnableDPMSatBlackScreen = atoi(value); + SetDPMSatBlackScreen(ConfigEnableDPMSatBlackScreen); + return true; + } +#endif + +#ifdef USE_OPENGLOSD + if (!strcasecmp(name, "MaxSizeGPUImageCache")) { + ConfigMaxSizeGPUImageCache = atoi(value); + return true; } #endif @@ -3316,14 +3492,6 @@ bool cPluginSoftHdDevice::Service(const char *id, void *data) { //dsyslog("[softhddev]%s: id %s\n", __FUNCTION__, id); - if (strcmp(id, OSD_3DMODE_SERVICE) == 0) { - SoftHDDevice_Osd3DModeService_v1_0_t *r; - - r = (SoftHDDevice_Osd3DModeService_v1_0_t *) data; - VideoSetOsd3DMode(r->Mode); - return true; - } - return false; } @@ -3386,9 +3554,6 @@ static const char *SVDRPHelpText[] = { " NOT_SUSPENDED == 0 (910)\n" " SUSPEND_NORMAL == 1 (911)\n" " SUSPEND_DETACHED == 2 (912)\n", - "3DOF\n" "\040 3D OSD off.\n", - "3DTB\n" "\040 3D OSD Top and Bottom.\n", - "3DSB\n" "\040 3D OSD Side by Side.\n", "RAIS\n" "\040 Raise softhddevice window\n\n" " If Xserver is not started by softhddevice, the window which\n" " contains the softhddevice frontend will be raised to the front.\n", @@ -3440,6 +3605,10 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command, cControl::Attach(); Suspend(ConfigSuspendClose, ConfigSuspendClose, ConfigSuspendX11); SuspendMode = SUSPEND_NORMAL; +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread svdrp STAT"); + cSoftOsdProvider::StopOpenGlThread(); +#endif return "SoftHdDevice is suspended"; } if (!strcasecmp(command, "RESU")) { @@ -3470,6 +3639,10 @@ cString cPluginSoftHdDevice::SVDRPCommand(const char *command, cControl::Attach(); Suspend(1, 1, 0); SuspendMode = SUSPEND_DETACHED; +#ifdef USE_OPENGLOSD + dsyslog("[softhddev]stopping Ogl Thread svdrp DETA"); + cSoftOsdProvider::StopOpenGlThread(); +#endif return "SoftHdDevice is detached"; } if (!strcasecmp(command, "ATTA")) { diff --git a/video.c b/video.c index 17db4d9..db251c2 100644 --- a/video.c +++ b/video.c @@ -438,7 +438,9 @@ static int OsdDirtyX; ///< osd dirty area x static int OsdDirtyY; ///< osd dirty area y static int OsdDirtyWidth; ///< osd dirty area width static int OsdDirtyHeight; ///< osd dirty area height - +#ifdef USE_OPENGLOSD +static void (*VideoEventCallback)(void) = NULL; /// callback function to notify VDR about Video Events +#endif static int64_t VideoDeltaPTS; ///< FIXME: fix pts #ifdef USE_SCREENSAVER @@ -458,7 +460,7 @@ static GLXContext GlxThreadContext; ///< our gl context for the thread static XVisualInfo *GlxVisualInfo; ///< our gl visual static GLuint OsdGlTextures[2]; ///< gl texture for OSD -static int OsdIndex; ///< index into OsdGlTextures +static int OsdIndex=0; ///< index into OsdGlTextures static void GlxSetupWindow(xcb_window_t window, int width, int height, GLXContext context); @@ -810,7 +812,8 @@ static inline void GlxRenderTexture(GLuint texture, int x, int y, int width, glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); - glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // no color +// glColor4f(1.0f, 1.0f, 1.0f, 1.0f); // no color +#ifndef USE_OPENGLOSD glBegin(GL_QUADS); { glTexCoord2f(1.0f, 1.0f); glVertex2i(x + width, y + height); @@ -822,6 +825,19 @@ static inline void GlxRenderTexture(GLuint texture, int x, int y, int width, glVertex2i(x + width, y); } glEnd(); +#else + glBegin(GL_QUADS); { + glTexCoord2f(1.0f, 1.0f); + glVertex2i(x+width , y ); + glTexCoord2f(0.0f, 1.0f); + glVertex2i(x, y ); + glTexCoord2f(0.0f, 0.0f); + glVertex2i(x, y+height); + glTexCoord2f(1.0f, 0.0f); + glVertex2i(x+width , y+height); + } + glEnd(); +#endif glBindTexture(GL_TEXTURE_2D, 0); glDisable(GL_TEXTURE_2D); @@ -885,6 +901,7 @@ static void GlxOsdInit(int width, int height) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glPixelStorei(GL_UNPACK_ALIGNMENT, 4); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_BYTE, NULL); + } glBindTexture(GL_TEXTURE_2D, 0); @@ -1290,6 +1307,7 @@ static void GlxInit(void) } + /// /// Cleanup GLX. /// @@ -1620,9 +1638,12 @@ static int CuvidDecoderN; ///< number of decoder streams GLuint vao_buffer; // //GLuint vao_vao[4]; // -GLuint gl_shader=0,gl_prog = 0; // shader programm +GLuint gl_shader=0,gl_prog = 0,gl_fbo=0; // shader programm GLint gl_colormatrix,gl_colormatrix_c; - +GLuint OSDfb=0; +GLuint OSDtexture; +GLXContext OSDcontext; +int OSDx,OSDy,OSDxsize,OSDysize; static struct timespec CuvidFrameTime; ///< time of last display @@ -3065,7 +3086,6 @@ static void CuvidAdvanceDecoderFrame(CuvidDecoder * decoder) // need 2 frames for progressive // need 4 frames for interlaced filled = atomic_read(&decoder->SurfacesFilled); - //JOJO if (filled <= 1 + 2 * decoder->Interlaced) { if (filled <= 1 + 2 * decoder->Interlaced) { // keep use of last surface ++decoder->FramesDuped; @@ -3096,33 +3116,6 @@ static void CuvidDisplayFrame(void) static unsigned int Count; int filled; CuvidDecoder *decoder; -#if 0 - - // - // wait for surface no longer visible (blocks max ~5ms) - // - status = - CuvidPresentationQueueBlockUntilSurfaceIdle(CuvidQueue, - CuvidSurfacesRb[CuvidOutputSurfaceIndex], &first_time); - if (status != VDP_STATUS_OK) { - Error(_("video/cuvid: can't block queue: %s\n"), - CuvidGetErrorString(status)); - } - - // check if surface was displayed for more than 1 frame - // FIXME: 21 only correct for 50Hz - if (last_time && first_time > last_time + 21 * 1000 * 1000) { - // FIXME: ignore still-frame, trick-speed - Debug(3, "video/cuvid: %" PRId64 " display time %" PRId64 "\n", first_time / 1000, (first_time - last_time) / 1000); - // FIXME: can be more than 1 frame long shown - for (i = 0; i < CuvidDecoderN; ++i) { - CuvidDecoders[i]->FramesMissed++; - CuvidMessage(2, _("video/cuvid: missed frame (%d/%d)\n"), - CuvidDecoders[i]->FramesMissed, - CuvidDecoders[i]->FrameCounter); - } - } -#endif glXMakeCurrent(XlibDisplay, VideoWindow, GlxSharedContext); @@ -3155,9 +3148,13 @@ static void CuvidDisplayFrame(void) // add osd to surface // if (OsdShown) { +#ifndef USE_OPENGLOSD glXMakeCurrent(XlibDisplay, VideoWindow, GlxThreadContext); -// GlxRenderTexture(OsdGlTextures[OsdIndex], decoder->OutputX, decoder->OutputY, decoder->OutputWidth, decoder->OutputHeight); -GlxRenderTexture(OsdGlTextures[OsdIndex], 0,0, VideoWindowWidth, VideoWindowHeight); + GlxRenderTexture(OsdGlTextures[OsdIndex], 0,0, VideoWindowWidth, VideoWindowHeight); +#else + glXMakeCurrent(XlibDisplay, VideoWindow, GlxContext ); + GlxRenderTexture(OSDtexture, OSDx, OSDy, OSDxsize, OSDysize); +#endif glXMakeCurrent(XlibDisplay, VideoWindow, GlxSharedContext); // FIXME: toggle osd } @@ -3854,6 +3851,7 @@ static const VideoModule NoopModule = { void VideoOsdClear(void) { VideoThreadLock(); + VideoUsedModule->OsdClear(); OsdDirtyX = OsdWidth; // reset dirty area @@ -3909,6 +3907,19 @@ void VideoOsdDrawARGB(int xi, int yi, int width, int height, int pitch, VideoThreadUnlock(); } +#ifdef USE_OPENGLOSD +void ActivateOsd(GLuint texture, int x, int y, int xsize, int ysize) { + OsdShown = 1; + OSDtexture = texture; + OSDx = x; + OSDy = y; + OSDxsize = xsize; + OSDysize = ysize; +} +#endif + + + /// /// Get OSD size. /// @@ -3920,8 +3931,8 @@ void VideoGetOsdSize(int *width, int *height) *width = 1920; *height = 1080; // unknown default if (OsdWidth && OsdHeight) { - *width = OsdWidth; - *height = OsdHeight; + *width = OsdWidth; + *height = OsdHeight; } } @@ -3933,10 +3944,10 @@ void VideoGetOsdSize(int *width, int *height) void VideoSetOsdSize(int width, int height) { if (OsdConfigWidth != width || OsdConfigHeight != height) { - VideoOsdExit(); - OsdConfigWidth = width; - OsdConfigHeight = height; - VideoOsdInit(); + VideoOsdExit(); + OsdConfigWidth = width; + OsdConfigHeight = height; + VideoOsdInit(); } } @@ -4148,7 +4159,12 @@ void VideoPollEvent(void) VideoEvent(); } } - +#ifdef USE_OPENGLOSD +void VideoSetVideoEventCallback(void (*videoEventCallback)(void)) +{ + VideoEventCallback = videoEventCallback; +} +#endif //---------------------------------------------------------------------------- // Thread //---------------------------------------------------------------------------- @@ -5110,7 +5126,10 @@ void VideoSetVideoMode( __attribute__ ((unused)) if ((unsigned)width == VideoWindowWidth && (unsigned)height == VideoWindowHeight) { return; // same size nothing todo } - +#ifdef USE_OPENGLOSD + if (VideoEventCallback) + VideoEventCallback(); +#endif VideoOsdExit(); VideoThreadLock(); @@ -5487,11 +5506,13 @@ void VideoInit(const char *display_name) if (VideoWindowWidth) { VideoWindowHeight = (VideoWindowWidth * 9) / 16; } else { // default to fullscreen -// VideoWindowHeight = screen->height_in_pixels; -// VideoWindowWidth = screen->width_in_pixels; + VideoWindowHeight = screen->height_in_pixels; + VideoWindowWidth = screen->width_in_pixels; //*********************************************************************************************** - VideoWindowHeight = 1080; - VideoWindowWidth = 1920; +// if (strcmp(":0.0",display_name) == 0) { +// VideoWindowHeight = 1080; +// VideoWindowWidth = 1920; +// } } } if (!VideoWindowWidth) { @@ -5631,4 +5652,37 @@ void VideoExit(void) } +int GlxInitopengl() { + + while (GlxSharedContext == NULL) { + sleep(1); // wait until Init from video thread is ready +// printf("GlxConext %p\n",GlxSharedContext); + } + OSDcontext = glXCreateContext(XlibDisplay, GlxVisualInfo, GlxSharedContext,GL_TRUE); + if (!OSDcontext) { + Debug(3,"video/osd: can't create glx context\n"); + return 0; + } + glXMakeCurrent(XlibDisplay, VideoWindow, OSDcontext); + glViewport(0, 0, VideoWindowWidth, VideoWindowHeight); + glDepthRange(-1.0, 1.0); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glColor3f(1.0f, 1.0f, 1.0f); + glClearDepth(1.0); + GlxCheck(); + if (glewInit()) + Fatal(_("glewinit failed\n")); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, VideoWindowWidth, VideoWindowHeight, 0.0, -1.0, 1.0); + GlxCheck(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glDisable(GL_DEPTH_TEST); // setup 2d drawing + return 1; + +} diff --git a/video.h b/video.h index 23b81b4..5b7cc89 100644 --- a/video.h +++ b/video.h @@ -23,6 +23,9 @@ /// @addtogroup Video /// @{ +#include +#include + //---------------------------------------------------------------------------- // Typedefs //---------------------------------------------------------------------------- @@ -75,6 +78,11 @@ extern void *VideoGetHwAccelContext(VideoHwDecoder *); extern void VideoDrawRenderState(VideoHwDecoder *, struct vdpau_render_state *); #endif +#endif + +#ifdef USE_OPENGLOSD + /// Set callback funktion to notify VDR about VideoEvents +extern void VideoSetVideoEventCallback(void (*)(void)); #endif /// Poll video events. @@ -227,7 +235,9 @@ extern void SetDPMSatBlackScreen(int); /// Raise the frontend window extern int VideoRaiseWindow(void); - +#ifdef USE_OPENGLOSD +extern void ActivateOsd(GLuint,int,int,int,int); +#endif #if 0 long int gettid() {