#ifndef _GLSFBOUTIL_HPP #define _GLSFBOUTIL_HPP /*------------------------------------------------------------------------- * drawElements Quality Program OpenGL (ES) Module * ----------------------------------------------- * * Copyright 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *//*! * \file * \brief Utilities for framebuffer objects. *//*--------------------------------------------------------------------*/ #include "gluRenderContext.hpp" #include "gluContextInfo.hpp" #include "glwDefs.hpp" #include "glwEnums.hpp" #include "glwFunctions.hpp" #include "gluTextureUtil.hpp" #include "tcuTestLog.hpp" #include "tcuDefs.hpp" #include <map> #include <set> #include <vector> #include <algorithm> #include <iterator> namespace deqp { namespace gls { // Utilities for standard containers. \todo [2013-12-10 lauri] Move to decpp? //! A pair of iterators to present a range. //! \note This must be POD to allow static initialization. //! \todo [2013-12-03 lauri] Move this to decpp? template <typename T> struct Range { typedef const T* const_iterator; const T* m_begin; const T* m_end; const T* begin (void) const { return m_begin; } const T* end (void) const { return m_end; } }; #define GLS_ARRAY_RANGE(ARR) { DE_ARRAY_BEGIN(ARR), DE_ARRAY_END(ARR) } #define GLS_NULL_RANGE { DE_NULL, DE_NULL } //! A pair type that, unlike stl::pair, is POD so it can be statically initialized. template <typename T1, typename T2> struct Pair { typedef T1 first_type; typedef T2 second_type; T1 first; T2 second; }; template<typename C> C intersection(const C& s1, const C& s2) { C ret; std::set_intersection(s1.begin(), s1.end(), s2.begin(), s2.end(), std::insert_iterator<C>(ret, ret.begin())); return ret; } // \todo [2013-12-03 lauri] move to decpp? template<typename C> inline bool isMember (const typename C::key_type& key, const C& container) { typename C::const_iterator it = container.find(key); return (it != container.end()); } template <typename M> inline const typename M::mapped_type* lookupMaybe (const M& map, const typename M::key_type& key) { typename M::const_iterator it = map.find(key); if (it == map.end()) return DE_NULL; return &it->second; } template<typename M> inline const typename M::mapped_type& lookupDefault (const M& map, const typename M::key_type& key, const typename M::mapped_type& fallback) { const typename M::mapped_type* ptr = lookupMaybe(map, key); return ptr == DE_NULL ? fallback : *ptr; } template<typename M> const typename M::mapped_type& lookup (const M& map, const typename M::key_type& key) { const typename M::mapped_type* ptr = lookupMaybe(map, key); if (ptr == DE_NULL) throw std::out_of_range("key not found in map"); return *ptr; } template<typename C> inline bool contains (const C& container, const typename C::value_type& item) { const typename C::const_iterator it = container.find(item); return (it != container.end()); } template<typename M> static inline bool insert(const typename M::key_type& key, const typename M::mapped_type& value, M& map) { typename M::value_type entry(key, value); std::pair<typename M::iterator,bool> ret = map.insert(entry); return ret.second; } std::vector<std::string> splitString(const std::string& s); namespace FboUtil { //! Configurations for framebuffer objects and their attachments. class FboVerifier; class FboBuilder; typedef deUint32 FormatKey; #define GLS_UNSIZED_FORMATKEY(FORMAT, TYPE) \ (deUint32(TYPE) << 16 | deUint32(FORMAT)) typedef Range<FormatKey> FormatKeys; struct ImageFormat { glw::GLenum format; //! Type if format is unsized, GL_NONE if sized. glw::GLenum unsizedType; bool operator< (const ImageFormat& other) const { return (format < other.format || (format == other.format && unsizedType < other.unsizedType)); } static ImageFormat none (void) { ImageFormat fmt = { GL_NONE, GL_NONE }; return fmt; } }; static inline ImageFormat formatKeyInfo(FormatKey key) { ImageFormat fmt = { key & 0xffff, key >> 16 }; return fmt; } enum FormatFlags { ANY_FORMAT = 0, COLOR_RENDERABLE = 1 << 0, DEPTH_RENDERABLE = 1 << 1, STENCIL_RENDERABLE = 1 << 2, RENDERBUFFER_VALID = 1 << 3, TEXTURE_VALID = 1 << 4, REQUIRED_RENDERABLE = 1 << 5, //< Without this, renderability is allowed, not required. }; static inline FormatFlags operator|(FormatFlags f1, FormatFlags f2) { return FormatFlags(deUint32(f1) | deUint32(f2)); } FormatFlags formatFlag(glw::GLenum context); typedef std::set<ImageFormat> Formats; class FormatDB { public: void addFormat (ImageFormat format, FormatFlags flags); Formats getFormats (FormatFlags requirements) const; FormatFlags getFormatInfo (ImageFormat format, FormatFlags fallback) const; private: typedef std::map<ImageFormat, FormatFlags> FormatMap; FormatMap m_map; }; typedef Pair<FormatFlags, FormatKeys> FormatEntry; typedef Range<FormatEntry> FormatEntries; // \todo [2013-12-20 lauri] It turns out that format properties in extensions // are actually far too fine-grained for this bundling to be reasonable, // especially given the syntactic cumbersomeness of static arrays. It's better // to list each entry separately. struct FormatExtEntry { const char* extensions; deUint32 flags; Range<FormatKey> formats; }; typedef Range<FormatExtEntry> FormatExtEntries; void addFormats (FormatDB& db, FormatEntries stdFmts); void addExtFormats (FormatDB& db, FormatExtEntries extFmts, const glu::RenderContext* ctx); glu::TransferFormat transferImageFormat (const ImageFormat& imgFormat); namespace config { struct Config { virtual ~Config (void) {}; }; struct Image : public Config { ImageFormat internalFormat; glw::GLsizei width; glw::GLsizei height; protected: Image (void) : internalFormat (ImageFormat::none()) , width (0) , height (0) {} }; struct Renderbuffer : public Image { Renderbuffer (void) : numSamples(0) {} glw::GLsizei numSamples; }; struct Texture : public Image { Texture (void) : numLevels(1) {} glw::GLint numLevels; }; struct TextureFlat : public Texture { }; struct Texture2D : public TextureFlat { }; struct TextureCubeMap : public TextureFlat { }; struct TextureLayered : public Texture { TextureLayered (void) : numLayers(1) {} glw::GLsizei numLayers; }; struct Texture3D : public TextureLayered { }; struct Texture2DArray : public TextureLayered { }; struct Attachment : public Config { Attachment (void) : target(GL_FRAMEBUFFER), imageName(0) {} glw::GLenum target; glw::GLuint imageName; //! Returns `true` iff this attachment is "framebuffer attachment //! complete" when bound to attachment point `attPoint`, and the current //! image with name `imageName` is `image`, using `vfr` to check format //! renderability. bool isComplete (glw::GLenum attPoint, const Image* image, const FboVerifier& vfr) const; }; struct RenderbufferAttachment : public Attachment { RenderbufferAttachment (void) : renderbufferTarget(GL_RENDERBUFFER) {} glw::GLenum renderbufferTarget; }; struct TextureAttachment : public Attachment { TextureAttachment (void) : level(0) {} glw::GLint level; }; struct TextureFlatAttachment : public TextureAttachment { TextureFlatAttachment (void) : texTarget(GL_NONE) {} glw::GLenum texTarget; }; struct TextureLayerAttachment : public TextureAttachment { TextureLayerAttachment (void) : layer(0) {} glw::GLsizei layer; }; glw::GLenum attachmentType (const Attachment& att); glw::GLsizei imageNumSamples (const Image& img); //! Mapping from attachment points to attachment configurations. typedef std::map<glw::GLenum, const Attachment*> AttachmentMap; //! Mapping from object names to texture configurations. typedef std::map<glw::GLuint, const Texture*> TextureMap; //! Mapping from object names to renderbuffer configurations. typedef std::map<glw::GLuint, const Renderbuffer*> RboMap; //! A framebuffer configuration. struct Framebuffer { AttachmentMap attachments; TextureMap textures; RboMap rbos; void attach (glw::GLenum attPoint, const Attachment* att); void setTexture (glw::GLuint texName, const Texture& texCfg); void setRbo (glw::GLuint rbName, const Renderbuffer& rbCfg); const Image* getImage (glw::GLenum type, glw::GLuint imgName) const; }; } // config void logFramebufferConfig(const config::Framebuffer& cfg, tcu::TestLog& log); class FboBuilder : public config::Framebuffer { public: void glAttach (glw::GLenum attPoint, const config::Attachment* att); glw::GLuint glCreateTexture (const config::Texture& texCfg); glw::GLuint glCreateRbo (const config::Renderbuffer& rbCfg); FboBuilder (glw::GLuint fbo, glw::GLenum target, const glw::Functions& gl); ~FboBuilder (void); glw::GLenum getError (void) { return m_error; } //! Allocate a new configuration of type `Config` (which must be a //! subclass of `config::Config`), and return a referenc to it. The newly //! allocated object will be freed when this builder object is destroyed. template<typename Config> Config& makeConfig (void) { Config* cfg = new Config(); m_configs.insert(cfg); return *cfg; } private: typedef std::set<config::Config*> Configs; void checkError (void); glw::GLenum m_error; //< The first GL error encountered. glw::GLenum m_target; const glw::Functions& m_gl; Configs m_configs; }; typedef std::set<glw::GLenum> StatusCodes; class Checker { public: Checker (void) { m_statusCodes.insert(GL_FRAMEBUFFER_COMPLETE); } virtual ~Checker (void) {} void require (bool condition, glw::GLenum error); void canRequire (bool condition, glw::GLenum error); StatusCodes getStatusCodes (void) { return m_statusCodes; } virtual void check (glw::GLenum attPoint, const config::Attachment& att, const config::Image* image) = 0; private: StatusCodes m_statusCodes; //< Allowed return values for glCheckFramebufferStatus. }; class CheckerFactory { public: virtual Checker* createChecker (void) = 0; }; typedef std::set<glw::GLenum> AttachmentPoints; typedef std::set<ImageFormat> Formats; class FboVerifier { public: FboVerifier (const FormatDB& formats, CheckerFactory& factory); StatusCodes validStatusCodes (const config::Framebuffer& cfg) const; private: const FormatDB& m_formats; CheckerFactory& m_factory; }; } // FboUtil } // gls } // deqp #endif // _GLSFBOUTIL_HPP