/*------------------------------------------------------------------------- * drawElements Quality Program OpenGL ES Utilities * ------------------------------------------------ * * 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 Render context implementation that does no rendering. *//*--------------------------------------------------------------------*/ #include "tcuNullRenderContext.hpp" #include "tcuTexture.hpp" #include "tcuTextureUtil.hpp" #include "deThreadLocal.hpp" #include "gluRenderConfig.hpp" #include "gluTextureUtil.hpp" #include "glwEnums.hpp" #include <string> #include <vector> namespace tcu { namespace null { using namespace glw; #include "tcuNullRenderContextFuncs.inl" using namespace glu; using std::string; using std::vector; class ObjectManager { public: ObjectManager (void) : m_lastObject(0) { } deUint32 allocate (void) { deUint32 object = ++m_lastObject; if (object == 0) object = ++m_lastObject; // Just ignore overflow. return object; } void free (deUint32 object) { DE_UNREF(object); } private: deUint32 m_lastObject; }; class Context { public: Context (ContextType ctxType_); ~Context (void); private: Context (const Context&); Context& operator= (const Context&); void addExtension (const char* name); public: // GL state exposed to implementation functions. const ContextType ctxType; string vendor; string version; string renderer; string shadingLanguageVersion; string extensions; vector<string> extensionList; vector<deUint32> compressedTextureList; GLenum lastError; int pixelPackRowLength; int pixelPackSkipRows; int pixelPackSkipPixels; int pixelPackAlignment; GLuint pixelPackBufferBufferBinding; ObjectManager shaders; ObjectManager programs; ObjectManager textures; ObjectManager buffers; ObjectManager renderbuffers; ObjectManager framebuffers; ObjectManager samplers; ObjectManager vertexArrays; ObjectManager queries; ObjectManager transformFeedbacks; ObjectManager programPipelines; }; Context::Context (ContextType ctxType_) : ctxType (ctxType_) , vendor ("drawElements") , renderer ("dummy") , lastError (GL_NO_ERROR) , pixelPackRowLength (0) , pixelPackSkipRows (0) , pixelPackSkipPixels (0) , pixelPackAlignment (0) , pixelPackBufferBufferBinding (0) { using glu::ApiType; if (ctxType.getAPI() == ApiType::es(2, 0)) { version = "OpenGL ES 2.0"; shadingLanguageVersion = "OpenGL ES GLSL ES 1.0"; } else if (ctxType.getAPI() == ApiType::es(3, 0)) { version = "OpenGL ES 3.0"; shadingLanguageVersion = "OpenGL ES GLSL ES 3.0"; } else if (ctxType.getAPI() == ApiType::es(3, 1)) { version = "OpenGL ES 3.1"; shadingLanguageVersion = "OpenGL ES GLSL ES 3.1"; addExtension("GL_OES_texture_stencil8"); addExtension("GL_OES_sample_shading"); addExtension("GL_OES_sample_variables"); addExtension("GL_OES_shader_multisample_interpolation"); addExtension("GL_OES_shader_image_atomic"); addExtension("GL_OES_texture_storage_multisample_2d_array"); addExtension("GL_KHR_blend_equation_advanced"); addExtension("GL_KHR_blend_equation_advanced_coherent"); addExtension("GL_EXT_shader_io_blocks"); addExtension("GL_EXT_geometry_shader"); addExtension("GL_EXT_geometry_point_size"); addExtension("GL_EXT_tessellation_shader"); addExtension("GL_EXT_tessellation_point_size"); addExtension("GL_EXT_gpu_shader5"); addExtension("GL_EXT_shader_implicit_conversions"); addExtension("GL_EXT_texture_buffer"); addExtension("GL_EXT_texture_cube_map_array"); addExtension("GL_EXT_draw_buffers_indexed"); addExtension("GL_EXT_texture_sRGB_decode"); addExtension("GL_EXT_texture_border_clamp"); addExtension("GL_KHR_debug"); addExtension("GL_EXT_primitive_bounding_box"); addExtension("GL_ANDROID_extension_pack_es31a"); addExtension("GL_EXT_copy_image"); } else if (ctxType.getAPI() == ApiType::es(3, 2)) { version = "OpenGL ES 3.2"; shadingLanguageVersion = "OpenGL ES GLSL ES 3.2"; } else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 3) { version = "3.3.0"; shadingLanguageVersion = "3.30"; } else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 4 && ctxType.getMinorVersion() <= 4) { version = "4.4.0"; shadingLanguageVersion = "4.40"; } else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 4 && ctxType.getMinorVersion() == 5) { version = "4.5.0"; shadingLanguageVersion = "4.50"; } else if (glu::isContextTypeGLCore(ctxType) && ctxType.getMajorVersion() == 4 && ctxType.getMinorVersion() == 6) { version = "4.6.0"; shadingLanguageVersion = "4.60"; } else throw tcu::NotSupportedError("Unsupported GL version", "", __FILE__, __LINE__); if (isContextTypeES(ctxType)) { addExtension("GL_EXT_color_buffer_float"); addExtension("GL_EXT_color_buffer_half_float"); } // support compressed formats { static deUint32 compressedFormats[] = { GL_ETC1_RGB8_OES, GL_COMPRESSED_R11_EAC, GL_COMPRESSED_SIGNED_R11_EAC, GL_COMPRESSED_RG11_EAC, GL_COMPRESSED_SIGNED_RG11_EAC, GL_COMPRESSED_RGB8_ETC2, GL_COMPRESSED_SRGB8_ETC2, GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2, GL_COMPRESSED_RGBA8_ETC2_EAC, GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC, GL_COMPRESSED_RGBA_ASTC_4x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x4_KHR, GL_COMPRESSED_RGBA_ASTC_5x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x5_KHR, GL_COMPRESSED_RGBA_ASTC_6x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x5_KHR, GL_COMPRESSED_RGBA_ASTC_8x6_KHR, GL_COMPRESSED_RGBA_ASTC_8x8_KHR, GL_COMPRESSED_RGBA_ASTC_10x5_KHR, GL_COMPRESSED_RGBA_ASTC_10x6_KHR, GL_COMPRESSED_RGBA_ASTC_10x8_KHR, GL_COMPRESSED_RGBA_ASTC_10x10_KHR, GL_COMPRESSED_RGBA_ASTC_12x10_KHR, GL_COMPRESSED_RGBA_ASTC_12x12_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_4x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x4_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_5x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_6x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_8x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x5_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x6_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x8_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_10x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x10_KHR, GL_COMPRESSED_SRGB8_ALPHA8_ASTC_12x12_KHR, }; addExtension("GL_KHR_texture_compression_astc_hdr"); addExtension("GL_KHR_texture_compression_astc_ldr"); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(compressedFormats); ++ndx) compressedTextureList.push_back(compressedFormats[ndx]); } } Context::~Context (void) { } void Context::addExtension (const char* name) { if (!extensions.empty()) extensions += " "; extensions += name; extensionList.push_back(name); } static de::ThreadLocal s_currentCtx; void setCurrentContext (Context* context) { s_currentCtx.set((void*)context); } Context* getCurrentContext (void) { return (Context*)s_currentCtx.get(); } GLW_APICALL GLenum GLW_APIENTRY glGetError (void) { Context* const ctx = getCurrentContext(); const GLenum lastErr = ctx->lastError; ctx->lastError = GL_NO_ERROR; return lastErr; } GLW_APICALL void GLW_APIENTRY glGetIntegerv (GLenum pname, GLint* params) { Context* const ctx = getCurrentContext(); switch (pname) { case GL_NUM_EXTENSIONS: *params = (int)ctx->extensionList.size(); break; case GL_MAX_VERTEX_ATTRIBS: *params = 32; break; case GL_MAX_DRAW_BUFFERS: case GL_MAX_COLOR_ATTACHMENTS: *params = 8; break; case GL_MAX_TEXTURE_IMAGE_UNITS: case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: case GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS: case GL_MAX_TESS_EVALUATION_TEXTURE_IMAGE_UNITS: case GL_MAX_TESS_CONTROL_TEXTURE_IMAGE_UNITS: *params = 32; break; case GL_MAX_VERTEX_SHADER_STORAGE_BLOCKS: case GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS: case GL_MAX_GEOMETRY_SHADER_STORAGE_BLOCKS: case GL_MAX_TESS_CONTROL_SHADER_STORAGE_BLOCKS: case GL_MAX_TESS_EVALUATION_SHADER_STORAGE_BLOCKS: case GL_MAX_COMPUTE_SHADER_STORAGE_BLOCKS: *params = 8; break; case GL_MAX_VERTEX_ATOMIC_COUNTER_BUFFERS: case GL_MAX_FRAGMENT_ATOMIC_COUNTER_BUFFERS: case GL_MAX_GEOMETRY_ATOMIC_COUNTER_BUFFERS: case GL_MAX_TESS_CONTROL_ATOMIC_COUNTER_BUFFERS: case GL_MAX_TESS_EVALUATION_ATOMIC_COUNTER_BUFFERS: case GL_MAX_COMPUTE_ATOMIC_COUNTER_BUFFERS: *params = 8; break; case GL_MAX_SHADER_STORAGE_BLOCK_SIZE: *params = 1u << 25; break; case GL_MAX_GEOMETRY_OUTPUT_VERTICES: *params = 256; break; case GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS: *params = 2048; break; case GL_MAX_GEOMETRY_SHADER_INVOCATIONS: *params = 4; break; case GL_MAX_COLOR_TEXTURE_SAMPLES: *params = 8; break; case GL_MAX_TEXTURE_SIZE: case GL_MAX_CUBE_MAP_TEXTURE_SIZE: case GL_MAX_3D_TEXTURE_SIZE: case GL_MAX_RENDERBUFFER_SIZE: case GL_MAX_TEXTURE_BUFFER_SIZE: *params = 2048; break; case GL_MAX_ARRAY_TEXTURE_LAYERS: *params = 128; break; case GL_NUM_SHADER_BINARY_FORMATS: *params = 0; break; case GL_NUM_COMPRESSED_TEXTURE_FORMATS: *params = (int)ctx->compressedTextureList.size(); break; case GL_COMPRESSED_TEXTURE_FORMATS: deMemcpy(params, &ctx->compressedTextureList[0], ctx->compressedTextureList.size()*sizeof(deUint32)); break; case GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS: *params = 16; break; case GL_MAX_UNIFORM_BUFFER_BINDINGS: *params = 32; break; case GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT: *params = 16; break; case GL_IMPLEMENTATION_COLOR_READ_FORMAT: *params = GL_RGBA; break; case GL_IMPLEMENTATION_COLOR_READ_TYPE: *params = GL_UNSIGNED_BYTE; break; case GL_SAMPLE_BUFFERS: *params = 0; break; default: break; } } GLW_APICALL void GLW_APIENTRY glGetBooleanv (GLenum pname, GLboolean* params) { switch (pname) { case GL_SHADER_COMPILER: *params = GL_TRUE; break; default: break; } } GLW_APICALL void GLW_APIENTRY glGetFloatv (GLenum pname, GLfloat* params) { switch (pname) { case GL_ALIASED_LINE_WIDTH_RANGE: case GL_ALIASED_POINT_SIZE_RANGE: params[0] = 0.0f; params[1] = 64.0f; break; default: break; } } GLW_APICALL void GLW_APIENTRY glGetInternalformativ (GLenum target, GLenum internalformat, GLenum pname, GLsizei bufSize, GLint* params) { static const int s_sampleCounts[] = { 16, 8, 4, 2, 1 }; DE_UNREF(internalformat); DE_UNREF(target); switch (pname) { case GL_NUM_SAMPLE_COUNTS: if (bufSize >= 1) *params = DE_LENGTH_OF_ARRAY(s_sampleCounts); break; case GL_SAMPLES: deMemcpy(params, s_sampleCounts, de::min(bufSize, DE_LENGTH_OF_ARRAY(s_sampleCounts))); break; default: break; } } GLW_APICALL const glw::GLubyte* GLW_APIENTRY glGetString (GLenum name) { Context* const ctx = getCurrentContext(); switch (name) { case GL_VENDOR: return (const glw::GLubyte*)ctx->vendor.c_str(); case GL_VERSION: return (const glw::GLubyte*)ctx->version.c_str(); case GL_RENDERER: return (const glw::GLubyte*)ctx->renderer.c_str(); case GL_SHADING_LANGUAGE_VERSION: return (const glw::GLubyte*)ctx->shadingLanguageVersion.c_str(); case GL_EXTENSIONS: return (const glw::GLubyte*)ctx->extensions.c_str(); default: ctx->lastError = GL_INVALID_ENUM; return DE_NULL; } } GLW_APICALL const glw::GLubyte* GLW_APIENTRY glGetStringi (GLenum name, GLuint index) { Context* const ctx = getCurrentContext(); if (name == GL_EXTENSIONS) { if ((size_t)index < ctx->extensionList.size()) return (const glw::GLubyte*)ctx->extensionList[index].c_str(); else { ctx->lastError = GL_INVALID_VALUE; return DE_NULL; } } else { ctx->lastError = GL_INVALID_ENUM; return DE_NULL; } } GLW_APICALL GLuint GLW_APIENTRY glCreateProgram () { Context* const ctx = getCurrentContext(); return (GLuint)ctx->programs.allocate(); } GLW_APICALL GLuint GLW_APIENTRY glCreateShader (GLenum type) { Context* const ctx = getCurrentContext(); DE_UNREF(type); return (GLuint)ctx->shaders.allocate(); } GLW_APICALL void GLW_APIENTRY glGetShaderiv (GLuint shader, GLenum pname, GLint* params) { DE_UNREF(shader); if (pname == GL_COMPILE_STATUS) *params = GL_TRUE; } GLW_APICALL void GLW_APIENTRY glGetProgramiv (GLuint program, GLenum pname, GLint* params) { DE_UNREF(program); if (pname == GL_LINK_STATUS) *params = GL_TRUE; } GLW_APICALL void GLW_APIENTRY glGenTextures (GLsizei n, GLuint* textures) { Context* const ctx = getCurrentContext(); if (textures) { for (int ndx = 0; ndx < n; ndx++) textures[ndx] = ctx->textures.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenQueries (GLsizei n, GLuint* ids) { Context* const ctx = getCurrentContext(); if (ids) { for (int ndx = 0; ndx < n; ndx++) ids[ndx] = ctx->queries.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenBuffers (GLsizei n, GLuint* buffers) { Context* const ctx = getCurrentContext(); if (buffers) { for (int ndx = 0; ndx < n; ndx++) buffers[ndx] = ctx->buffers.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenRenderbuffers (GLsizei n, GLuint* renderbuffers) { Context* const ctx = getCurrentContext(); if (renderbuffers) { for (int ndx = 0; ndx < n; ndx++) renderbuffers[ndx] = ctx->renderbuffers.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenFramebuffers (GLsizei n, GLuint* framebuffers) { Context* const ctx = getCurrentContext(); if (framebuffers) { for (int ndx = 0; ndx < n; ndx++) framebuffers[ndx] = ctx->framebuffers.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenVertexArrays (GLsizei n, GLuint* arrays) { Context* const ctx = getCurrentContext(); if (arrays) { for (int ndx = 0; ndx < n; ndx++) arrays[ndx] = ctx->vertexArrays.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenSamplers (GLsizei count, GLuint* samplers) { Context* const ctx = getCurrentContext(); if (samplers) { for (int ndx = 0; ndx < count; ndx++) samplers[ndx] = ctx->samplers.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenTransformFeedbacks (GLsizei n, GLuint* ids) { Context* const ctx = getCurrentContext(); if (ids) { for (int ndx = 0; ndx < n; ndx++) ids[ndx] = ctx->transformFeedbacks.allocate(); } } GLW_APICALL void GLW_APIENTRY glGenProgramPipelines (GLsizei n, GLuint* pipelines) { Context* const ctx = getCurrentContext(); if (pipelines) { for (int ndx = 0; ndx < n; ndx++) pipelines[ndx] = ctx->programPipelines.allocate(); } } GLW_APICALL GLvoid* GLW_APIENTRY glMapBufferRange (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access) { Context* const ctx = getCurrentContext(); DE_UNREF(target); DE_UNREF(offset); DE_UNREF(length); DE_UNREF(access); if (ctx->lastError == GL_NO_ERROR) ctx->lastError = GL_INVALID_OPERATION; return (GLvoid*)0; } GLW_APICALL GLenum GLW_APIENTRY glCheckFramebufferStatus (GLenum target) { DE_UNREF(target); return GL_FRAMEBUFFER_COMPLETE; } GLW_APICALL void GLW_APIENTRY glReadPixels (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) { DE_UNREF(x); DE_UNREF(y); Context* const ctx = getCurrentContext(); const tcu::Vec4 clearColor (0.0f, 0.0f, 0.0f, 1.0f); // black const tcu::TextureFormat transferFormat = glu::mapGLTransferFormat(format, type); // invalid formats if (transferFormat.order == TextureFormat::CHANNELORDER_LAST || transferFormat.type == TextureFormat::CHANNELTYPE_LAST) { if (ctx->lastError == GL_NO_ERROR) ctx->lastError = GL_INVALID_ENUM; return; } // unsupported formats if (!(format == GL_RGBA && type == GL_UNSIGNED_BYTE) && !(format == GL_RGBA_INTEGER && type == GL_INT) && !(format == GL_RGBA_INTEGER && type == GL_UNSIGNED_INT) && !(format == GL_RGBA && type == GL_FLOAT)) { if (ctx->lastError == GL_NO_ERROR) ctx->lastError = GL_INVALID_ENUM; return; } // invalid arguments if (width < 0 || height < 0) { if (ctx->lastError == GL_NO_ERROR) ctx->lastError = GL_INVALID_OPERATION; return; } // read to buffer if (ctx->pixelPackBufferBufferBinding) return; // read to use pointer { const int targetRowLength = (ctx->pixelPackRowLength != 0) ? (ctx->pixelPackRowLength) : (width); const int targetSkipRows = ctx->pixelPackSkipRows; const int targetSkipPixels = ctx->pixelPackSkipPixels; const int infiniteHeight = targetSkipRows + height; // as much as needed const int targetRowPitch = (ctx->pixelPackAlignment == 0) ? (targetRowLength * transferFormat.getPixelSize()) : (deAlign32(targetRowLength * transferFormat.getPixelSize(), ctx->pixelPackAlignment)); // Create access to the whole copy target const tcu::PixelBufferAccess targetAccess (transferFormat, targetRowLength, infiniteHeight, 1, targetRowPitch, 0, pixels); // Select (skip_pixels, skip_rows, width, height) subregion from it. Clip to horizontal boundaries const tcu::PixelBufferAccess targetRectAccess = tcu::getSubregion(targetAccess, de::clamp(targetSkipPixels, 0, targetAccess.getWidth()-1), targetSkipRows, de::clamp(width, 0, de::max(0, targetAccess.getWidth() - targetSkipPixels)), height); tcu::clear(targetRectAccess, clearColor); } } GLW_APICALL void GLW_APIENTRY glBindBuffer (GLenum target, GLuint buffer) { Context* const ctx = getCurrentContext(); if (target == GL_PIXEL_PACK_BUFFER) ctx->pixelPackBufferBufferBinding = buffer; } GLW_APICALL void GLW_APIENTRY glDeleteBuffers (GLsizei n, const GLuint* buffers) { Context* const ctx = getCurrentContext(); for (GLsizei ndx = 0; ndx < n; ++ndx) if (buffers[ndx] && buffers[ndx] == ctx->pixelPackBufferBufferBinding) ctx->pixelPackBufferBufferBinding = 0; } GLW_APICALL GLint GLW_APIENTRY glGetAttribLocation (GLuint program, const GLchar* name) { DE_UNREF(program); return (GLint)(deStringHash(name) & 0x7FFFFFFF); } void initFunctions (glw::Functions* gl) { # include "tcuNullRenderContextInitFuncs.inl" } static tcu::RenderTarget toRenderTarget (const RenderConfig& renderCfg) { const int width = getValueOrDefault(renderCfg, &RenderConfig::width, 256); const int height = getValueOrDefault(renderCfg, &RenderConfig::height, 256); const int redBits = getValueOrDefault(renderCfg, &RenderConfig::redBits, 8); const int greenBits = getValueOrDefault(renderCfg, &RenderConfig::greenBits, 8); const int blueBits = getValueOrDefault(renderCfg, &RenderConfig::blueBits, 8); const int alphaBits = getValueOrDefault(renderCfg, &RenderConfig::alphaBits, 8); const int depthBits = getValueOrDefault(renderCfg, &RenderConfig::depthBits, 24); const int stencilBits = getValueOrDefault(renderCfg, &RenderConfig::stencilBits, 8); const int numSamples = getValueOrDefault(renderCfg, &RenderConfig::numSamples, 0); return tcu::RenderTarget(width, height, tcu::PixelFormat(redBits, greenBits, blueBits, alphaBits), depthBits, stencilBits, numSamples); } RenderContext::RenderContext (const RenderConfig& renderCfg) : m_ctxType (renderCfg.type) , m_renderTarget (toRenderTarget(renderCfg)) , m_context (DE_NULL) { m_context = new Context(m_ctxType); initFunctions(&m_functions); setCurrentContext(m_context); } RenderContext::~RenderContext (void) { setCurrentContext(DE_NULL); delete m_context; } void RenderContext::postIterate (void) { } void RenderContext::makeCurrent (void) { } } // null } // tcu