diff --git a/build-nacl32 b/build-nacl32 new file mode 100755 index 00000000..09006d0c --- /dev/null +++ b/build-nacl32 @@ -0,0 +1,10 @@ +#!/bin/sh + +# This can't hurt +make distclean + +set -e +./build/lol-build bootstrap nacl-i386 +./build/lol-build configure nacl-i386 +./build/lol-build build nacl-i386 + diff --git a/build-nacl b/build-nacl64 similarity index 100% rename from build-nacl rename to build-nacl64 diff --git a/build/lol-build b/build/lol-build index 046104f6..dbe2707d 100755 --- a/build/lol-build +++ b/build/lol-build @@ -15,6 +15,7 @@ # And is one of: # - linux-i386 # - linux-amd64 +# - nacl-i386 # - nacl-amd64 # - ios-arm # - osx-amd64 @@ -119,9 +120,11 @@ configure() cd monsterz/android android update project --path . ;; + nacl-i386) + ./configure CXX=i686-nacl-g++ CC=i686-nacl-gcc ac_cv_exeext=.32.nexe --host=none LOL_LIBS="-lppapi -lppapi_gles2 -lppapi_cpp -u _ZN2pp12CreateModuleEv" + ;; nacl-amd64) - # no need for "-u _ZN2pp12CreateModuleEv" but it could be helpful - ./configure CXX=x86_64-nacl-g++ CC=x86_64-nacl-gcc ac_cv_exeext=.nexe --host=none LOL_LIBS="-lppapi -lppapi_gles2 -lppapi_cpp" + ./configure CXX=x86_64-nacl-g++ CC=x86_64-nacl-gcc ac_cv_exeext=.64.nexe --host=none LOL_LIBS="-lppapi -lppapi_gles2 -lppapi_cpp -u _ZN2pp12CreateModuleEv" ;; ps3-ppu) PATH="$PATH" ./configure CXX=ppu-lv2-g++ CC=ppu-lv2-gcc ac_cv_exeext=.elf --host=none diff --git a/configure.ac b/configure.ac index 9bbd7472..d4fc0745 100644 --- a/configure.ac +++ b/configure.ac @@ -191,6 +191,7 @@ LIBS="${LIBS} ${GL_LIBS} ${GLES2_LIBS}" AC_CHECK_FUNCS(glBegin) LIBS="${save_LIBS}" + dnl Use SDL? (always required on Linux or Win32) ac_cv_my_have_sdl="no" ac_cv_my_have_sdl_image="no" @@ -252,6 +253,13 @@ AM_CONDITIONAL(USE_SDL_MIXER, test "${ac_cv_my_have_sdl_mixer}" = "yes") AM_CONDITIONAL(USE_SDL_IMAGE, test "${ac_cv_my_have_sdl_image}" = "yes") +dnl Use NativeClient? +ac_cv_my_have_nacl="no" +AC_LANG_PUSH(C++) +AC_CHECK_HEADERS(ppapi/cpp/instance.h, [ac_cv_my_have_nacl="yes"]) +AC_LANG_POP(C++) +AM_CONDITIONAL(USE_NACL, test "${ac_cv_my_have_nacl}" != "no") + dnl Use EGL? ac_cv_my_have_egl="no" PKG_CHECK_MODULES(EGL, egl, [ac_cv_my_have_egl="yes"], [:]) diff --git a/src/Makefile.am b/src/Makefile.am index 81bc9f31..f0cb045a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -43,8 +43,14 @@ sdl_sources = \ platform/sdl/sdlapp.cpp platform/sdl/sdlapp.h \ platform/sdl/sdlinput.cpp platform/sdl/sdlinput.h +if USE_NACL nacl_sources = \ - platform/nacl/naclapp.cpp platform/nacl/naclapp.h + platform/nacl/naclapp.cpp platform/nacl/naclapp.h \ + platform/nacl/nacl_instance.cpp platform/nacl/nacl_instance.h \ + platform/nacl/nacl_module.cpp \ + platform/nacl/opengl_context.cpp platform/nacl/opengl_context.h \ + platform/nacl/opengl_context_ptrs.h +endif if HAVE_PS3 ps3_sources = \ diff --git a/src/platform/nacl/nacl_instance.cpp b/src/platform/nacl/nacl_instance.cpp new file mode 100644 index 00000000..2a68c81d --- /dev/null +++ b/src/platform/nacl/nacl_instance.cpp @@ -0,0 +1,118 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "core.h" +#include "debug/quad.h" + +#include "platform/nacl/nacl_instance.h" +#include "platform/nacl/opengl_context.h" + +namespace lol +{ + +NaClInstance::NaClInstance(PP_Instance instance) + : pp::Instance(instance), + m_size(0, 0) +{ + ; +} + +NaClInstance::~NaClInstance() +{ + // Destroy the cube view while GL context is current. + opengl_context_->MakeContextCurrent(this); +} + +static double const DELTA_MS = 1000.0 / 60.0; + +void TickCallback(void* data, int32_t result) +{ + NaClInstance *instance = (NaClInstance *)data; + instance->DrawSelf(); + + /* FIXME: only set if if Ticker isn't finished */ + pp::Module::Get()->core()->CallOnMainThread( + DELTA_MS, pp::CompletionCallback(&TickCallback, data), PP_OK); +} + +} +#define main OLDMAIN +#include "../test/tutorial/tut03.cpp" +#undef main +namespace lol { + +bool NaClInstance::Init(uint32_t /* argc */, + const char* /* argn */[], + const char* /* argv */[]) +{ + Ticker::Setup(60.0f); + + //new Kub(); + //new DebugQuad(); + new Fractal(ivec2(640, 480)); + + // My timer callback + pp::Module::Get()->core()->CallOnMainThread( + DELTA_MS, pp::CompletionCallback(&TickCallback, this), PP_OK); + + return true; +} + +void NaClInstance::HandleMessage(const pp::Var& message) +{ + if (!message.is_string()) + return; + /* FIXME: do some shit here */ +} + +void NaClInstance::DidChangeView(const pp::Rect& position, const pp::Rect& clip) +{ + if (position.size().width() == m_size.x && + position.size().height() == m_size.y) + return; // Size didn't change, no need to update anything. + + m_size = ivec2(position.size().width(), position.size().height()); + + if (opengl_context_ == NULL) + opengl_context_.reset(new OpenGLContext(this)); + opengl_context_->InvalidateContext(this); + opengl_context_->ResizeContext(position.size()); + if (!opengl_context_->MakeContextCurrent(this)) + return; + + Video::Setup(m_size); + DrawSelf(); +} + +void NaClInstance::DrawSelf() +{ + if (opengl_context_ == NULL) + return; + + Ticker::ClampFps(); + Ticker::TickGame(); + + opengl_context_->MakeContextCurrent(this); + Ticker::TickDraw(); + opengl_context_->FlushContext(); +} + +} // namespace lol + diff --git a/src/platform/nacl/nacl_instance.h b/src/platform/nacl/nacl_instance.h new file mode 100644 index 00000000..3facd3ae --- /dev/null +++ b/src/platform/nacl/nacl_instance.h @@ -0,0 +1,53 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXAMPLES_TUMBLER_TUMBLER_H_ +#define EXAMPLES_TUMBLER_TUMBLER_H_ + +#include +#include +#include + +#include + +#include "platform/nacl/opengl_context.h" +#include "platform/nacl/opengl_context_ptrs.h" + +namespace lol { + +class NaClInstance : public pp::Instance { + public: + explicit NaClInstance(PP_Instance instance); + + // The dtor makes the 3D context current before deleting the cube view, then + // destroys the 3D context both in the module and in the browser. + virtual ~NaClInstance(); + + // Called by the browser when the NaCl module is loaded and all ready to go. + virtual bool Init(uint32_t argc, const char* argn[], const char* argv[]); + + // Called whenever the in-browser window changes size. + virtual void DidChangeView(const pp::Rect& position, const pp::Rect& clip); + + // Called by the browser to handle the postMessage() call in Javascript. + virtual void HandleMessage(const pp::Var& message); + + // Bind and publish the module's methods to JavaScript. + //void InitializeMethods(ScriptingBridge* bridge); + + // Called to draw the contents of the module's browser area. + void DrawSelf(); + +// private: + // Browser connectivity and scripting support. +// ScriptingBridge scripting_bridge_; + + SharedOpenGLContext opengl_context_; + + ivec2 m_size; +}; + +} // namespace lol + +#endif // EXAMPLES_TUMBLER_TUMBLER_H_ diff --git a/src/platform/nacl/nacl_module.cpp b/src/platform/nacl/nacl_module.cpp new file mode 100644 index 00000000..b4408f78 --- /dev/null +++ b/src/platform/nacl/nacl_module.cpp @@ -0,0 +1,60 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "core.h" +#include "lolgl.h" + +#include "platform/nacl/nacl_instance.h" + +/// The Module class. The browser calls the CreateInstance() method to create +/// an instance of your NaCl module on the web page. The browser creates a new +/// instance for each tag with type="application/x-nacl". +class NaClModule : public pp::Module +{ +public: + NaClModule() : pp::Module() {} + virtual ~NaClModule() + { + glTerminatePPAPI(); + } + + /// Called by the browser when the module is first loaded and ready to run. + /// This is called once per module, not once per instance of the module on + /// the page. + virtual bool Init() + { + return glInitializePPAPI(get_browser_interface()) == GL_TRUE; + } + + /// Create and return a Lol instance object. + /// @param[in] instance The browser-side instance. + /// @return the plugin-side instance. + virtual pp::Instance* CreateInstance(PP_Instance instance) + { + return new lol::NaClInstance(instance); + } +}; + +namespace pp +{ +/// Factory function called by the browser when the module is first loaded. +/// The browser keeps a singleton of this module. It calls the +/// CreateInstance() method on the object you return to make instances. There +/// is one instance per tag on the page. This is the main binding +/// point for your NaCl module with the browser. +Module* CreateModule() +{ + return new NaClModule(); +} + +} // namespace pp + diff --git a/src/platform/nacl/naclapp.cpp b/src/platform/nacl/naclapp.cpp index f85546de..e7935b2e 100644 --- a/src/platform/nacl/naclapp.cpp +++ b/src/platform/nacl/naclapp.cpp @@ -13,9 +13,9 @@ #endif #if defined __native_client__ -# include "ppapi/cpp/instance.h" -# include "ppapi/cpp/module.h" -# include "ppapi/cpp/var.h" +# include +# include +# include #endif #include "core.h" diff --git a/src/platform/nacl/opengl_context.cpp b/src/platform/nacl/opengl_context.cpp new file mode 100644 index 00000000..bbb82931 --- /dev/null +++ b/src/platform/nacl/opengl_context.cpp @@ -0,0 +1,90 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include +#include +#include + +#include "core.h" + +#include "platform/nacl/opengl_context.h" + +namespace { +// This is called by the brower when the 3D context has been flushed to the +// browser window. +void FlushCallback(void* data, int32_t result) { + static_cast(data)->set_flush_pending(false); +} +} // namespace + +namespace lol { + +OpenGLContext::OpenGLContext(pp::Instance* instance) + : pp::Graphics3DClient(instance), + flush_pending_(false) { + pp::Module* module = pp::Module::Get(); + assert(module); + gles2_interface_ = static_cast( + module->GetBrowserInterface(PPB_OPENGLES2_INTERFACE)); + assert(gles2_interface_); +} + +OpenGLContext::~OpenGLContext() { + glSetCurrentContextPPAPI(0); +} + +bool OpenGLContext::MakeContextCurrent(pp::Instance* instance) { + if (instance == NULL) { + glSetCurrentContextPPAPI(0); + return false; + } + // Lazily create the Pepper context. + if (context_.is_null()) { + int32_t attribs[] = { + PP_GRAPHICS3DATTRIB_ALPHA_SIZE, 8, + PP_GRAPHICS3DATTRIB_DEPTH_SIZE, 24, + PP_GRAPHICS3DATTRIB_STENCIL_SIZE, 8, + PP_GRAPHICS3DATTRIB_SAMPLES, 0, + PP_GRAPHICS3DATTRIB_SAMPLE_BUFFERS, 0, + PP_GRAPHICS3DATTRIB_WIDTH, size_.width(), + PP_GRAPHICS3DATTRIB_HEIGHT, size_.height(), + PP_GRAPHICS3DATTRIB_NONE + }; + context_ = pp::Graphics3D(instance, pp::Graphics3D(), attribs); + if (context_.is_null()) { + glSetCurrentContextPPAPI(0); + return false; + } + instance->BindGraphics(context_); + } + glSetCurrentContextPPAPI(context_.pp_resource()); + return true; +} + +void OpenGLContext::InvalidateContext(pp::Instance* instance) { + glSetCurrentContextPPAPI(0); +} + +void OpenGLContext::ResizeContext(const pp::Size& size) { + size_ = size; + if (!context_.is_null()) { + context_.ResizeBuffers(size.width(), size.height()); + } +} + + +void OpenGLContext::FlushContext() { + if (flush_pending()) { + // A flush is pending so do nothing; just drop this flush on the floor. + return; + } + set_flush_pending(true); + context_.SwapBuffers(pp::CompletionCallback(&FlushCallback, this)); +} +} // namespace lol + diff --git a/src/platform/nacl/opengl_context.h b/src/platform/nacl/opengl_context.h new file mode 100644 index 00000000..c5d1b91f --- /dev/null +++ b/src/platform/nacl/opengl_context.h @@ -0,0 +1,94 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ +#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + +/// +/// @file +/// OpenGLContext manages the OpenGL context in the browser that is associated +/// with a @a pp::Instance instance. +/// + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "platform/nacl/opengl_context_ptrs.h" + +namespace lol { + +/// OpenGLContext manages an OpenGL rendering context in the browser. +/// +class OpenGLContext : public pp::Graphics3DClient { + public: + explicit OpenGLContext(pp::Instance* instance); + + /// Release all the in-browser resources used by this context, and make this + /// context invalid. + virtual ~OpenGLContext(); + + /// The Graphics3DClient interfcace. + virtual void Graphics3DContextLost() { + assert(!"Unexpectedly lost graphics context"); + } + + /// Make @a this the current 3D context in @a instance. + /// @param instance The instance of the NaCl module that will receive the + /// the current 3D context. + /// @return success. + bool MakeContextCurrent(pp::Instance* instance); + + /// Flush the contents of this context to the browser's 3D device. + void FlushContext(); + + /// Make the underlying 3D device invalid, so that any subsequent rendering + /// commands will have no effect. The next call to MakeContextCurrent() will + /// cause the underlying 3D device to get rebound and start receiving + /// receiving rendering commands again. Use InvalidateContext(), for + /// example, when resizing the context's viewing area. + void InvalidateContext(pp::Instance* instance); + + /// Resize the context. + void ResizeContext(const pp::Size& size); + + /// The OpenGL ES 2.0 interface. + const struct PPB_OpenGLES2* gles2() const { + return gles2_interface_; + } + + /// The PP_Resource needed to make GLES2 calls through the Pepper interface. + const PP_Resource gl_context() const { + return context_.pp_resource(); + } + + /// Indicate whether a flush is pending. This can only be called from the + /// main thread; it is not thread safe. + bool flush_pending() const { + return flush_pending_; + } + void set_flush_pending(bool flag) { + flush_pending_ = flag; + } + + private: + pp::Size size_; + pp::Graphics3D context_; + bool flush_pending_; + + const struct PPB_OpenGLES2* gles2_interface_; +}; + +} // namespace lol + +#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_H_ + diff --git a/src/platform/nacl/opengl_context_ptrs.h b/src/platform/nacl/opengl_context_ptrs.h new file mode 100644 index 00000000..f4715190 --- /dev/null +++ b/src/platform/nacl/opengl_context_ptrs.h @@ -0,0 +1,22 @@ +// Copyright (c) 2011 The Native Client Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_ +#define EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_ + +// A convenience wrapper for a shared OpenGLContext pointer type. As other +// smart pointer types are needed, add them here. + +#include + +namespace lol { + +class OpenGLContext; + +typedef std::tr1::shared_ptr SharedOpenGLContext; + +} // namespace lol + +#endif // EXAMPLES_TUMBLER_OPENGL_CONTEXT_PTRS_H_ + diff --git a/test/tutorial/tut01.cpp b/test/tutorial/tut01.cpp index 4eadd70d..3dd6da44 100644 --- a/test/tutorial/tut01.cpp +++ b/test/tutorial/tut01.cpp @@ -23,6 +23,10 @@ using namespace lol; # include #endif +#if defined __native_client__ +# define main old_main +#endif + #if defined _WIN32 # undef main /* FIXME: still needed? */ #endif diff --git a/test/tutorial/tut03.cpp b/test/tutorial/tut03.cpp index 44543d8e..9ad991ee 100644 --- a/test/tutorial/tut03.cpp +++ b/test/tutorial/tut03.cpp @@ -59,7 +59,12 @@ public: /* Window size decides the world aspect ratio. For instance, 640×480 * will be mapped to (-0.66,-0.5) - (0.66,0.5). */ +#if !defined __native_client__ m_window_size = Video::GetSize(); +#else + /* FIXME: it's illegal to call this on the game thread! */ + m_window_size = ivec2(640, 480); +#endif if (m_window_size.y < m_window_size.x) m_window2world = 0.5 / m_window_size.y; else @@ -76,7 +81,11 @@ public: m_dirty[i] = 2; } m_center = -0.75; +#if defined __CELLOS_LV2__ || defined __native_client__ + m_zoom_speed = -0.0025; +#else m_zoom_speed = 0.0; +#endif m_radius = 5.0; m_ready = false; @@ -100,9 +109,14 @@ public: uint8_t red = r * 255.99f; uint8_t green = g * 255.99f; uint8_t blue = b * 255.99f; - m_palette[i] = u8vec4(blue, green, red, 0); +#if defined __native_client__ + m_palette[i] = u8vec4(red, green, blue, 255); +#else + m_palette[i] = u8vec4(blue, green, red, 255); +#endif } +#if !defined __native_client__ m_centertext = new Text(NULL, "gfx/font/ascii.png"); m_centertext->SetPos(ivec3(5, m_window_size.y - 15, 1)); Ticker::Ref(m_centertext); @@ -114,6 +128,7 @@ public: m_zoomtext = new Text(NULL, "gfx/font/ascii.png"); m_zoomtext->SetPos(ivec3(5, m_window_size.y - 43, 1)); Ticker::Ref(m_zoomtext); +#endif position = ivec3(0, 0, 0); bbox[0] = position; @@ -124,9 +139,11 @@ public: ~Fractal() { Input::UntrackMouse(this); +#if !defined __native_client__ Ticker::Unref(m_centertext); Ticker::Unref(m_mousetext); Ticker::Unref(m_zoomtext); +#endif delete m_pixels; delete m_tmppixels; delete m_palette; @@ -158,9 +175,7 @@ public: f64cmplx worldmouse = m_center + ScreenToWorldOffset(mousepos); ivec3 buttons = Input::GetMouseButtons(); -#ifdef __CELLOS_LV2__ - m_zoom_speed = 0.0005; -#else +#if !defined __CELLOS_LV2__ && !defined __native_client__ if ((buttons[0] || buttons[2]) && mousepos.x != -1) { double zoom = buttons[0] ? -0.0005 : 0.0005; @@ -182,13 +197,20 @@ public: double oldradius = m_radius; double zoom = pow(2.0, deltams * m_zoom_speed); if (m_radius * zoom > 8.0) + { + m_zoom_speed *= -1.0; zoom = 8.0 / m_radius; + } else if (m_radius * zoom < 1e-14) + { + m_zoom_speed *= -1.0; zoom = 1e-14 / m_radius; + } m_radius *= zoom; -#ifdef __CELLOS_LV2__ - m_center = f64cmplx(-.22815528839841, -1.11514249704382); +#if defined __CELLOS_LV2__ || defined __native_client__ + //m_center = f64cmplx(-.22815528839841, -1.11514249704382); //m_center = f64cmplx(0.001643721971153, 0.822467633298876); + m_center = f64cmplx(-0.65823419062254, .50221777363480); #else m_center = (m_center - worldmouse) * zoom + worldmouse; worldmouse = m_center + ScreenToWorldOffset(mousepos); @@ -230,6 +252,7 @@ public: m_zoom_settings[cur_index][2] = m_zoom_settings[prev_index][2] * m_deltascale[cur_index]; } +#if !defined __native_client__ char buf[128]; sprintf(buf, "center: %+16.14f%+16.14fi", m_center.x, m_center.y); m_centertext->SetText(buf); @@ -237,6 +260,7 @@ public: m_mousetext->SetText(buf); sprintf(buf, " zoom: %g", 1.0 / m_radius); m_zoomtext->SetText(buf); +#endif u8vec4 *m_pixelstart = m_pixels + m_size.x * m_size.y / 4 * m_frame; @@ -284,7 +308,7 @@ public: } else { - *m_pixelstart++ = u8vec4(0, 0, 0, 0); + *m_pixelstart++ = u8vec4(0, 0, 0, 255); } } } @@ -334,16 +358,35 @@ public: m_shader = Shader::Create( #if !defined __CELLOS_LV2__ +#if !defined HAVE_GLES_2X "#version 120\n" - "attribute vec2 in_TexCoord;\n" +#else + "precision highp float;" +#endif + "" +#if defined HAVE_GLES_2X + "varying vec2 pass_TexCoord;" +#endif + "attribute vec2 in_TexCoord;" "attribute vec2 in_Vertex;" "void main(void) {" " gl_Position = vec4(in_Vertex, 0.0, 1.0);" - " gl_TexCoord[0] = vec4(in_TexCoord, 0.0, 0.0);\n" +#if defined HAVE_GLES_2X + " pass_TexCoord = in_TexCoord;" +#else + " gl_TexCoord[0] = vec4(in_TexCoord, 0.0, 0.0);" +#endif "}", +#if !defined HAVE_GLES_2X "#version 120\n" +#else + "precision highp float;" +#endif "" +#if defined HAVE_GLES_2X + "varying vec2 pass_TexCoord;" +#endif "uniform vec4 in_TexelSize;" "uniform mat4 in_ZoomSettings;" "uniform sampler2D in_Texture;" @@ -411,7 +454,11 @@ public: "}" "" "void main(void) {" +#if defined HAVE_GLES_2X + " vec2 coord = pass_TexCoord;" +#else " vec2 coord = gl_TexCoord[0].xy;" +#endif /* Slightly shift our pixel so that it does not lie at * an exact texel boundary. This would lead to visual * artifacts. */ @@ -513,7 +560,9 @@ public: /* FIXME: this object never cleans up */ } +#if !defined HAVE_GLES_2X glEnable(GL_TEXTURE_2D); +#endif glBindTexture(GL_TEXTURE_2D, m_texid); if (m_dirty[m_frame]) @@ -598,7 +647,9 @@ private: double m_deltascale[4]; /* Debug information */ +#if !defined __native_client__ Text *m_centertext, *m_mousetext, *m_zoomtext; +#endif }; int main(int argc, char **argv)