diff --git a/Makefile b/Makefile index e75dfa0..0582a8b 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ include $(NACL_SDK_ROOT)/tools/common.mk HTTPD_PY := $(HTTPD_PY) --no-dir-check -LIBS = ppapi ppapi_cpp pthread nacl_io +LIBS = ppapi_gles2 ppapi ppapi_cpp pthread nacl_io CFLAGS = -Wall -Wno-missing-braces SOURCES = \ diff --git a/main.cpp b/main.cpp index 055b8cc..63fca50 100644 --- a/main.cpp +++ b/main.cpp @@ -96,24 +96,6 @@ bool MoonlightInstance::Init(uint32_t argc, const char* argn[], const char* argv[]) { g_Instance = this; - - int32_t context_attributes[] = { - PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, - PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, - PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, - PP_GRAPHICS3DATTRIB_RED_SIZE, 8, - PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, - PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, - PP_GRAPHICS3DATTRIB_SAMPLES, 0, - PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, - PP_GRAPHICS3DATTRIB_WIDTH, 500, - PP_GRAPHICS3DATTRIB_HEIGHT, 500, - PP_GRAPHICS3DATTRIB_NONE, - }; - m_Graphics3D = new pp::Graphics3D(this, context_attributes); - assert(!m_Graphics3D->is_null()); - assert(BindGraphics(*m_Graphics3D)); - return true; } diff --git a/moonlight.hpp b/moonlight.hpp index 3a668a0..ea074fe 100644 --- a/moonlight.hpp +++ b/moonlight.hpp @@ -6,6 +6,9 @@ #include "ppapi/cpp/video_decoder.h" #include "ppapi/c/ppb_gamepad.h" +#include "ppapi/c/ppb_opengles2.h" +#include "ppapi/cpp/graphics_3d.h" +#include "ppapi/cpp/graphics_3d_client.h" #include "ppapi/utility/completion_callback_factory.h" @@ -13,17 +16,27 @@ #include -class MoonlightInstance : public pp::Instance, public pp::MouseLock { +struct Shader { + Shader() : program(0), texcoord_scale_location(0) {} + ~Shader() {} + + GLuint program; + GLint texcoord_scale_location; +}; + +class MoonlightInstance : public pp::Instance, public pp::MouseLock, public pp::Graphics3DClient { public: MoonlightInstance(PP_Instance instance) : pp::Instance(instance), pp::MouseLock(this), + pp::Graphics3DClient(this), m_CallbackFactory(this), m_MouseLocked(false) { // This function MUST be used otherwise sockets don't work (nacl_io_init() doesn't work!) nacl_io_init_ppapi(pp_instance(), pp::Module::Get()->get_browser_interface()); m_GamepadApi = static_cast(pp::Module::Get()->GetBrowserInterface(PPB_GAMEPAD_INTERFACE)); + m_GlesApi = static_cast(pp::Module::Get()->GetBrowserInterface(PPB_OPENGLES2_INTERFACE)); } virtual ~MoonlightInstance(); @@ -42,6 +55,9 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { void OnConnectionStopped(uint32_t unused); void OnConnectionStarted(uint32_t error); + void DidChangeView(const pp::Rect& position, + const pp::Rect& clip_ignored); + static void* ConnectionThreadFunc(void* context); static void ClStageStarting(int stage); @@ -51,6 +67,12 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { static void ClDisplayMessage(char* message); static void ClDisplayTransientMessage(char* message); + + virtual void Graphics3DContextLost() {} + static Shader CreateProgram(const char* vertexShader, const char* fragmentShader); + static void CreateShader(GLuint program, GLenum type, const char* source, int size); + static void PaintPicture(PP_VideoPicture picture); + void DispatchGetPicture(uint32_t unused); void PictureReady(int32_t result, PP_VideoPicture picture); @@ -64,6 +86,12 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock { pp::Graphics3D* m_Graphics3D; pp::VideoDecoder* m_VideoDecoder; + pp::Size m_ViewSize; + const PPB_OpenGLES2* m_GlesApi; + Shader m_Texture2DShader; + Shader m_RectangleArbShader; + Shader m_ExternalOesShader; + double m_LastPadTimestamps[4]; const PPB_Gamepad* m_GamepadApi; pp::CompletionCallbackFactory m_CallbackFactory; diff --git a/viddec.cpp b/viddec.cpp index a397ba4..25d3dc0 100644 --- a/viddec.cpp +++ b/viddec.cpp @@ -1,10 +1,111 @@ #include "moonlight.hpp" +#include +#include + #define INITIAL_DECODE_BUFFER_LEN 128 * 1024 static unsigned char* s_DecodeBuffer; static unsigned int s_DecodeBufferLength; +#define assertNoGLError() assert(!g_Instance->m_GlesApi->GetError(g_Instance->m_Graphics3D->pp_resource())) + +static const char k_VertexShader[] = + "varying vec2 v_texCoord; \n" + "attribute vec4 a_position; \n" + "attribute vec2 a_texCoord; \n" + "uniform vec2 v_scale; \n" + "void main() \n" + "{ \n" + " v_texCoord = v_scale * a_texCoord; \n" + " gl_Position = a_position; \n" + "}"; + +static const char k_FragmentShader2D[] = + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2D s_texture; \n" + "void main() \n" + "{" + " gl_FragColor = texture2D(s_texture, v_texCoord); \n" + "}"; + +static const char k_FragmentShaderRectangle[] = + "#extension GL_ARB_texture_rectangle : require\n" + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform sampler2DRect s_texture; \n" + "void main() \n" + "{" + " gl_FragColor = texture2DRect(s_texture, v_texCoord).rgba; \n" + "}"; + +static const char k_FragmentShaderExternal[] = + "#extension GL_OES_EGL_image_external : require\n" + "precision mediump float; \n" + "varying vec2 v_texCoord; \n" + "uniform samplerExternalOES s_texture; \n" + "void main() \n" + "{" + " gl_FragColor = texture2D(s_texture, v_texCoord); \n" + "}"; + +void MoonlightInstance::DidChangeView(const pp::Rect& position, + const pp::Rect& clip) { + + if (position.width() == 0 || position.height() == 0) { + return; + } + if (m_ViewSize.width()) { + assert(position.size() == m_ViewSize); + return; + } + + m_ViewSize = position.size(); + printf("View size: %dx%d\n", m_ViewSize.width(), m_ViewSize.height()); + + int32_t contextAttributes[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, + PP_GRAPHICS3DATTRIB_BLUE_SIZE, 8, + PP_GRAPHICS3DATTRIB_GREEN_SIZE, 8, + PP_GRAPHICS3DATTRIB_RED_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 0, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 0, + PP_GRAPHICS3DATTRIB_SAMPLES, 0, + PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, + PP_GRAPHICS3DATTRIB_WIDTH, g_Instance->m_ViewSize.width(), + PP_GRAPHICS3DATTRIB_HEIGHT, g_Instance->m_ViewSize.height(), + PP_GRAPHICS3DATTRIB_NONE, + }; + g_Instance->m_Graphics3D = new pp::Graphics3D(g_Instance, contextAttributes); + assert(!g_Instance->m_Graphics3D->is_null()); + + assert(BindGraphics(*m_Graphics3D)); + + PP_Resource graphics3D = g_Instance->m_Graphics3D->pp_resource(); + + g_Instance->m_GlesApi->ClearColor(graphics3D, 1, 0, 0, 1); + g_Instance->m_GlesApi->Clear(graphics3D, GL_COLOR_BUFFER_BIT); + + assertNoGLError(); + + static const float k_Vertices[] = { + -1, -1, -1, 1, 1, -1, 1, 1, // Position coordinates. + 0, 1, 0, 0, 1, 1, 1, 0, // Texture coordinates. + }; + + GLuint buffer; + g_Instance->m_GlesApi->GenBuffers(graphics3D, 1, &buffer); + g_Instance->m_GlesApi->BindBuffer(graphics3D, GL_ARRAY_BUFFER, buffer); + + g_Instance->m_GlesApi->BufferData(graphics3D, + GL_ARRAY_BUFFER, + sizeof(k_Vertices), + k_Vertices, + GL_STATIC_DRAW); + assertNoGLError(); +} + void MoonlightInstance::VidDecSetup(int width, int height, int redrawRate, void* context, int drFlags) { g_Instance->m_VideoDecoder = new pp::VideoDecoder(g_Instance); @@ -30,6 +131,19 @@ void MoonlightInstance::DispatchGetPicture(uint32_t unused) { void MoonlightInstance::VidDecCleanup(void) { free(s_DecodeBuffer); delete g_Instance->m_VideoDecoder; + + PP_Resource graphics3D = g_Instance->m_Graphics3D->pp_resource(); + if (g_Instance->m_Texture2DShader.program) { + g_Instance->m_GlesApi->DeleteProgram(graphics3D, g_Instance->m_Texture2DShader.program); + } + if (g_Instance->m_RectangleArbShader.program) { + g_Instance->m_GlesApi->DeleteProgram(graphics3D, g_Instance->m_RectangleArbShader.program); + } + if (g_Instance->m_ExternalOesShader.program) { + g_Instance->m_GlesApi->DeleteProgram(graphics3D, g_Instance->m_ExternalOesShader.program); + } + + delete g_Instance->m_Graphics3D; } int MoonlightInstance::VidDecSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { @@ -58,12 +172,90 @@ int MoonlightInstance::VidDecSubmitDecodeUnit(PDECODE_UNIT decodeUnit) { return DR_OK; } +void MoonlightInstance::CreateShader(GLuint program, GLenum type, + const char* source, int size) { + PP_Resource graphics3D = g_Instance->m_Graphics3D->pp_resource(); + GLuint shader = g_Instance->m_GlesApi->CreateShader(graphics3D, type); + g_Instance->m_GlesApi->ShaderSource(graphics3D, shader, 1, &source, &size); + g_Instance->m_GlesApi->CompileShader(graphics3D, shader); + g_Instance->m_GlesApi->AttachShader(graphics3D, program, shader); + g_Instance->m_GlesApi->DeleteShader(graphics3D, shader); +} + +Shader MoonlightInstance::CreateProgram(const char* vertexShader, const char* fragmentShader) { + Shader shader; + PP_Resource graphics3D = g_Instance->m_Graphics3D->pp_resource(); + + shader.program = g_Instance->m_GlesApi->CreateProgram(graphics3D); + CreateShader(shader.program, GL_VERTEX_SHADER, vertexShader, strlen(vertexShader)); + CreateShader(shader.program, GL_FRAGMENT_SHADER, fragmentShader, strlen(fragmentShader)); + g_Instance->m_GlesApi->LinkProgram(graphics3D, shader.program); + g_Instance->m_GlesApi->UseProgram(graphics3D, shader.program); + + g_Instance->m_GlesApi->Uniform1i(graphics3D, + g_Instance->m_GlesApi->GetUniformLocation(graphics3D, shader.program, "s_texture"), 0); + assertNoGLError(); + + shader.texcoord_scale_location = g_Instance->m_GlesApi->GetUniformLocation(graphics3D, shader.program, "v_scale"); + + GLint pos_location = g_Instance->m_GlesApi->GetAttribLocation(graphics3D, shader.program, "a_position"); + GLint tc_location = g_Instance->m_GlesApi->GetAttribLocation(graphics3D, shader.program, "a_texCoord"); + assertNoGLError(); + + g_Instance->m_GlesApi->EnableVertexAttribArray(graphics3D, pos_location); + g_Instance->m_GlesApi->VertexAttribPointer(graphics3D, pos_location, 2, GL_FLOAT, GL_FALSE, 0, 0); + g_Instance->m_GlesApi->EnableVertexAttribArray(graphics3D, tc_location); + g_Instance->m_GlesApi->VertexAttribPointer(graphics3D, tc_location, 2, GL_FLOAT, GL_FALSE, 0, static_cast(0) + 8); + + g_Instance->m_GlesApi->UseProgram(graphics3D, 0); + assertNoGLError(); + return shader; +} + +void MoonlightInstance::PaintPicture(PP_VideoPicture picture) { + PP_Resource graphics3D = g_Instance->m_Graphics3D->pp_resource(); + + if (picture.texture_target == GL_TEXTURE_2D) { + if (!g_Instance->m_Texture2DShader.program) { + g_Instance->m_Texture2DShader = CreateProgram(k_VertexShader, k_FragmentShader2D); + } + g_Instance->m_GlesApi->UseProgram(graphics3D, g_Instance->m_Texture2DShader.program); + g_Instance->m_GlesApi->Uniform2f(graphics3D, g_Instance->m_Texture2DShader.texcoord_scale_location, + 1.0, 1.0); + } + else if (picture.texture_target == GL_TEXTURE_RECTANGLE_ARB) { + if (!g_Instance->m_RectangleArbShader.program) { + g_Instance->m_RectangleArbShader = CreateProgram(k_VertexShader, k_FragmentShaderRectangle); + } + g_Instance->m_GlesApi->UseProgram(graphics3D, g_Instance->m_RectangleArbShader.program); + g_Instance->m_GlesApi->Uniform2f(graphics3D, g_Instance->m_RectangleArbShader.texcoord_scale_location, + picture.texture_size.width, picture.texture_size.height); + } + else { + if (!g_Instance->m_ExternalOesShader.program) { + g_Instance->m_ExternalOesShader = CreateProgram(k_VertexShader, k_FragmentShaderExternal); + } + g_Instance->m_GlesApi->UseProgram(graphics3D, g_Instance->m_ExternalOesShader.program); + g_Instance->m_GlesApi->Uniform2f(graphics3D, g_Instance->m_ExternalOesShader.texcoord_scale_location, + 1.0, 1.0); + } + + g_Instance->m_GlesApi->Viewport(graphics3D, 0, 0, g_Instance->m_ViewSize.width(), g_Instance->m_ViewSize.height()); + g_Instance->m_GlesApi->ActiveTexture(graphics3D, GL_TEXTURE0); + g_Instance->m_GlesApi->BindTexture(graphics3D, picture.texture_target, picture.texture_id); + g_Instance->m_GlesApi->DrawArrays(graphics3D, GL_TRIANGLE_STRIP, 0, 4); + g_Instance->m_GlesApi->UseProgram(graphics3D, 0); + + g_Instance->m_Graphics3D->SwapBuffers(pp::BlockUntilComplete()); +} + void MoonlightInstance::PictureReady(int32_t result, PP_VideoPicture picture) { if (result == PP_ERROR_ABORTED) { return; } - // FIXME: Draw video + // Paint the image on screen + PaintPicture(picture); g_Instance->m_VideoDecoder->RecyclePicture(picture);