diff options
Diffstat (limited to 'o3d')
38 files changed, 7025 insertions, 0 deletions
diff --git a/o3d/build/common.gypi b/o3d/build/common.gypi index e03653d..765f7d2 100644 --- a/o3d/build/common.gypi +++ b/o3d/build/common.gypi @@ -113,6 +113,13 @@ ], }, ], + ['renderer == "gles2"', + { + 'defines': [ + 'RENDERER_GLES2', + ], + }, + ], ], }, }, @@ -152,6 +159,13 @@ ], }, ], + ['renderer == "gles2"', + { + 'defines': [ + 'RENDERER_GLES2', + ], + }, + ], ], }, }, @@ -180,6 +194,13 @@ ], }, ], + ['renderer == "gles2"', + { + 'defines': [ + 'RENDERER_GLES2', + ], + }, + ], ], }, }, diff --git a/o3d/build/libs.gyp b/o3d/build/libs.gyp index 32e5f42..315ce7c 100644 --- a/o3d/build/libs.gyp +++ b/o3d/build/libs.gyp @@ -73,6 +73,61 @@ ], }, { + 'target_name': 'gles2_libs', + 'type': 'none', + 'all_dependent_settings': { + 'include_dirs': [ + '../../<(glewdir)/include', + ], + }, + 'conditions': [ + [ 'OS=="linux"', + { + 'all_dependent_settings': { + 'defines': [ + 'GL_GLEXT_PROTOTYPES', + ], + 'ldflags': [ + '-L<(PRODUCT_DIR)', + ], + 'libraries': [ + '-lGL', + '-lGLEW', + '-lX11', + ], + }, + }, + ], + [ 'OS=="mac"', + { + 'direct_dependent_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', + ], + }, + }, + ], + [ 'OS=="win"', + { + 'all_dependent_settings': { + 'libraries': [ + '-lOpenGL32.lib', + '../../<(glewdir)/lib/glew32.lib', + ], + }, + 'copies': [ + { + 'destination': '<(PRODUCT_DIR)', + 'files': [ + "../../<(glewdir)/bin/glew32.dll", + ] + }, + ], + }, + ], + ], + }, + { 'target_name': 'cg_libs', 'type': 'none', 'hard_dependency': 1, diff --git a/o3d/build/o3d_all.gyp b/o3d/build/o3d_all.gyp index 9901e71..e172ed2 100644 --- a/o3d/build/o3d_all.gyp +++ b/o3d/build/o3d_all.gyp @@ -28,6 +28,7 @@ '../plugin/idl/idl.gyp:o3dNpnApi', '../plugin/plugin.gyp:npo3dautoplugin', '../samples/samples.gyp:*', + '../standalone/standalone.gyp:*', '../tests/selenium/selenium.gyp:*', '../tests/tests.gyp:unit_tests', '../utils/utils.gyp:o3dUtils', diff --git a/o3d/converter/converter.gyp b/o3d/converter/converter.gyp index ffede11f..ddb5344 100644 --- a/o3d/converter/converter.gyp +++ b/o3d/converter/converter.gyp @@ -62,6 +62,13 @@ ], }, ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:cg_libs', + ], + }, + ], ['OS == "mac"', { 'postbuilds': [ diff --git a/o3d/core/core.gyp b/o3d/core/core.gyp index f2773ec..323a4fe 100644 --- a/o3d/core/core.gyp +++ b/o3d/core/core.gyp @@ -53,6 +53,14 @@ ], }, ], + ['renderer == "gles2"', + { + 'include_dirs': [ + '../../<(glewdir)/include', + '../../<(cgdir)/include', + ], + }, + ], ], }, 'includes': [ @@ -285,6 +293,14 @@ ], }, ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:cg_libs', + '../build/libs.gyp:gles2_libs', + ], + }, + ], ['OS == "linux"', { 'sources': [ @@ -365,6 +381,39 @@ ], }, ], + ['renderer == "gles2"', + { + 'sources': [ + 'cross/gles2/buffer_gles2.cc', + 'cross/gles2/buffer_gles2.h', + 'cross/gles2/draw_element_gles2.cc', + 'cross/gles2/draw_element_gles2.h', + 'cross/gles2/effect_gles2.cc', + 'cross/gles2/effect_gles2.h', + 'cross/gles2/install_check.cc', + 'cross/gles2/param_cache_gles2.cc', + 'cross/gles2/param_cache_gles2.h', + 'cross/gles2/primitive_gles2.cc', + 'cross/gles2/primitive_gles2.h', + 'cross/gles2/render_surface_gles2.cc', + 'cross/gles2/render_surface_gles2.h', + 'cross/gles2/renderer_gles2.cc', + 'cross/gles2/renderer_gles2.h', + 'cross/gles2/sampler_gles2.cc', + 'cross/gles2/sampler_gles2.h', + 'cross/gles2/stream_bank_gles2.cc', + 'cross/gles2/stream_bank_gles2.h', + 'cross/gles2/texture_gles2.cc', + 'cross/gles2/texture_gles2.h', + 'cross/gles2/utils_gles2-inl.h', + 'cross/gles2/utils_gles2.cc', + 'cross/gles2/utils_gles2.h', + ], + 'dependencies': [ + '../build/libs.gyp:gles2_libs', + ], + }, + ], ['renderer == "d3d9" and OS == "win"', { 'sources': [ diff --git a/o3d/core/cross/gles2/buffer_gles2.cc b/o3d/core/cross/gles2/buffer_gles2.cc new file mode 100644 index 0000000..05b1276 --- /dev/null +++ b/o3d/core/cross/gles2/buffer_gles2.cc @@ -0,0 +1,253 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementatinos of VertexBufferGL and +// IndexBufferGL, used to implement O3D using OpenGL. +// +// To force the vertex and index buffers to be created by Cg Runtime +// control, define the compile flag "USE_CG_BUFFERS". This option is off by +// default and buffers are created, locked and managed using the OpenGL +// "ARB_vertex_buffer_object" extension. + +#include "core/cross/error.h" +#include "core/cross/gles2/buffer_gles2.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/utils_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" + +namespace o3d { + +namespace { + +GLenum BufferAccessModeToGLenum(Buffer::AccessMode access_mode) { + switch (access_mode) { + case Buffer::READ_ONLY: + return GL_READ_ONLY_ARB; + case Buffer::WRITE_ONLY: + return GL_WRITE_ONLY_ARB; + case Buffer::READ_WRITE: + return GL_READ_WRITE_ARB; + case Buffer::NONE: + break; + } + DCHECK(false); + return GL_READ_WRITE_ARB; +} + +} // anonymous namespace + +// Vertex Buffers -------------------------------------------------------------- + +// Initializes the O3D VertexBuffer object but does not allocate an +// OpenGL vertex buffer object yet. +VertexBufferGL::VertexBufferGL(ServiceLocator* service_locator) + : VertexBuffer(service_locator), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())), + gl_buffer_(0) { + DLOG(INFO) << "VertexBufferGL Construct"; +} + +// Destructor releases the OpenGL VBO. +VertexBufferGL::~VertexBufferGL() { + DLOG(INFO) << "VertexBufferGL Destruct \"" << name() << "\""; + ConcreteFree(); +} + +// Creates a OpenGL vertex buffer of the requested size. +bool VertexBufferGL::ConcreteAllocate(size_t size_in_bytes) { + DLOG(INFO) << "VertexBufferGL Allocate \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + ConcreteFree(); + // Create a new VBO. + glGenBuffersARB(1, &gl_buffer_); + + if (!gl_buffer_) return false; + + // Give the VBO a size, but no data, and set the hint to "STATIC_DRAW" + // to mark the buffer as set up once then used often. + glBindBufferARB(GL_ARRAY_BUFFER_ARB, gl_buffer_); + glBufferDataARB(GL_ARRAY_BUFFER_ARB, + size_in_bytes, + NULL, + GL_STATIC_DRAW_ARB); + CHECK_GL_ERROR(); + return true; +} + +void VertexBufferGL::ConcreteFree() { + if (gl_buffer_) { + renderer_->MakeCurrentLazy(); + glDeleteBuffersARB(1, &gl_buffer_); + gl_buffer_ = 0; + CHECK_GL_ERROR(); + } +} + +// Calls Lock on the OpenGL buffer to get the address in memory of where the +// buffer data is currently stored. +bool VertexBufferGL::ConcreteLock(Buffer::AccessMode access_mode, + void **buffer_data) { + DLOG(INFO) << "VertexBufferGL Lock \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, gl_buffer_); + *buffer_data = glMapBufferARB(GL_ARRAY_BUFFER_ARB, + BufferAccessModeToGLenum(access_mode)); + if (*buffer_data == NULL) { + GLenum error = glGetError(); + if (error == GL_OUT_OF_MEMORY) { + O3D_ERROR(service_locator()) << "Out of memory for buffer lock."; + } else { + O3D_ERROR(service_locator()) << "Unable to lock a GL Array Buffer"; + } + return false; + } + CHECK_GL_ERROR(); + return true; +} + +// Calls Unlock on the OpenGL buffer to notify that the contents of the buffer +// are now ready for use. +bool VertexBufferGL::ConcreteUnlock() { + DLOG(INFO) << "VertexBufferGL Unlock \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + glBindBufferARB(GL_ARRAY_BUFFER_ARB, gl_buffer_); + if (!glUnmapBufferARB(GL_ARRAY_BUFFER)) { + GLenum error = glGetError(); + if (error == GL_INVALID_OPERATION) { + O3D_ERROR(service_locator()) << + "Buffer was unlocked without first being locked."; + } else { + O3D_ERROR( + service_locator()) << "Unable to unlock a GL Element Array Buffer"; + } + return false; + } + CHECK_GL_ERROR(); + return true; +} + + +// Index Buffers --------------------------------------------------------------- + +// Initializes the O3D IndexBuffer object but does not create a OpenGL +// buffer yet. + +IndexBufferGL::IndexBufferGL(ServiceLocator* service_locator) + : IndexBuffer(service_locator), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())), + gl_buffer_(0) { + DLOG(INFO) << "IndexBufferGL Construct"; +} + +// Destructor releases the OpenGL index buffer. +IndexBufferGL::~IndexBufferGL() { + DLOG(INFO) << "IndexBufferGL Destruct \"" << name() << "\""; + ConcreteFree(); +} + +// Creates a OpenGL index buffer of the requested size. +bool IndexBufferGL::ConcreteAllocate(size_t size_in_bytes) { + DLOG(INFO) << "IndexBufferGL Allocate \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + ConcreteFree(); + // Create a new VBO. + glGenBuffersARB(1, &gl_buffer_); + if (!gl_buffer_) return false; + // Give the VBO a size, but no data, and set the hint to "STATIC_DRAW" + // to mark the buffer as set up once then used often. + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, gl_buffer_); + glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER_ARB, + size_in_bytes, + NULL, + GL_STATIC_DRAW_ARB); + CHECK_GL_ERROR(); + return true; +} + +void IndexBufferGL::ConcreteFree() { + if (gl_buffer_) { + renderer_->MakeCurrentLazy(); + glDeleteBuffersARB(1, &gl_buffer_); + gl_buffer_ = 0; + CHECK_GL_ERROR(); + } +} + +// Maps the OpenGL buffer to get the address in memory of the buffer data. +bool IndexBufferGL::ConcreteLock(Buffer::AccessMode access_mode, + void **buffer_data) { + DLOG(INFO) << "IndexBufferGL Lock \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, gl_buffer_); + if (!num_elements()) + return true; + *buffer_data = glMapBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, + BufferAccessModeToGLenum(access_mode)); + if (*buffer_data == NULL) { + GLenum error = glGetError(); + if (error == GL_OUT_OF_MEMORY) { + O3D_ERROR(service_locator()) << "Out of memory for buffer lock."; + } else { + O3D_ERROR( + service_locator()) << "Unable to lock a GL Element Array Buffer"; + } + return false; + } + CHECK_GL_ERROR(); + return true; +} + +// Calls Unlock on the OpenGL buffer to notify that the contents of the buffer +// are now ready for use. +bool IndexBufferGL::ConcreteUnlock() { + DLOG(INFO) << "IndexBufferGL Unlock \"" << name() << "\""; + renderer_->MakeCurrentLazy(); + if (!num_elements()) + return true; + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER_ARB, gl_buffer_); + if (!glUnmapBufferARB(GL_ELEMENT_ARRAY_BUFFER)) { + GLenum error = glGetError(); + if (error == GL_INVALID_OPERATION) { + O3D_ERROR(service_locator()) << + "Buffer was unlocked without first being locked."; + } else { + O3D_ERROR( + service_locator()) << "Unable to unlock a GL Element Array Buffer"; + } + return false; + } + CHECK_GL_ERROR(); + return true; +} +} // namespace o3d diff --git a/o3d/core/cross/gles2/buffer_gles2.h b/o3d/core/cross/gles2/buffer_gles2.h new file mode 100644 index 0000000..90a8327 --- /dev/null +++ b/o3d/core/cross/gles2/buffer_gles2.h @@ -0,0 +1,120 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the platform specific +// VertexBufferGL and IndexBufferGL objects used by O3D + +#ifndef O3D_CORE_CROSS_GLES2_BUFFER_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_BUFFER_GLES2_H_ + +#include "core/cross/buffer.h" +#include "core/cross/gles2/gles2_headers.h" + +namespace o3d { + +class RendererGL; + +// VertexBufferGL is a wrapper around an OpenGL Vertex Buffer Object (VBO). +// The buffer starts out empty. Calling Allocate() will reserve video memory +// for the buffer. Buffer contents are are updated by calling Lock() to get a +// pointer to the memory allocated for the buffer, updating that data in place +// and calling Unlock() to notify OpenGL that the edits are done. +// +// To force the vertex and index buffers to be created by Cg Runtime +// control, define the compile flag "USE_CG_BUFFERS". This option is off by +// default and buffers are created, locked and managed using the OpenGL +// "ARB_vertex_buffer_object" extension. + +class VertexBufferGL : public VertexBuffer { + public: + explicit VertexBufferGL(ServiceLocator* service_locator); + ~VertexBufferGL(); + + // Returns the OpenGL vertex buffer Object handle. + GLuint gl_buffer() const { return gl_buffer_; } + + protected: + // Creates a OpenGL vertex buffer object of the specified size. + virtual bool ConcreteAllocate(size_t size_in_bytes); + + // Frees the OpenGL vertex buffer object. + virtual void ConcreteFree(); + + // Returns a pointer to the current contents of the buffer. A matching + // call to Unlock is necessary to update the contents of the buffer. + virtual bool ConcreteLock(AccessMode access_mode, void** buffer_data); + + // Notifies OpenGL that the buffer data has been updated. Unlock is only + // valid if it follows a Lock operation. + virtual bool ConcreteUnlock(); + + private: + RendererGL* renderer_; + GLuint gl_buffer_; +}; + +// IndexBufferGL is a wrapper around an OpenGL Index Buffer Object (VBO). +// The buffer starts out empty. A call to Allocate() will create an OpenGL +// index buffer of the requested size. Updates the to the contents of the +// buffer are done via the Lock/Unlock calls. +class IndexBufferGL : public IndexBuffer { + public: + explicit IndexBufferGL(ServiceLocator* service_locator); + ~IndexBufferGL(); + + // Returns the OpenGL vertex buffer Object handle. + GLuint gl_buffer() const { return gl_buffer_; } + + protected: + // Creates a OpenGL index buffer of the specified size. + virtual bool ConcreteAllocate(size_t size_in_bytes); + + // Frees the OpenGL vertex buffer object. + virtual void ConcreteFree(); + + // Returns a pointer to the current contents of the buffer. After calling + // Lock, the contents of the buffer can be updated in place. + virtual bool ConcreteLock(AccessMode access_mode, void** buffer_data); + + // Notifies OpenGL that the buffer data has been updated. Unlock is only + // valid if it follows a Lock operation. + virtual bool ConcreteUnlock(); + + private: + RendererGL* renderer_; + GLuint gl_buffer_; +}; + +} // namespace o3d + + +#endif // O3D_CORE_CROSS_GLES2_BUFFER_GLES2_H_ diff --git a/o3d/core/cross/gles2/draw_element_gles2.cc b/o3d/core/cross/gles2/draw_element_gles2.cc new file mode 100644 index 0000000..efe3aca --- /dev/null +++ b/o3d/core/cross/gles2/draw_element_gles2.cc @@ -0,0 +1,46 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the DrawElementGL class. + +#include "core/cross/gles2/draw_element_gles2.h" + +namespace o3d { + +DrawElementGL::DrawElementGL(ServiceLocator* service_locator) + : DrawElement(service_locator) { +} + +DrawElementGL::~DrawElementGL() { +} +} // namespace o3d + diff --git a/o3d/core/cross/gles2/draw_element_gles2.h b/o3d/core/cross/gles2/draw_element_gles2.h new file mode 100644 index 0000000..a6c769e --- /dev/null +++ b/o3d/core/cross/gles2/draw_element_gles2.h @@ -0,0 +1,57 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the DrawElementGL class. + +#ifndef O3D_CORE_CROSS_GLES2_DRAW_ELEMENT_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_DRAW_ELEMENT_GLES2_H_ + +#include <map> +#include "core/cross/draw_element.h" + +namespace o3d { + +class Element; + +// DrawElementGL is the OpenGL implementation of the DrawElement. It +// provides a place for the renderer to store platform specific cache +// information. +class DrawElementGL : public DrawElement { + public: + explicit DrawElementGL(ServiceLocator* service_locator); + ~DrawElementGL(); + + private: +}; +} // o3d + +#endif // O3D_CORE_CROSS_GLES2_DRAW_ELEMENT_GLES2_H_ diff --git a/o3d/core/cross/gles2/effect_gles2.cc b/o3d/core/cross/gles2/effect_gles2.cc new file mode 100644 index 0000000..efff57e --- /dev/null +++ b/o3d/core/cross/gles2/effect_gles2.cc @@ -0,0 +1,634 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of EffectGL, the OpenGL implementation +// of the abstract O3D class Effect. + + +// Disable pointer casting warning for openGL calls that require a void* to +// be cast to a GLuint +#if defined(OS_WIN) +#pragma warning(disable : 4312) +#pragma warning(disable : 4311) +#endif + +#include <sstream> +#include "base/cross/std_functional.h" +#include "core/cross/semantic_manager.h" +#include "core/cross/error.h" +#include "core/cross/standard_param.h" +#include "core/cross/gles2/effect_gles2.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/primitive_gles2.h" +#include "core/cross/gles2/draw_element_gles2.h" +#include "core/cross/gles2/texture_gles2.h" +#include "core/cross/gles2/utils_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" + +#if defined(OS_WIN) +#include "core/cross/core_metrics.h" +#endif + +namespace o3d { + +// Number of repeating events to log before giving up, e.g. setup frame, +// draw polygons, etc. +const int kNumLoggedEvents = 5; + +// Convert a CGparameter data type into a Param type. Note that +// Param::BOOLEAN is never generated by the Cg compiler. +static const ObjectBase::Class* CgTypeToParamType(CGtype cg_type) { + switch (cg_type) { + case CG_FLOAT : + case CG_FLOAT1 : return ParamFloat::GetApparentClass(); + case CG_FLOAT2 : return ParamFloat2::GetApparentClass(); + case CG_FLOAT3 : return ParamFloat3::GetApparentClass(); + case CG_FLOAT4 : return ParamFloat4::GetApparentClass(); + case CG_INT : return ParamInteger::GetApparentClass(); + case CG_INT1 : return ParamInteger::GetApparentClass(); + case CG_FLOAT4x4 : return ParamMatrix4::GetApparentClass(); + case CG_BOOL : + case CG_BOOL1 : return ParamBoolean::GetApparentClass(); + case CG_SAMPLER : + case CG_SAMPLER1D : + case CG_SAMPLER2D : + case CG_SAMPLER3D : + case CG_SAMPLERCUBE : return ParamSampler::GetApparentClass(); + default : { + DLOG(ERROR) << "Cannot convert CGtype " + << cgGetTypeString(cg_type) + << " to a Param type."; + return NULL; + } + } +} + +// ----------------------------------------------------------------------------- + +EffectGL::EffectGL(ServiceLocator* service_locator, CGcontext cg_context) + : Effect(service_locator), + semantic_manager_(service_locator->GetService<SemanticManager>()), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())), + cg_context_(cg_context), + cg_vertex_(NULL), + cg_fragment_(NULL) { + DLOG(INFO) << "EffectGL Construct"; +} + +// Destructor releases vertex and fragment shaders and their correspoding +// constants tables. +EffectGL::~EffectGL() { + DLOG(INFO) << "EffectGL Destruct \"" << name() << "\""; + if (cg_vertex_) { + cgDestroyProgram(cg_vertex_); + } + if (cg_fragment_) { + cgDestroyProgram(cg_fragment_); + } +} + +// Rewrites vertex program assembly code to match GL semantics for clipping. +// This parses the source, breaking it down into pieces: +// - declaration ("!!ARBvp1.0") +// - comments (that contain the parameter information) +// - instructions +// - "END" token. +// Then it rewrites the instructions so that 'result.position' doesn't get +// written directly, instead it is written to a temporary variable. Then a +// transformation is done on that variable before outputing to +// 'result.position': +// - offset x and y by half a pixel (times w). +// - remap z from [0..w] to [-w..w]. +// - invert y, if render targets are active. +// +// Note that for the 1/2 pixel offset, we need a parameter that depends on the +// current viewport. This is done through 'program.env[0]' which is shared +// across all programs (so we only have to update it once when we change the +// viewport), because Cg won't use them currently (it uses 'program.local' +// instead). +static bool RewriteVertexProgramSource(String *source) { + String::size_type pos = source->find('\n'); + if (pos == String::npos) { + DLOG(ERROR) << "could not find program declaration"; + return false; + } + String decl(*source, 0, pos + 1); + String::size_type start_comments = pos + 1; + // skip the comments that contain the parameters etc. + for (; pos < source->size(); pos = source->find('\n', pos)) { + ++pos; + if (pos >= source->size()) + break; + if ((*source)[pos] != '#') + break; + } + if (pos >= source->size()) { + // we only found comments. + return false; + } + String comments(*source, start_comments, pos - start_comments); + + String::size_type end_token = source->find("\nEND", pos + 1); + if (end_token == String::npos) { + DLOG(ERROR) << "Compiled shader doesn't have an END token"; + return false; + } + String instructions(*source, pos, end_token + 1 - pos); + + // Replace accesses to 'result.position' by accesses to our temp variable + // '$O3D_HPOS'. + // '$' is a valid symbol for identifiers, but Cg doesn't seem to be using + // it, so we can use it to ensure we don't have name conflicts. + static const char kOutPositionRegister[] = "result.position"; + for (String::size_type i = instructions.find(kOutPositionRegister); + i < String::npos; i = instructions.find(kOutPositionRegister, i)) { + instructions.replace(i, strlen(kOutPositionRegister), "$O3D_HPOS"); + } + + *source = decl + + comments + + // .x = 1/viewport.width; .y = -1/viewport.height; .z = 2.0; w = +/-1.0; + "PARAM $O3D_HELPER = program.env[0];\n" + "TEMP $O3D_HPOS;\n" + + instructions + + // hpos.x <- hpos.x + hpos.w / viewport.width; + // hpos.y <- hpos.y - hpos.w / viewport.height; + "MAD $O3D_HPOS.xy, $O3D_HELPER.xyyy, $O3D_HPOS.w, $O3D_HPOS.xyyy;\n" + // Invert y, based on the w component of the helper arg. + "MUL $O3D_HPOS.y, $O3D_HELPER.w, $O3D_HPOS.y;\n" + // hpos.z <- hpos.z * 2 - hpos.w + "MAD $O3D_HPOS.z, $O3D_HPOS.z, $O3D_HELPER.z, -$O3D_HPOS.w;\n" + "MOV result.position, $O3D_HPOS;\n" + "END\n"; + return true; +} + +// Initializes the Effect object using the shaders found in an FX formatted +// string. +bool EffectGL::LoadFromFXString(const String& effect) { + DLOG(INFO) << "EffectGL LoadFromFXString"; + renderer_->MakeCurrentLazy(); + + set_source(""); + + String vertex_shader_entry_point; + String fragment_shader_entry_point; + MatrixLoadOrder matrix_load_order; + // TODO(gman): Check for failure once shader parser is in. + if (!ValidateFX(effect, + &vertex_shader_entry_point, + &fragment_shader_entry_point, + &matrix_load_order)) { + return false; + } + + set_matrix_load_order(matrix_load_order); + + // Compile the original vertex program once, to get the ARBVP1 assembly code. + CGprogram original_vp = cgCreateProgram(cg_context_, CG_SOURCE, + effect.c_str(), CG_PROFILE_ARBVP1, + vertex_shader_entry_point.c_str(), + NULL); + const char* listing = cgGetLastListing(cg_context_); + if (original_vp == NULL) { + O3D_ERROR(service_locator()) << "Effect Compile Error: " + << cgGetErrorString(cgGetError()) << " : " + << listing; + return false; + } + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + String vp_assembly = cgGetProgramString(original_vp, CG_COMPILED_PROGRAM); + cgDestroyProgram(original_vp); + if (!RewriteVertexProgramSource(&vp_assembly)) { + return false; + } + cg_vertex_ = cgCreateProgram(cg_context_, CG_OBJECT, vp_assembly.c_str(), + CG_PROFILE_ARBVP1, + vertex_shader_entry_point.c_str(), NULL); + listing = cgGetLastListing(cg_context_); + if (cg_vertex_ == NULL) { + O3D_ERROR(service_locator()) << "Effect post-rewrite Compile Error: " + << cgGetErrorString(cgGetError()) << " : " + << listing; + return false; + } + +#ifdef OS_WIN + // Get metrics for length of the vertex shader + const char* shader_data = cgGetProgramString(cg_vertex_, CG_COMPILED_PROGRAM); + metric_vertex_shader_instruction_count.AddSample(strlen(shader_data)); +#endif + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect post-rewrite compile warnings: " << listing; + } + + CHECK_GL_ERROR(); + + // If the program rewrite introduced some syntax or semantic errors, we won't + // know it until we load the program (through a GL error). + // So flush all GL errors first... + do {} while (glGetError() != GL_NO_ERROR); + + // ... Then load the program ... + cgGLLoadProgram(cg_vertex_); + + // ... And check for GL errors. + if (glGetError() != GL_NO_ERROR) { + O3D_ERROR(service_locator()) + << "Effect post-rewrite GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB) + << "\nSource: \n" + << vp_assembly; + return false; + } + + cg_fragment_ = cgCreateProgram(cg_context_, CG_SOURCE, effect.c_str(), + CG_PROFILE_ARBFP1, + fragment_shader_entry_point.c_str(), NULL); + listing = cgGetLastListing(cg_context_); + if (cg_fragment_ == NULL) { + O3D_ERROR(service_locator()) << "Effect Compile Error: " + << cgGetErrorString(cgGetError()) << " : " + << listing; + return false; + } + +#ifdef OS_WIN + // Get metrics for length of the fragment shader + shader_data = cgGetProgramString(cg_fragment_, CG_COMPILED_PROGRAM); + metric_pixel_shader_instruction_count.AddSample(strlen(shader_data)); +#endif + + if (listing && listing[0] != 0) { + DLOG(WARNING) << "Effect Compile Warnings: " << listing; + } + + cgGLLoadProgram(cg_fragment_); + + // Also check for GL errors, in case Cg managed to compile, but generated a + // bad program. + if (glGetError() != GL_NO_ERROR) { + O3D_ERROR(service_locator()) + << "Effect GL Error: " + << glGetString(GL_PROGRAM_ERROR_STRING_ARB); + return false; + } + + // TODO(o3d): remove this (OLD path for textures). + FillSamplerToTextureMap(effect); + + CHECK_GL_ERROR(); + + set_source(effect); + return true; +} + +// Fills the sampler->texture map. This needs to compile the source as an +// effect because the state assignments get lost when compiled as a +// shader. +// Note that we compile the raw effect, which shouldn't have any +// technique/pass, so we don't actually create programs, just parse the +// uniforms and state assignments. +void EffectGL::FillSamplerToTextureMap(const String &effect) { + CGeffect cg_effect = cgCreateEffect(cg_context_, effect.c_str(), NULL); + if (!cg_effect) { + DLOG(ERROR) << "Could not compile the effect to find " + << "Sampler->Texture associations"; + return; + } + for (CGparameter param = cgGetFirstEffectParameter(cg_effect); + param; param = cgGetNextLeafParameter(param)) { + CGtype cg_type = cgGetParameterType(param); + switch (cg_type) { + case CG_SAMPLER: + case CG_SAMPLER1D: + case CG_SAMPLER2D: + case CG_SAMPLER3D: + case CG_SAMPLERCUBE: + break; + default: + continue; + } + CGstateassignment state_assignment = + cgGetNamedSamplerStateAssignment(param, "Texture"); + if (!state_assignment) + continue; + CGparameter texture_param = + cgGetTextureStateAssignmentValue(state_assignment); + if (!texture_param) + continue; + DCHECK((cgGetParameterType(texture_param) == CG_TEXTURE)); + sampler_to_texture_map_[cgGetParameterName(param)] = + cgGetParameterName(texture_param); + } + cgDestroyEffect(cg_effect); +} + +// TODO(o3d): remove this (OLD path for textures). +String EffectGL::GetTextureNameFromSamplerParamName( + const String &sampler_name) { + std::map<String, String>::iterator it = + sampler_to_texture_map_.find(sampler_name); + if (it != sampler_to_texture_map_.end()) { + return it->second; + } else { + return ""; + } +} + +// Given a CG_SAMPLER parameter, find the corresponding CG_TEXTURE +// parameter. From this CG_TEXTURE, find a matching Param by name. +ParamTexture* EffectGL::GetTextureParamFromCgSampler( + CGparameter cg_sampler, + const std::vector<ParamObject*> ¶m_objects) { + DLOG(INFO) << "EffectGL GetTextureParamFromCgSampler"; + DLOG_ASSERT(cgGetParameterType(cg_sampler) != CG_SAMPLER); + String sampler_name = cgGetParameterName(cg_sampler); + String param_name = GetTextureNameFromSamplerParamName(sampler_name); + if (param_name.size() == 0) { + // Sampler has no texture associated with it. + return NULL; + } + // Find a matching Param with the same name as the CG_TEXTURE. + for (unsigned int i = 0; i < param_objects.size(); ++i) { + Param* param = param_objects[i]->GetUntypedParam(param_name); + if (param && param->IsA(ParamTexture::GetApparentClass())) { + // Success. + DLOG(INFO) << "EffectGL Matched CG_SAMPLER \"" + << sampler_name + << "\" To Param \"" + << param_name << "\""; + return down_cast<ParamTexture*>(param); + } + } + DLOG(INFO) << "No matching Param for CG_TEXTURE \"" + << param_name + << "\" used by CG_SAMPLER \"" + << sampler_name << "\""; + return NULL; +} + +void EffectGL::GetShaderParamInfo( + CGprogram program, + CGenum name_space, + std::map<String, EffectParameterInfo>* info_map) { + DCHECK(info_map); + + // Loop over all parameters, visiting only CGparameters that have + // had storage allocated to them. + CGparameter cg_param = cgGetFirstParameter(program, name_space); + for (; cg_param != NULL; cg_param = cgGetNextParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_UNIFORM) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + String name = cgGetParameterName(cg_param); + CGtype cg_type = cgGetParameterType(cg_param); + // Texture parameters need special handling as the c3cImport system + // records a handle to the CG_TEXTURE param, not the CG_SAMPLER + // param. D3D sets textures by binding a bitmap to the Texture + // param, Cg binds bitmaps to the Sampler parameter. We solve this + // by keeping an internal collection of Texture-Sampler mappings + // that is built here, so we can later to do the reverse lookup. + // + // TODO(o3d): This will not solve the one-to-many problem of one + // Texture being used by many Sampler params, but it's enough to get + // us up and running. + // + // TODO(o3d): Once we start using samplers exclusively, this special + // treatment of textures should go away. For the time being though, we do + // end up creating a texture param on the param_object. + if (cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + // rename the parameter to have the name of the texture. + String texture_param_name = GetTextureNameFromSamplerParamName(name); + if (texture_param_name.size() != 0) { + (*info_map)[texture_param_name] = EffectParameterInfo( + texture_param_name, + ParamTexture::GetApparentClass(), + 0, + "", + false); + } + } else if (cg_type == CG_TEXTURE) { + continue; + } + int num_elements; + if (cg_type == CG_ARRAY) { + num_elements = cgGetArraySize(cg_param, 0); + // Substitute the first element's type for our type. + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } else { + num_elements = 0; + } + const ObjectBase::Class *param_class = CgTypeToParamType(cg_type); + if (!param_class) + continue; + const char* cg_semantic = cgGetParameterSemantic(cg_param); + const ObjectBase::Class *sem_class = NULL; + if (cg_semantic != NULL && cg_semantic[0] != '\0') { + // NOTE: this semantic is not the regularised profile semantic output + // from the CGC compiler but the actual user supplied semantic from + // the shader source code, so this match is valid. + sem_class = semantic_manager_->LookupSemantic(cg_semantic); + } + (*info_map)[name] = EffectParameterInfo( + name, + param_class, + num_elements, + (cg_semantic != NULL) ? cg_semantic : "", + sem_class); + } +} + +void EffectGL::GetParameterInfo(EffectParameterInfoArray* info_array) { + DCHECK(info_array); + std::map<String, EffectParameterInfo> info_map; + renderer_->MakeCurrentLazy(); + if (cg_vertex_) { + GetShaderParamInfo(cg_vertex_, CG_PROGRAM, &info_map); + GetShaderParamInfo(cg_vertex_, CG_GLOBAL, &info_map); + } + if (cg_fragment_) { + // create Param objects based on the parameters found in the fragment + // program. + GetShaderParamInfo(cg_fragment_, CG_PROGRAM, &info_map); + GetShaderParamInfo(cg_fragment_, CG_GLOBAL, &info_map); + } + info_array->clear(); + info_array->reserve(info_map.size()); + std::transform( + info_map.begin(), + info_map.end(), + std::back_inserter(*info_array), + base::select2nd<std::map<String, EffectParameterInfo>::value_type>()); +} + +void EffectGL::GetVaryingVertexShaderParamInfo( + CGprogram program, + CGenum name_space, + std::vector<EffectStreamInfo>* info_array) { + CGparameter cg_param = cgGetFirstLeafParameter(cg_vertex_, name_space); + for (; cg_param != NULL; cg_param = cgGetNextLeafParameter(cg_param)) { + CGenum variability = cgGetParameterVariability(cg_param); + if (variability != CG_VARYING) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + + const char* cg_semantic = cgGetParameterSemantic(cg_param); + if (cg_semantic == NULL) + continue; + + int attr = SemanticNameToGLVertexAttribute(cg_semantic); + if (attr < 0) + continue; + + int semantic_index = 0; + Stream::Semantic semantic = GLVertexAttributeToStream(attr, + &semantic_index); + if (semantic == Stream::UNKNOWN_SEMANTIC) + continue; + + info_array->push_back(EffectStreamInfo(semantic, + semantic_index)); + } +} + +void EffectGL::GetStreamInfo( + EffectStreamInfoArray* info_array) { + DCHECK(info_array); + renderer_->MakeCurrentLazy(); + info_array->clear(); + GetVaryingVertexShaderParamInfo(cg_vertex_, CG_PROGRAM, info_array); + GetVaryingVertexShaderParamInfo(cg_vertex_, CG_GLOBAL, info_array); +} + + +// private functions ----------------------------------------------------------- + +// Loop over all the CG_SAMPLER objects and attach the GLuint handle for the +// GL texture object that we discovered earlier. Then execute the +// CGstateassignments in the sampler_state to set up the texture unit. +// TODO(o3d): remove this (OLD path for textures). +void EffectGL::SetTexturesFromEffect(ParamCacheGL* param_cache_gl) { + DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGL EnableTexturesFromEffect"; + ParamCacheGL::SamplerParameterMap& map = param_cache_gl->sampler_map(); + ParamCacheGL::SamplerParameterMap::iterator i; + for (i = map.begin(); i != map.end(); ++i) { + CGparameter cg_param = i->first; + ParamTexture *param = i->second; + if (param != NULL) { + Texture *t = param->value(); + if (t) { + GLuint handle = static_cast<GLuint>(reinterpret_cast<intptr_t>( + t->GetTextureHandle())); + cgGLSetTextureParameter(cg_param, handle); + cgGLEnableTextureParameter(cg_param); + } + } + } + CHECK_GL_ERROR(); +} + +// Loop through all the uniform CGparameters on the effect and set their +// values from their corresponding Params on the various ParamObject (as stored +// in the ParamCacheGL). +void EffectGL::UpdateShaderUniformsFromEffect(ParamCacheGL* param_cache_gl) { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "EffectGL UpdateShaderUniformsFromEffect"; + ParamCacheGL::UniformParameterMap& map = param_cache_gl->uniform_map(); + ParamCacheGL::UniformParameterMap::iterator i; + for (i = map.begin(); i != map.end(); ++i) { + CGparameter cg_param = i->first; + i->second->SetEffectParam(renderer_, cg_param); + } + CHECK_GL_ERROR(); +} + +// Loop through all the uniform CGparameters on the effect and reset their +// values. For now, this unbinds textures contained in sampler parameters. +void EffectGL::ResetShaderUniforms(ParamCacheGL* param_cache_gl) { + DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGL ResetShaderUniforms"; + ParamCacheGL::UniformParameterMap& map = param_cache_gl->uniform_map(); + ParamCacheGL::UniformParameterMap::iterator i; + for (i = map.begin(); i != map.end(); ++i) { + CGparameter cg_param = i->first; + i->second->ResetEffectParam(renderer_, cg_param); + } + CHECK_GL_ERROR(); +} + +// Updates the values of the vertex and fragment shader parameters using the +// current values in the param/cgparam caches. +void EffectGL::PrepareForDraw(ParamCacheGL* param_cache_gl) { + DLOG_FIRST_N(INFO, kNumLoggedEvents) << "EffectGL PrepareForDraw \"" + << name() + << "\""; + DCHECK(renderer_->IsCurrent()); + if (cg_vertex_ && cg_fragment_) { + // Initialise the render states for this pass, this includes the shaders. + cgGLBindProgram(cg_vertex_); + cgGLBindProgram(cg_fragment_); + UpdateShaderUniformsFromEffect(param_cache_gl); + + // TODO(o3d): remove this (OLD path for textures). + SetTexturesFromEffect(param_cache_gl); + } else { + DLOG_FIRST_N(ERROR, kNumLoggedEvents) + << "No valid CGeffect found " + << "in Effect \"" << name() << "\""; + } + CHECK_GL_ERROR(); +} + +// Resets the render states back to their default value. +void EffectGL::PostDraw(ParamCacheGL* param_cache_gl) { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "EffectGL PostDraw \"" << name() << "\""; + DCHECK(renderer_->IsCurrent()); + ResetShaderUniforms(param_cache_gl); + CHECK_GL_ERROR(); +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/effect_gles2.h b/o3d/core/cross/gles2/effect_gles2.h new file mode 100644 index 0000000..93bc857 --- /dev/null +++ b/o3d/core/cross/gles2/effect_gles2.h @@ -0,0 +1,147 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the EffectGL class. + +#ifndef O3D_CORE_CROSS_GLES2_EFFECT_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_EFFECT_GLES2_H_ + +// Disable compiler warning for openGL calls that require a void* to +// be cast to a GLuint +#if defined(OS_WIN) +#pragma warning(disable : 4312) +#pragma warning(disable : 4311) +#endif + +#include <utility> +#include <vector> +#include <map> +#include "core/cross/gles2/gles2_headers.h" +#include "core/cross/effect.h" +#include "core/cross/gles2/utils_gles2.h" + +namespace o3d { + +class DrawElementGL; +class ParamCacheGL; +class ParamObject; +class Param; +class ParamTexture; +class RendererGL; +class SemanticManager; + +// A class to set an effect parameter from an O3D parameter. +class EffectParamHandlerGL : public RefCounted { + public: + typedef SmartPointer<EffectParamHandlerGL> Ref; + virtual ~EffectParamHandlerGL() { } + + // Sets a GL/Cg Effect Parameter by an O3D Param. + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) = 0; + + // Resets a GL/Cg Effect parameter to default (currently only + // unbinds textures contained in Sampler params). + virtual void ResetEffectParam(RendererGL* renderer, CGparameter cg_param) {} +}; + +// EffectGL is an implementation of the Effect object for OpenGL. It +// provides the API for setting the vertex and framgent shaders for the +// Effect using the Cg Runtime. Currently the two shaders can either be +// provided separately as shader code or together in an FX file. +class EffectGL : public Effect { + public: + EffectGL(ServiceLocator* service_locator, CGcontext cg_context); + virtual ~EffectGL(); + + // Reads the vertex and fragment shaders from string in the FX format. + // It returns true if the shaders were successfully compiled. + virtual bool LoadFromFXString(const String& effect); + + // Binds the shaders to the device and sets up all the shader parameters using + // the values from the matching Param's of the param_object. + void PrepareForDraw(ParamCacheGL* param_cache_gl); + + // Removes any pipeline state-changes installed during a draw. + void PostDraw(ParamCacheGL* param_cache_gl); + + // Gets info about the parameters this effect needs. + // Overriden from Effect. + virtual void GetParameterInfo(EffectParameterInfoArray* info_array); + + // Gets info about the streams this effect needs. + // Overriden from Effect. + virtual void GetStreamInfo( + EffectStreamInfoArray* info_array); + + // Given a CG_SAMPLER parameter, find the corresponding CG_TEXTURE + // parameterand from this CG_TEXTURE, find a matching Param by name in a list + // of ParamObject. + // TODO: remove this (OLD path for textures). + ParamTexture* GetTextureParamFromCgSampler( + CGparameter cg_sampler, + const std::vector<ParamObject*> ¶m_objects); + + CGprogram cg_vertex_program() { return cg_vertex_; } + CGprogram cg_fragment_program() { return cg_fragment_; } + + private: + // Loops through all the parameters in the ShapeDataGL and updates the + // corresponding parameter EffectGL object + void UpdateShaderUniformsFromEffect(ParamCacheGL* param_cache_gl); + // Undoes the effect of the above. For now, this unbinds textures. + void ResetShaderUniforms(ParamCacheGL* param_cache_gl); + void GetShaderParamInfo(CGprogram program, + CGenum name_space, + std::map<String, EffectParameterInfo>* info_map); + void GetVaryingVertexShaderParamInfo( + CGprogram program, + CGenum name_space, + std::vector<EffectStreamInfo>* info_array); + + // TODO: remove these (OLD path for textures). + void SetTexturesFromEffect(ParamCacheGL* param_cache_gl); + void FillSamplerToTextureMap(const String &effect); + String GetTextureNameFromSamplerParamName(const String &sampler_name); + + SemanticManager* semantic_manager_; + RendererGL* renderer_; + + CGcontext cg_context_; + CGprogram cg_vertex_; + CGprogram cg_fragment_; + + // TODO: remove this (OLD path for textures). + std::map<String, String> sampler_to_texture_map_; +}; +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_EFFECT_GLES2_H_ diff --git a/o3d/core/cross/gles2/gles2_headers.h b/o3d/core/cross/gles2/gles2_headers.h new file mode 100644 index 0000000..6fc9c9c --- /dev/null +++ b/o3d/core/cross/gles2/gles2_headers.h @@ -0,0 +1,43 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef O3D_CORE_CROSS_GLES2_GL_HEADERS_H_ +#define O3D_CORE_CROSS_GLES2_GL_HEADERS_H_ + +#include <GL/glew.h> +#if defined(OS_WIN) +#include <GL/wglew.h> +#endif +#include <Cg/cg.h> +#include <Cg/cgGL.h> + +#endif // O3D_CORE_CROSS_GLES2_GL_HEADERS_H_ + diff --git a/o3d/core/cross/gles2/install_check.cc b/o3d/core/cross/gles2/install_check.cc new file mode 100644 index 0000000..9ae59dd --- /dev/null +++ b/o3d/core/cross/gles2/install_check.cc @@ -0,0 +1,41 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "core/cross/install_check.h" + +namespace o3d { + +bool RendererInstallCheck(std::string *error) { + return true; +} + +} // o3d diff --git a/o3d/core/cross/gles2/param_cache_gles2.cc b/o3d/core/cross/gles2/param_cache_gles2.cc new file mode 100644 index 0000000..dc42a45 --- /dev/null +++ b/o3d/core/cross/gles2/param_cache_gles2.cc @@ -0,0 +1,762 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the ParamCacheGL class. + +#include "core/cross/error.h" +#include "core/cross/param_array.h" +#include "core/cross/renderer.h" +#include "core/cross/semantic_manager.h" +#include "core/cross/gles2/param_cache_gles2.h" +#include "core/cross/gles2/effect_gles2.h" +#include "core/cross/gles2/sampler_gles2.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/element.h" +#include "core/cross/draw_element.h" + +namespace o3d { + +typedef std::vector<ParamObject *> ParamObjectList; + +ParamCacheGL::ParamCacheGL(SemanticManager* semantic_manager, + Renderer* renderer) + : semantic_manager_(semantic_manager), + renderer_(renderer), + last_vertex_program_(0), + last_fragment_program_(0) { +} + +bool ParamCacheGL::ValidateEffect(Effect* effect) { + DLOG_ASSERT(effect); + + EffectGL* effect_gl = down_cast<EffectGL*>(effect); + return (effect_gl->cg_vertex_program() == last_vertex_program_ || + effect_gl->cg_fragment_program() == last_fragment_program_); +} + +void ParamCacheGL::UpdateCache(Effect* effect, + DrawElement* draw_element, + Element* element, + Material* material, + ParamObject* override) { + DLOG_ASSERT(effect); + EffectGL* effect_gl = down_cast<EffectGL*>(effect); + + ScanCgEffectParameters(effect_gl->cg_vertex_program(), + effect_gl->cg_fragment_program(), + draw_element, + element, + material, + override); + + last_vertex_program_ = effect_gl->cg_vertex_program(); + last_fragment_program_ = effect_gl->cg_fragment_program(); +} + +template <typename T> +class TypedEffectParamHandlerGL : public EffectParamHandlerGL { + public: + explicit TypedEffectParamHandlerGL(T* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param); + private: + T* param_; +}; + +class EffectParamHandlerGLMatrixRows : public EffectParamHandlerGL { + public: + explicit EffectParamHandlerGLMatrixRows(ParamMatrix4* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + // set the data as floats in row major order. + Matrix4 mat = param_->value(); + cgSetMatrixParameterfr(cg_param, &mat[0][0]); + } + private: + ParamMatrix4* param_; +}; + +class EffectParamHandlerGLMatrixColumns : public EffectParamHandlerGL { + public: + explicit EffectParamHandlerGLMatrixColumns(ParamMatrix4* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + // set the data as floats in column major order. + Matrix4 mat = param_->value(); + cgSetMatrixParameterfc(cg_param, &mat[0][0]); + } + private: + ParamMatrix4* param_; +}; + +template <> +void TypedEffectParamHandlerGL<ParamFloat>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + Float f = param_->value(); + cgSetParameter1f(cg_param, f); +}; + +template <> +void TypedEffectParamHandlerGL<ParamFloat2>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + Float2 f = param_->value(); + cgSetParameter2fv(cg_param, f.GetFloatArray()); +}; + +template <> +void TypedEffectParamHandlerGL<ParamFloat3>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + Float3 f = param_->value(); + cgSetParameter3fv(cg_param, f.GetFloatArray()); +}; + +template <> +void TypedEffectParamHandlerGL<ParamFloat4>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + Float4 f = param_->value(); + cgSetParameter4fv(cg_param, f.GetFloatArray()); +}; + +template <> +void TypedEffectParamHandlerGL<ParamInteger>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + int i = param_->value(); + cgSetParameter1i(cg_param, i); +}; + +template <> +void TypedEffectParamHandlerGL<ParamBoolean>::SetEffectParam( + RendererGL* renderer, + CGparameter cg_param) { + int i = param_->value(); + cgSetParameter1i(cg_param, i); +}; + +class EffectParamHandlerForSamplersGL : public EffectParamHandlerGL { + public: + explicit EffectParamHandlerForSamplersGL(ParamSampler* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + SamplerGL* sampler_gl = down_cast<SamplerGL*>(param_->value()); + if (!sampler_gl) { + // Use the error sampler. + sampler_gl = down_cast<SamplerGL*>(renderer->error_sampler()); + // If no error texture is set then generate an error. + if (!renderer->error_texture()) { + O3D_ERROR(param_->service_locator()) + << "Missing Sampler for ParamSampler " << param_->name(); + } + } + sampler_gl->SetTextureAndStates(cg_param); + } + virtual void ResetEffectParam(RendererGL* renderer, CGparameter cg_param) { + SamplerGL* sampler_gl = down_cast<SamplerGL*>(param_->value()); + if (!sampler_gl) { + sampler_gl = down_cast<SamplerGL*>(renderer->error_sampler()); + } + sampler_gl->ResetTexture(cg_param); + } + private: + ParamSampler* param_; +}; + +template <typename T> +class EffectParamArrayHandlerGL : public EffectParamHandlerGL { + public: + explicit EffectParamArrayHandlerGL(ParamParamArray* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + ParamArray* param = param_->value(); + if (param) { + int size = cgGetArraySize(cg_param, 0); + if (size != static_cast<int>(param->size())) { + O3D_ERROR(param->service_locator()) + << "number of params in ParamArray does not match number of params " + << "needed by shader array"; + } else { + for (int i = 0; i < size; ++i) { + Param* untyped_element = param->GetUntypedParam(i); + // TODO(gman): Make this check happen when building the param cache. + // To do that would require that ParamParamArray mark it's owner + // as changed if a Param in it's ParamArray changes. + if (untyped_element->IsA(T::GetApparentClass())) { + CGparameter cg_element = cgGetArrayParameter(cg_param, i); + SetElement(cg_element, down_cast<T*>(untyped_element)); + } else { + O3D_ERROR(param->service_locator()) + << "Param in ParamArray at index " << i << " is not a " + << T::GetApparentClassName(); + } + } + } + } + } + void SetElement(CGparameter cg_element, T* param); + + private: + ParamParamArray* param_; +}; + +template <bool column_major> +class EffectParamArrayMatrix4HandlerGL : public EffectParamHandlerGL { + public: + explicit EffectParamArrayMatrix4HandlerGL(ParamParamArray* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + ParamArray* param = param_->value(); + if (param) { + int size = cgGetArraySize(cg_param, 0); + if (size != static_cast<int>(param->size())) { + O3D_ERROR(param->service_locator()) + << "number of params in ParamArray does not match number of params " + << "needed by shader array"; + } else { + for (int i = 0; i < size; ++i) { + Param* untyped_element = param->GetUntypedParam(i); + // TODO(gman): Make this check happen when building the param cache. + // To do that would require that ParamParamArray mark it's owner + // as changed if a Param in it's ParamArray changes. + if (untyped_element->IsA(ParamMatrix4::GetApparentClass())) { + CGparameter cg_element = cgGetArrayParameter(cg_param, i); + SetElement(cg_element, down_cast<ParamMatrix4*>(untyped_element)); + } else { + O3D_ERROR(param->service_locator()) + << "Param in ParamArray at index " << i + << " is not a ParamMatrix4"; + } + } + } + } + } + void SetElement(CGparameter cg_element, ParamMatrix4* param); + + private: + ParamParamArray* param_; +}; + +class EffectParamArraySamplerHandlerGL : public EffectParamHandlerGL { + public: + explicit EffectParamArraySamplerHandlerGL(ParamParamArray* param) + : param_(param) { + } + virtual void SetEffectParam(RendererGL* renderer, CGparameter cg_param) { + ParamArray* param = param_->value(); + if (param) { + int size = cgGetArraySize(cg_param, 0); + if (size != static_cast<int>(param->size())) { + O3D_ERROR(param->service_locator()) + << "number of params in ParamArray does not match number of params " + << "needed by shader array"; + } else { + for (int i = 0; i < size; ++i) { + Param* untyped_element = param->GetUntypedParam(i); + // TODO(gman): Make this check happen when building the param cache. + // To do that would require that ParamParamArray mark it's owner + // as changed if a Param in it's ParamArray changes. + if (untyped_element->IsA(ParamSampler::GetApparentClass())) { + CGparameter cg_element = cgGetArrayParameter(cg_param, i); + ParamSampler* element = down_cast<ParamSampler*>(untyped_element); + SamplerGL* sampler_gl = down_cast<SamplerGL*>(element->value()); + if (!sampler_gl) { + // Use the error sampler. + sampler_gl = down_cast<SamplerGL*>(renderer->error_sampler()); + // If no error texture is set then generate an error. + if (!renderer->error_texture()) { + O3D_ERROR(param_->service_locator()) + << "Missing Sampler for ParamSampler '" << param_->name() + << "' index " << i; + } + } + sampler_gl->SetTextureAndStates(cg_element); + } else { + O3D_ERROR(param->service_locator()) + << "Param in ParamArray at index " << i + << " is not a ParamSampler"; + } + } + } + } + } + virtual void ResetEffectParam(RendererGL* renderer, CGparameter cg_param) { + ParamArray* param = param_->value(); + if (param) { + int size = cgGetArraySize(cg_param, 0); + if (size == static_cast<int>(param->size())) { + for (int i = 0; i < size; ++i) { + Param* untyped_element = param->GetUntypedParam(i); + if (untyped_element->IsA(ParamSampler::GetApparentClass())) { + CGparameter cg_element = cgGetArrayParameter(cg_param, i); + ParamSampler* element = down_cast<ParamSampler*>(untyped_element); + SamplerGL* sampler_gl = down_cast<SamplerGL*>(element->value()); + if (!sampler_gl) { + sampler_gl = down_cast<SamplerGL*>(renderer->error_sampler()); + } + sampler_gl->ResetTexture(cg_element); + } + } + } + } + } + + private: + ParamParamArray* param_; +}; + +template<> +void EffectParamArrayHandlerGL<ParamFloat>::SetElement( + CGparameter cg_element, + ParamFloat* param) { + cgSetParameter1f(cg_element, param->value()); +} + +template<> +void EffectParamArrayHandlerGL<ParamFloat2>::SetElement( + CGparameter cg_element, + ParamFloat2* param) { + Float2 f = param->value(); + cgSetParameter2fv(cg_element, f.GetFloatArray()); +} + +template<> +void EffectParamArrayHandlerGL<ParamFloat3>::SetElement( + CGparameter cg_element, + ParamFloat3* param) { + Float3 f = param->value(); + cgSetParameter3fv(cg_element, f.GetFloatArray()); +} + +template<> +void EffectParamArrayHandlerGL<ParamFloat4>::SetElement( + CGparameter cg_element, + ParamFloat4* param) { + Float4 f = param->value(); + cgSetParameter4fv(cg_element, f.GetFloatArray()); +} + +template<> +void EffectParamArrayMatrix4HandlerGL<false>::SetElement( + CGparameter cg_element, + ParamMatrix4* param) { + // set the data as floats in row major order. + Matrix4 mat = param->value(); + cgSetMatrixParameterfr(cg_element, &mat[0][0]); +} + +template<> +void EffectParamArrayMatrix4HandlerGL<true>::SetElement( + CGparameter cg_element, + ParamMatrix4* param) { + // set the data as floats in column major order. + Matrix4 mat = param->value(); + cgSetMatrixParameterfc(cg_element, &mat[0][0]); +} + +template<> +void EffectParamArrayHandlerGL<ParamInteger>::SetElement( + CGparameter cg_element, + ParamInteger* param) { + cgSetParameter1i(cg_element, param->value()); +} + +template<> +void EffectParamArrayHandlerGL<ParamBoolean>::SetElement( + CGparameter cg_element, + ParamBoolean* param) { + cgSetParameter1i(cg_element, param->value()); +} + +static EffectParamHandlerGL::Ref GetHandlerFromParamAndCgType( + EffectGL* effect_gl, + Param *param, + CGtype cg_type) { + EffectParamHandlerGL::Ref handler; + if (param->IsA(ParamParamArray::GetApparentClass())) { + ParamParamArray* param_param_array = down_cast<ParamParamArray*>(param); + switch (cg_type) { + case CG_FLOAT: + case CG_FLOAT1: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamFloat>(param_param_array)); + break; + case CG_FLOAT2: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamFloat2>(param_param_array)); + break; + case CG_FLOAT3: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamFloat3>(param_param_array)); + break; + case CG_FLOAT4: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamFloat4>(param_param_array)); + break; + case CG_FLOAT4x4: + if (effect_gl->matrix_load_order() == Effect::COLUMN_MAJOR) { + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayMatrix4HandlerGL<true>(param_param_array)); + } else { + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayMatrix4HandlerGL<false>(param_param_array)); + } + break; + case CG_INT: + case CG_INT1: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamInteger>(param_param_array)); + break; + case CG_BOOL: + case CG_BOOL1: + handler = EffectParamHandlerGL::Ref( + new EffectParamArrayHandlerGL<ParamBoolean>(param_param_array)); + break; + case CG_SAMPLER: + case CG_SAMPLER1D: + case CG_SAMPLER2D: + case CG_SAMPLER3D: + case CG_SAMPLERCUBE: + handler = EffectParamHandlerGL::Ref( + new EffectParamArraySamplerHandlerGL(param_param_array)); + break; + default: + break; + } + } else if (param->IsA(ParamMatrix4::GetApparentClass())) { + if (cg_type == CG_FLOAT4x4) { + if (effect_gl->matrix_load_order() == Effect::COLUMN_MAJOR) { + // set the data as floats in column major order. + handler = EffectParamHandlerGL::Ref( + new EffectParamHandlerGLMatrixColumns( + down_cast<ParamMatrix4*>(param))); + } else { + // set the data as floats in row major order. + handler = EffectParamHandlerGL::Ref( + new EffectParamHandlerGLMatrixRows( + down_cast<ParamMatrix4*>(param))); + } + } + } else if (param->IsA(ParamFloat::GetApparentClass())) { + if (cg_type == CG_FLOAT || + cg_type == CG_FLOAT1) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamFloat>( + down_cast<ParamFloat*>(param))); + } + } else if (param->IsA(ParamFloat2::GetApparentClass())) { + if (cg_type == CG_FLOAT2) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamFloat2>( + down_cast<ParamFloat2*>(param))); + } + } else if (param->IsA(ParamFloat3::GetApparentClass())) { + if (cg_type == CG_FLOAT3) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamFloat3>( + down_cast<ParamFloat3*>(param))); + } + } else if (param->IsA(ParamFloat4::GetApparentClass())) { + if (cg_type == CG_FLOAT4) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamFloat4>( + down_cast<ParamFloat4*>(param))); + } + } else if (param->IsA(ParamInteger::GetApparentClass())) { + if (cg_type == CG_INT || cg_type == CG_INT1) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamInteger>( + down_cast<ParamInteger*>(param))); + } + } else if (param->IsA(ParamBoolean::GetApparentClass())) { + if (cg_type == CG_BOOL || cg_type == CG_BOOL1) { + handler = EffectParamHandlerGL::Ref( + new TypedEffectParamHandlerGL<ParamBoolean>( + down_cast<ParamBoolean*>(param))); + } + } else if (param->IsA(ParamSampler::GetApparentClass())) { + if (cg_type == CG_SAMPLER || + cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + handler = EffectParamHandlerGL::Ref( + new EffectParamHandlerForSamplersGL( + down_cast<ParamSampler*>(param))); + } + } + return handler; +} + +// Local helper function for scanning varying Cg parameters of a +// program or effect and recording their entries into the varying map. +static void ScanVaryingParameters(CGprogram program, + CGenum name_space, + ParamCacheGL* param_cache_gl) { + CGparameter cg_param = cgGetFirstLeafParameter(program, name_space); + for (; cg_param; cg_param = cgGetNextLeafParameter(cg_param)) { + if (!cgIsParameterReferenced(cg_param)) + continue; + CGenum variability = cgGetParameterVariability(cg_param); + CGenum direction = cgGetParameterDirection(cg_param); + if (variability == CG_VARYING && direction == CG_IN) { + // Add a link between the parameter and no stream (index -1) + // NOTE: Stream indexes will be set later in + // InsertMissingVertexStreams(). + if (param_cache_gl->varying_map().find(cg_param) == + param_cache_gl->varying_map().end()) { + const char* cg_name = cgGetParameterName(cg_param); + param_cache_gl->varying_map().insert(std::make_pair(cg_param, -1)); + DLOG(INFO) << "ElementGL Found CG_VARYING \"" + << cg_name << " : " + << cgGetParameterSemantic(cg_param) << "\""; + } + } + } +} + +// Local helper function for scanning uniform Cg parameters of a +// program or effect and recording their entries into the parameter maps. +static void ScanUniformParameters(SemanticManager* semantic_manager, + Renderer* renderer, + CGprogram program, + CGenum name_space, + ParamCacheGL* param_cache_gl, + const ParamObjectList& param_objects, + EffectGL* effect_gl) { + CGparameter cg_param = cgGetFirstParameter(program, name_space); + for (; cg_param; cg_param = cgGetNextParameter(cg_param)) { + if (!cgIsParameterReferenced(cg_param)) + continue; + CGenum direction = cgGetParameterDirection(cg_param); + if (direction != CG_IN) + continue; + CGtype cg_type = cgGetParameterType(cg_param); + CGenum variability = cgGetParameterVariability(cg_param); + const char* cg_name = cgGetParameterName(cg_param); + + if (variability == CG_UNIFORM) { + // We have a CGparameter to add, find a Param that matches it by name. + if (cg_type == CG_TEXTURE) { + // CG_TEXTURE objects are handled by CG_SAMPLER objects. + continue; + } + + // TODO(o3d): The following code block should be removed once we start + // creating sampler params for all effects coming in via the importer. For + // the time being, we keep an extra ParamTexture that does the job it used + // to do. If we are using a ParamSampler on the object then the + // ParamTexture will have no value and therefore its handler will have no + // side-effects. + if (cg_type == CG_SAMPLER || + cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + // Uniform is a Sampler object. Find the CG_TEXTURE object + // assigned to the CG_SAMPLER, then find a Param object with the + // same name as the CG_TEXTURE. This is the tricky bit! + if (param_cache_gl->sampler_map().find(cg_param) == + param_cache_gl->sampler_map().end()) { + ParamTexture* param = + effect_gl->GetTextureParamFromCgSampler(cg_param, + param_objects); + if (param) { + param_cache_gl->sampler_map().insert(std::make_pair(cg_param, + param)); + } + } + } + + // Find a Param of the same name, and record the link. + if (param_cache_gl->uniform_map().find(cg_param) == + param_cache_gl->uniform_map().end()) { + const ObjectBase::Class *sem_class = NULL; + // Try looking by SAS class name. + const char* cg_semantic = cgGetParameterSemantic(cg_param); + if (cg_semantic != NULL && cg_semantic[0] != '\0') { + // NOTE: this semantic is not the regularised profile semantic + // output from the CGC compiler but the actual user supplied + // semantic from the shader source code, so this match is valid. + sem_class = semantic_manager->LookupSemantic(cg_semantic); + } + EffectParamHandlerGL::Ref handler; + // Look through all the param objects to find a matching param. + unsigned last = param_objects.size() - 1; + for (unsigned int i = 0; i < param_objects.size(); ++i) { + ParamObject *param_object = param_objects[i]; + Param *param = param_object->GetUntypedParam(cg_name); + if (!param && sem_class) { + param = param_object->GetUntypedParam(sem_class->name()); + } + if (!param) { + // If this is the last param object and we didn't find a matching + // param then if it's a sampler use the error sampler + if (i == last) { + if (cg_type == CG_SAMPLER || + cg_type == CG_SAMPLER1D || + cg_type == CG_SAMPLER2D || + cg_type == CG_SAMPLER3D || + cg_type == CG_SAMPLERCUBE) { + param = + renderer->error_param_sampler(); + } + } + if (!param) { + continue; + } + } + if (cg_type == CG_ARRAY) { + // Substitute the first element's type for our type. + cg_type = cgGetParameterType(cgGetArrayParameter(cg_param, 0)); + } + handler = GetHandlerFromParamAndCgType(effect_gl, param, cg_type); + if (!handler.IsNull()) { + param_cache_gl->uniform_map().insert( + std::make_pair(cg_param, handler)); + DLOG(INFO) << "ElementGL Matched CG_PARAMETER \"" + << cg_name << "\" to Param \"" + << param->name() << "\" from \"" + << param_object->name() << "\""; + break; + } else { + // We found a param, but it didn't match the type. keep looking. + DLOG(ERROR) << "ElementGL Param \"" + << param->name() << "\" type \"" + << param->GetClassName() << "\" from \"" + << param_object->name() + << "\" does not match CG_PARAMETER \"" + << cg_name << "\""; + } + } + if (handler.IsNull()) { + DLOG(ERROR) << "No matching Param for CG_PARAMETER \"" + << cg_name << "\""; + } + } + } + } +} + +static void DoScanCgEffectParameters(SemanticManager* semantic_manager, + Renderer* renderer, + ParamCacheGL* param_cache_gl, + CGprogram cg_vertex, + CGprogram cg_fragment, + EffectGL* effect_gl, + const ParamObjectList& param_objects) { + ScanVaryingParameters(cg_vertex, CG_PROGRAM, param_cache_gl); + ScanVaryingParameters(cg_vertex, CG_GLOBAL, param_cache_gl); + ScanUniformParameters(semantic_manager, + renderer, + cg_vertex, + CG_PROGRAM, + param_cache_gl, + param_objects, + effect_gl); + ScanUniformParameters(semantic_manager, + renderer, + cg_vertex, + CG_GLOBAL, + param_cache_gl, + param_objects, + effect_gl); + // Do not record varying inputs for a fragment program + ScanUniformParameters(semantic_manager, + renderer, + cg_fragment, + CG_PROGRAM, + param_cache_gl, + param_objects, + effect_gl); + ScanUniformParameters(semantic_manager, + renderer, + cg_fragment, + CG_GLOBAL, + param_cache_gl, + param_objects, + effect_gl); +} + +// Search the leaf parameters of a CGeffect and it's shaders for parameters +// using cgGetFirstEffectParameter() / cgGetFirstLeafParameter() / +// cgGetNextLeafParameter(). Add the CGparameters found to the parameter +// maps on the DrawElement. +void ParamCacheGL::ScanCgEffectParameters(CGprogram cg_vertex, + CGprogram cg_fragment, + ParamObject* draw_element, + ParamObject* element, + Material* material, + ParamObject* override) { + DLOG(INFO) << "DrawElementGL ScanCgEffectParameters"; + DLOG_ASSERT(material); + DLOG_ASSERT(draw_element); + DLOG_ASSERT(element); + EffectGL* effect_gl = static_cast<EffectGL*>(material->effect()); + DLOG_ASSERT(effect_gl); + if (cg_vertex == NULL) { + DLOG(ERROR) << "Can't scan an empty Vertex Program for Cg Parameters."; + return; + } + if (cg_fragment == NULL) { + DLOG(ERROR) << "Can't scan an empty Fragment Program for Cg Parameters."; + return; + } + + uniform_map_.clear(); + varying_map_.clear(); + sampler_map_.clear(); + ParamObjectList param_object_list; + param_object_list.push_back(override); + param_object_list.push_back(draw_element); + param_object_list.push_back(element); + param_object_list.push_back(material); + param_object_list.push_back(effect_gl); + param_object_list.push_back(semantic_manager_->sas_param_object()); + DoScanCgEffectParameters(semantic_manager_, + renderer_, + this, + cg_vertex, + cg_fragment, + effect_gl, + param_object_list); +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/param_cache_gles2.h b/o3d/core/cross/gles2/param_cache_gles2.h new file mode 100644 index 0000000..d8f14c9 --- /dev/null +++ b/o3d/core/cross/gles2/param_cache_gles2.h @@ -0,0 +1,103 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the ParamCacheGL class. + +#ifndef O3D_CORE_CROSS_GLES2_PARAM_CACHE_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_PARAM_CACHE_GLES2_H_ + +#include <map> +#include "core/cross/gles2/gles2_headers.h" +#include "core/cross/param_cache.h" +#include "core/cross/gles2/effect_gles2.h" + +namespace o3d { + +class ParamTexture; +class SemanticManager; + +class ParamCacheGL : public ParamCache { + public: + ParamCacheGL(SemanticManager* semantic_manager, Renderer* renderer); + + typedef std::map<CGparameter, int> VaryingParameterMap; + typedef std::map<CGparameter, EffectParamHandlerGL::Ref> UniformParameterMap; + typedef std::map<CGparameter, ParamTexture*> SamplerParameterMap; + + // Overridden from ParamCache. + virtual void UpdateCache(Effect* effect, + DrawElement* draw_element, + Element* element, + Material* material, + ParamObject* override); + + VaryingParameterMap& varying_map() { return varying_map_; } + UniformParameterMap& uniform_map() { return uniform_map_; } + SamplerParameterMap& sampler_map() { return sampler_map_; } + + protected: + // Overridden from ParamCache + // Validates platform specific information about the effect. + virtual bool ValidateEffect(Effect* effect); + + private: + + SemanticManager* semantic_manager_; + Renderer* renderer_; + + // Records the last two shaders used on this cache, allowing us to rescan the + // shader parameters if the user changes the shader on an active cache. + CGprogram last_vertex_program_; + CGprogram last_fragment_program_; + + // Search the leaf parameters of a CGeffect and it's shaders for + // parameters using cgGetFirstEffectParameter() / + // cgGetFirstLeafParameter() / cgGetNextLeafParameter(). Add the + // CGparameters found to the parameter maps on the DrawElement. + void ScanCgEffectParameters(CGprogram cg_vertex, + CGprogram fragment, + ParamObject* draw_element, + ParamObject* element, + Material* material, + ParamObject* override); + + // A map of varying CGparameter to Stream index. + VaryingParameterMap varying_map_; + // A map of uniform CGparameter to Param objects. + UniformParameterMap uniform_map_; + // A map of uniform CG_SAMPLER CGparameters to ParamTexture objects. + // TODO: remove this (OLD path for textures). + SamplerParameterMap sampler_map_; +}; +} // o3d + +#endif // O3D_CORE_CROSS_GLES2_PARAM_CACHE_GLES2_H_ diff --git a/o3d/core/cross/gles2/primitive_gles2.cc b/o3d/core/cross/gles2/primitive_gles2.cc new file mode 100644 index 0000000..3770ca2 --- /dev/null +++ b/o3d/core/cross/gles2/primitive_gles2.cc @@ -0,0 +1,258 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of PrimitiveGL. + +#include <algorithm> + +#include "core/cross/stream.h" +#include "core/cross/error.h" +#include "core/cross/gles2/buffer_gles2.h" +#include "core/cross/gles2/effect_gles2.h" +#include "core/cross/gles2/primitive_gles2.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/draw_element_gles2.h" +#include "core/cross/gles2/stream_bank_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" +#include "Cg/cgGL.h" + +// Someone defines min, conflicting with std::min +#ifdef min +#undef min +#endif + +namespace o3d { + +// Number of times to log a repeated event before giving up. +const int kNumLoggedEvents = 5; + +// PrimitiveGL functions ------------------------------------------------------- + +PrimitiveGL::PrimitiveGL(ServiceLocator* service_locator) + : Primitive(service_locator) { + DLOG(INFO) << "PrimitiveGL Construct"; +} + +PrimitiveGL::~PrimitiveGL() { + DLOG(INFO) << "PrimitiveGL Destruct"; +} + +// Binds the vertex and index streams required to draw the shape. If the +// vertex or fragment programs have changed since the last time this method +// was called (or it's the first time it's getting called) then it forces +// an update of the mapping between the Shape Param's and the shader parameters +// and also fills in for any missing streams. +void PrimitiveGL::PlatformSpecificRender(Renderer* renderer, + DrawElement* draw_element, + Material* material, + ParamObject* override, + ParamCache* param_cache) { + DLOG_ASSERT(material); + DLOG_ASSERT(draw_element); + DLOG_ASSERT(param_cache); + DLOG_FIRST_N(INFO, kNumLoggedEvents) << "PrimitiveGL Draw \"" + << draw_element->name() << "\""; + DrawElementGL* draw_element_gl = down_cast<DrawElementGL*>(draw_element); + EffectGL* effect_gl = down_cast<EffectGL*>(material->effect()); + DLOG_ASSERT(effect_gl); + StreamBankGL* stream_bank_gl = down_cast<StreamBankGL*>(stream_bank()); + DLOG_ASSERT(stream_bank_gl); + + ParamCacheGL* param_cache_gl = down_cast<ParamCacheGL*>(param_cache); + ParamCacheGL::VaryingParameterMap& varying_map = + param_cache_gl->varying_map(); + + // If this PrimitiveGL has an effect we haven't seen before (or it's the first + // time through), initalize the parameter lists before drawing with it. + if (effect_gl->cg_vertex_program() && effect_gl->cg_fragment_program()) { + // Set up the current CGeffect. + if (!param_cache_gl->ValidateAndCacheParams(effect_gl, + draw_element_gl, + this, + stream_bank_gl, + material, + override)) { + Stream::Semantic missing_semantic; + int missing_semnatic_index; + if (!stream_bank_gl->CheckForMissingVertexStreams( + varying_map, + &missing_semantic, + &missing_semnatic_index)) { + param_cache_gl->ClearParamCache(); + O3D_ERROR(service_locator()) + << "Required Stream " + << Stream::GetSemanticDescription(missing_semantic) << ":" + << missing_semnatic_index << " missing on Primitive '" << name() + << "' using Material '" << material->name() + << "' with Effect '" << effect_gl->name() << "'"; + return; + } + } + } else { + O3D_ERROR(service_locator()) + << "No CG effect provided in Effect \"" + << effect_gl->name() << "\" used by Material \"" + << material->name() << "\" in Shape \"" + << draw_element_gl->name() << "\". Drawing nothing."; + return; + } + + // Make sure our streams are up to date (skinned, etc..) + stream_bank_gl->UpdateStreams(); + + unsigned int max_vertices; + if (!stream_bank_gl->BindStreamsForRendering(varying_map, &max_vertices)) { + return; + } + + // TODO: move these checks at 'set' time instead of draw time. + + bool draw = true; + if (number_vertices_ > max_vertices) { + O3D_ERROR(service_locator()) + << "Trying to draw with " << number_vertices_ + << " vertices when there are only " << max_vertices + << " available in the buffers. Skipping primitive."; + draw = false; + } + + unsigned int index_count; + + if (!Primitive::GetIndexCount(primitive_type_, + number_primitives_, + &index_count)) { + O3D_ERROR(service_locator()) + << "Unknown Primitive Type in GetIndexCount: " + << primitive_type_ << ". Skipping primitive " + << name(); + draw = false; + } + + if (indexed()) { + // Re-bind the index buffer for this shape + IndexBufferGL *ibuffer = down_cast<IndexBufferGL*>(index_buffer()); + + unsigned int max_indices = ibuffer->num_elements(); + + if (index_count > max_indices) { + O3D_ERROR(service_locator()) + << "Trying to draw with " << index_count + << " indices when only " << max_indices + << " are available in the buffer. Skipping shape."; + draw = false; + } + + // TODO: Also check that indices in the index buffer are less than + // max_vertices_. Needs support from the index buffer (scan indices on + // Unlock). + + glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, ibuffer->gl_buffer()); + } + + // Set up the shaders in this drawcall from the Effect. + effect_gl->PrepareForDraw(param_cache_gl); + + // Do the drawcall. + GLenum gl_primitive_type = GL_NONE; + switch (primitive_type_) { + case Primitive::POINTLIST : { + if (indexed()) { + O3D_ERROR(service_locator()) + << "POINTLIST unsupported for indexed primitives for primitive " + << name(); + draw = false; + } else { + gl_primitive_type = GL_POINTS; + } + break; + } + case Primitive::LINELIST : { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "Draw " << number_primitives_ << " GL_LINES"; + gl_primitive_type = GL_LINES; + break; + } + case Primitive::LINESTRIP : { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "Draw " << number_primitives_ << " GL_LINE_STRIP"; + gl_primitive_type = GL_LINE_STRIP; + break; + } + case Primitive::TRIANGLELIST : { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "Draw " << number_primitives_ << " GL_TRIANGLES"; + gl_primitive_type = GL_TRIANGLES; + break; + } + case Primitive::TRIANGLESTRIP : { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "Draw " << number_primitives_ << " GL_TRIANGLE_STRIP"; + gl_primitive_type = GL_TRIANGLE_STRIP; + break; + } + case Primitive::TRIANGLEFAN : { + DLOG_FIRST_N(INFO, kNumLoggedEvents) + << "Draw " << number_primitives_ << " GL_TRIANGLE_FAN"; + gl_primitive_type = GL_TRIANGLE_FAN; + break; + } + default : { + DLOG(ERROR) << "Unknown Primitive Type in Primitive: " + << primitive_type_; + draw = false; + } + } + if (draw) { + DCHECK_NE(gl_primitive_type, static_cast<unsigned int>(GL_NONE)); + renderer->AddPrimitivesRendered(number_primitives_); + if (indexed()) + glDrawElements(gl_primitive_type, + index_count, + GL_UNSIGNED_INT, + BUFFER_OFFSET(start_index() * sizeof(uint32))); // NOLINT + else + glDrawArrays(gl_primitive_type, start_index(), index_count); + } + + // Clean up the shaders. + effect_gl->PostDraw(param_cache_gl); + + // Disable the vertex attribute states set earlier. + for (ParamCacheGL::VaryingParameterMap::iterator i = varying_map.begin(); + i != varying_map.end(); + ++i) { + cgGLDisableClientState(i->first); + } + CHECK_GL_ERROR(); +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/primitive_gles2.h b/o3d/core/cross/gles2/primitive_gles2.h new file mode 100644 index 0000000..7dd1c75 --- /dev/null +++ b/o3d/core/cross/gles2/primitive_gles2.h @@ -0,0 +1,63 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the PrimitiveGL class. + +#ifndef O3D_CORE_CROSS_GLES2_PRIMITIVE_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_PRIMITIVE_GLES2_H_ + +#include <map> +#include "core/cross/primitive.h" +#include "core/cross/gles2/param_cache_gles2.h" + +namespace o3d { + +// PrimitiveGL is the OpenGL implementation of the Primitive. It provides the +// necessary interfaces for setting the geometry streams on the Primitive. +class PrimitiveGL : public Primitive { + public: + explicit PrimitiveGL(ServiceLocator* service_locator); + virtual ~PrimitiveGL(); + + protected: + // Overridden from Primitive. + virtual void PlatformSpecificRender(Renderer* renderer, + DrawElement* draw_element, + Material* material, + ParamObject* override, + ParamCache* param_cache); + + private: +}; +} // o3d + +#endif // O3D_CORE_CROSS_GLES2_PRIMITIVE_GLES2_H_ diff --git a/o3d/core/cross/gles2/render_surface_gles2.cc b/o3d/core/cross/gles2/render_surface_gles2.cc new file mode 100644 index 0000000..b57403f --- /dev/null +++ b/o3d/core/cross/gles2/render_surface_gles2.cc @@ -0,0 +1,125 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation of RenderSurfaceGL and +// RenderDepthStencilSurfaceGL. + +#include "core/cross/gles2/render_surface_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" +#include "core/cross/renderer.h" + +namespace o3d { + +RenderSurfaceGL::RenderSurfaceGL(ServiceLocator *service_locator, + int width, + int height, + GLenum cube_face, + int mip_level, + Texture *texture) + : RenderSurface(service_locator, width, height, texture), + cube_face_(cube_face), + mip_level_(mip_level) { + DCHECK(texture); +} + +RenderSurfaceGL::~RenderSurfaceGL() { +} + +Bitmap::Ref RenderSurfaceGL::PlatformSpecificGetBitmap() const { + Renderer* renderer = service_locator()->GetService<Renderer>(); + DCHECK(renderer); + + Bitmap::Ref bitmap = Bitmap::Ref(new Bitmap(service_locator())); + bitmap->Allocate( + Texture::ARGB8, clip_width(), clip_height(), 1, Bitmap::IMAGE); + + const RenderSurface* old_render_surface; + const RenderDepthStencilSurface* old_depth_surface; + bool old_is_back_buffer; + + renderer->GetRenderSurfaces(&old_render_surface, &old_depth_surface, + &old_is_back_buffer); + renderer->SetRenderSurfaces(this, NULL, false); + + ::glReadPixels(0, 0, clip_width(), clip_height(), GL_BGRA, GL_UNSIGNED_BYTE, + bitmap->image_data()); + + renderer->SetRenderSurfaces(old_render_surface, old_depth_surface, + old_is_back_buffer); + + return bitmap; +} + +RenderDepthStencilSurfaceGL::RenderDepthStencilSurfaceGL( + ServiceLocator *service_locator, + int width, + int height) + : RenderDepthStencilSurface(service_locator, width, height) { + + // If packed depth stencil is supported, create only one buffer for both + // depth and stencil. + if (GLEW_EXT_packed_depth_stencil) { + glGenRenderbuffersEXT(1, render_buffers_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[0]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_DEPTH24_STENCIL8_EXT, + width, + height); + CHECK_GL_ERROR(); + render_buffers_[1] = render_buffers_[0]; + } else { + glGenRenderbuffersEXT(2, render_buffers_); + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[0]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_DEPTH_COMPONENT24, + width, + height); + CHECK_GL_ERROR(); + + glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_buffers_[1]); + glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, + GL_STENCIL_INDEX8_EXT, + width, + height); + CHECK_GL_ERROR(); + } +} + +RenderDepthStencilSurfaceGL::~RenderDepthStencilSurfaceGL() { + if (GLEW_EXT_packed_depth_stencil) { + glDeleteRenderbuffersEXT(1, render_buffers_); + } else { + glDeleteRenderbuffersEXT(2, render_buffers_); + } +} + +} // end namespace o3d diff --git a/o3d/core/cross/gles2/render_surface_gles2.h b/o3d/core/cross/gles2/render_surface_gles2.h new file mode 100644 index 0000000..c75aa18 --- /dev/null +++ b/o3d/core/cross/gles2/render_surface_gles2.h @@ -0,0 +1,109 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declarations for RenderSurfaceGL and +// RenderDepthStencilSurfaceGL. + +#ifndef O3D_CORE_CROSS_GLES2_RENDER_SURFACE_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_RENDER_SURFACE_GLES2_H_ + +#include "core/cross/gles2/gles2_headers.h" +#include "core/cross/render_surface.h" +#include "core/cross/texture.h" + +namespace o3d { + +class RenderSurfaceGL : public RenderSurface { + public: + typedef SmartPointer<RenderSurfaceGL> Ref; + + // Constructs a RenderSurfaceGL instance associated with the texture argument. + // Parameters: + // service_locator: Service locator for the instance. + // width: The width of the surface, in pixels. + // height: The height of the surface, in pixels. + // cube_face: The face of the cube texture to which the surface is to be + // associated. NOTE: If the texture is a 2d texture, then the value of + // this argument is irrelevent. + // mip_level: The mip-level of the texture to associate with the surface. + // texture: The texture to associate with the surface. + RenderSurfaceGL(ServiceLocator *service_locator, + int width, + int height, + GLenum cube_face, + int mip_level, + Texture *texture); + virtual ~RenderSurfaceGL(); + + GLenum cube_face() const { + return cube_face_; + } + + int mip_level() const { + return mip_level_; + } + + protected: + // The platform specific part of GetBitmap. + virtual Bitmap::Ref PlatformSpecificGetBitmap() const; + + private: + GLenum cube_face_; + int mip_level_; + DISALLOW_COPY_AND_ASSIGN(RenderSurfaceGL); +}; + +class RenderDepthStencilSurfaceGL : public RenderDepthStencilSurface { + public: + typedef SmartPointer<RenderDepthStencilSurfaceGL> Ref; + + RenderDepthStencilSurfaceGL(ServiceLocator *service_locator, + int width, + int height); + virtual ~RenderDepthStencilSurfaceGL(); + + GLuint depth_buffer() const { + return render_buffers_[0]; + } + + GLuint stencil_buffer() const { + return render_buffers_[1]; + } + private: + // Handles to the depth and stencil render-buffers, respectively. + GLuint render_buffers_[2]; + DISALLOW_COPY_AND_ASSIGN(RenderDepthStencilSurfaceGL); +}; + +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_RENDER_SURFACE_GLES2_H_ diff --git a/o3d/core/cross/gles2/renderer_gles2.cc b/o3d/core/cross/gles2/renderer_gles2.cc new file mode 100644 index 0000000..e2f4a04 --- /dev/null +++ b/o3d/core/cross/gles2/renderer_gles2.cc @@ -0,0 +1,1561 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the RendererGL class that +// implements the abstract Renderer API using OpenGL and the Cg +// Runtime. + + +#include "core/cross/gles2/renderer_gles2.h" + +#include "core/cross/error.h" +#include "core/cross/gles2/buffer_gles2.h" +#include "core/cross/gles2/draw_element_gles2.h" +#include "core/cross/gles2/effect_gles2.h" +#include "core/cross/gles2/param_cache_gles2.h" +#include "core/cross/gles2/primitive_gles2.h" +#include "core/cross/gles2/render_surface_gles2.h" +#include "core/cross/gles2/sampler_gles2.h" +#include "core/cross/gles2/stream_bank_gles2.h" +#include "core/cross/gles2/texture_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" +#include "core/cross/gles2/utils_gles2.h" +#include "core/cross/material.h" +#include "core/cross/semantic_manager.h" +#include "core/cross/features.h" +#include "core/cross/shape.h" +#include "core/cross/types.h" + +namespace o3d { + +namespace { + +GLenum ConvertCmpFunc(State::Comparison cmp) { + switch (cmp) { + case State::CMP_ALWAYS: + return GL_ALWAYS; + case State::CMP_NEVER: + return GL_NEVER; + case State::CMP_LESS: + return GL_LESS; + case State::CMP_GREATER: + return GL_GREATER; + case State::CMP_LEQUAL: + return GL_LEQUAL; + case State::CMP_GEQUAL: + return GL_GEQUAL; + case State::CMP_EQUAL: + return GL_EQUAL; + case State::CMP_NOTEQUAL: + return GL_NOTEQUAL; + default: + break; + } + return GL_ALWAYS; +} + +GLenum ConvertFillMode(State::Fill mode) { + switch (mode) { + case State::POINT: + return GL_POINT; + case State::WIREFRAME: + return GL_LINE; + case State::SOLID: + return GL_FILL; + default: + break; + } + return GL_FILL; +} + +GLenum ConvertBlendFunc(State::BlendingFunction blend_func) { + switch (blend_func) { + case State::BLENDFUNC_ZERO: + return GL_ZERO; + case State::BLENDFUNC_ONE: + return GL_ONE; + case State::BLENDFUNC_SOURCE_COLOR: + return GL_SRC_COLOR; + case State::BLENDFUNC_INVERSE_SOURCE_COLOR: + return GL_ONE_MINUS_SRC_COLOR; + case State::BLENDFUNC_SOURCE_ALPHA: + return GL_SRC_ALPHA; + case State::BLENDFUNC_INVERSE_SOURCE_ALPHA: + return GL_ONE_MINUS_SRC_ALPHA; + case State::BLENDFUNC_DESTINATION_ALPHA: + return GL_DST_ALPHA; + case State::BLENDFUNC_INVERSE_DESTINATION_ALPHA: + return GL_ONE_MINUS_DST_ALPHA; + case State::BLENDFUNC_DESTINATION_COLOR: + return GL_DST_COLOR; + case State::BLENDFUNC_INVERSE_DESTINATION_COLOR: + return GL_ONE_MINUS_DST_COLOR; + case State::BLENDFUNC_SOURCE_ALPHA_SATUTRATE: + return GL_SRC_ALPHA_SATURATE; + default: + break; + } + return GL_ONE; +} + +GLenum ConvertBlendEquation(State::BlendingEquation blend_equation) { + switch (blend_equation) { + case State::BLEND_ADD: + return GL_FUNC_ADD; + case State::BLEND_SUBTRACT: + return GL_FUNC_SUBTRACT; + case State::BLEND_REVERSE_SUBTRACT: + return GL_FUNC_REVERSE_SUBTRACT; + case State::BLEND_MIN: + return GL_MIN; + case State::BLEND_MAX: + return GL_MAX; + default: + break; + } + return GL_FUNC_ADD; +} + +GLenum ConvertStencilOp(State::StencilOperation stencil_func) { + switch (stencil_func) { + case State::STENCIL_KEEP: + return GL_KEEP; + case State::STENCIL_ZERO: + return GL_ZERO; + case State::STENCIL_REPLACE: + return GL_REPLACE; + case State::STENCIL_INCREMENT_SATURATE: + return GL_INCR; + case State::STENCIL_DECREMENT_SATURATE: + return GL_DECR; + case State::STENCIL_INVERT: + return GL_INVERT; + case State::STENCIL_INCREMENT: + return GL_INCR_WRAP; + case State::STENCIL_DECREMENT: + return GL_DECR_WRAP; + default: + break; + } + return GL_KEEP; +} + +// Helper routine that will bind the surfaces stored in the RenderSurface and +// RenderDepthStencilSurface arguments to the current OpenGL context. +// Returns true upon success. +// Note: This routine assumes that a frambuffer object is presently bound +// to the context. +bool InstallFramebufferObjects(const RenderSurface* surface, + const RenderDepthStencilSurface* surface_depth) { +#ifdef _DEBUG + GLint bound_framebuffer; + ::glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &bound_framebuffer); + DCHECK(bound_framebuffer != 0); +#endif + + // Reset the bound attachments to the current framebuffer object. + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_RENDERBUFFER_EXT, + 0); + + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + 0); + + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + 0); + + if (surface) { + const RenderSurfaceGL *gl_surface = + down_cast<const RenderSurfaceGL*>(surface); + Texture *texture = gl_surface->texture(); + GLuint handle = static_cast<GLuint>(reinterpret_cast<intptr_t>( + texture->GetTextureHandle())); + if (texture->IsA(Texture2D::GetApparentClass())) { + ::glFramebufferTexture2DEXT( + GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, + handle, + gl_surface->mip_level()); + } else if (texture->IsA(TextureCUBE::GetApparentClass())) { + ::glFramebufferTexture2DEXT( + GL_FRAMEBUFFER_EXT, + GL_COLOR_ATTACHMENT0_EXT, + gl_surface->cube_face(), + handle, + gl_surface->mip_level()); + } + } + + if (surface_depth) { + // Bind both the depth and stencil attachments. + const RenderDepthStencilSurfaceGL* gl_surface = + down_cast<const RenderDepthStencilSurfaceGL*>(surface_depth); + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_DEPTH_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + gl_surface->depth_buffer()); + ::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, + GL_STENCIL_ATTACHMENT_EXT, + GL_RENDERBUFFER_EXT, + gl_surface->stencil_buffer()); + } + GLenum framebuffer_status = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT); + if (GL_FRAMEBUFFER_COMPLETE_EXT != framebuffer_status) { + return false; + } + + CHECK_GL_ERROR(); + return true; +} + +// Helper routine that returns a pointer to the non-NULL entry in the renderer's +// stack of bound surfaces. +const RenderSurfaceBase* GetValidRenderSurface( + const std::pair<RenderSurface*, RenderDepthStencilSurface*> &stack_entry) { + if (stack_entry.first) { + return stack_entry.first; + } else { + return stack_entry.second; + } +} + +} // unnamed namespace + +// This class wraps StateHandler to make it typesafe. +template <typename T> +class TypedStateHandler : public RendererGL::StateHandler { + public: + // Override this function to set a specific state. + // Parameters: + // renderer: The platform specific renderer. + // param: A concrete param with state data. + virtual void SetStateFromTypedParam(RendererGL* renderer, T* param) const = 0; + + // Gets Class of State's Parameter + virtual const ObjectBase::Class* GetClass() const { + return T::GetApparentClass(); + } + + private: + // Calls SetStateFromTypedParam if the Param type is the correct type. + // Parameters: + // renderer: The platform specific renderer. + // param: A param with state data. + virtual void SetState(Renderer* renderer, Param* param) const { + RendererGL *renderer_gl = down_cast<RendererGL *>(renderer); + // This is safe because State guarntees Params match by type. + DCHECK(param->IsA(T::GetApparentClass())); + SetStateFromTypedParam(renderer_gl, down_cast<T*>(param)); + } +}; + +// A template the generates a handler for enable/disable states. +// Parameters: +// state_constant: GLenum of state we want to enable/disable +template <GLenum state_constant> +class StateEnableHandler : public TypedStateHandler<ParamBoolean> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamBoolean* param) const { + if (param->value()) { + ::glEnable(state_constant); + } else { + ::glDisable(state_constant); + } + } +}; + +class BoolHandler : public TypedStateHandler<ParamBoolean> { + public: + explicit BoolHandler(bool* var, bool* changed_var) + : var_(*var), + changed_var_(*changed_var) { + } + virtual void SetStateFromTypedParam(RendererGL *renderer, + ParamBoolean *param) const { + var_ = param->value(); + } + private: + bool& var_; + bool& changed_var_; +}; + +class ZWriteEnableHandler : public TypedStateHandler<ParamBoolean> { + public: + virtual void SetStateFromTypedParam(RendererGL *renderer, + ParamBoolean *param) const { + ::glDepthMask(param->value()); + } +}; + +class AlphaReferenceHandler : public TypedStateHandler<ParamFloat> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamFloat* param) const { + float refFloat = param->value(); + + // cap the float to the required range + if (refFloat < 0.0f) { + refFloat = 0.0f; + } else if (refFloat > 1.0f) { + refFloat = 1.0f; + } + + renderer->alpha_function_ref_changed_ = true; + renderer->alpha_ref_ = refFloat; + } +}; + +class CullModeHandler : public TypedStateHandler<ParamInteger> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + State::Cull cull = static_cast<State::Cull>(param->value()); + switch (cull) { + case State::CULL_CW: + ::glEnable(GL_CULL_FACE); + ::glCullFace(GL_BACK); + break; + case State::CULL_CCW: + ::glEnable(GL_CULL_FACE); + ::glCullFace(GL_FRONT); + break; + default: + ::glDisable(GL_CULL_FACE); + break; + } + } +}; + +class PolygonOffset1Handler : public TypedStateHandler<ParamFloat> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamFloat* param) const { + renderer->polygon_offset_factor_ = param->value(); + renderer->polygon_offset_changed_ = true; + } +}; + +class PolygonOffset2Handler : public TypedStateHandler<ParamFloat> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamFloat* param) const { + renderer->polygon_offset_bias_ = param->value(); + renderer->polygon_offset_changed_ = true; + } +}; + +class FillModeHandler : public TypedStateHandler<ParamInteger> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + ::glPolygonMode(GL_FRONT_AND_BACK, + ConvertFillMode(static_cast<State::Fill>(param->value()))); + } +}; + +class ZFunctionHandler : public TypedStateHandler<ParamInteger> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + ::glDepthFunc( + ConvertCmpFunc(static_cast<State::Comparison>(param->value()))); + } +}; + +class BlendEquationHandler : public TypedStateHandler<ParamInteger> { + public: + explicit BlendEquationHandler(GLenum* var) + : var_(*var) { + } + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + renderer->alpha_blend_settings_changed_ = true; + var_ = ConvertBlendEquation( + static_cast<State::BlendingEquation>(param->value())); + } + private: + GLenum& var_; +}; + +class BlendFunctionHandler : public TypedStateHandler<ParamInteger> { + public: + explicit BlendFunctionHandler(GLenum* var) + : var_(*var) { + } + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + renderer->alpha_blend_settings_changed_ = true; + var_ = ConvertBlendFunc( + static_cast<State::BlendingFunction>(param->value())); + } + private: + GLenum& var_; +}; + + +class StencilOperationHandler : public TypedStateHandler<ParamInteger> { + public: + StencilOperationHandler(int face, int condition) + : face_(face) , + condition_(condition) { + } + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + renderer->stencil_settings_changed_ = true; + renderer->stencil_settings_[face_].op_[condition_] = ConvertStencilOp( + static_cast<State::StencilOperation>(param->value())); + } + private: + int face_; + int condition_; +}; + +class ComparisonFunctionHandler : public TypedStateHandler<ParamInteger> { + public: + ComparisonFunctionHandler(GLenum* var, bool* changed_var) + : var_(*var), + changed_var_(*changed_var) { + } + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + changed_var_ = true; + var_ = ConvertCmpFunc(static_cast<State::Comparison>(param->value())); + } + private: + GLenum& var_; + bool& changed_var_; +}; + +class StencilRefHandler : public TypedStateHandler<ParamInteger> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + renderer->stencil_settings_changed_ = true; + renderer->stencil_ref_ = param->value(); + } +}; + +class StencilMaskHandler : public TypedStateHandler<ParamInteger> { + public: + explicit StencilMaskHandler(int mask_index) + : mask_index_(mask_index) { + } + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + renderer->stencil_settings_changed_ = true; + renderer->stencil_mask_[mask_index_] = param->value(); + } + private: + int mask_index_; +}; + +class ColorWriteEnableHandler : public TypedStateHandler<ParamInteger> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamInteger* param) const { + int mask = param->value(); + ::glColorMask((mask & 0x1) != 0, + (mask & 0x2) != 0, + (mask & 0x4) != 0, + (mask & 0x8) != 0); + renderer->SetWriteMask(mask); + } +}; + +class PointSpriteEnableHandler : public TypedStateHandler<ParamBoolean> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamBoolean* param) const { + if (param->value()) { + ::glEnable(GL_POINT_SPRITE); + // TODO: It's not clear from D3D docs that point sprites affect + // TEXCOORD0, but that's my guess. Check that. + ::glActiveTextureARB(GL_TEXTURE0); + ::glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE); + } else { + ::glActiveTextureARB(GL_TEXTURE0); + ::glTexEnvi(GL_POINT_SPRITE, GL_COORD_REPLACE, GL_FALSE); + ::glDisable(GL_POINT_SPRITE); + } + } +}; + +class PointSizeHandler : public TypedStateHandler<ParamFloat> { + public: + virtual void SetStateFromTypedParam(RendererGL* renderer, + ParamFloat* param) const { + ::glPointSize(param->value()); + } +}; + +RendererGL* RendererGL::CreateDefault(ServiceLocator* service_locator) { + return new RendererGL(service_locator); +} + +RendererGL::RendererGL(ServiceLocator* service_locator) + : Renderer(service_locator), + semantic_manager_(service_locator), +#ifdef OS_WIN + gl_context_(NULL), +#endif + fullscreen_(0), +#ifdef OS_LINUX + display_(NULL), + window_(0), + context_(0), +#endif +#ifdef OS_MACOSX + mac_agl_context_(0), + mac_cgl_context_(0), +#endif + render_surface_framebuffer_(0), + cg_context_(NULL), + alpha_function_ref_changed_(true), + alpha_function_(GL_ALWAYS), + alpha_ref_(0.f), + alpha_blend_settings_changed_(true), + separate_alpha_blend_enable_(false), + stencil_settings_changed_(true), + separate_stencil_settings_enable_(false), + stencil_ref_(0), + polygon_offset_changed_(true), + polygon_offset_factor_(0.f), + polygon_offset_bias_(0.f) { + DLOG(INFO) << "RendererGL Construct"; + + // Setup default state values. + for (int ii = 0; ii < 2; ++ii) { + stencil_settings_[ii].func_ = GL_ALWAYS; + stencil_settings_[ii].op_[StencilStates::FAIL_OP] = GL_KEEP; + stencil_settings_[ii].op_[StencilStates::ZFAIL_OP] = GL_KEEP; + stencil_settings_[ii].op_[StencilStates::PASS_OP] = GL_KEEP; + stencil_mask_[ii] = -1; + blend_function_[ii][FRONT] = GL_ONE; + blend_function_[ii][BACK] = GL_ZERO; + blend_equation_[ii] = GL_FUNC_ADD; + } + + // Setup state handlers + AddStateHandler(State::kAlphaTestEnableParamName, + new StateEnableHandler<GL_ALPHA_TEST>); + AddStateHandler(State::kAlphaReferenceParamName, + new AlphaReferenceHandler); + AddStateHandler(State::kAlphaComparisonFunctionParamName, + new ComparisonFunctionHandler(&alpha_function_, + &alpha_function_ref_changed_)); + AddStateHandler(State::kCullModeParamName, + new CullModeHandler); + AddStateHandler(State::kDitherEnableParamName, + new StateEnableHandler<GL_DITHER>); + AddStateHandler(State::kLineSmoothEnableParamName, + new StateEnableHandler<GL_LINE_SMOOTH>); + AddStateHandler(State::kPointSpriteEnableParamName, + new PointSpriteEnableHandler); + AddStateHandler(State::kPointSizeParamName, + new PointSizeHandler); + AddStateHandler(State::kPolygonOffset1ParamName, + new PolygonOffset1Handler); + AddStateHandler(State::kPolygonOffset2ParamName, + new PolygonOffset2Handler); + AddStateHandler(State::kFillModeParamName, + new FillModeHandler); + AddStateHandler(State::kZEnableParamName, + new StateEnableHandler<GL_DEPTH_TEST>); + AddStateHandler(State::kZWriteEnableParamName, + new ZWriteEnableHandler); + AddStateHandler(State::kZComparisonFunctionParamName, + new ZFunctionHandler); + AddStateHandler(State::kAlphaBlendEnableParamName, + new StateEnableHandler<GL_BLEND>); + AddStateHandler(State::kSourceBlendFunctionParamName, + new BlendFunctionHandler(&blend_function_[SRC][RGB])); + AddStateHandler(State::kDestinationBlendFunctionParamName, + new BlendFunctionHandler(&blend_function_[DST][RGB])); + AddStateHandler(State::kStencilEnableParamName, + new StateEnableHandler<GL_STENCIL_TEST>); + AddStateHandler(State::kStencilFailOperationParamName, + new StencilOperationHandler(FRONT, StencilStates::FAIL_OP)); + AddStateHandler(State::kStencilZFailOperationParamName, + new StencilOperationHandler(FRONT, StencilStates::ZFAIL_OP)); + AddStateHandler(State::kStencilPassOperationParamName, + new StencilOperationHandler(FRONT, StencilStates::PASS_OP)); + AddStateHandler(State::kStencilComparisonFunctionParamName, + new ComparisonFunctionHandler( + &stencil_settings_[FRONT].func_, + &stencil_settings_changed_)); + AddStateHandler(State::kStencilReferenceParamName, + new StencilRefHandler); + AddStateHandler(State::kStencilMaskParamName, + new StencilMaskHandler(READ_MASK)); + AddStateHandler(State::kStencilWriteMaskParamName, + new StencilMaskHandler(WRITE_MASK)); + AddStateHandler(State::kColorWriteEnableParamName, + new ColorWriteEnableHandler); + AddStateHandler(State::kBlendEquationParamName, + new BlendEquationHandler(&blend_equation_[RGB])); + AddStateHandler(State::kTwoSidedStencilEnableParamName, + new BoolHandler(&separate_stencil_settings_enable_, + &stencil_settings_changed_)); + AddStateHandler(State::kCCWStencilFailOperationParamName, + new StencilOperationHandler(BACK, StencilStates::FAIL_OP)); + AddStateHandler(State::kCCWStencilZFailOperationParamName, + new StencilOperationHandler(BACK, StencilStates::ZFAIL_OP)); + AddStateHandler(State::kCCWStencilPassOperationParamName, + new StencilOperationHandler(BACK, StencilStates::PASS_OP)); + AddStateHandler(State::kCCWStencilComparisonFunctionParamName, + new ComparisonFunctionHandler( + &stencil_settings_[BACK].func_, + &stencil_settings_changed_)); + AddStateHandler(State::kSeparateAlphaBlendEnableParamName, + new BoolHandler(&separate_alpha_blend_enable_, + &alpha_blend_settings_changed_)); + AddStateHandler(State::kSourceBlendAlphaFunctionParamName, + new BlendFunctionHandler(&blend_function_[SRC][ALPHA])); + AddStateHandler(State::kDestinationBlendAlphaFunctionParamName, + new BlendFunctionHandler(&blend_function_[DST][ALPHA])); + AddStateHandler(State::kBlendAlphaEquationParamName, + new BlendEquationHandler(&blend_equation_[ALPHA])); +} + +RendererGL::~RendererGL() { + Destroy(); +} + +// platform neutral initialization code +// +Renderer::InitStatus RendererGL::InitCommonGL() { + GLenum glew_error = glewInit(); + if (glew_error != GLEW_OK) { + DLOG(ERROR) << "Unable to initialise GLEW : " + << ::glewGetErrorString(glew_error); + return INITIALIZATION_ERROR; + } + + // Check to see that we can use the OpenGL vertex attribute APIs + // TODO: We should return false if this check fails, but because some + // Intel hardware does not support OpenGL 2.0, yet does support all of the + // extensions we require, we only log an error. A future CL should change + // this check to ensure that all of the extension strings we require are + // present. + if (!GLEW_VERSION_2_0) { + DLOG(ERROR) << "GL drivers do not have OpenGL 2.0 functionality."; + } + + if (!GLEW_ARB_vertex_buffer_object) { + // NOTE: Linux NVidia drivers claim to support OpenGL 2.0 when using + // indirect rendering (e.g. remote X), but it is actually lying. The + // ARB_vertex_buffer_object functions silently no-op (!) when using + // indirect rendering, leading to crashes. Fortunately, in that case, the + // driver claims to not support ARB_vertex_buffer_object, so fail in that + // case. + DLOG(ERROR) << "GL drivers do not support vertex buffer objects."; + return GPU_NOT_UP_TO_SPEC; + } + + if (!GLEW_EXT_framebuffer_object) { + DLOG(ERROR) << "GL drivers do not support framebuffer objects."; + return GPU_NOT_UP_TO_SPEC; + } + + SetSupportsNPOT(GLEW_ARB_texture_non_power_of_two != 0); + +#ifdef OS_MACOSX + // The Radeon X1600 says it supports NPOT, but in most situations it doesn't. + if (supports_npot() && + !strcmp("ATI Radeon X1600 OpenGL Engine", + reinterpret_cast<const char*>(::glGetString(GL_RENDERER)))) + SetSupportsNPOT(false); +#endif + + // Check for necessary extensions + if (!GLEW_VERSION_2_0 && !GLEW_EXT_stencil_two_side) { + DLOG(ERROR) << "Two sided stencil extension missing."; + } + if (!GLEW_VERSION_1_4 && !GLEW_EXT_blend_func_separate) { + DLOG(ERROR) << "Separate blend func extension missing."; + } + if (!GLEW_VERSION_2_0 && !GLEW_EXT_blend_equation_separate) { + DLOG(ERROR) << "Separate blend function extension missing."; + } + // create a Cg Runtime. + cg_context_ = cgCreateContext(); + DLOG_CG_ERROR("Creating Cg context"); + // NOTE: the first CGerror number after the recreation of a + // CGcontext (the second time through) seems to be trashed. Please + // ignore any "CG ERROR: Invalid context handle." message on this + // function - Invalid context handle isn't one of therror states of + // cgCreateContext(). + DLOG(INFO) << "OpenGL Vendor: " << ::glGetString(GL_VENDOR); + DLOG(INFO) << "OpenGL Renderer: " << ::glGetString(GL_RENDERER); + DLOG(INFO) << "OpenGL Version: " << ::glGetString(GL_VERSION); + DLOG(INFO) << "Cg Version: " << cgGetString(CG_VERSION); + cg_vertex_profile_ = cgGLGetLatestProfile(CG_GL_VERTEX); + cgGLSetOptimalOptions(cg_vertex_profile_); + DLOG(INFO) << "Best Cg vertex profile = " + << cgGetProfileString(cg_vertex_profile_); + cg_fragment_profile_ = cgGLGetLatestProfile(CG_GL_FRAGMENT); + cgGLSetOptimalOptions(cg_fragment_profile_); + DLOG(INFO) << "Best Cg fragment profile = " + << cgGetProfileString(cg_fragment_profile_); + // Set up all Cg State Assignments for OpenGL. + cgGLRegisterStates(cg_context_); + DLOG_CG_ERROR("Registering GL StateAssignments"); + cgGLSetDebugMode(CG_FALSE); + // Enable the profiles we use. + cgGLEnableProfile(CG_PROFILE_ARBVP1); + cgGLEnableProfile(CG_PROFILE_ARBFP1); + // get some limits for this profile. + GLint max_vertex_attribs = 0; + ::glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); + DLOG(INFO) << "Max Vertex Attribs = " << max_vertex_attribs; + // Initialize global GL settings. + // Tell GL that texture buffers can be single-byte aligned. + ::glPixelStorei(GL_PACK_ALIGNMENT, 1); + ::glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + ::glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); + ::glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST); + CHECK_GL_ERROR(); + + GLint viewport[4]; + ::glGetIntegerv(GL_VIEWPORT, &viewport[0]); + SetClientSize(viewport[2], viewport[3]); + CHECK_GL_ERROR(); + + ::glGenFramebuffersEXT(1, &render_surface_framebuffer_); + CHECK_GL_ERROR(); + + return SUCCESS; +} + +// platform neutral destruction code +void RendererGL::DestroyCommonGL() { + MakeCurrentLazy(); + if (render_surface_framebuffer_) { + ::glDeleteFramebuffersEXT(1, &render_surface_framebuffer_); + } + + if (cg_context_) { + cgDestroyContext(cg_context_); + cg_context_ = NULL; + } +} + +#ifdef OS_WIN + +namespace { + +PIXELFORMATDESCRIPTOR kPixelFormatDescriptor = { + sizeof(kPixelFormatDescriptor), // Size of structure. + 1, // Default version. + PFD_DRAW_TO_WINDOW | // Window drawing support. + PFD_SUPPORT_OPENGL | // OpenGL support. + PFD_DOUBLEBUFFER, // Double buffering support (not stereo). + PFD_TYPE_RGBA, // RGBA color mode (not indexed). + 24, // 24 bit color mode. + 0, 0, 0, 0, 0, 0, // Don't set RGB bits & shifts. + 8, 0, // 8 bit alpha + 0, // No accumulation buffer. + 0, 0, 0, 0, // Ignore accumulation bits. + 24, // 24 bit z-buffer size. + 8, // 8-bit stencil buffer. + 0, // No aux buffer. + PFD_MAIN_PLANE, // Main drawing plane (not overlay). + 0, // Reserved. + 0, 0, 0, // Layer masks ignored. +}; + +LRESULT CALLBACK IntermediateWindowProc(HWND window, + UINT message, + WPARAM w_param, + LPARAM l_param) { + return ::DefWindowProc(window, message, w_param, l_param); +} + +// Helper routine that returns the highest quality pixel format supported on +// the current platform. Returns true upon success. +Renderer::InitStatus GetWindowsPixelFormat(HWND window, + Features* features, + int* pixel_format) { + // We must initialize a GL context before we can determine the multi-sampling + // supported on the current hardware, so we create an intermediate window + // and context here. + HINSTANCE module_handle; + if (!::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT | + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, + reinterpret_cast<wchar_t*>(IntermediateWindowProc), + &module_handle)) { + return Renderer::INITIALIZATION_ERROR; + } + + WNDCLASS intermediate_class; + intermediate_class.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; + intermediate_class.lpfnWndProc = IntermediateWindowProc; + intermediate_class.cbClsExtra = 0; + intermediate_class.cbWndExtra = 0; + intermediate_class.hInstance = module_handle; + intermediate_class.hIcon = LoadIcon(NULL, IDI_APPLICATION); + intermediate_class.hCursor = LoadCursor(NULL, IDC_ARROW); + intermediate_class.hbrBackground = NULL; + intermediate_class.lpszMenuName = NULL; + intermediate_class.lpszClassName = L"Intermediate GL Window"; + + ATOM class_registration = ::RegisterClass(&intermediate_class); + if (!class_registration) { + return Renderer::INITIALIZATION_ERROR; + } + + HWND intermediate_window = ::CreateWindow( + reinterpret_cast<wchar_t*>(class_registration), + L"", + WS_OVERLAPPEDWINDOW, + 0, 0, + CW_USEDEFAULT, CW_USEDEFAULT, + NULL, + NULL, + NULL, + NULL); + + if (!intermediate_window) { + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return Renderer::INITIALIZATION_ERROR; + } + + HDC intermediate_dc = ::GetDC(intermediate_window); + int format_index = ::ChoosePixelFormat(intermediate_dc, + &kPixelFormatDescriptor); + if (format_index == 0) { + DLOG(ERROR) << "Unable to get the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return Renderer::INITIALIZATION_ERROR; + } + if (!::SetPixelFormat(intermediate_dc, format_index, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return Renderer::INITIALIZATION_ERROR; + } + + // Store the pixel format without multisampling. + *pixel_format = format_index; + HGLRC gl_context = ::wglCreateContext(intermediate_dc); + if (::wglMakeCurrent(intermediate_dc, gl_context)) { + // GL context was successfully created and applied to the window's DC. + // Startup GLEW, the GL extensions wrangler. + GLenum glew_error = ::glewInit(); + if (glew_error == GLEW_OK) { + DLOG(INFO) << "Initialized GLEW " << ::glewGetString(GLEW_VERSION); + } else { + DLOG(ERROR) << "Unable to initialise GLEW : " + << ::glewGetErrorString(glew_error); + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return Renderer::INITIALIZATION_ERROR; + } + + // If the multi-sample extensions are present, query the api to determine + // the pixel format. + if (!features->not_anti_aliased() && + WGLEW_ARB_pixel_format && WGLEW_ARB_multisample) { + int pixel_attributes[] = { + WGL_SAMPLES_ARB, 4, + WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, + WGL_SUPPORT_OPENGL_ARB, GL_TRUE, + WGL_ACCELERATION_ARB, WGL_FULL_ACCELERATION_ARB, + WGL_COLOR_BITS_ARB, 24, + WGL_ALPHA_BITS_ARB, 8, + WGL_DEPTH_BITS_ARB, 24, + WGL_STENCIL_BITS_ARB, 8, + WGL_DOUBLE_BUFFER_ARB, GL_TRUE, + WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, + 0, 0}; + + float pixel_attributes_f[] = {0, 0}; + int msaa_pixel_format; + unsigned int num_formats; + + // Query for the highest sampling rate supported, starting at 4x. + static const int kSampleCount[] = {4, 2}; + static const int kNumSamples = 2; + for (int sample = 0; sample < kNumSamples; ++sample) { + pixel_attributes[1] = kSampleCount[sample]; + if (GL_TRUE == ::wglChoosePixelFormatARB(intermediate_dc, + pixel_attributes, + pixel_attributes_f, + 1, + &msaa_pixel_format, + &num_formats)) { + *pixel_format = msaa_pixel_format; + break; + } + } + } + } + + ::wglMakeCurrent(intermediate_dc, NULL); + ::wglDeleteContext(gl_context); + ::ReleaseDC(intermediate_window, intermediate_dc); + ::DestroyWindow(intermediate_window); + ::UnregisterClass(reinterpret_cast<wchar_t*>(class_registration), + module_handle); + return Renderer::SUCCESS; +} + +} // unnamed namespace + +Renderer::InitStatus RendererGL::InitPlatformSpecific( + const DisplayWindow& display, + bool off_screen) { + const DisplayWindowWindows &display_platform = + static_cast<const DisplayWindowWindows&>(display); + + DLOG(INFO) << "RendererGL Init"; + + // TODO: Add support for off-screen rendering using OpenGL. + if (off_screen) { + return INITIALIZATION_ERROR; + } + + int pixel_format; + InitStatus init_status; + + init_status = GetWindowsPixelFormat(display_platform.hwnd(), + features(), + &pixel_format); + if (init_status != SUCCESS) { + return init_status; + } + + window_ = display_platform.hwnd(); + device_context_ = ::GetDC(window_); + if (!::SetPixelFormat(device_context_, pixel_format, + &kPixelFormatDescriptor)) { + DLOG(ERROR) << "Unable to set the pixel format for GL context."; + return INITIALIZATION_ERROR; + } + + gl_context_ = ::wglCreateContext(device_context_); + if (MakeCurrent()) { + // Ensure that glew has been initialized for the created rendering context. + init_status = InitCommonGL(); + if (init_status != SUCCESS) { + DLOG(ERROR) << "Failed to initialize GL rendering context."; + return init_status; + } + if (WGLEW_ARB_multisample) { + ::glEnable(GL_MULTISAMPLE_ARB); + } + } else { + DLOG(ERROR) << "Failed to create the GL Context."; + return INITIALIZATION_ERROR; + } + CHECK_GL_ERROR(); + return SUCCESS; +} + +// Releases the Cg Context and deletes the GL device. +void RendererGL::Destroy() { + DLOG(INFO) << "Destroy RendererGL"; + DestroyCommonGL(); + if (device_context_) { + CHECK_GL_ERROR(); + // Release the OpenGL rendering context. + ::wglMakeCurrent(device_context_, NULL); + if (gl_context_) { + ::wglDeleteContext(gl_context_); + gl_context_ = NULL; + } + // release the hDC obtained through GetDC(). + ::ReleaseDC(window_, device_context_); + device_context_ = NULL; + window_ = NULL; + } + DLOG(INFO) << "Renderer destroyed."; +} + +#endif // OS_WIN + +#ifdef OS_MACOSX + +Renderer::InitStatus RendererGL::InitPlatformSpecific( + const DisplayWindow& display, + bool /*off_screen*/) { + const DisplayWindowMac &display_platform = + static_cast<const DisplayWindowMac&>(display); + // TODO: Add support for off screen rendering on the Mac. + mac_agl_context_ = display_platform.agl_context(); + mac_cgl_context_ = display_platform.cgl_context(); + + return InitCommonGL(); +} + +void RendererGL::Destroy() { + DestroyCommonGL(); + // We only have to destroy agl contexts, + // cgl contexts are not owned by us. + if (mac_agl_context_) { + ::aglDestroyContext(mac_agl_context_); + mac_agl_context_ = NULL; + } +} + +#endif // OS_MACOSX + +#ifdef OS_LINUX +Renderer::InitStatus RendererGL::InitPlatformSpecific( + const DisplayWindow& display_window, + bool off_screen) { + const DisplayWindowLinux &display_platform = + static_cast<const DisplayWindowLinux&>(display_window); + Display *display = display_platform.display(); + Window window = display_platform.window(); + XWindowAttributes attributes; + ::XGetWindowAttributes(display, window, &attributes); + XVisualInfo visual_info_template; + visual_info_template.visualid = ::XVisualIDFromVisual(attributes.visual); + int visual_info_count = 0; + XVisualInfo *visual_info_list = ::XGetVisualInfo(display, VisualIDMask, + &visual_info_template, + &visual_info_count); + DCHECK(visual_info_list); + DCHECK_GT(visual_info_count, 0); + context_ = 0; + for (int i = 0; i < visual_info_count; ++i) { + context_ = ::glXCreateContext(display, visual_info_list + i, 0, + True); + if (context_) break; + } + ::XFree(visual_info_list); + if (!context_) { + DLOG(ERROR) << "Couldn't create GL context."; + return INITIALIZATION_ERROR; + } + display_ = display; + window_ = window; + if (!MakeCurrent()) { + ::glXDestroyContext(display, context_); + context_ = 0; + display_ = NULL; + window_ = 0; + DLOG(ERROR) << "Couldn't create GL context."; + return INITIALIZATION_ERROR; + } + + InitStatus init_status = InitCommonGL(); + if (init_status != SUCCESS) { + ::glXDestroyContext(display, context_); + context_ = 0; + display_ = NULL; + window_ = 0; + } + return init_status; +} + +void RendererGL::Destroy() { + DestroyCommonGL(); + if (display_) { + ::glXMakeCurrent(display_, 0, 0); + if (context_) { + ::glXDestroyContext(display_, context_); + context_ = 0; + } + display_ = NULL; + window_ = 0; + } +} + +#endif + +bool RendererGL::MakeCurrent() { +#ifdef OS_WIN + if (!device_context_ || !gl_context_) return false; + bool result = ::wglMakeCurrent(device_context_, gl_context_) != 0; + return result; +#endif +#ifdef OS_MACOSX + if (mac_cgl_context_ != NULL) { + ::CGLSetCurrentContext(mac_cgl_context_); + return true; + } else if (mac_agl_context_ != NULL) { + ::aglSetCurrentContext(mac_agl_context_); + return true; + } else { + return false; + } +#endif +#ifdef OS_LINUX + if (context_ != NULL) { + bool result = ::glXMakeCurrent(display_, window_, context_) == True; + return result; + } else { + return false; + } +#endif +} + +void RendererGL::PlatformSpecificClear(const Float4 &color, + bool color_flag, + float depth, + bool depth_flag, + int stencil, + bool stencil_flag) { + MakeCurrentLazy(); + ::glClearColor(color[0], color[1], color[2], color[3]); + ::glClearDepth(depth); + ::glClearStencil(stencil); + + ::glClear((color_flag ? GL_COLOR_BUFFER_BIT : 0) | + (depth_flag ? GL_DEPTH_BUFFER_BIT : 0) | + (stencil_flag ? GL_STENCIL_BUFFER_BIT : 0)); + CHECK_GL_ERROR(); +} + +// Updates the helper constant used for the D3D -> GL remapping. +// See effect_gles2.cc for details. +void RendererGL::UpdateHelperConstant(float width, float height) { + MakeCurrentLazy(); + // If render-targets are active, pass -1 to invert the Y axis. OpenGL uses + // a different viewport orientation than DX. Without the inversion, the + // output of render-target rendering will be upside down. + if (RenderSurfaceActive()) { + ::glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, + 0, + 1.0f / width, + -1.0f / height, + 2.0f, + -1.0f); + } else { + // Only apply the origin offset when rendering to the client area. + ::glProgramEnvParameter4fARB(GL_VERTEX_PROGRAM_ARB, + 0, + (1.0f + (2.0f * -dest_x_offset())) / width, + (-1.0f + (2.0f * dest_y_offset())) / height, + 2.0f, + 1.0f); + } + CHECK_GL_ERROR(); +} + +void RendererGL::SetViewportInPixels(int left, + int top, + int width, + int height, + float min_z, + float max_z) { + MakeCurrentLazy(); + int vieport_top = + RenderSurfaceActive() ? top : display_height() - top - height; + ::glViewport(left, vieport_top, width, height); + UpdateHelperConstant(static_cast<float>(width), static_cast<float>(height)); + + // If it's the full client area turn off scissor test for speed. + if (left == 0 && + top == 0 && + width == display_width() && + height == display_height()) { + ::glDisable(GL_SCISSOR_TEST); + } else { + ::glScissor(left, vieport_top, width, height); + ::glEnable(GL_SCISSOR_TEST); + } + ::glDepthRange(min_z, max_z); +} + +// Resizes the viewport. +void RendererGL::Resize(int width, int height) { + MakeCurrentLazy(); + SetClientSize(width, height); + CHECK_GL_ERROR(); +} + +bool RendererGL::GoFullscreen(const DisplayWindow& display, + int mode_id) { +#ifdef OS_LINUX + // This actually just switches the GLX context to the new window. The real + // work is in main_linux.cc. + const DisplayWindowLinux &display_platform = + static_cast<const DisplayWindowLinux&>(display); + display_ = display_platform.display(); + window_ = display_platform.window(); + if (!MakeCurrent()) { + return false; + } +#endif + fullscreen_ = true; + return true; +} + +bool RendererGL::CancelFullscreen(const DisplayWindow& display, + int width, int height) { +#ifdef OS_LINUX + // This actually just switches the GLX context to the old window. The real + // work is in main_linux.cc. + const DisplayWindowLinux &display_platform = + static_cast<const DisplayWindowLinux&>(display); + display_ = display_platform.display(); + window_ = display_platform.window(); + if (!MakeCurrent()) { + return false; + } +#endif + fullscreen_ = false; + return true; +} + +void RendererGL::GetDisplayModes(std::vector<DisplayMode> *modes) { +#ifdef OS_MACOSX + // Mac is supposed to call a different function in plugin_mac.mm instead. + DLOG(FATAL) << "Not supposed to be called"; +#endif + // On all other platforms this is unimplemented. Linux only supports + // DISPLAY_MODE_DEFAULT for now. + modes->clear(); +} + +bool RendererGL::GetDisplayMode(int id, DisplayMode *mode) { +#ifdef OS_MACOSX + // Mac is supposed to call a different function in plugin_mac.mm instead. + DLOG(FATAL) << "Not supposed to be called"; + return false; +#elif defined(OS_LINUX) + if (id == DISPLAY_MODE_DEFAULT) { + // Don't need to know any of this on Linux. + mode->Set(0, 0, 0, id); + return true; + } else { + // There are no other valid ids until we implement GetDisplayModes() and + // mode switching. + return false; + } +#else + return false; +#endif +} + +bool RendererGL::PlatformSpecificStartRendering() { + DLOG_FIRST_N(INFO, 10) << "RendererGL StartRendering"; + MakeCurrentLazy(); + + // Currently always returns true. + // Should be modified if current behavior changes. + CHECK_GL_ERROR(); + return true; +} + +// Clears the color, depth and stncil buffers and prepares GL for rendering +// the frame. +// Returns true on success. +bool RendererGL::PlatformSpecificBeginDraw() { + DLOG_FIRST_N(INFO, 10) << "RendererGL BeginDraw"; + + MakeCurrentLazy(); + + // Currently always returns true. + // Should be modified if current behavior changes. + CHECK_GL_ERROR(); + return true; +} + +// Assign the surface arguments to the renderer, and update the stack +// of pushed surfaces. +void RendererGL::SetRenderSurfacesPlatformSpecific( + const RenderSurface* surface, + const RenderDepthStencilSurface* surface_depth) { + // TODO: This routine re-uses a single global framebuffer object for + // all RenderSurface rendering. Because of the validation checks performed + // at attachment-change time, it may be more performant to create a pool + // of framebuffer objects with different attachment characterists and + // switch between them here. + MakeCurrentLazy(); + ::glBindFramebufferEXT(GL_FRAMEBUFFER, render_surface_framebuffer_); + if (!InstallFramebufferObjects(surface, surface_depth)) { + O3D_ERROR(service_locator()) + << "Failed to bind OpenGL render target objects:" + << surface->name() <<", "<< surface_depth->name(); + } + // RenderSurface rendering is performed with an inverted Y, so the front + // face winding must be changed to clock-wise. See comments for + // UpdateHelperConstant. + glFrontFace(GL_CW); +} + +void RendererGL::SetBackBufferPlatformSpecific() { + MakeCurrentLazy(); + // Bind the default context, and restore the default front-face winding. + ::glBindFramebufferEXT(GL_FRAMEBUFFER, 0); + glFrontFace(GL_CCW); +} + +// Executes a post rendering step +void RendererGL::PlatformSpecificEndDraw() { + DLOG_FIRST_N(INFO, 10) << "RendererGL EndDraw"; + DCHECK(IsCurrent()); +} + +// Swaps the buffers. +void RendererGL::PlatformSpecificFinishRendering() { + DLOG_FIRST_N(INFO, 10) << "RendererGL FinishRendering"; + DCHECK(IsCurrent()); + ::glFlush(); + CHECK_GL_ERROR(); +} + +void RendererGL::PlatformSpecificPresent() { + DLOG_FIRST_N(INFO, 10) << "RendererGL Present"; + DCHECK(IsCurrent()); +#ifdef OS_WIN + ::SwapBuffers(device_context_); +#endif +#ifdef OS_MACOSX +#ifdef USE_AGL_DOUBLE_BUFFER + if (mac_agl_context_) { + ::aglSwapBuffers(mac_agl_context_); + } +#endif +#endif +#ifdef OS_LINUX + ::glXSwapBuffers(display_, window_); +#endif +} + +StreamBank::Ref RendererGL::CreateStreamBank() { + return StreamBank::Ref(new StreamBankGL(service_locator())); +} + +Primitive::Ref RendererGL::CreatePrimitive() { + return Primitive::Ref(new PrimitiveGL(service_locator())); +} + +DrawElement::Ref RendererGL::CreateDrawElement() { + return DrawElement::Ref(new DrawElementGL(service_locator())); +} + +void RendererGL::SetStencilStates(GLenum face, + const StencilStates& stencil_state) { + DCHECK(IsCurrent()); + if (face == GL_FRONT_AND_BACK) { + ::glStencilFunc(stencil_state.func_, + stencil_ref_, + stencil_mask_[READ_MASK]); + ::glStencilOp(stencil_state.op_[StencilStates::FAIL_OP], + stencil_state.op_[StencilStates::ZFAIL_OP], + stencil_state.op_[StencilStates::PASS_OP]); + ::glStencilMask(stencil_mask_[WRITE_MASK]); + } else if (GLEW_VERSION_2_0) { + ::glStencilFuncSeparate(face, + stencil_state.func_, + stencil_ref_, + stencil_mask_[READ_MASK]); + ::glStencilOpSeparate(face, + stencil_state.op_[StencilStates::FAIL_OP], + stencil_state.op_[StencilStates::ZFAIL_OP], + stencil_state.op_[StencilStates::PASS_OP]); + ::glStencilMaskSeparate(face, + stencil_mask_[WRITE_MASK]); + } else if (GLEW_EXT_stencil_two_side) { + ::glEnable(GL_STENCIL_TEST_TWO_SIDE_EXT); + ::glActiveStencilFaceEXT(face); + ::glStencilFunc(stencil_state.func_, + stencil_ref_, + stencil_mask_[READ_MASK]); + ::glStencilOp(stencil_state.op_[StencilStates::FAIL_OP], + stencil_state.op_[StencilStates::ZFAIL_OP], + stencil_state.op_[StencilStates::PASS_OP]); + ::glStencilMask(stencil_mask_[WRITE_MASK]); + ::glDisable(GL_STENCIL_TEST_TWO_SIDE_EXT); + } + CHECK_GL_ERROR(); +} + +void RendererGL::ApplyDirtyStates() { + MakeCurrentLazy(); + DCHECK(IsCurrent()); + // Set blend settings. + if (alpha_blend_settings_changed_) { + if (separate_alpha_blend_enable_) { + if (GLEW_VERSION_1_4) { + ::glBlendFuncSeparate(blend_function_[SRC][RGB], + blend_function_[DST][RGB], + blend_function_[SRC][ALPHA], + blend_function_[DST][ALPHA]); + } else if (GLEW_EXT_blend_func_separate) { + ::glBlendFuncSeparateEXT(blend_function_[SRC][RGB], + blend_function_[DST][RGB], + blend_function_[SRC][ALPHA], + blend_function_[DST][ALPHA]); + } + if (GLEW_VERSION_2_0) { + ::glBlendEquationSeparate(blend_equation_[RGB], + blend_equation_[ALPHA]); + } else if (GLEW_EXT_blend_equation_separate) { + ::glBlendEquationSeparateEXT(blend_equation_[RGB], + blend_equation_[ALPHA]); + } + } else { + ::glBlendFunc(blend_function_[SRC][RGB], + blend_function_[DST][RGB]); + if (::glBlendEquation != NULL) + ::glBlendEquation(blend_equation_[RGB]); + } + alpha_blend_settings_changed_ = false; + } + + // Set alpha settings. + if (alpha_function_ref_changed_) { + ::glAlphaFunc(alpha_function_, alpha_ref_); + alpha_function_ref_changed_ = false; + } + + // Set stencil settings. + if (stencil_settings_changed_) { + if (separate_stencil_settings_enable_) { + SetStencilStates(GL_FRONT, stencil_settings_[FRONT]); + SetStencilStates(GL_BACK, stencil_settings_[BACK]); + } else { + SetStencilStates(GL_FRONT_AND_BACK, stencil_settings_[FRONT]); + } + stencil_settings_changed_ = false; + } + + if (polygon_offset_changed_) { + bool enable = (polygon_offset_factor_ != 0.f) || + (polygon_offset_bias_ != 0.f); + if (enable) { + ::glEnable(GL_POLYGON_OFFSET_POINT); + ::glEnable(GL_POLYGON_OFFSET_LINE); + ::glEnable(GL_POLYGON_OFFSET_FILL); + ::glPolygonOffset(polygon_offset_factor_, polygon_offset_bias_); + } else { + ::glDisable(GL_POLYGON_OFFSET_POINT); + ::glDisable(GL_POLYGON_OFFSET_LINE); + ::glDisable(GL_POLYGON_OFFSET_FILL); + } + polygon_offset_changed_ = false; + } + CHECK_GL_ERROR(); +} + +VertexBuffer::Ref RendererGL::CreateVertexBuffer() { + DLOG(INFO) << "RendererGL CreateVertexBuffer"; + MakeCurrentLazy(); + return VertexBuffer::Ref(new VertexBufferGL(service_locator())); +} + +IndexBuffer::Ref RendererGL::CreateIndexBuffer() { + DLOG(INFO) << "RendererGL CreateIndexBuffer"; + MakeCurrentLazy(); + return IndexBuffer::Ref(new IndexBufferGL(service_locator())); +} + +Effect::Ref RendererGL::CreateEffect() { + DLOG(INFO) << "RendererGL CreateEffect"; + MakeCurrentLazy(); + return Effect::Ref(new EffectGL(service_locator(), cg_context_)); +} + +Sampler::Ref RendererGL::CreateSampler() { + return Sampler::Ref(new SamplerGL(service_locator())); +} + +ParamCache* RendererGL::CreatePlatformSpecificParamCache() { + return new ParamCacheGL(semantic_manager_.Get(), this); +} + + +Texture2D::Ref RendererGL::CreatePlatformSpecificTexture2D( + int width, + int height, + Texture::Format format, + int levels, + bool enable_render_surfaces) { + DLOG(INFO) << "RendererGL CreateTexture2D"; + MakeCurrentLazy(); + return Texture2D::Ref(Texture2DGL::Create(service_locator(), + format, + levels, + width, + height, + enable_render_surfaces)); +} + +TextureCUBE::Ref RendererGL::CreatePlatformSpecificTextureCUBE( + int edge_length, + Texture::Format format, + int levels, + bool enable_render_surfaces) { + DLOG(INFO) << "RendererGL CreateTextureCUBE"; + MakeCurrentLazy(); + return TextureCUBE::Ref(TextureCUBEGL::Create(service_locator(), + format, + levels, + edge_length, + enable_render_surfaces)); +} + +RenderDepthStencilSurface::Ref RendererGL::CreateDepthStencilSurface( + int width, + int height) { + return RenderDepthStencilSurface::Ref( + new RenderDepthStencilSurfaceGL(service_locator(), + width, + height)); +} + +const int* RendererGL::GetRGBAUByteNSwizzleTable() { + static int swizzle_table[] = { 0, 1, 2, 3, }; + return swizzle_table; +} + +// This is a factory function for creating Renderer objects. Since +// we're implementing GL, we only ever return a GL renderer. +Renderer* Renderer::CreateDefaultRenderer(ServiceLocator* service_locator) { + return RendererGL::CreateDefault(service_locator); +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/renderer_gles2.h b/o3d/core/cross/gles2/renderer_gles2.h new file mode 100644 index 0000000..43ba395 --- /dev/null +++ b/o3d/core/cross/gles2/renderer_gles2.h @@ -0,0 +1,335 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of the RendererGL class that provides +// low-level access for O3D to graphics hardware using the OpenGL API +// and Cg Runtime. + +#ifndef O3D_CORE_CROSS_GLES2_RENDERER_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_RENDERER_GLES2_H_ + +#include "core/cross/gles2/gles2_headers.h" +#include <build/build_config.h> +#include "core/cross/renderer.h" +#include "core/cross/renderer_platform.h" +#include "core/cross/types.h" +#include "core/cross/state.h" + +namespace o3d { + +class Material; +class Effect; +class DrawEffect; +class SemanticManager; + +// Implements the genereric Renderer interface using OpenGL and the Cg Runtime. +class RendererGL : public Renderer { + public: + // Creates a default Renderer. + static RendererGL* CreateDefault(ServiceLocator* service_locator); + virtual ~RendererGL(); + + // Initialises the renderer for use, claiming hardware resources. + virtual InitStatus InitPlatformSpecific(const DisplayWindow& display, + bool off_screen); + + // Released all hardware resources. + virtual void Destroy(); + + // Overridden from Renderer. + virtual bool GoFullscreen(const DisplayWindow& display, + int mode_id); + + // Overridden from Renderer. + virtual bool CancelFullscreen(const DisplayWindow& display, + int width, int height); + + // Tells whether we're currently displayed fullscreen or not. + virtual bool fullscreen() const { + return fullscreen_; + } + + // Get a vector of the available fullscreen display modes. + // Clears *modes on error. + virtual void GetDisplayModes(std::vector<DisplayMode> *modes); + + // Get a single fullscreen display mode by id. + // Returns true on success, false on error. + virtual bool GetDisplayMode(int id, DisplayMode *mode); + + // Resizes the viewport in OpenGL. + virtual void Resize(int width, int height); + + // Creates a StreamBank, returning a platform specific implementation class. + virtual StreamBank::Ref CreateStreamBank(); + + // Creates a Primitive, returning a platform specific implementation class. + virtual Primitive::Ref CreatePrimitive(); + + // Creates a DrawElement, returning a platform specific implementation + // class. + virtual DrawElement::Ref CreateDrawElement(); + + // Creates and returns a GL specific float buffer. + virtual VertexBuffer::Ref CreateVertexBuffer(); + + // Creates and returns a GL specific integer buffer. + virtual IndexBuffer::Ref CreateIndexBuffer(); + + // Creates and returns a GL specific Effect object. + virtual Effect::Ref CreateEffect(); + + // Creates and returns a GL specific Sampler object. + virtual Sampler::Ref CreateSampler(); + + // Creates and returns a platform-specific RenderDepthStencilSurface object + // for use as a depth-stencil render target. + virtual RenderDepthStencilSurface::Ref CreateDepthStencilSurface( + int width, + int height); + + // Overridden from Renderer. + virtual const int* GetRGBAUByteNSwizzleTable(); + + // Makes this renderer active on the current thread if it is not active + // already. + void MakeCurrentLazy() { + if (!IsCurrent()) + MakeCurrent(); + } + + // Returns whether or not this renderer is active on the current thread. + // Don't worry, the "get" calls are el cheapo. + bool IsCurrent() { +#if defined(OS_MACOSX) + if ((mac_agl_context_ != NULL) && + (mac_agl_context_ == aglGetCurrentContext())) { + return true; + } else if ((mac_cgl_context_ != NULL) && + (mac_cgl_context_ == CGLGetCurrentContext())) { + return true; + } +#elif defined(OS_WIN) + if ((gl_context_ != NULL) && + (gl_context_ == wglGetCurrentContext())) { + return true; + } +#elif defined(OS_LINUX) + if ((context_ != NULL) && + (context_ == glXGetCurrentContext())) { + return true; + } +#else + Error: must port RendererGL::IsCurrent() to your platform. +#endif + return false; + } + + // Makes this renderer active on the current thread. + bool MakeCurrent(); + + inline CGcontext cg_context() const { return cg_context_; } + inline CGprofile cg_vertex_profile() const { return cg_vertex_profile_; } + inline CGprofile cg_fragment_profile() const { return cg_fragment_profile_; } + + protected: + // Keep the constructor protected so only factory methods can create + // renderers. + explicit RendererGL(ServiceLocator* service_locator); + + // Overridden from Renderer. + virtual bool PlatformSpecificBeginDraw(); + + // Overridden from Renderer. + virtual void PlatformSpecificEndDraw(); + + // Overridden from Renderer. + virtual bool PlatformSpecificStartRendering(); + + // Overridden from Renderer. + virtual void PlatformSpecificFinishRendering(); + + // Overridden from Renderer. + virtual void PlatformSpecificPresent(); + + // Overridden from Renderer. + virtual void PlatformSpecificClear(const Float4 &color, + bool color_flag, + float depth, + bool depth_flag, + int stencil, + bool stencil_flag); + + // Overridden from Renderer. + virtual ParamCache* CreatePlatformSpecificParamCache(); + + // Sets the viewport. This is the platform specific version. + void SetViewportInPixels(int left, + int top, + int width, + int height, + float min_z, + float max_z); + + // Overridden from Renderer. + virtual void SetBackBufferPlatformSpecific(); + + // Overridden from Renderer. + virtual void SetRenderSurfacesPlatformSpecific( + const RenderSurface* surface, + const RenderDepthStencilSurface* depth_surface); + + // Overridden from Renderer. + virtual Texture2D::Ref CreatePlatformSpecificTexture2D( + int width, + int height, + Texture::Format format, + int levels, + bool enable_render_surfaces); + + // Overridden from Renderer. + virtual TextureCUBE::Ref CreatePlatformSpecificTextureCUBE( + int edge_length, + Texture::Format format, + int levels, + bool enable_render_surfaces); + + // Overridden from Renderer. + virtual void ApplyDirtyStates(); + + private: + // Platform-independent GL initialization + InitStatus InitCommonGL(); + + // Platform-independent GL destruction + void DestroyCommonGL(); + + // Updates the helper constant used to remap D3D clip coordinates to GL ones. + void UpdateHelperConstant(float width, float height); + + ServiceDependency<SemanticManager> semantic_manager_; + + // Indicates we're rendering fullscreen rather than in the plugin region. + bool fullscreen_; + + +#ifdef OS_WIN + // Handle to the GL device. + HWND window_; + HDC device_context_; + HGLRC gl_context_; +#endif + +#ifdef OS_MACOSX + AGLContext mac_agl_context_; + CGLContextObj mac_cgl_context_; +#endif + +#ifdef OS_LINUX + Display *display_; + Window window_; + GLXContext context_; +#endif + + // Handle to the framebuffer-object used while rendering to off-screen + // targets. + GLuint render_surface_framebuffer_; + + // Cg Runtime variables. + CGcontext cg_context_; + CGprofile cg_vertex_profile_; + CGprofile cg_fragment_profile_; + + friend class AlphaReferenceHandler; + bool alpha_function_ref_changed_; + GLenum alpha_function_; + GLclampf alpha_ref_; + + friend class BlendFunctionHandler; + friend class BlendEquationHandler; + bool alpha_blend_settings_changed_; + bool separate_alpha_blend_enable_; + enum { + RGB, + ALPHA, + }; + enum { + SRC, + DST, + }; + GLenum blend_function_[2][2]; // SRC/DST, RGB/ALPHA + GLenum blend_equation_[2]; // RGB/ALPHA + + bool stencil_settings_changed_; + bool separate_stencil_settings_enable_; + + // States for Stencils + friend class StencilOperationHandler; + friend class StencilRefHandler; + friend class StencilMaskHandler; + struct StencilStates { + GLenum func_; + enum { + FAIL_OP, + ZFAIL_OP, + PASS_OP, + }; + int op_[3]; + }; + + enum { + FRONT, + BACK, + }; + StencilStates stencil_settings_[2]; + + enum { + READ_MASK, + WRITE_MASK, + }; + int stencil_mask_[2]; + int stencil_ref_; + + // States for PolygonOffset + friend class PolygonOffset1Handler; + friend class PolygonOffset2Handler; + bool polygon_offset_changed_; + float polygon_offset_factor_; + float polygon_offset_bias_; + + // Sets the stencils states for either front, back or both facing polys. + void SetStencilStates(GLenum face, const StencilStates& stencil_states); +}; + +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_RENDERER_GLES2_H_ diff --git a/o3d/core/cross/gles2/sampler_gles2.cc b/o3d/core/cross/gles2/sampler_gles2.cc new file mode 100644 index 0000000..f275254 --- /dev/null +++ b/o3d/core/cross/gles2/sampler_gles2.cc @@ -0,0 +1,217 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the implementation for SamplerGL. + +#include "core/cross/gles2/gles2_headers.h" +#include "core/cross/error.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/sampler_gles2.h" + +namespace o3d { + +SamplerGL::SamplerGL(ServiceLocator* service_locator) + : Sampler(service_locator), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())) { +} + +SamplerGL::~SamplerGL() { +} + +namespace { + +unsigned int GLAddressMode(Sampler::AddressMode o3d_mode, + unsigned int default_mode) { + unsigned int gl_mode = default_mode; + switch (o3d_mode) { + case Sampler::WRAP: + gl_mode = GL_REPEAT; + break; + case Sampler::MIRROR: + gl_mode = GL_MIRRORED_REPEAT; + break; + case Sampler::CLAMP: + gl_mode = GL_CLAMP_TO_EDGE; + break; + case Sampler::BORDER: + gl_mode = GL_CLAMP_TO_BORDER; + break; + default: + DLOG(ERROR) << "Unknown Address mode " << static_cast<int>(o3d_mode); + break; + } + return gl_mode; +} + +unsigned int GLMinFilter(Sampler::FilterType o3d_filter, + Sampler::FilterType mip_filter) { + switch (o3d_filter) { + case Sampler::NONE: + return GL_NEAREST; + case Sampler::POINT: + if (mip_filter == Sampler::NONE) + return GL_NEAREST; + else if (mip_filter == Sampler::POINT) + return GL_NEAREST_MIPMAP_NEAREST; + else if (mip_filter == Sampler::LINEAR) + return GL_NEAREST_MIPMAP_LINEAR; + else if (mip_filter == Sampler::ANISOTROPIC) + return GL_NEAREST_MIPMAP_LINEAR; + case Sampler::ANISOTROPIC: // Anisotropy is handled in SetTextureAndStates + case Sampler::LINEAR: + if (mip_filter == Sampler::NONE) + return GL_LINEAR; + else if (mip_filter == Sampler::POINT) + return GL_LINEAR_MIPMAP_NEAREST; + else if (mip_filter == Sampler::LINEAR) + return GL_LINEAR_MIPMAP_LINEAR; + else if (mip_filter == Sampler::ANISOTROPIC) + return GL_LINEAR_MIPMAP_LINEAR; + } + // fall through + DLOG(ERROR) << "Unknown filter " << static_cast<int>(o3d_filter); + DCHECK(false); + return GL_NONE; +} + +unsigned int GLMagFilter(Sampler::FilterType o3d_filter) { + switch (o3d_filter) { + case Sampler::NONE: + case Sampler::POINT: + return GL_NEAREST; + case Sampler::LINEAR: + case Sampler::ANISOTROPIC: + return GL_LINEAR; + default: + DLOG(ERROR) << "Unknown filter " << static_cast<int>(o3d_filter); + return GL_LINEAR; + } +} + +GLenum GLTextureTarget(Texture* texture) { + if (texture->IsA(Texture2D::GetApparentClass())) { + return GL_TEXTURE_2D; + } else if (texture->IsA(TextureCUBE::GetApparentClass())) { + return GL_TEXTURE_CUBE_MAP; + } else { + DLOG(ERROR) << "Unknown texture target"; + return 0; + } +} + +} // namespace + +void SamplerGL::SetTextureAndStates(CGparameter cg_param) { + // Get the texture object associated with this sampler. + Texture* texture_object = texture(); + + if (!texture_object) { + texture_object = renderer_->error_texture(); + if (!texture_object) { + O3D_ERROR(service_locator()) + << "Missing texture for sampler " << name(); + texture_object = renderer_->fallback_error_texture(); + } + } + + if (!renderer_->SafeToBindTexture(texture_object)) { + O3D_ERROR(renderer_->service_locator()) + << "Attempt to bind texture, " << texture_object->name() + << " when drawing to same texture as a RenderSurface"; + texture_object = renderer_->error_texture(); + } + + GLuint handle = static_cast<GLuint>(reinterpret_cast<intptr_t>( + texture_object->GetTextureHandle())); + if (handle) { + cgGLSetTextureParameter(cg_param, handle); + cgGLEnableTextureParameter(cg_param); + } else { + cgGLSetTextureParameter(cg_param, 0); + cgGLDisableTextureParameter(cg_param); + return; + } + + // TODO: this is a slow check and needs to be moved to initialization + // time. + GLenum target = GLTextureTarget(texture_object); + + if (target) { + GLenum texture_unit = cgGLGetTextureEnum(cg_param); + ::glActiveTextureARB(texture_unit); + glBindTexture(target, handle); + glTexParameteri(target, + GL_TEXTURE_WRAP_S, + GLAddressMode(address_mode_u(), GL_REPEAT)); + glTexParameteri(target, + GL_TEXTURE_WRAP_T, + GLAddressMode(address_mode_v(), GL_REPEAT)); + if (texture_object->IsA(TextureCUBE::GetApparentClass())) { + glTexParameteri(target, + GL_TEXTURE_WRAP_R, + GLAddressMode(address_mode_w(), GL_REPEAT)); + } + glTexParameteri(target, + GL_TEXTURE_MIN_FILTER, + GLMinFilter(min_filter(), mip_filter())); + glTexParameteri(target, + GL_TEXTURE_MAG_FILTER, + GLMagFilter(mag_filter())); + + Float4 color = border_color(); + GLfloat gl_color[4] = {color[0], color[1], color[2], color[3]}; + glTexParameterfv(target, GL_TEXTURE_BORDER_COLOR, gl_color); + + // Check for anisotropic texture filtering. + if (GLEW_EXT_texture_filter_anisotropic) { + int gl_max_anisotropy = + (min_filter() == ANISOTROPIC) ? max_anisotropy() : 1; + glTexParameteri(target, GL_TEXTURE_MAX_ANISOTROPY_EXT, gl_max_anisotropy); + } + } +} + +void SamplerGL::ResetTexture(CGparameter cg_param) { + Texture* the_texture = texture(); + if (the_texture) { + // TODO: this is a slow check and needs to be moved to initialization + // time. + GLenum target = GLTextureTarget(the_texture); + if (target) { + GLenum texture_unit = cgGLGetTextureEnum(cg_param); + glActiveTextureARB(texture_unit); + glBindTexture(target, 0); + } + } +} +} // namespace o3d diff --git a/o3d/core/cross/gles2/sampler_gles2.h b/o3d/core/cross/gles2/sampler_gles2.h new file mode 100644 index 0000000..4da4be8 --- /dev/null +++ b/o3d/core/cross/gles2/sampler_gles2.h @@ -0,0 +1,65 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the class declaration for SamplerGL. + +#ifndef O3D_CORE_CROSS_GLES2_SAMPLER_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_SAMPLER_GLES2_H_ + +#include "core/cross/sampler.h" + +namespace o3d { + +class RendererGL; + +// SamplerGL is an implementation of the Sampler object for GL. +class SamplerGL : public Sampler { + public: + explicit SamplerGL(ServiceLocator* service_locator); + virtual ~SamplerGL(); + + // Sets the gl texture and sampler states. + void SetTextureAndStates(CGparameter cg_param); + + // Unbinds the GL texture. + void ResetTexture(CGparameter cg_param); + + private: + + RendererGL* renderer_; + + DISALLOW_COPY_AND_ASSIGN(SamplerGL); +}; +} // namespace o3d + + +#endif // O3D_CORE_CROSS_GLES2_SAMPLER_GLES2_H_ diff --git a/o3d/core/cross/gles2/stream_bank_gles2.cc b/o3d/core/cross/gles2/stream_bank_gles2.cc new file mode 100644 index 0000000..70f6b2a --- /dev/null +++ b/o3d/core/cross/gles2/stream_bank_gles2.cc @@ -0,0 +1,201 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the definition of StreamBankGL. + +#include <algorithm> + +#include "core/cross/stream.h" +#include "core/cross/error.h" +#include "core/cross/gles2/buffer_gles2.h" +#include "core/cross/gles2/effect_gles2.h" +#include "core/cross/gles2/stream_bank_gles2.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/draw_element_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" +#include "Cg/cgGL.h" + +// Someone defines min, conflicting with std::min +#ifdef min +#undef min +#endif + +namespace o3d { + +namespace { + +// Converts from a Field datatype to a suitable GL type +GLenum GLDataType(const Field& field) { + if (field.IsA(FloatField::GetApparentClass())) { + return GL_FLOAT; + } else if (field.IsA(UByteNField::GetApparentClass())) { + switch (field.num_components()) { + case 4: + return GL_UNSIGNED_BYTE; + } + } + DLOG(ERROR) << "Unknown Stream DataType"; + return GL_INVALID_ENUM; +} + +} // anonymous namespace + +// Number of times to log a repeated event before giving up. +const int kNumLoggedEvents = 5; + +// StreamBankGL functions ------------------------------------------------------ + +StreamBankGL::StreamBankGL(ServiceLocator* service_locator) + : StreamBank(service_locator) { + DLOG(INFO) << "StreamBankGL Construct"; +} + +StreamBankGL::~StreamBankGL() { + DLOG(INFO) << "StreamBankGL Destruct"; +} + +bool StreamBankGL::CheckForMissingVertexStreams( + ParamCacheGL::VaryingParameterMap& varying_map, + Stream::Semantic* missing_semantic, + int* missing_semantic_index) { + DCHECK(missing_semantic); + DCHECK(missing_semantic_index); + DLOG(INFO) << "StreamBankGL InsertMissingVertexStreams"; + // Match CG_VARYING parameters to Buffers with the matching semantics. + ParamCacheGL::VaryingParameterMap::iterator i; + for (i = varying_map.begin(); i != varying_map.end(); ++i) { + CGparameter cg_param = i->first; + const char* semantic_string = cgGetParameterSemantic(cg_param); + int attr = SemanticNameToGLVertexAttribute(semantic_string); + int index = 0; + Stream::Semantic semantic = GLVertexAttributeToStream(attr, &index); + int stream_index = FindVertexStream(semantic, index); + if (stream_index >= 0) { + // record the matched stream into the varying parameter map for later + // use by StreamBankGL::Draw(). + i->second = stream_index; + DLOG(INFO) + << "StreamBankGL Matched CG_PARAMETER \"" + << cgGetParameterName(cg_param) << " : " + << semantic_string << "\" to stream " + << stream_index << " \"" + << vertex_stream_params_.at( + stream_index)->stream().field().buffer()->name() + << "\""; + } else { + // no matching stream was found. + *missing_semantic = semantic; + *missing_semantic_index = index; + return false; + } + } + CHECK_GL_ERROR(); + return true; +} + +bool StreamBankGL::BindStreamsForRendering( + const ParamCacheGL::VaryingParameterMap& varying_map, + unsigned int* max_vertices) { + *max_vertices = UINT_MAX; + // Loop over varying params setting up the streams. + ParamCacheGL::VaryingParameterMap::const_iterator i; + for (i = varying_map.begin(); i != varying_map.end(); ++i) { + const Stream& stream = vertex_stream_params_.at(i->second)->stream(); + const Field& field = stream.field(); + GLenum type = GLDataType(field); + if (type == GL_INVALID_ENUM) { + // TODO: support other kinds of buffers. + O3D_ERROR(service_locator()) + << "unsupported field of type '" << field.GetClassName() + << "' on StreamBank '" << name() << "'"; + return false; + } + VertexBufferGL *vbuffer = down_cast<VertexBufferGL*>(field.buffer()); + if (!vbuffer) { + O3D_ERROR(service_locator()) + << "stream has no buffer in StreamBank '" << name() << "'"; + return false; + } + // TODO support all data types and packings here. Currently it + // only supports GL_FLOAT buffers, but buffers of GL_HALF and GL_INT are + // also possible as streamed parameter inputs. + GLint element_count = field.num_components(); + if (element_count > 4) { + element_count = 0; + DLOG_FIRST_N(ERROR, kNumLoggedEvents) + << "Unable to find stream for CGparameter: " + << cgGetParameterName(i->first); + } + + // In the num_elements = 1 case we want to do the D3D stride = 0 thing. + // but see below. + if (vbuffer->num_elements() == 1) { + // TODO: passing a stride of 0 has a different meaning in GL + // (compute a stride as if it was packed) than in DX (re-use the vertex + // over and over again). The equivalent of the DX behavior is by + // disabling the vertex array, and setting a constant value. Currently, + // this just avoids de-referencing outside of the vertex buffer, but it + // doesn't set the proper value: we'd need to map the buffer, get the + // value, and unmap it (slow !!). A better solution is to disallow 0 + // stride at the API level, and instead maybe provide a way to pss a + // constant value - but the DX version relies on being able to pass a 0 + // stride, so the whole thing needs a bit of rewrite. + cgGLDisableClientState(i->first); + } else { + glBindBufferARB(GL_ARRAY_BUFFER, vbuffer->gl_buffer()); + cgGLSetParameterPointer(i->first, + element_count, + GLDataType(field), + vbuffer->stride(), + BUFFER_OFFSET(field.offset())); + cgGLEnableClientState(i->first); + *max_vertices = std::min(*max_vertices, stream.GetMaxVertices()); + } + } + return true; +} + +// private member functions ---------------------------------------------------- + +// Searches the array of streams and returns the index of the stream that +// matches the semantic and index pair. if no match was found, return "-1" +int StreamBankGL::FindVertexStream(Stream::Semantic semantic, int index) { + for (unsigned ii = 0; ii < vertex_stream_params_.size(); ++ii) { + const Stream& stream = vertex_stream_params_[ii]->stream(); + if (stream.semantic() == semantic && stream.semantic_index() == index) { + return static_cast<int>(ii); + } + } + return -1; +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/stream_bank_gles2.h b/o3d/core/cross/gles2/stream_bank_gles2.h new file mode 100644 index 0000000..7fd8702 --- /dev/null +++ b/o3d/core/cross/gles2/stream_bank_gles2.h @@ -0,0 +1,72 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declaration of the StreamBankGL class. + +#ifndef O3D_CORE_CROSS_GLES2_STREAM_BANK_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_STREAM_BANK_GLES2_H_ + +#include <map> +#include "core/cross/stream_bank.h" +#include "core/cross/gles2/param_cache_gles2.h" + +namespace o3d { + +// StreamBankGL is the OpenGL implementation of the StreamBank. +class StreamBankGL : public StreamBank { + public: + explicit StreamBankGL(ServiceLocator* service_locator); + virtual ~StreamBankGL(); + + // Sets the streams for rendering. + // Parameter: + // varying_map: Map of streams. + // max_vertrices: pointer to variable to receive the maximum vertices + // the streams can render. + // Returns: + // true if all streams were bound. + bool BindStreamsForRendering( + const ParamCacheGL::VaryingParameterMap& varying_map, + unsigned int* max_vertices); + + // Checks for all required streams before rendering. + bool CheckForMissingVertexStreams( + ParamCacheGL::VaryingParameterMap& varying_map, + Stream::Semantic* missing_semantic, + int* missing_semantic_index); + + private: + int FindVertexStream(Stream::Semantic semantic, int index); +}; +} // o3d + +#endif // O3D_CORE_CROSS_GLES2_STREAM_BANK_GLES2_H_ diff --git a/o3d/core/cross/gles2/texture_gles2.cc b/o3d/core/cross/gles2/texture_gles2.cc new file mode 100644 index 0000000..4fd9ae9 --- /dev/null +++ b/o3d/core/cross/gles2/texture_gles2.cc @@ -0,0 +1,875 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// Implementations of the abstract Texture2D and TextureCUBE classes using +// the OpenGL graphics API. + +#include "core/cross/gles2/gles2_headers.h" +#include "core/cross/error.h" +#include "core/cross/types.h" +#include "core/cross/pointer_utils.h" +#include "core/cross/gles2/renderer_gles2.h" +#include "core/cross/gles2/render_surface_gles2.h" +#include "core/cross/gles2/texture_gles2.h" +#include "core/cross/gles2/utils_gles2.h" +#include "core/cross/gles2/utils_gles2-inl.h" + +namespace o3d { + +namespace { + +Texture::RGBASwizzleIndices g_gl_abgr32f_swizzle_indices = {0, 1, 2, 3}; + +} // anonymous namespace. + +// Converts an O3D texture format to a GL texture format. +// Input is 'format'. +// GL has 2 notions of the format: +// - the internal format which describes how the format should be stored on the +// GPU +// - the (format, type) pair which describes how the input data to glTexImage2D +// is laid out. If format is 0, the data is compressed and needs to be passed +// to glCompressedTexImage2D. +// The internal format is returned in internal_format. +// The format is the return value of the function. +// The type is returned in data_type. +static GLenum GLFormatFromO3DFormat(Texture::Format format, + GLenum *internal_format, + GLenum *data_type) { + switch (format) { + case Texture::XRGB8: { + *internal_format = GL_RGB; + *data_type = GL_UNSIGNED_BYTE; + return GL_BGRA; + } + case Texture::ARGB8: { + *internal_format = GL_RGBA; + *data_type = GL_UNSIGNED_BYTE; + return GL_BGRA; + } + case Texture::ABGR16F: { + *internal_format = GL_RGBA16F_ARB; + *data_type = GL_HALF_FLOAT_ARB; + return GL_RGBA; + } + case Texture::R32F: { + *internal_format = GL_LUMINANCE32F_ARB; + *data_type = GL_FLOAT; + return GL_LUMINANCE; + } + case Texture::ABGR32F: { + *internal_format = GL_RGBA32F_ARB; + *data_type = GL_FLOAT; + return GL_BGRA; + } + case Texture::DXT1: { + if (GL_EXT_texture_compression_s3tc) { + *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; + *data_type = 0; + return 0; + } else { + // TODO: we need to convert DXT1 -> RGBA8 but keep around the + // pixels so that we can read them back (we won't try to convert back + // to DXTC). + LOG(ERROR) << "DXT1 compressed textures not supported yet."; + *internal_format = 0; + *data_type = GL_BYTE; + return 0; + } + } + case Texture::DXT3: { + if (GL_EXT_texture_compression_s3tc) { + *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; + *data_type = 0; + return 0; + } else { + // TODO: we need to convert DXT3 -> RGBA8 but keep around the + // pixels so that we can read them back (we won't try to convert back + // to DXTC). + LOG(ERROR) << "DXT3 compressed textures not supported yet."; + *internal_format = 0; + *data_type = GL_BYTE; + return 0; + } + } + case Texture::DXT5: { + if (GL_EXT_texture_compression_s3tc) { + *internal_format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; + *data_type = 0; + return 0; + } else { + // TODO: we need to convert DXT3 -> RGBA8 but keep around the + // pixels so that we can read them back (we won't try to convert back + // to DXTC). + LOG(ERROR) << "DXT5 compressed textures not supported yet."; + *internal_format = 0; + *data_type = GL_BYTE; + return 0; + } + } + case Texture::UNKNOWN_FORMAT: + break; + } + // failed to find a matching format + LOG(ERROR) << "Unrecognized Texture format type."; + *internal_format = 0; + *data_type = 0; + return 0; +} + +// Updates a GL image from a bitmap, rescaling if necessary. +static bool UpdateGLImageFromBitmap(GLenum target, + unsigned int level, + TextureCUBE::CubeFace face, + const Bitmap &bitmap, + bool resize_to_pot) { + DCHECK(bitmap.image_data()); + unsigned int mip_width = std::max(1U, bitmap.width() >> level); + unsigned int mip_height = std::max(1U, bitmap.height() >> level); + const uint8 *mip_data = bitmap.GetMipData(level); + size_t mip_size = + image::ComputeBufferSize(mip_width, mip_height, bitmap.format()); + scoped_array<uint8> temp_data; + if (resize_to_pot) { + DCHECK(!Texture::IsCompressedFormat(bitmap.format())); + unsigned int pot_width = + std::max(1U, image::ComputePOTSize(bitmap.width()) >> level); + unsigned int pot_height = + std::max(1U, image::ComputePOTSize(bitmap.height()) >> level); + size_t pot_size = image::ComputeBufferSize(pot_width, pot_height, + bitmap.format()); + temp_data.reset(new uint8[pot_size]); + image::Scale(mip_width, mip_height, bitmap.format(), mip_data, + pot_width, pot_height, temp_data.get(), + image::ComputePitch(bitmap.format(), pot_width)); + mip_width = pot_width; + mip_height = pot_height; + mip_size = pot_size; + mip_data = temp_data.get(); + } + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(bitmap.format(), &gl_internal_format, + &gl_data_type); + if (gl_format) { + glTexSubImage2D(target, level, 0, 0, mip_width, mip_height, + gl_format, gl_data_type, mip_data); + } else { + glCompressedTexSubImage2D(target, level, 0, 0, mip_width, mip_height, + gl_internal_format, mip_size, mip_data); + } + return glGetError() == GL_NO_ERROR; +} + +// Creates the array of GL images for a particular face and upload the pixel +// data from the bitmap. +static bool CreateGLImages(GLenum target, + GLenum internal_format, + GLenum gl_format, + GLenum type, + TextureCUBE::CubeFace face, + Texture::Format format, + int levels, + int width, + int height, + bool resize_to_pot) { + unsigned int mip_width = width; + unsigned int mip_height = height; + if (resize_to_pot) { + mip_width = image::ComputePOTSize(mip_width); + mip_height = image::ComputePOTSize(mip_height); + } + // glCompressedTexImage2D does't accept NULL as a parameter, so we need + // to pass in some data. If we can pass in the original pixel data, we'll + // do that, otherwise we'll pass an empty buffer. In that case, prepare it + // here once for all. + scoped_array<uint8> temp_data; + size_t size = image::ComputeBufferSize(mip_width, mip_height, format); + temp_data.reset(new uint8[size]); + memset(temp_data.get(), 0, size); + + for (int i = 0; i < levels; ++i) { + if (gl_format) { + glTexImage2D(target, i, internal_format, mip_width, mip_height, + 0, gl_format, type, temp_data.get()); + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "glTexImage2D failed"; + return false; + } + } else { + size_t mip_size = image::ComputeBufferSize(mip_width, mip_height, format); + glCompressedTexImage2DARB(target, i, internal_format, mip_width, + mip_height, 0, mip_size, temp_data.get()); + if (glGetError() != GL_NO_ERROR) { + DLOG(ERROR) << "glCompressedTexImage2D failed"; + return false; + } + } + mip_width = std::max(1U, mip_width >> 1); + mip_height = std::max(1U, mip_height >> 1); + } + return true; +} + +// Texture2DGL ----------------------------------------------------------------- + +// Constructs a 2D texture object from an existing OpenGL 2D texture. +// NOTE: the Texture2DGL now owns the GL texture and will destroy it on exit. +Texture2DGL::Texture2DGL(ServiceLocator* service_locator, + GLint texture, + Texture::Format format, + int levels, + int width, + int height, + bool resize_to_pot, + bool enable_render_surfaces) + : Texture2D(service_locator, + width, + height, + format, + levels, + enable_render_surfaces), + resize_to_pot_(resize_to_pot), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())), + gl_texture_(texture), + backing_bitmap_(Bitmap::Ref(new Bitmap(service_locator))), + has_levels_(0), + locked_levels_(0) { + DLOG(INFO) << "Texture2DGL Construct from GLint"; + DCHECK_NE(format, Texture::UNKNOWN_FORMAT); +} + +// Creates a new texture object from scratch. +Texture2DGL* Texture2DGL::Create(ServiceLocator* service_locator, + Texture::Format format, + int levels, + int width, + int height, + bool enable_render_surfaces) { + DLOG(INFO) << "Texture2DGL Create"; + DCHECK_NE(format, Texture::UNKNOWN_FORMAT); + RendererGL *renderer = static_cast<RendererGL *>( + service_locator->GetService<Renderer>()); + renderer->MakeCurrentLazy(); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format, + &gl_internal_format, + &gl_data_type); + if (gl_internal_format == 0) { + DLOG(ERROR) << "Unsupported format in Texture2DGL::Create."; + return NULL; + } + + bool resize_to_pot = !renderer->supports_npot() && + !image::IsPOT(width, height); + + // Creates the OpenGL texture object, with all the required mip levels. + GLuint gl_texture = 0; + glGenTextures(1, &gl_texture); + glBindTexture(GL_TEXTURE_2D, gl_texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, + levels - 1); + + if (!CreateGLImages(GL_TEXTURE_2D, gl_internal_format, gl_format, + gl_data_type, TextureCUBE::FACE_POSITIVE_X, + format, levels, width, height, resize_to_pot)) { + DLOG(ERROR) << "Failed to create texture images."; + glDeleteTextures(1, &gl_texture); + return NULL; + } + glTexParameteri(GL_TEXTURE_2D, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + + GLint gl_width; + GLint gl_height; + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_WIDTH, &gl_width); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_HEIGHT, &gl_height); + + DLOG(INFO) << "Created 2D texture (size=" << gl_width << "x" << gl_height + << ", GLuint=" << gl_texture << ")"; + Texture2DGL *texture = new Texture2DGL(service_locator, + gl_texture, + format, + levels, + width, + height, + resize_to_pot, + enable_render_surfaces); + + // If the hardware does not support npot textures, allocate a 0-initialized + // mip-chain here for use during Texture2DGL::Lock. + if (resize_to_pot) { + texture->backing_bitmap_->Allocate(format, width, height, levels, + Bitmap::IMAGE); + texture->has_levels_ = (1 << levels) - 1; + } + CHECK_GL_ERROR(); + return texture; +} + +void Texture2DGL::UpdateBackedMipLevel(unsigned int level) { + DCHECK_LT(static_cast<int>(level), levels()); + DCHECK(backing_bitmap_->image_data()); + DCHECK_EQ(backing_bitmap_->width(), static_cast<unsigned int>(width())); + DCHECK_EQ(backing_bitmap_->height(), static_cast<unsigned int>(height())); + DCHECK_EQ(backing_bitmap_->format(), format()); + renderer_->MakeCurrentLazy(); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + UpdateGLImageFromBitmap(GL_TEXTURE_2D, level, TextureCUBE::FACE_POSITIVE_X, + *backing_bitmap_.Get(), resize_to_pot_); +} + +Texture2DGL::~Texture2DGL() { + DLOG(INFO) << "Texture2DGL Destruct"; + if (gl_texture_) { + renderer_->MakeCurrentLazy(); + glDeleteTextures(1, &gl_texture_); + gl_texture_ = 0; + } + CHECK_GL_ERROR(); +} + +void Texture2DGL::SetRect(int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect on non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width = image::ComputeMipDimension(level, width()); + unsigned mip_height = image::ComputeMipDimension(level, height()); + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + DCHECK(backing_bitmap_->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap_->SetRect( + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(level); + } else { + renderer_->MakeCurrentLazy(); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), &gl_internal_format, + &gl_data_type); + if (gl_format) { + if (src_pitch == image::ComputePitch(format(), src_width)) { + glTexSubImage2D(GL_TEXTURE_2D, level, + dst_left, dst_top, + src_width, src_height, + gl_format, + gl_data_type, + src_data); + } else { + int limit = src_height; + for (int yy = 0; yy < limit; ++yy) { + glTexSubImage2D(GL_TEXTURE_2D, level, + dst_left, dst_top + yy, + src_width, 1, + gl_format, + gl_data_type, + src_data); + src_data = AddPointerOffset<const void*>(src_data, src_pitch); + } + } + } else { + glCompressedTexSubImage2D( + GL_TEXTURE_2D, level, 0, 0, src_width, src_height, + gl_internal_format, + image::ComputeMipChainSize(src_width, src_height, format(), 1), + src_data); + } + } +} + +// Locks the given mipmap level of this texture for loading from main memory, +// and returns a pointer to the buffer. +bool Texture2DGL::PlatformSpecificLock( + int level, void** data, int* pitch, Texture::AccessMode mode) { + DLOG(INFO) << "Texture2DGL Lock"; + DCHECK(data); + DCHECK(pitch); + DCHECK_GE(level, 0); + DCHECK_LT(level, levels()); + renderer_->MakeCurrentLazy(); + if (!backing_bitmap_->image_data()) { + DCHECK_EQ(has_levels_, 0u); + backing_bitmap_->Allocate(format(), width(), height(), levels(), + Bitmap::IMAGE); + } + *data = backing_bitmap_->GetMipData(level); + unsigned int mip_width = image::ComputeMipDimension(level, width()); + if (!IsCompressed()) { + *pitch = image::ComputePitch(format(), mip_width); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } + if (mode != kWriteOnly && !HasLevel(level)) { + DCHECK(!resize_to_pot_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), + &gl_internal_format, + &gl_data_type); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + glGetTexImage(GL_TEXTURE_2D, level, gl_format, gl_data_type, *data); + has_levels_ |= 1 << level; + } + locked_levels_ |= 1 << level; + CHECK_GL_ERROR(); + return true; +} + +// Unlocks the given mipmap level of this texture, uploading the main memory +// data buffer to GL. +bool Texture2DGL::PlatformSpecificUnlock(int level) { + DLOG(INFO) << "Texture2DGL Unlock"; + DCHECK_GE(level, 0); + DCHECK_LT(level, levels()); + if (LockedMode(level) != kReadOnly) { + renderer_->MakeCurrentLazy(); + UpdateBackedMipLevel(level); + } + locked_levels_ &= ~(1 << level); + if (!resize_to_pot_ && (locked_levels_ == 0)) { + backing_bitmap_->FreeData(); + has_levels_ = 0; + } + CHECK_GL_ERROR(); + return true; +} + +RenderSurface::Ref Texture2DGL::PlatformSpecificGetRenderSurface( + int mip_level) { + DCHECK_LT(mip_level, levels()); + if (!render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to get RenderSurface from non-render-surface-enabled" + << " Texture: " << name(); + return RenderSurface::Ref(NULL); + } + + if (mip_level >= levels() || mip_level < 0) { + O3D_ERROR(service_locator()) + << "Attempting to access non-existent mip_level " << mip_level + << " in render-target texture \"" << name() << "\"."; + return RenderSurface::Ref(NULL); + } + + return RenderSurface::Ref(new RenderSurfaceGL( + service_locator(), + width()>> mip_level, + height() >> mip_level, + 0, + mip_level, + this)); +} + +const Texture::RGBASwizzleIndices& Texture2DGL::GetABGR32FSwizzleIndices() { + return g_gl_abgr32f_swizzle_indices; +} + +// TextureCUBEGL --------------------------------------------------------------- + +// Creates a texture from a pre-existing GL texture object. +TextureCUBEGL::TextureCUBEGL(ServiceLocator* service_locator, + GLint texture, + Texture::Format format, + int levels, + int edge_length, + bool resize_to_pot, + bool enable_render_surfaces) + : TextureCUBE(service_locator, + edge_length, + format, + levels, + enable_render_surfaces), + resize_to_pot_(resize_to_pot), + renderer_(static_cast<RendererGL*>( + service_locator->GetService<Renderer>())), + gl_texture_(texture) { + DLOG(INFO) << "TextureCUBEGL Construct"; + for (int ii = 0; ii < static_cast<int>(NUMBER_OF_FACES); ++ii) { + backing_bitmaps_[ii] = Bitmap::Ref(new Bitmap(service_locator)); + has_levels_[ii] = 0; + locked_levels_[ii] = 0; + } +} + +TextureCUBEGL::~TextureCUBEGL() { + DLOG(INFO) << "TextureCUBEGL Destruct"; + if (gl_texture_) { + renderer_->MakeCurrentLazy(); + glDeleteTextures(1, &gl_texture_); + gl_texture_ = 0; + } + CHECK_GL_ERROR(); +} + +static const int kCubemapFaceList[] = { + GL_TEXTURE_CUBE_MAP_POSITIVE_X, + GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + GL_TEXTURE_CUBE_MAP_NEGATIVE_Z +}; + +// Create a new Cube texture from scratch. +TextureCUBEGL* TextureCUBEGL::Create(ServiceLocator* service_locator, + Texture::Format format, + int levels, + int edge_length, + bool enable_render_surfaces) { + DLOG(INFO) << "TextureCUBEGL Create"; + CHECK_GL_ERROR(); + RendererGL *renderer = static_cast<RendererGL *>( + service_locator->GetService<Renderer>()); + renderer->MakeCurrentLazy(); + + bool resize_to_pot = !renderer->supports_npot() && + !image::IsPOT(edge_length, edge_length); + + // Get gl formats + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format, + &gl_internal_format, + &gl_data_type); + if (gl_internal_format == 0) { + DLOG(ERROR) << "Unsupported format in TextureCUBEGL::Create."; + return NULL; + } + + // Creates the OpenGL texture object, with all the required mip levels. + GLuint gl_texture = 0; + glGenTextures(1, &gl_texture); + glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAX_LEVEL, + levels - 1); + + for (int face = 0; face < static_cast<int>(NUMBER_OF_FACES); ++face) { + CreateGLImages(kCubemapFaceList[face], gl_internal_format, + gl_format, gl_data_type, + static_cast<CubeFace>(face), + format, levels, edge_length, edge_length, + resize_to_pot); + } + glTexParameteri(GL_TEXTURE_CUBE_MAP, + GL_TEXTURE_MIN_FILTER, + GL_NEAREST_MIPMAP_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + // Create a new texture object, which initializes the base Texture class + // from the Bitmap information. + TextureCUBEGL* texture = new TextureCUBEGL(service_locator, + gl_texture, + format, + levels, + edge_length, + resize_to_pot, + enable_render_surfaces); + // If the hardware does not support npot textures, allocate a 0-initialized + // mip-chain here for use during TextureCUBEGL::Lock. + if (resize_to_pot) { + for (int face = 0; face < static_cast<int>(NUMBER_OF_FACES); ++face) { + texture->backing_bitmaps_[face]->Allocate( + format, edge_length, edge_length, levels, Bitmap::IMAGE); + texture->has_levels_[face] = (1 << levels) - 1; + } + } + CHECK_GL_ERROR(); + DLOG(INFO) << "Created cube map texture (GLuint=" << gl_texture << ")"; + return texture; +} + +void TextureCUBEGL::UpdateBackedMipLevel(unsigned int level, + TextureCUBE::CubeFace face) { + Bitmap* backing_bitmap = backing_bitmaps_[face].Get(); + DCHECK_LT(static_cast<int>(level), levels()); + DCHECK(backing_bitmap->image_data()); + DCHECK_EQ(backing_bitmap->width(), static_cast<unsigned int>(edge_length())); + DCHECK_EQ(backing_bitmap->height(), static_cast<unsigned int>(edge_length())); + DCHECK_EQ(backing_bitmap->format(), format()); + renderer_->MakeCurrentLazy(); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + UpdateGLImageFromBitmap(kCubemapFaceList[face], level, face, + *backing_bitmap, + resize_to_pot_); +} + +RenderSurface::Ref TextureCUBEGL::PlatformSpecificGetRenderSurface( + TextureCUBE::CubeFace face, + int mip_level) { + DCHECK_LT(mip_level, levels()); + if (!render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to get RenderSurface from non-render-surface-enabled" + << " Texture: " << name(); + return RenderSurface::Ref(NULL); + } + + if (mip_level >= levels() || mip_level < 0) { + O3D_ERROR(service_locator()) + << "Attempting to access non-existent mip_level " << mip_level + << " in render-target texture \"" << name() << "\"."; + return RenderSurface::Ref(NULL); + } + + return RenderSurface::Ref(new RenderSurfaceGL( + service_locator(), + edge_length() >> mip_level, + edge_length() >> mip_level, + kCubemapFaceList[face], + mip_level, + this)); +} + +void TextureCUBEGL::SetRect(TextureCUBE::CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned src_width, + unsigned src_height, + const void* src_data, + int src_pitch) { + if (level >= levels() || level < 0) { + O3D_ERROR(service_locator()) + << "Trying to SetRect non-existent level " << level + << " on Texture \"" << name() << "\""; + return; + } + if (render_surfaces_enabled()) { + O3D_ERROR(service_locator()) + << "Attempting to SetRect a render-target texture: " << name(); + return; + } + + unsigned mip_width = image::ComputeMipDimension(level, edge_length()); + unsigned mip_height = mip_width; + + if (dst_left + src_width > mip_width || + dst_top + src_height > mip_height) { + O3D_ERROR(service_locator()) + << "SetRect(" << level << ", " << dst_left << ", " << dst_top << ", " + << src_width << ", " << src_height << ") out of range for texture << \"" + << name() << "\""; + return; + } + + bool entire_rect = dst_left == 0 && dst_top == 0 && + src_width == mip_width && src_height == mip_height; + bool compressed = IsCompressed(); + + if (compressed && !entire_rect) { + O3D_ERROR(service_locator()) + << "SetRect must be full rectangle for compressed textures"; + return; + } + + if (resize_to_pot_) { + Bitmap* backing_bitmap = backing_bitmaps_[face].Get(); + DCHECK(backing_bitmap->image_data()); + DCHECK(!compressed); + // We need to update the backing mipmap and then use that to update the + // texture. + backing_bitmap->SetRect( + level, dst_left, dst_top, src_width, src_height, src_data, src_pitch); + UpdateBackedMipLevel(level, face); + } else { + // TODO(gman): Should this bind be using a FACE id? + renderer_->MakeCurrentLazy(); + glBindTexture(GL_TEXTURE_2D, gl_texture_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), &gl_internal_format, + &gl_data_type); + int gl_face = kCubemapFaceList[face]; + if (gl_format) { + if (src_pitch == image::ComputePitch(format(), src_width)) { + glTexSubImage2D(gl_face, level, + dst_left, dst_top, + src_width, src_height, + gl_format, + gl_data_type, + src_data); + } else { + int limit = src_height; + for (int yy = 0; yy < limit; ++yy) { + glTexSubImage2D(gl_face, level, + dst_left, dst_top + yy, + src_width, 1, + gl_format, + gl_data_type, + src_data); + src_data = AddPointerOffset<const void*>(src_data, src_pitch); + } + } + } else { + glCompressedTexSubImage2D( + gl_face, level, 0, 0, src_width, src_height, + gl_internal_format, + image::ComputeMipChainSize(src_width, src_height, format(), 1), + src_data); + } + } +} + +// Locks the given face and mipmap level of this texture for loading from +// main memory, and returns a pointer to the buffer. +bool TextureCUBEGL::PlatformSpecificLock( + CubeFace face, int level, void** data, int* pitch, + Texture::AccessMode mode) { + DLOG(INFO) << "TextureCUBEGL Lock"; + DCHECK_GE(level, 0); + DCHECK_LT(level, levels()); + renderer_->MakeCurrentLazy(); + Bitmap* backing_bitmap = backing_bitmaps_[face].Get(); + if (!backing_bitmap->image_data()) { + for (int i = 0; i < static_cast<int>(NUMBER_OF_FACES); ++i) { + DCHECK_EQ(has_levels_[i], 0u); + } + backing_bitmap->Allocate(format(), edge_length(), edge_length(), levels(), + Bitmap::IMAGE); + } + *data = backing_bitmap->GetMipData(level); + unsigned int mip_width = image::ComputeMipDimension(level, edge_length()); + if (!IsCompressed()) { + *pitch = image::ComputePitch(format(), mip_width); + } else { + unsigned blocks_across = (mip_width + 3) / 4; + unsigned bytes_per_block = format() == Texture::DXT1 ? 8 : 16; + unsigned bytes_per_row = bytes_per_block * blocks_across; + *pitch = bytes_per_row; + } + GLenum gl_target = kCubemapFaceList[face]; + if (mode != kWriteOnly && !HasLevel(face, level)) { + // TODO: add some API so we don't have to copy back the data if we + // will rewrite it all. + DCHECK(!resize_to_pot_); + GLenum gl_internal_format = 0; + GLenum gl_data_type = 0; + GLenum gl_format = GLFormatFromO3DFormat(format(), + &gl_internal_format, + &gl_data_type); + glBindTexture(GL_TEXTURE_CUBE_MAP, gl_texture_); + glGetTexImage(gl_target, level, gl_format, gl_data_type, *data); + has_levels_[face] |= 1 << level; + } + CHECK_GL_ERROR(); + + locked_levels_[face] |= 1 << level; + + return false; +} + +// Unlocks the given face and mipmap level of this texture. +bool TextureCUBEGL::PlatformSpecificUnlock(CubeFace face, int level) { + DLOG(INFO) << "TextureCUBEGL Unlock"; + DCHECK_GE(level, 0); + DCHECK_LT(level, levels()); + if (LockedMode(face, level) != kReadOnly) { + renderer_->MakeCurrentLazy(); + UpdateBackedMipLevel(level, face); + } + locked_levels_[face] &= ~(1 << level); + + if (!resize_to_pot_) { + // See if we can throw away the backing bitmap. + Bitmap* backing_bitmap = backing_bitmaps_[face].Get(); + bool has_locked_level = false; + for (int i = 0; i < static_cast<int>(NUMBER_OF_FACES); ++i) { + if (locked_levels_[i]) { + has_locked_level = true; + break; + } + } + if (!has_locked_level) { + backing_bitmap->FreeData(); + for (int i = 0; i < static_cast<int>(NUMBER_OF_FACES); ++i) { + has_levels_[i] = 0; + } + } + } + CHECK_GL_ERROR(); + return false; +} + +const Texture::RGBASwizzleIndices& TextureCUBEGL::GetABGR32FSwizzleIndices() { + return g_gl_abgr32f_swizzle_indices; +} + +} // namespace o3d diff --git a/o3d/core/cross/gles2/texture_gles2.h b/o3d/core/cross/gles2/texture_gles2.h new file mode 100644 index 0000000..b420eca --- /dev/null +++ b/o3d/core/cross/gles2/texture_gles2.h @@ -0,0 +1,243 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +// This file contains the declarations for Texture2DGL and TextureCUBEGL. + +#ifndef O3D_CORE_CROSS_GLES2_TEXTURE_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_TEXTURE_GLES2_H_ + +// Precompiled header comes before everything else. + +// Disable compiler warning for openGL calls that require a void* to +// be cast to a GLuint +#if defined(OS_WIN) +#pragma warning(disable : 4312) +#pragma warning(disable : 4311) +#endif + +#include "core/cross/bitmap.h" +#include "core/cross/texture.h" +#include "core/cross/types.h" + +namespace o3d { + +class RendererGL; + +// Texture2DGL ----------------------------------------------------------------- + +// Texture2DGL implements the Texture2D interface with OpenGL. +class Texture2DGL : public Texture2D { + public: + typedef SmartPointer<Texture2DGL> Ref; + + virtual ~Texture2DGL(); + + // Overridden from Texture2D + virtual void SetRect(int level, + unsigned left, + unsigned top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); + + // Creates a new Texture2DGL with the given specs. If the GL texture + // creation fails then it returns NULL otherwise it returns a pointer to the + // newly created Texture object. + // The created texture takes ownership of the bitmap data. + static Texture2DGL* Create(ServiceLocator* service_locator, + Texture::Format format, + int levels, + int width, + int height, + bool enable_render_surfaces); + + // Returns the implementation-specific texture handle for this texture. + void* GetTextureHandle() const { + return reinterpret_cast<void*>(gl_texture_); + } + + // Gets the GL texture handle. + GLuint gl_texture() const { return gl_texture_; } + + // Gets a RGBASwizzleIndices that contains a mapping from + // RGBA to the internal format used by the rendering API. + virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + + protected: + // Overridden from Texture2D + virtual bool PlatformSpecificLock( + int level, void** texture_data, int* pitch, AccessMode mode); + + // Overridden from Texture2D + virtual bool PlatformSpecificUnlock(int level); + + // Overridden from Texture2D + virtual RenderSurface::Ref PlatformSpecificGetRenderSurface(int mip_level); + + private: + // Initializes the Texture2DGL from a preexisting OpenGL texture handle + // and raw Bitmap data. + // The texture takes ownership of the bitmap data. + Texture2DGL(ServiceLocator* service_locator, + GLint texture, + Texture::Format format, + int levels, + int width, + int height, + bool resize_npot, + bool enable_render_surfaces); + + // Updates a mip level, sending it from the backing bitmap to GL, rescaling + // it if resize_to_pot_ is set. + void UpdateBackedMipLevel(unsigned int level); + + // Returns true if the backing bitmap has the data for the level. + bool HasLevel(unsigned int level) const { + DCHECK_LT(static_cast<int>(level), levels()); + return (has_levels_ & (1 << level)) != 0; + } + + // Whether or not this texture needs to be resized from NPOT to pot behind + // the scenes. + bool resize_to_pot_; + + RendererGL* renderer_; + + // The handle of the OpenGL texture object. + GLuint gl_texture_; + + // A bitmap used to back the NPOT textures on POT-only hardware, and to back + // the pixel buffer for Lock(). + Bitmap::Ref backing_bitmap_; + + // Bitfield that indicates mip levels that are currently stored in the + // backing bitmap. + unsigned int has_levels_; + + // Bitfield that indicates which levels are currently locked. + unsigned int locked_levels_; +}; + + +// TextureCUBEGL --------------------------------------------------------------- + +// TextureCUBEGL implements the TextureCUBE interface with OpenGL. +class TextureCUBEGL : public TextureCUBE { + public: + typedef SmartPointer<TextureCUBEGL> Ref; + virtual ~TextureCUBEGL(); + + // Create a new Cube texture from scratch. + static TextureCUBEGL* Create(ServiceLocator* service_locator, + Texture::Format format, + int levels, + int edge_length, + bool enable_render_surfaces); + + // Overridden from TextureCUBE + virtual void SetRect(CubeFace face, + int level, + unsigned dst_left, + unsigned dst_top, + unsigned width, + unsigned height, + const void* src_data, + int src_pitch); + + // Returns the implementation-specific texture handle for this texture. + virtual void* GetTextureHandle() const { + return reinterpret_cast<void*>(gl_texture_); + } + + // Gets the GL texture handle. + GLuint gl_texture() const { return gl_texture_; } + + // Gets a RGBASwizzleIndices that contains a mapping from + // RGBA to the internal format used by the rendering API. + virtual const RGBASwizzleIndices& GetABGR32FSwizzleIndices(); + + protected: + // Overridden from TextureCUBE + virtual bool PlatformSpecificLock( + CubeFace face, int level, void** texture_data, int* pitch, + AccessMode mode); + + // Overridden from TextureCUBE + virtual bool PlatformSpecificUnlock(CubeFace face, int level); + + // Overridden from TextureCUBE. + virtual RenderSurface::Ref PlatformSpecificGetRenderSurface(CubeFace face, + int level); + private: + // Creates a texture from a pre-existing GL texture object. + TextureCUBEGL(ServiceLocator* service_locator, + GLint texture, + Texture::Format format, + int levels, + int edge_length, + bool resize_to_pot, + bool enable_render_surfaces); + + // Updates a mip level, sending it from the backing bitmap to GL, rescaling + // it if resize_to_pot_ is set. + void UpdateBackedMipLevel(unsigned int level, CubeFace face); + + // Returns true if the backing bitmap has the data for the level. + bool HasLevel(CubeFace face, unsigned int level) const { + DCHECK_LT(static_cast<int>(level), levels()); + return (has_levels_[face] & (1 << level)) != 0; + } + + // Whether or not this texture needs to be resized from NPOT to pot behind + // the scenes. + bool resize_to_pot_; + + RendererGL* renderer_; + + // The handle of the OpenGL texture object. + GLuint gl_texture_; + + // Bitmaps used to back the NPOT textures on POT-only hardware. + Bitmap::Ref backing_bitmaps_[NUMBER_OF_FACES]; + + // Bitfields that indicates mip levels that are currently stored in the + // backing bitmap, one per face. + unsigned int has_levels_[NUMBER_OF_FACES]; + + // Bitfields that indicates which levels are currently locked, one per face. + unsigned int locked_levels_[NUMBER_OF_FACES]; +}; + +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_TEXTURE_GLES2_H_ diff --git a/o3d/core/cross/gles2/utils_gles2-inl.h b/o3d/core/cross/gles2/utils_gles2-inl.h new file mode 100644 index 0000000..f5aea15 --- /dev/null +++ b/o3d/core/cross/gles2/utils_gles2-inl.h @@ -0,0 +1,81 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_CORE_CROSS_GLES2_UTILS_GLES2_INL_H_ +#define O3D_CORE_CROSS_GLES2_UTILS_GLES2_INL_H_ + +#include "core/cross/types.h" + +namespace o3d { + +// Define this to debug GL errors. This has a significant performance hit. +// #define GL_ERROR_DEBUGGING + +// convert a byte offset into a Vertex Buffer Object into a GLvoid* for +// use with glVertexPointer(), glNormalPointer(), glVertexAttribPointer(), +// etc. after having used a glBindBuffer(). +#define BUFFER_OFFSET(i) (reinterpret_cast<char *>(NULL)+(i)) + +// Writes any Cg errors to the log with a descriptive message. +// NOTE: macros used to make sure the LOG calls note the correct +// line number and source file. +#define DLOG_CG_ERROR(message) { \ + CGerror error = cgGetError(); \ + DLOG_IF(INFO, error != CG_NO_ERROR) \ + << message << " : " \ + << cgGetErrorString(error); \ +} + +// Writes any Cg errors to the log with a descriptive message, along with +// the error messages from the CGC compiler. +#define DLOG_CG_COMPILER_ERROR(message, cg_context) { \ + CGerror error = cgGetError(); \ + DLOG_IF(ERROR, error == CG_NO_ERROR) \ + << message << " : " << cgGetErrorString(error); \ + if (error == CG_COMPILER_ERROR) { \ + DLOG(ERROR) << "CGC compiler output :\n" \ + << cgGetLastListing(cg_context); \ + } \ +} + +#ifdef GL_ERROR_DEBUGGING +#define CHECK_GL_ERROR() do { \ + GLenum gl_error = glGetError(); \ + LOG_IF(ERROR, gl_error != GL_NO_ERROR) << "GL Error :" << gl_error; \ +} while(0) +#else // GL_ERROR_DEBUGGING +#define CHECK_GL_ERROR() void(0) +#endif // GL_ERROR_DEBUGGING + +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_UTILS_GLES2_INL_H_ diff --git a/o3d/core/cross/gles2/utils_gles2.cc b/o3d/core/cross/gles2/utils_gles2.cc new file mode 100644 index 0000000..cec893a --- /dev/null +++ b/o3d/core/cross/gles2/utils_gles2.cc @@ -0,0 +1,228 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include "core/cross/stream.h" +#include "core/cross/types.h" +#include "core/cross/gles2/utils_gles2.h" +#include "core/cross/gles2/gles2_headers.h" + +// Required OpenGL extensions: +// GL_ARB_vertex_buffer_object +// GL_ARB_vertex_program +// GL_ARB_texture_compression +// GL_EXT_texture_compression_dxt1 + +namespace o3d { + +typedef std::pair<String, int> SemanticMapElement; +typedef std::map<String, int> SemanticMap; + +// The map batween the semantics on vertex program varying parameters names +// and vertex attribute indices under the VP_30 profile. +SemanticMapElement semantic_map_vp_30[] = { + SemanticMapElement("POSITION", 0), + SemanticMapElement("ATTR0", 0), + SemanticMapElement("BLENDWEIGHT", 1), + SemanticMapElement("ATTR1", 1), + SemanticMapElement("NORMAL", 2), + SemanticMapElement("ATTR2", 2), + SemanticMapElement("COLOR0", 3), + SemanticMapElement("DIFFUSE", 3), + SemanticMapElement("ATTR3", 3), + SemanticMapElement("COLOR1", 4), + SemanticMapElement("SPECULAR", 4), + SemanticMapElement("ATTR4", 4), + SemanticMapElement("TESSFACTOR", 5), + SemanticMapElement("FOGCOORD", 5), + SemanticMapElement("ATTR5", 5), + SemanticMapElement("PSIZE", 6), + SemanticMapElement("ATTR6", 6), + SemanticMapElement("BLENDINDICES", 7), + SemanticMapElement("ATTR7", 7), + SemanticMapElement("TEXCOORD0", 8), + SemanticMapElement("ATTR8", 8), + SemanticMapElement("TEXCOORD1", 9), + SemanticMapElement("ATTR9", 9), + SemanticMapElement("TEXCOORD2", 10), + SemanticMapElement("ATTR10", 10), + SemanticMapElement("TEXCOORD3", 11), + SemanticMapElement("ATTR11", 11), + SemanticMapElement("TEXCOORD4", 12), + SemanticMapElement("ATTR12", 12), + SemanticMapElement("TEXCOORD5", 13), + SemanticMapElement("ATTR13", 13), + SemanticMapElement("TEXCOORD6", 14), + SemanticMapElement("TANGENT", 14), + SemanticMapElement("ATTR14", 14), + SemanticMapElement("TEXCOORD7", 15), + SemanticMapElement("BINORMAL", 15), + SemanticMapElement("ATTR15", 15), +}; + +// The map batween the semantics on vertex program varying parameters names +// and vertex attribute indices under the VP_40 profile. +SemanticMapElement semantic_map_vp_40[] = { + SemanticMapElement("POSITION", 0), + SemanticMapElement("POSITION0", 0), + SemanticMapElement("ATTR0", 0), + SemanticMapElement("BLENDWEIGHT", 1), + SemanticMapElement("BLENDWEIGHT0", 1), + SemanticMapElement("ATTR1", 1), + SemanticMapElement("NORMAL", 2), + SemanticMapElement("NORMAL0", 2), + SemanticMapElement("ATTR2", 2), + SemanticMapElement("COLOR", 3), + SemanticMapElement("COLOR0", 3), + SemanticMapElement("DIFFUSE", 3), + SemanticMapElement("ATTR3", 3), + SemanticMapElement("COLOR1", 4), + SemanticMapElement("SPECULAR", 4), + SemanticMapElement("ATTR4", 4), + SemanticMapElement("TESSFACTOR", 5), + SemanticMapElement("FOGCOORD", 5), + SemanticMapElement("TESSFACTOR0", 5), + SemanticMapElement("FOGCOORD0", 5), + SemanticMapElement("ATTR5", 5), + SemanticMapElement("PSIZE", 6), + SemanticMapElement("PSIZE0", 6), + SemanticMapElement("ATTR6", 6), + SemanticMapElement("BLENDINDICES", 7), + SemanticMapElement("BLENDINDICES0", 7), + SemanticMapElement("ATTR7", 7), + SemanticMapElement("TEXCOORD", 8), + SemanticMapElement("TEXCOORD0", 8), + SemanticMapElement("ATTR8", 8), + SemanticMapElement("TEXCOORD1", 9), + SemanticMapElement("ATTR9", 9), + SemanticMapElement("TEXCOORD2", 10), + SemanticMapElement("ATTR10", 10), + SemanticMapElement("TEXCOORD3", 11), + SemanticMapElement("ATTR11", 11), + SemanticMapElement("TEXCOORD4", 12), + SemanticMapElement("ATTR12", 12), + SemanticMapElement("TEXCOORD5", 13), + SemanticMapElement("ATTR13", 13), + SemanticMapElement("TANGENT", 14), + SemanticMapElement("TANGENT0", 14), + SemanticMapElement("TEXCOORD6", 14), + SemanticMapElement("ATTR14", 14), + SemanticMapElement("BINORMAL", 15), + SemanticMapElement("BINORMAL0", 15), + SemanticMapElement("TEXCOORD7", 15), + SemanticMapElement("ATTR15", 15), +}; + +// The map batween OpenGL Vertex Attribute indexes under the VP_40 profile +// to Stream::Semantic identifiers (with index offsets). +struct AttrMapElement { + AttrMapElement(Stream::Semantic s, int i) : semantic(s), index(i) {} + Stream::Semantic semantic; + int index; +}; +AttrMapElement attr_map_vp_40[] = { + AttrMapElement(Stream::POSITION, 0), + AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), + AttrMapElement(Stream::NORMAL, 0), + AttrMapElement(Stream::COLOR, 0), + AttrMapElement(Stream::COLOR, 1), + AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), + AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), + AttrMapElement(Stream::UNKNOWN_SEMANTIC, 0), + AttrMapElement(Stream::TEXCOORD, 0), + AttrMapElement(Stream::TEXCOORD, 1), + AttrMapElement(Stream::TEXCOORD, 2), + AttrMapElement(Stream::TEXCOORD, 3), + AttrMapElement(Stream::TEXCOORD, 4), + AttrMapElement(Stream::TEXCOORD, 5), + AttrMapElement(Stream::TANGENT, 0), + AttrMapElement(Stream::BINORMAL, 0), +}; + +// TODO: make this choice a runtime decision in RendererGL +// initialisation. +static SemanticMap semantic_map(semantic_map_vp_40, + semantic_map_vp_40 + + sizeof(semantic_map_vp_40) / + sizeof(SemanticMapElement) ); + +// Converts a semantic string to an OpenGL vertex attribute number using the +// standard VP_40 shader semantic mappings. If the semantic is not +// recognised, it returns an index of -1. +int SemanticNameToGLVertexAttribute(const char* semantic) { + SemanticMap::const_iterator i = semantic_map.find(semantic); + if (i == semantic_map.end()) { + return -1; + } + return i->second; +} + +// Given a vertex attribute stream, convert it to a Stream::Semantic number +// and index. This is an imprecise operation. +Stream::Semantic GLVertexAttributeToStream(const unsigned int attr, + int *index) { + // kMaxAttrIndex is available from: + // glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &max_vertex_attribs); + // + // TODO: make this a runtime provided value discovered during + // Renderer creation. + const unsigned int kMaxAttrIndex = 15u; + if (attr > kMaxAttrIndex) { + //TODO: Figure out how to get errors out of here to the client. + DLOG(ERROR) << "Invalid vertex attribute index."; + *index = 0; + return Stream::UNKNOWN_SEMANTIC; + } + *index = attr_map_vp_40[attr].index; + return attr_map_vp_40[attr].semantic; +} + +#ifdef OS_WIN + +// Given a CGcontext object, check to see if any errors have occurred since +// the last Cg API call, and report the message and any compiler errors (if +// necessary). +inline void CheckForCgError(const String& logmessage, CGcontext cg_context) { + CGerror error = CG_NO_ERROR; + const char *error_string = cgGetLastErrorString(&error); + if (error == CG_NO_ERROR) { + return; + } else { + DLOG(ERROR) << logmessage << ": " << error_string; + if (error == CG_COMPILER_ERROR) { + DLOG(ERROR) << "Compiler message:\n" << cgGetLastListing(cg_context); + } + } +} + +#endif + +} // namespace o3d diff --git a/o3d/core/cross/gles2/utils_gles2.h b/o3d/core/cross/gles2/utils_gles2.h new file mode 100644 index 0000000..4eb71c0 --- /dev/null +++ b/o3d/core/cross/gles2/utils_gles2.h @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Google Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#ifndef O3D_CORE_CROSS_GLES2_UTILS_GLES2_H_ +#define O3D_CORE_CROSS_GLES2_UTILS_GLES2_H_ + +#include "base/basictypes.h" +#include "core/cross/stream.h" + +namespace o3d { + +bool GetGLProcedures(); +int SemanticNameToGLVertexAttribute(const char* semantic); +Stream::Semantic GLVertexAttributeToStream(const unsigned int attr, int *index); + +} // namespace o3d + +#endif // O3D_CORE_CROSS_GLES2_UTILS_GLES2_H_ diff --git a/o3d/core/cross/renderer_platform.h b/o3d/core/cross/renderer_platform.h index 639459e..faabb28 100644 --- a/o3d/core/cross/renderer_platform.h +++ b/o3d/core/cross/renderer_platform.h @@ -48,6 +48,9 @@ #elif defined(OS_WIN) && defined(RENDERER_GL) #include "core/cross/gl/gl_headers.h" #include <gl/GL.h> +#elif defined(OS_WIN) && defined(RENDERER_GLES2) +#include "core/cross/gles2/gles2_headers.h" +#include <gl/GL.h> #endif #if defined(OS_WIN) @@ -64,6 +67,8 @@ #include "core/win/d3d9/renderer_d3d9.h" #elif defined(RENDERER_GL) #include "core/cross/gl/renderer_gl.h" +#elif defined(RENDERER_GLES2) +#include "core/cross/gles2/renderer_gles2.h" #else #error Renderer not recognized. #endif diff --git a/o3d/core/cross/renderer_test.cc b/o3d/core/cross/renderer_test.cc index 190aabc..50c719a 100644 --- a/o3d/core/cross/renderer_test.cc +++ b/o3d/core/cross/renderer_test.cc @@ -92,6 +92,10 @@ TEST_F(RendererTest, InitAndDestroyRenderer) { // test that the Cg Context was correctly created RendererGL* gl_renderer = down_cast<RendererGL*>(renderer.get()); EXPECT_TRUE(gl_renderer->cg_context() != NULL); +#elif defined(RENDERER_GLES2) + // test that the Cg Context was correctly created + RendererGL* gles2_renderer = down_cast<RendererGL*>(renderer.get()); + EXPECT_TRUE(gles2_renderer->cg_context() != NULL); #endif // destroy the renderer renderer->Destroy(); @@ -102,6 +106,9 @@ TEST_F(RendererTest, InitAndDestroyRenderer) { #elif defined(RENDERER_GL) // check that the renderer no longer has a Cg Context. EXPECT_FALSE(gl_renderer->cg_context() != NULL); +#elif defined(RENDERER_GLES2) + // check that the renderer no longer has a Cg Context. + EXPECT_FALSE(gles2_renderer->cg_context() != NULL); #endif } diff --git a/o3d/import/cross/precompile.h b/o3d/import/cross/precompile.h index c858aa4..98ad5ca 100644 --- a/o3d/import/cross/precompile.h +++ b/o3d/import/cross/precompile.h @@ -52,6 +52,13 @@ #endif #endif // defined(RENDERER_GL) +#if defined(RENDERER_GLES2) +#include <GL/glew.h> +#if defined(OS_WIN) +#include <GL/wglew.h> +#endif +#endif // defined(RENDERER_GLES2) + #include <Cg/cg.h> #include <Cg/cgGL.h> #include <FCollada.h> diff --git a/o3d/plugin/plugin.gyp b/o3d/plugin/plugin.gyp index d320b61..caff190 100644 --- a/o3d/plugin/plugin.gyp +++ b/o3d/plugin/plugin.gyp @@ -102,6 +102,14 @@ ], }, ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:gles2_libs', + '../build/libs.gyp:cg_libs', + ], + }, + ], ['OS == "mac"', { 'mac_bundle': 1, @@ -287,6 +295,14 @@ ], }, ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:gles2_libs', + '../build/libs.gyp:cg_libs', + ], + }, + ], ['OS == "mac"', { 'mac_bundle': 1, diff --git a/o3d/samples/o3djs/js_list.manifest b/o3d/samples/o3djs/js_list.manifest index d88fcad..3820b06 100644 --- a/o3d/samples/o3djs/js_list.manifest +++ b/o3d/samples/o3djs/js_list.manifest @@ -41,6 +41,7 @@ 'io.js', 'loader.js', 'material.js', + 'manipulators.js', 'math.js', 'pack.js', 'particles.js', diff --git a/o3d/standalone/standalone.cc b/o3d/standalone/standalone.cc new file mode 100644 index 0000000..2b6fc72 --- /dev/null +++ b/o3d/standalone/standalone.cc @@ -0,0 +1,5 @@ +#include <stdio.h> + +int main(int argc, const char** argv) { + printf("Hello World\n"); +} diff --git a/o3d/standalone/standalone.gyp b/o3d/standalone/standalone.gyp new file mode 100644 index 0000000..3e0c61c --- /dev/null +++ b/o3d/standalone/standalone.gyp @@ -0,0 +1,150 @@ +# Copyright (c) 2009 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'variables': { + 'chromium_code': 1, + }, + 'includes': [ + '../build/common.gypi', + ], + 'target_defaults': { + 'include_dirs': [ + '..', + '../..', + ], + # TODO(rlp): remove this after fixing signed / unsigned issues in + # command buffer code and tests. + 'target_conditions': [ + ['OS == "mac"', + { + 'xcode_settings': { + 'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO' + }, + }, + ], + ], + }, + 'targets': [ + { + 'target_name': 'standalone', + 'type': 'executable', + 'dependencies': [ + '../../<(antlrdir)/antlr.gyp:antlr3c', + '../../<(fcolladadir)/fcollada.gyp:fcollada', + '../../<(jpegdir)/libjpeg.gyp:libjpeg', + '../../<(pngdir)/libpng.gyp:libpng', + '../../<(zlibdir)/zlib.gyp:zlib', + '../../base/base.gyp:base', + '../../skia/skia.gyp:skia', + '../../native_client/src/shared/imc/imc.gyp:google_nacl_imc', + '../compiler/technique/technique.gyp:o3dTechnique', + '../core/core.gyp:o3dCore', + '../core/core.gyp:o3dCorePlatform', + '../utils/utils.gyp:o3dUtils', + ], + 'sources': [ + 'standalone.cc', + ], + 'conditions' : [ + ['renderer == "gl"', + { + 'dependencies': [ + '../build/libs.gyp:cg_libs', + '../build/libs.gyp:gl_libs', + ], + }, + ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:cg_libs', + '../build/libs.gyp:gles2_libs', + ], + }, + ], + ['OS == "mac"', + { + 'include_dirs': [ + '../../third_party/glew/files/include', + ], + 'link_settings': { + 'libraries': [ + '$(SDKROOT)/System/Library/Frameworks/AGL.framework', + '$(SDKROOT)/System/Library/Frameworks/Foundation.framework', + '$(SDKROOT)/System/Library/Frameworks/GLUT.framework', + '$(SDKROOT)/System/Library/Frameworks/OpenGL.framework', + '../../third_party/cg/files/mac/Cg.framework', + '../../third_party/glew/files/lib/libMacStaticGLEW.a', + ], + }, + }, + ], + ['OS == "win"', + { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'rpcrt4.lib', + '../../<(cgdir)/lib/cg.lib', + '../../<(cgdir)/lib/cgGL.lib', + ], + # Set /SUBSYSTEM:WINDOWS for unit_tests.exe, since + # it is a windows app. + 'SubSystem': '2', + # Don't optimize away unreferenced symbols when + # linking. If we didn't do this, then none of the + # tests would auto-register. + 'OptimizeReferences': '1', + }, + }, + # We switch it to console post-build so that we have a + # windows app that can output to the console and still + # open windows. + 'msvs_postbuild': + 'editbin /SUBSYSTEM:CONSOLE $(OutDir)/$(TargetFileName)', + }, + ], + ['OS == "win" and renderer == "d3d9"', + { + 'include_dirs': [ + '"$(DXSDK_DIR)/Include"', + ], + 'link_settings': { + 'libraries': [ + '"$(DXSDK_DIR)/Lib/x86/d3dx9.lib"', + 'd3d9.lib', + '"$(DXSDK_DIR)/Lib/x86/DxErr.lib"', + ], + }, + }, + ], + ['OS == "win" and renderer == "gl"', + { + 'dependencies': [ + '../build/libs.gyp:gl_libs', + ], + }, + ], + ['OS == "win" and renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:gles2_libs', + ], + }, + ], + ['OS == "linux"', + { + }, + ], + ], + }, + ], +} + +# Local Variables: +# tab-width:2 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=2 shiftwidth=2: diff --git a/o3d/tests/tests.gyp b/o3d/tests/tests.gyp index 129f2d7..6593ec1 100644 --- a/o3d/tests/tests.gyp +++ b/o3d/tests/tests.gyp @@ -102,6 +102,14 @@ ], }, ], + ['renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:cg_libs', + '../build/libs.gyp:gles2_libs', + ], + }, + ], ['OS == "mac"', { 'dependencies': [ @@ -210,6 +218,13 @@ ], }, ], + ['OS == "win" and renderer == "gles2"', + { + 'dependencies': [ + '../build/libs.gyp:gles2_libs', + ], + }, + ], ['OS == "linux"', { 'sources': [ |