/*------------------------------------------------------------------------- * 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 Reference Rendering Context. *//*--------------------------------------------------------------------*/ #include "sglrReferenceContext.hpp" #include "sglrReferenceUtils.hpp" #include "sglrShaderProgram.hpp" #include "tcuTextureUtil.hpp" #include "tcuMatrix.hpp" #include "tcuMatrixUtil.hpp" #include "tcuVectorUtil.hpp" #include "gluDefs.hpp" #include "gluTextureUtil.hpp" #include "gluContextInfo.hpp" #include "glwFunctions.hpp" #include "glwEnums.hpp" #include "deMemory.h" #include "rrFragmentOperations.hpp" #include "rrRenderer.hpp" #include namespace sglr { using std::map; using std::vector; using tcu::IVec2; using tcu::IVec4; using tcu::RGBA; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; // Reference context implementation using namespace rc; using tcu::ConstPixelBufferAccess; using tcu::PixelBufferAccess; using tcu::TextureFormat; // Utilities for ReferenceContext #define RC_RET_VOID #define RC_ERROR_RET(ERR, RET) \ do \ { \ setError(ERR); \ return RET; \ } while (false) #define RC_IF_ERROR(COND, ERR, RET) \ do \ { \ if (COND) \ RC_ERROR_RET(ERR, RET); \ } while (false) static inline tcu::PixelBufferAccess nullAccess(void) { return tcu::PixelBufferAccess(TextureFormat(TextureFormat::R, TextureFormat::UNSIGNED_INT8), 0, 0, 0, DE_NULL); } static inline bool isEmpty(const tcu::ConstPixelBufferAccess &access) { return access.getWidth() == 0 || access.getHeight() == 0 || access.getDepth() == 0; } static inline bool isEmpty(const rr::MultisampleConstPixelBufferAccess &access) { return access.raw().getWidth() == 0 || access.raw().getHeight() == 0 || access.raw().getDepth() == 0; } static inline bool isEmpty(const IVec4 &rect) { return rect.z() == 0 || rect.w() == 0; } inline int getNumMipLevels1D(int size) { return deLog2Floor32(size) + 1; } inline int getNumMipLevels2D(int width, int height) { return deLog2Floor32(de::max(width, height)) + 1; } inline int getNumMipLevels3D(int width, int height, int depth) { return deLog2Floor32(de::max(width, de::max(height, depth))) + 1; } inline int getMipLevelSize(int baseLevelSize, int levelNdx) { return de::max(baseLevelSize >> levelNdx, 1); } inline bool isMipmapFilter(const tcu::Sampler::FilterMode mode) { return mode != tcu::Sampler::NEAREST && mode != tcu::Sampler::LINEAR; } static tcu::CubeFace texTargetToFace(Framebuffer::TexTarget target) { switch (target) { case Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_X: return tcu::CUBEFACE_NEGATIVE_X; case Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_X: return tcu::CUBEFACE_POSITIVE_X; case Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Y: return tcu::CUBEFACE_NEGATIVE_Y; case Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_Y: return tcu::CUBEFACE_POSITIVE_Y; case Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Z: return tcu::CUBEFACE_NEGATIVE_Z; case Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_Z: return tcu::CUBEFACE_POSITIVE_Z; default: return tcu::CUBEFACE_LAST; } } static Framebuffer::TexTarget texLayeredTypeToTarget(Texture::Type type) { switch (type) { case Texture::TYPE_2D_ARRAY: return Framebuffer::TEXTARGET_2D_ARRAY; case Texture::TYPE_3D: return Framebuffer::TEXTARGET_3D; case Texture::TYPE_CUBE_MAP_ARRAY: return Framebuffer::TEXTARGET_CUBE_MAP_ARRAY; default: return Framebuffer::TEXTARGET_LAST; } } static tcu::CubeFace mapGLCubeFace(uint32_t face) { switch (face) { case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: return tcu::CUBEFACE_NEGATIVE_X; case GL_TEXTURE_CUBE_MAP_POSITIVE_X: return tcu::CUBEFACE_POSITIVE_X; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return tcu::CUBEFACE_NEGATIVE_Y; case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: return tcu::CUBEFACE_POSITIVE_Y; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return tcu::CUBEFACE_NEGATIVE_Z; case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: return tcu::CUBEFACE_POSITIVE_Z; default: return tcu::CUBEFACE_LAST; } } tcu::TextureFormat toTextureFormat(const tcu::PixelFormat &pixelFmt) { static const struct { tcu::PixelFormat pixelFmt; tcu::TextureFormat texFmt; } pixelFormatMap[] = { {tcu::PixelFormat(8, 8, 8, 8), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8)}, {tcu::PixelFormat(8, 8, 8, 0), tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_INT8)}, {tcu::PixelFormat(4, 4, 4, 4), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_SHORT_4444)}, {tcu::PixelFormat(5, 5, 5, 1), tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_SHORT_5551)}, {tcu::PixelFormat(5, 6, 5, 0), tcu::TextureFormat(tcu::TextureFormat::RGB, tcu::TextureFormat::UNORM_SHORT_565)}}; for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(pixelFormatMap); ndx++) { if (pixelFormatMap[ndx].pixelFmt == pixelFmt) return pixelFormatMap[ndx].texFmt; } TCU_FAIL("Can't map pixel format to texture format"); } tcu::TextureFormat toNonSRGBFormat(const tcu::TextureFormat &fmt) { switch (fmt.order) { case tcu::TextureFormat::sRGB: return tcu::TextureFormat(tcu::TextureFormat::RGB, fmt.type); case tcu::TextureFormat::sRGBA: return tcu::TextureFormat(tcu::TextureFormat::RGBA, fmt.type); default: return fmt; } } tcu::TextureFormat getDepthFormat(int depthBits) { switch (depthBits) { case 8: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT8); case 16: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNORM_INT16); case 24: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::UNSIGNED_INT_24_8); case 32: return tcu::TextureFormat(tcu::TextureFormat::D, tcu::TextureFormat::FLOAT); default: TCU_FAIL("Can't map depth buffer format"); } } tcu::TextureFormat getStencilFormat(int stencilBits) { switch (stencilBits) { case 8: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT8); case 16: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT16); case 24: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT_24_8); case 32: return tcu::TextureFormat(tcu::TextureFormat::S, tcu::TextureFormat::UNSIGNED_INT32); default: TCU_FAIL("Can't map depth buffer format"); } } static inline tcu::IVec4 intersect(const tcu::IVec4 &a, const tcu::IVec4 &b) { int x0 = de::max(a.x(), b.x()); int y0 = de::max(a.y(), b.y()); int x1 = de::min(a.x() + a.z(), b.x() + b.z()); int y1 = de::min(a.y() + a.w(), b.y() + b.w()); int w = de::max(0, x1 - x0); int h = de::max(0, y1 - y0); return tcu::IVec4(x0, y0, w, h); } static inline tcu::IVec4 getBufferRect(const rr::MultisampleConstPixelBufferAccess &access) { return tcu::IVec4(0, 0, access.raw().getHeight(), access.raw().getDepth()); } ReferenceContextLimits::ReferenceContextLimits(const glu::RenderContext &renderCtx) : contextType(renderCtx.getType()) , maxTextureImageUnits(0) , maxTexture2DSize(0) , maxTextureCubeSize(0) , maxTexture2DArrayLayers(0) , maxTexture3DSize(0) , maxRenderbufferSize(0) , maxVertexAttribs(0) , subpixelBits(0) { const glw::Functions &gl = renderCtx.getFunctions(); // When the OpenGL ES's major version bigger than 3, and the expect context version is 3, // we need query the real GL context version and update the real version to reference context. if (glu::IsES3Compatible(gl) && isES2Context(contextType)) { int majorVersion = contextType.getMajorVersion(); int minorVersion = contextType.getMinorVersion(); gl.getIntegerv(GL_MAJOR_VERSION, &majorVersion); gl.getIntegerv(GL_MINOR_VERSION, &minorVersion); contextType.setAPI(glu::ApiType::es(majorVersion, minorVersion)); } gl.getIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureImageUnits); gl.getIntegerv(GL_MAX_TEXTURE_SIZE, &maxTexture2DSize); gl.getIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &maxTextureCubeSize); gl.getIntegerv(GL_MAX_RENDERBUFFER_SIZE, &maxRenderbufferSize); gl.getIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs); gl.getIntegerv(GL_SUBPIXEL_BITS, &subpixelBits); if (contextSupports(contextType, glu::ApiType::es(3, 0)) || glu::isContextTypeGLCore(contextType)) { gl.getIntegerv(GL_MAX_ARRAY_TEXTURE_LAYERS, &maxTexture2DArrayLayers); gl.getIntegerv(GL_MAX_3D_TEXTURE_SIZE, &maxTexture3DSize); } // Limit texture sizes to supported values maxTexture2DSize = de::min(maxTexture2DSize, (int)MAX_TEXTURE_SIZE); maxTextureCubeSize = de::min(maxTextureCubeSize, (int)MAX_TEXTURE_SIZE); maxTexture3DSize = de::min(maxTexture3DSize, (int)MAX_TEXTURE_SIZE); GLU_EXPECT_NO_ERROR(gl.getError(), GL_NO_ERROR); // \todo [pyry] Figure out following things: // + supported fbo configurations // ... // \todo [2013-08-01 pyry] Do we want to make these conditional based on renderCtx? addExtension("GL_EXT_color_buffer_half_float"); addExtension("GL_EXT_color_buffer_float"); if (contextSupports(contextType, glu::ApiType::es(3, 1))) addExtension("GL_EXT_texture_cube_map_array"); } void ReferenceContextLimits::addExtension(const char *extension) { extensionList.push_back(extension); if (!extensionStr.empty()) extensionStr += " "; extensionStr += extension; } ReferenceContextBuffers::ReferenceContextBuffers(const tcu::PixelFormat &colorBits, int depthBits, int stencilBits, int width, int height, int samples) { m_colorbuffer.setStorage(toTextureFormat(colorBits), samples, width, height); if (depthBits > 0) m_depthbuffer.setStorage(getDepthFormat(depthBits), samples, width, height); if (stencilBits > 0) m_stencilbuffer.setStorage(getStencilFormat(stencilBits), samples, width, height); } ReferenceContext::StencilState::StencilState(void) : func(GL_ALWAYS) , ref(0) , opMask(~0u) , opStencilFail(GL_KEEP) , opDepthFail(GL_KEEP) , opDepthPass(GL_KEEP) , writeMask(~0u) { } ReferenceContext::ReferenceContext(const ReferenceContextLimits &limits, const rr::MultisamplePixelBufferAccess &colorbuffer, const rr::MultisamplePixelBufferAccess &depthbuffer, const rr::MultisamplePixelBufferAccess &stencilbuffer) : Context(limits.contextType) , m_limits(limits) , m_defaultColorbuffer(colorbuffer) , m_defaultDepthbuffer(depthbuffer) , m_defaultStencilbuffer(stencilbuffer) , m_clientVertexArray(0, m_limits.maxVertexAttribs) , m_viewport(0, 0, colorbuffer.raw().getHeight(), colorbuffer.raw().getDepth()) , m_activeTexture(0) , m_textureUnits(m_limits.maxTextureImageUnits) , m_emptyTex1D() , m_emptyTex2D(isES2Context(limits.contextType)) , m_emptyTexCube(!isES2Context(limits.contextType)) , m_emptyTex2DArray() , m_emptyTex3D() , m_emptyTexCubeArray() , m_pixelUnpackRowLength(0) , m_pixelUnpackSkipRows(0) , m_pixelUnpackSkipPixels(0) , m_pixelUnpackImageHeight(0) , m_pixelUnpackSkipImages(0) , m_pixelUnpackAlignment(4) , m_pixelPackAlignment(4) , m_readFramebufferBinding(DE_NULL) , m_drawFramebufferBinding(DE_NULL) , m_renderbufferBinding(DE_NULL) , m_vertexArrayBinding(DE_NULL) , m_currentProgram(DE_NULL) , m_arrayBufferBinding(DE_NULL) , m_pixelPackBufferBinding(DE_NULL) , m_pixelUnpackBufferBinding(DE_NULL) , m_transformFeedbackBufferBinding(DE_NULL) , m_uniformBufferBinding(DE_NULL) , m_copyReadBufferBinding(DE_NULL) , m_copyWriteBufferBinding(DE_NULL) , m_drawIndirectBufferBinding(DE_NULL) , m_clearColor(0.0f, 0.0f, 0.0f, 0.0f) , m_clearDepth(1.0f) , m_clearStencil(0) , m_scissorEnabled(false) , m_scissorBox(m_viewport) , m_stencilTestEnabled(false) , m_depthTestEnabled(false) , m_depthFunc(GL_LESS) , m_depthRangeNear(0.0f) , m_depthRangeFar(1.0f) , m_polygonOffsetFactor(0.0f) , m_polygonOffsetUnits(0.0f) , m_polygonOffsetFillEnabled(false) , m_provokingFirstVertexConvention(false) , m_blendEnabled(false) , m_blendModeRGB(GL_FUNC_ADD) , m_blendModeAlpha(GL_FUNC_ADD) , m_blendFactorSrcRGB(GL_ONE) , m_blendFactorDstRGB(GL_ZERO) , m_blendFactorSrcAlpha(GL_ONE) , m_blendFactorDstAlpha(GL_ZERO) , m_blendColor(0.0f, 0.0f, 0.0f, 0.0f) , m_sRGBUpdateEnabled(true) , m_depthClampEnabled(false) , m_colorMask(true, true, true, true) , m_depthMask(true) , m_currentAttribs(m_limits.maxVertexAttribs, rr::GenericVec4(tcu::Vec4(0, 0, 0, 1))) , m_lineWidth(1.0f) , m_primitiveRestartFixedIndex(false) , m_primitiveRestartSettableIndex(false) , m_primitiveRestartIndex(0) , m_lastError(GL_NO_ERROR) { // Create empty textures to be used when texture objects are incomplete. m_emptyTex1D.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex1D.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex1D.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTex1D.getSampler().magFilter = tcu::Sampler::NEAREST; m_emptyTex1D.allocLevel(0, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1); m_emptyTex1D.getLevel(0).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0); m_emptyTex1D.updateView(tcu::Sampler::MODE_LAST); m_emptyTex2D.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex2D.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex2D.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTex2D.getSampler().magFilter = tcu::Sampler::NEAREST; m_emptyTex2D.allocLevel(0, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1); m_emptyTex2D.getLevel(0).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0); m_emptyTex2D.updateView(tcu::Sampler::MODE_LAST); m_emptyTexCube.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTexCube.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTexCube.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTexCube.getSampler().magFilter = tcu::Sampler::NEAREST; for (int face = 0; face < tcu::CUBEFACE_LAST; face++) { m_emptyTexCube.allocFace(0, (tcu::CubeFace)face, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1); m_emptyTexCube.getFace(0, (tcu::CubeFace)face).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0); } m_emptyTexCube.updateView(tcu::Sampler::MODE_LAST); m_emptyTex2DArray.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex2DArray.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex2DArray.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTex2DArray.getSampler().magFilter = tcu::Sampler::NEAREST; m_emptyTex2DArray.allocLevel(0, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1, 1); m_emptyTex2DArray.getLevel(0).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0); m_emptyTex2DArray.updateView(tcu::Sampler::MODE_LAST); m_emptyTex3D.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex3D.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex3D.getSampler().wrapR = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTex3D.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTex3D.getSampler().magFilter = tcu::Sampler::NEAREST; m_emptyTex3D.allocLevel(0, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1, 1); m_emptyTex3D.getLevel(0).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0); m_emptyTex3D.updateView(tcu::Sampler::MODE_LAST); m_emptyTexCubeArray.getSampler().wrapS = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTexCubeArray.getSampler().wrapT = tcu::Sampler::CLAMP_TO_EDGE; m_emptyTexCubeArray.getSampler().minFilter = tcu::Sampler::NEAREST; m_emptyTexCubeArray.getSampler().magFilter = tcu::Sampler::NEAREST; m_emptyTexCubeArray.allocLevel(0, tcu::TextureFormat(tcu::TextureFormat::RGBA, tcu::TextureFormat::UNORM_INT8), 1, 1, 6); for (int faceNdx = 0; faceNdx < 6; faceNdx++) m_emptyTexCubeArray.getLevel(0).setPixel(Vec4(0.0f, 0.0f, 0.0f, 1.0f), 0, 0, faceNdx); m_emptyTexCubeArray.updateView(tcu::Sampler::MODE_LAST); for (int unitNdx = 0; unitNdx < m_limits.maxTextureImageUnits; unitNdx++) m_textureUnits[unitNdx].defaultCubeTex.getSampler().seamlessCubeMap = !isES2Context(limits.contextType); if (glu::isContextTypeGLCore(getType())) m_sRGBUpdateEnabled = false; } ReferenceContext::~ReferenceContext(void) { // Destroy all objects -- verifies that ref counting works { vector vertexArrays; m_vertexArrays.getAll(vertexArrays); for (vector::iterator i = vertexArrays.begin(); i != vertexArrays.end(); i++) deleteVertexArray(*i); DE_ASSERT(m_clientVertexArray.getRefCount() == 1); } { vector textures; m_textures.getAll(textures); for (vector::iterator i = textures.begin(); i != textures.end(); i++) deleteTexture(*i); } { vector framebuffers; m_framebuffers.getAll(framebuffers); for (vector::iterator i = framebuffers.begin(); i != framebuffers.end(); i++) deleteFramebuffer(*i); } { vector renderbuffers; m_renderbuffers.getAll(renderbuffers); for (vector::iterator i = renderbuffers.begin(); i != renderbuffers.end(); i++) deleteRenderbuffer(*i); } { vector buffers; m_buffers.getAll(buffers); for (vector::iterator i = buffers.begin(); i != buffers.end(); i++) deleteBuffer(*i); } { vector programs; m_programs.getAll(programs); for (vector::iterator i = programs.begin(); i != programs.end(); i++) deleteProgramObject(*i); } } void ReferenceContext::activeTexture(uint32_t texture) { if (deInBounds32(texture, GL_TEXTURE0, GL_TEXTURE0 + (uint32_t)m_textureUnits.size())) m_activeTexture = texture - GL_TEXTURE0; else setError(GL_INVALID_ENUM); } void ReferenceContext::setTex1DBinding(int unitNdx, Texture1D *texture) { if (m_textureUnits[unitNdx].tex1DBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].tex1DBinding); m_textureUnits[unitNdx].tex1DBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].tex1DBinding = texture; } } void ReferenceContext::setTex2DBinding(int unitNdx, Texture2D *texture) { if (m_textureUnits[unitNdx].tex2DBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].tex2DBinding); m_textureUnits[unitNdx].tex2DBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].tex2DBinding = texture; } } void ReferenceContext::setTexCubeBinding(int unitNdx, TextureCube *texture) { if (m_textureUnits[unitNdx].texCubeBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].texCubeBinding); m_textureUnits[unitNdx].texCubeBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].texCubeBinding = texture; } } void ReferenceContext::setTex2DArrayBinding(int unitNdx, Texture2DArray *texture) { if (m_textureUnits[unitNdx].tex2DArrayBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].tex2DArrayBinding); m_textureUnits[unitNdx].tex2DArrayBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].tex2DArrayBinding = texture; } } void ReferenceContext::setTex3DBinding(int unitNdx, Texture3D *texture) { if (m_textureUnits[unitNdx].tex3DBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].tex3DBinding); m_textureUnits[unitNdx].tex3DBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].tex3DBinding = texture; } } void ReferenceContext::setTexCubeArrayBinding(int unitNdx, TextureCubeArray *texture) { if (m_textureUnits[unitNdx].texCubeArrayBinding) { m_textures.releaseReference(m_textureUnits[unitNdx].texCubeArrayBinding); m_textureUnits[unitNdx].texCubeArrayBinding = DE_NULL; } if (texture) { m_textures.acquireReference(texture); m_textureUnits[unitNdx].texCubeArrayBinding = texture; } } void ReferenceContext::bindTexture(uint32_t target, uint32_t texture) { int unitNdx = m_activeTexture; RC_IF_ERROR(target != GL_TEXTURE_1D && target != GL_TEXTURE_2D && target != GL_TEXTURE_CUBE_MAP && target != GL_TEXTURE_2D_ARRAY && target != GL_TEXTURE_3D && target != GL_TEXTURE_CUBE_MAP_ARRAY, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(glu::isContextTypeES(m_limits.contextType) && (target == GL_TEXTURE_1D), GL_INVALID_ENUM, RC_RET_VOID); if (texture == 0) { // Clear binding. switch (target) { case GL_TEXTURE_1D: setTex1DBinding(unitNdx, DE_NULL); break; case GL_TEXTURE_2D: setTex2DBinding(unitNdx, DE_NULL); break; case GL_TEXTURE_CUBE_MAP: setTexCubeBinding(unitNdx, DE_NULL); break; case GL_TEXTURE_2D_ARRAY: setTex2DArrayBinding(unitNdx, DE_NULL); break; case GL_TEXTURE_3D: setTex3DBinding(unitNdx, DE_NULL); break; case GL_TEXTURE_CUBE_MAP_ARRAY: setTexCubeArrayBinding(unitNdx, DE_NULL); break; default: DE_ASSERT(false); } } else { Texture *texObj = m_textures.find(texture); if (texObj) { // Validate type. Texture::Type expectedType = Texture::TYPE_LAST; switch (target) { case GL_TEXTURE_1D: expectedType = Texture::TYPE_1D; break; case GL_TEXTURE_2D: expectedType = Texture::TYPE_2D; break; case GL_TEXTURE_CUBE_MAP: expectedType = Texture::TYPE_CUBE_MAP; break; case GL_TEXTURE_2D_ARRAY: expectedType = Texture::TYPE_2D_ARRAY; break; case GL_TEXTURE_3D: expectedType = Texture::TYPE_3D; break; case GL_TEXTURE_CUBE_MAP_ARRAY: expectedType = Texture::TYPE_CUBE_MAP_ARRAY; break; default: DE_ASSERT(false); } RC_IF_ERROR(texObj->getType() != expectedType, GL_INVALID_OPERATION, RC_RET_VOID); } else { // New texture object. bool seamlessCubeMap = !isES2Context(m_limits.contextType); switch (target) { case GL_TEXTURE_1D: texObj = new Texture1D(texture); break; case GL_TEXTURE_2D: texObj = new Texture2D(texture); break; case GL_TEXTURE_CUBE_MAP: texObj = new TextureCube(texture, seamlessCubeMap); break; case GL_TEXTURE_2D_ARRAY: texObj = new Texture2DArray(texture); break; case GL_TEXTURE_3D: texObj = new Texture3D(texture); break; case GL_TEXTURE_CUBE_MAP_ARRAY: texObj = new TextureCubeArray(texture); break; default: DE_ASSERT(false); } m_textures.insert(texObj); } switch (target) { case GL_TEXTURE_1D: setTex1DBinding(unitNdx, static_cast(texObj)); break; case GL_TEXTURE_2D: setTex2DBinding(unitNdx, static_cast(texObj)); break; case GL_TEXTURE_CUBE_MAP: setTexCubeBinding(unitNdx, static_cast(texObj)); break; case GL_TEXTURE_2D_ARRAY: setTex2DArrayBinding(unitNdx, static_cast(texObj)); break; case GL_TEXTURE_3D: setTex3DBinding(unitNdx, static_cast(texObj)); break; case GL_TEXTURE_CUBE_MAP_ARRAY: setTexCubeArrayBinding(unitNdx, static_cast(texObj)); break; default: DE_ASSERT(false); } } } void ReferenceContext::genTextures(int numTextures, uint32_t *textures) { while (numTextures--) *textures++ = m_textures.allocateName(); } void ReferenceContext::deleteTextures(int numTextures, const uint32_t *textures) { for (int i = 0; i < numTextures; i++) { uint32_t name = textures[i]; Texture *texture = name ? m_textures.find(name) : DE_NULL; if (texture) deleteTexture(texture); } } void ReferenceContext::deleteTexture(Texture *texture) { // Unbind from context for (int unitNdx = 0; unitNdx < (int)m_textureUnits.size(); unitNdx++) { if (m_textureUnits[unitNdx].tex1DBinding == texture) setTex1DBinding(unitNdx, DE_NULL); else if (m_textureUnits[unitNdx].tex2DBinding == texture) setTex2DBinding(unitNdx, DE_NULL); else if (m_textureUnits[unitNdx].texCubeBinding == texture) setTexCubeBinding(unitNdx, DE_NULL); else if (m_textureUnits[unitNdx].tex2DArrayBinding == texture) setTex2DArrayBinding(unitNdx, DE_NULL); else if (m_textureUnits[unitNdx].tex3DBinding == texture) setTex3DBinding(unitNdx, DE_NULL); else if (m_textureUnits[unitNdx].texCubeArrayBinding == texture) setTexCubeArrayBinding(unitNdx, DE_NULL); } // Unbind from currently bound framebuffers for (int ndx = 0; ndx < 2; ndx++) { rc::Framebuffer *framebufferBinding = ndx ? m_drawFramebufferBinding : m_readFramebufferBinding; if (framebufferBinding) { int releaseRefCount = (framebufferBinding == m_drawFramebufferBinding ? 1 : 0) + (framebufferBinding == m_readFramebufferBinding ? 1 : 0); for (int point = 0; point < Framebuffer::ATTACHMENTPOINT_LAST; point++) { Framebuffer::Attachment &attachment = framebufferBinding->getAttachment((Framebuffer::AttachmentPoint)point); if (attachment.name == texture->getName()) { for (int refNdx = 0; refNdx < releaseRefCount; refNdx++) releaseFboAttachmentReference(attachment); attachment = Framebuffer::Attachment(); } } } } DE_ASSERT(texture->getRefCount() == 1); m_textures.releaseReference(texture); } void ReferenceContext::bindFramebuffer(uint32_t target, uint32_t name) { Framebuffer *fbo = DE_NULL; RC_IF_ERROR(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER, GL_INVALID_ENUM, RC_RET_VOID); if (name != 0) { // Find or create framebuffer object. fbo = m_framebuffers.find(name); if (!fbo) { fbo = new Framebuffer(name); m_framebuffers.insert(fbo); } } for (int ndx = 0; ndx < 2; ndx++) { uint32_t bindingTarget = ndx ? GL_DRAW_FRAMEBUFFER : GL_READ_FRAMEBUFFER; rc::Framebuffer *&binding = ndx ? m_drawFramebufferBinding : m_readFramebufferBinding; if (target != GL_FRAMEBUFFER && target != bindingTarget) continue; // Doesn't match this target. // Remove old references if (binding) { // Clear all attachment point references for (int point = 0; point < Framebuffer::ATTACHMENTPOINT_LAST; point++) releaseFboAttachmentReference(binding->getAttachment((Framebuffer::AttachmentPoint)point)); m_framebuffers.releaseReference(binding); } // Create new references if (fbo) { m_framebuffers.acquireReference(fbo); for (int point = 0; point < Framebuffer::ATTACHMENTPOINT_LAST; point++) acquireFboAttachmentReference(fbo->getAttachment((Framebuffer::AttachmentPoint)point)); } binding = fbo; } } void ReferenceContext::genFramebuffers(int numFramebuffers, uint32_t *framebuffers) { while (numFramebuffers--) *framebuffers++ = m_framebuffers.allocateName(); } void ReferenceContext::deleteFramebuffer(Framebuffer *framebuffer) { // Remove bindings. if (m_drawFramebufferBinding == framebuffer) bindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); if (m_readFramebufferBinding == framebuffer) bindFramebuffer(GL_READ_FRAMEBUFFER, 0); DE_ASSERT(framebuffer->getRefCount() == 1); m_framebuffers.releaseReference(framebuffer); } void ReferenceContext::deleteFramebuffers(int numFramebuffers, const uint32_t *framebuffers) { for (int i = 0; i < numFramebuffers; i++) { uint32_t name = framebuffers[i]; Framebuffer *framebuffer = name ? m_framebuffers.find(name) : DE_NULL; if (framebuffer) deleteFramebuffer(framebuffer); } } void ReferenceContext::bindRenderbuffer(uint32_t target, uint32_t name) { Renderbuffer *rbo = DE_NULL; RC_IF_ERROR(target != GL_RENDERBUFFER, GL_INVALID_ENUM, RC_RET_VOID); if (name != 0) { rbo = m_renderbuffers.find(name); if (!rbo) { rbo = new Renderbuffer(name); m_renderbuffers.insert(rbo); } } // Remove old reference if (m_renderbufferBinding) m_renderbuffers.releaseReference(m_renderbufferBinding); // Create new reference if (rbo) m_renderbuffers.acquireReference(rbo); m_renderbufferBinding = rbo; } void ReferenceContext::genRenderbuffers(int numRenderbuffers, uint32_t *renderbuffers) { while (numRenderbuffers--) *renderbuffers++ = m_renderbuffers.allocateName(); } void ReferenceContext::deleteRenderbuffer(Renderbuffer *renderbuffer) { if (m_renderbufferBinding == renderbuffer) bindRenderbuffer(GL_RENDERBUFFER, 0); // Unbind from currently bound framebuffers for (int ndx = 0; ndx < 2; ndx++) { rc::Framebuffer *framebufferBinding = ndx ? m_drawFramebufferBinding : m_readFramebufferBinding; if (framebufferBinding) { int releaseRefCount = (framebufferBinding == m_drawFramebufferBinding ? 1 : 0) + (framebufferBinding == m_readFramebufferBinding ? 1 : 0); for (int point = 0; point < Framebuffer::ATTACHMENTPOINT_LAST; point++) { Framebuffer::Attachment &attachment = framebufferBinding->getAttachment((Framebuffer::AttachmentPoint)point); if (attachment.name == renderbuffer->getName()) { for (int refNdx = 0; refNdx < releaseRefCount; refNdx++) releaseFboAttachmentReference(attachment); attachment = Framebuffer::Attachment(); } } } } DE_ASSERT(renderbuffer->getRefCount() == 1); m_renderbuffers.releaseReference(renderbuffer); } void ReferenceContext::deleteRenderbuffers(int numRenderbuffers, const uint32_t *renderbuffers) { for (int i = 0; i < numRenderbuffers; i++) { uint32_t name = renderbuffers[i]; Renderbuffer *renderbuffer = name ? m_renderbuffers.find(name) : DE_NULL; if (renderbuffer) deleteRenderbuffer(renderbuffer); } } void ReferenceContext::pixelStorei(uint32_t pname, int param) { switch (pname) { case GL_UNPACK_ALIGNMENT: RC_IF_ERROR(param != 1 && param != 2 && param != 4 && param != 8, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackAlignment = param; break; case GL_PACK_ALIGNMENT: RC_IF_ERROR(param != 1 && param != 2 && param != 4 && param != 8, GL_INVALID_VALUE, RC_RET_VOID); m_pixelPackAlignment = param; break; case GL_UNPACK_ROW_LENGTH: RC_IF_ERROR(param < 0, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackRowLength = param; break; case GL_UNPACK_SKIP_ROWS: RC_IF_ERROR(param < 0, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackSkipRows = param; break; case GL_UNPACK_SKIP_PIXELS: RC_IF_ERROR(param < 0, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackSkipPixels = param; break; case GL_UNPACK_IMAGE_HEIGHT: RC_IF_ERROR(param < 0, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackImageHeight = param; break; case GL_UNPACK_SKIP_IMAGES: RC_IF_ERROR(param < 0, GL_INVALID_VALUE, RC_RET_VOID); m_pixelUnpackSkipImages = param; break; default: setError(GL_INVALID_ENUM); } } tcu::ConstPixelBufferAccess ReferenceContext::getUnpack2DAccess(const tcu::TextureFormat &format, int width, int height, const void *data) { int pixelSize = format.getPixelSize(); int rowLen = m_pixelUnpackRowLength > 0 ? m_pixelUnpackRowLength : width; int rowPitch = deAlign32(rowLen * pixelSize, m_pixelUnpackAlignment); const uint8_t *ptr = (const uint8_t *)data + m_pixelUnpackSkipRows * rowPitch + m_pixelUnpackSkipPixels * pixelSize; return tcu::ConstPixelBufferAccess(format, width, height, 1, rowPitch, 0, ptr); } tcu::ConstPixelBufferAccess ReferenceContext::getUnpack3DAccess(const tcu::TextureFormat &format, int width, int height, int depth, const void *data) { int pixelSize = format.getPixelSize(); int rowLen = m_pixelUnpackRowLength > 0 ? m_pixelUnpackRowLength : width; int imageHeight = m_pixelUnpackImageHeight > 0 ? m_pixelUnpackImageHeight : height; int rowPitch = deAlign32(rowLen * pixelSize, m_pixelUnpackAlignment); int slicePitch = imageHeight * rowPitch; const uint8_t *ptr = (const uint8_t *)data + m_pixelUnpackSkipImages * slicePitch + m_pixelUnpackSkipRows * rowPitch + m_pixelUnpackSkipPixels * pixelSize; return tcu::ConstPixelBufferAccess(format, width, height, depth, rowPitch, slicePitch, ptr); } static tcu::TextureFormat mapInternalFormat(uint32_t internalFormat) { switch (internalFormat) { case GL_ALPHA: return TextureFormat(TextureFormat::A, TextureFormat::UNORM_INT8); case GL_LUMINANCE: return TextureFormat(TextureFormat::L, TextureFormat::UNORM_INT8); case GL_LUMINANCE_ALPHA: return TextureFormat(TextureFormat::LA, TextureFormat::UNORM_INT8); case GL_RGB: return TextureFormat(TextureFormat::RGB, TextureFormat::UNORM_INT8); case GL_RGBA: return TextureFormat(TextureFormat::RGBA, TextureFormat::UNORM_INT8); default: return glu::mapGLInternalFormat(internalFormat); } } static void depthValueFloatClampCopy(const PixelBufferAccess &dst, const ConstPixelBufferAccess &src) { int width = dst.getWidth(); int height = dst.getHeight(); int depth = dst.getDepth(); DE_ASSERT(src.getWidth() == width && src.getHeight() == height && src.getDepth() == depth); // clamping copy if (src.getFormat().order == tcu::TextureFormat::DS && dst.getFormat().order == tcu::TextureFormat::DS) { // copy only depth and stencil for (int z = 0; z < depth; z++) for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) { dst.setPixDepth(de::clamp(src.getPixDepth(x, y, z), 0.0f, 1.0f), x, y, z); dst.setPixStencil(src.getPixStencil(x, y, z), x, y, z); } } else { // copy only depth for (int z = 0; z < depth; z++) for (int y = 0; y < height; y++) for (int x = 0; x < width; x++) dst.setPixDepth(de::clamp(src.getPixDepth(x, y, z), 0.0f, 1.0f), x, y, z); } } void ReferenceContext::texImage1D(uint32_t target, int level, uint32_t internalFormat, int width, int border, uint32_t format, uint32_t type, const void *data) { texImage2D(target, level, internalFormat, width, 1, border, format, type, data); } void ReferenceContext::texImage2D(uint32_t target, int level, uint32_t internalFormat, int width, int height, int border, uint32_t format, uint32_t type, const void *data) { texImage3D(target, level, internalFormat, width, height, 1, border, format, type, data); } static void clearToTextureInitialValue(PixelBufferAccess access) { const bool hasDepth = access.getFormat().order == tcu::TextureFormat::D || access.getFormat().order == tcu::TextureFormat::DS; const bool hasStencil = access.getFormat().order == tcu::TextureFormat::S || access.getFormat().order == tcu::TextureFormat::DS; const bool hasColor = !hasDepth && !hasStencil; if (hasDepth) tcu::clearDepth(access, 0.0f); if (hasStencil) tcu::clearStencil(access, 0u); if (hasColor) tcu::clear(access, Vec4(0.0f, 0.0f, 0.0f, 1.0f)); } void ReferenceContext::texImage3D(uint32_t target, int level, uint32_t internalFormat, int width, int height, int depth, int border, uint32_t format, uint32_t type, const void *data) { TextureUnit &unit = m_textureUnits[m_activeTexture]; const void *unpackPtr = getPixelUnpackPtr(data); const bool isDstFloatDepthFormat = (internalFormat == GL_DEPTH_COMPONENT32F || internalFormat == GL_DEPTH32F_STENCIL8); // depth components are limited to [0,1] range TextureFormat storageFmt; TextureFormat transferFmt; RC_IF_ERROR(border != 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || height < 0 || depth < 0 || level < 0, GL_INVALID_VALUE, RC_RET_VOID); // Map storage format. storageFmt = mapInternalFormat(internalFormat); RC_IF_ERROR(storageFmt.order == TextureFormat::CHANNELORDER_LAST || storageFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); // Map transfer format. transferFmt = glu::mapGLTransferFormat(format, type); RC_IF_ERROR(transferFmt.order == TextureFormat::CHANNELORDER_LAST || transferFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); if (target == GL_TEXTURE_1D && glu::isContextTypeGLCore(m_limits.contextType)) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture2DSize || height != 1 || depth != 1, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture1D *texture = unit.tex1DBinding ? unit.tex1DBinding : &unit.default1DTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack2DAccess(transferFmt, width, 1, unpackPtr); PixelBufferAccess dst(texture->getLevel(level)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getLevel(level)); } } else if (target == GL_TEXTURE_2D) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture2DSize || height > m_limits.maxTexture2DSize || depth != 1, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture2D *texture = unit.tex2DBinding ? unit.tex2DBinding : &unit.default2DTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width, height); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack2DAccess(transferFmt, width, height, unpackPtr); PixelBufferAccess dst(texture->getLevel(level)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getLevel(level)); } } else if (target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z) { // Validate size and level. RC_IF_ERROR(width != height || width > m_limits.maxTextureCubeSize || depth != 1, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTextureCubeSize), GL_INVALID_VALUE, RC_RET_VOID); TextureCube *texture = unit.texCubeBinding ? unit.texCubeBinding : &unit.defaultCubeTex; tcu::CubeFace face = mapGLCubeFace(target); if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasFace(level, face), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getFace(level, face)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocFace(level, face, storageFmt, width, height); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack2DAccess(transferFmt, width, height, unpackPtr); PixelBufferAccess dst(texture->getFace(level, face)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getFace(level, face)); } } else if (target == GL_TEXTURE_2D_ARRAY) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture2DSize || height > m_limits.maxTexture2DSize || depth > m_limits.maxTexture2DArrayLayers, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture2DArray *texture = unit.tex2DArrayBinding ? unit.tex2DArrayBinding : &unit.default2DArrayTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight() || depth != dst.getDepth(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width, height, depth); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack3DAccess(transferFmt, width, height, depth, unpackPtr); PixelBufferAccess dst(texture->getLevel(level)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getLevel(level)); } } else if (target == GL_TEXTURE_3D) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture3DSize || height > m_limits.maxTexture3DSize || depth > m_limits.maxTexture3DSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture3DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture3D *texture = unit.tex3DBinding ? unit.tex3DBinding : &unit.default3DTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight() || depth != dst.getDepth(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width, height, depth); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack3DAccess(transferFmt, width, height, depth, unpackPtr); PixelBufferAccess dst(texture->getLevel(level)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getLevel(level)); } } else if (target == GL_TEXTURE_CUBE_MAP_ARRAY) { // Validate size and level. RC_IF_ERROR(width != height || width > m_limits.maxTexture2DSize || depth % 6 != 0 || depth > m_limits.maxTexture2DArrayLayers, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); TextureCubeArray *texture = unit.texCubeArrayBinding ? unit.texCubeArrayBinding : &unit.defaultCubeArrayTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight() || depth != dst.getDepth(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width, height, depth); if (unpackPtr) { ConstPixelBufferAccess src = getUnpack3DAccess(transferFmt, width, height, depth, unpackPtr); PixelBufferAccess dst(texture->getLevel(level)); if (isDstFloatDepthFormat) depthValueFloatClampCopy(dst, src); else tcu::copy(dst, src); } else { // No data supplied, clear to initial clearToTextureInitialValue(texture->getLevel(level)); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::texSubImage1D(uint32_t target, int level, int xoffset, int width, uint32_t format, uint32_t type, const void *data) { texSubImage2D(target, level, xoffset, 0, width, 1, format, type, data); } void ReferenceContext::texSubImage2D(uint32_t target, int level, int xoffset, int yoffset, int width, int height, uint32_t format, uint32_t type, const void *data) { texSubImage3D(target, level, xoffset, yoffset, 0, width, height, 1, format, type, data); } void ReferenceContext::texSubImage3D(uint32_t target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, uint32_t format, uint32_t type, const void *data) { TextureUnit &unit = m_textureUnits[m_activeTexture]; RC_IF_ERROR(xoffset < 0 || yoffset < 0 || zoffset < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || height < 0 || depth < 0, GL_INVALID_VALUE, RC_RET_VOID); TextureFormat transferFmt = glu::mapGLTransferFormat(format, type); RC_IF_ERROR(transferFmt.order == TextureFormat::CHANNELORDER_LAST || transferFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); ConstPixelBufferAccess src = getUnpack3DAccess(transferFmt, width, height, depth, getPixelUnpackPtr(data)); if (target == GL_TEXTURE_1D && glu::isContextTypeGLCore(m_limits.contextType)) { Texture1D &texture = unit.tex1DBinding ? *unit.tex1DBinding : unit.default1DTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else if (target == GL_TEXTURE_2D) { Texture2D &texture = unit.tex2DBinding ? *unit.tex2DBinding : unit.default2DTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else if (target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z) { TextureCube &texture = unit.texCubeBinding ? *unit.texCubeBinding : unit.defaultCubeTex; tcu::CubeFace face = mapGLCubeFace(target); RC_IF_ERROR(!texture.hasFace(level, face), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getFace(level, face); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else if (target == GL_TEXTURE_3D) { Texture3D &texture = unit.tex3DBinding ? *unit.tex3DBinding : unit.default3DTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else if (target == GL_TEXTURE_2D_ARRAY) { Texture2DArray &texture = unit.tex2DArrayBinding ? *unit.tex2DArrayBinding : unit.default2DArrayTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else if (target == GL_TEXTURE_CUBE_MAP_ARRAY) { TextureCubeArray &texture = unit.texCubeArrayBinding ? *unit.texCubeArrayBinding : unit.defaultCubeArrayTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight() || zoffset + depth > dst.getDepth(), GL_INVALID_VALUE, RC_RET_VOID); // depth components are limited to [0,1] range if (dst.getFormat().order == tcu::TextureFormat::D || dst.getFormat().order == tcu::TextureFormat::DS) depthValueFloatClampCopy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); else tcu::copy(tcu::getSubregion(dst, xoffset, yoffset, zoffset, width, height, depth), src); } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::copyTexImage1D(uint32_t target, int level, uint32_t internalFormat, int x, int y, int width, int border) { TextureUnit &unit = m_textureUnits[m_activeTexture]; TextureFormat storageFmt; rr::MultisampleConstPixelBufferAccess src = getReadColorbuffer(); RC_IF_ERROR(border != 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || level < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(isEmpty(src), GL_INVALID_OPERATION, RC_RET_VOID); // Map storage format. storageFmt = mapInternalFormat(internalFormat); RC_IF_ERROR(storageFmt.order == TextureFormat::CHANNELORDER_LAST || storageFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); if (target == GL_TEXTURE_1D) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture2DSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture1D *texture = unit.tex1DBinding ? unit.tex1DBinding : &unit.default1DTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width); // Copy from current framebuffer. PixelBufferAccess dst = texture->getLevel(level); for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight())) continue; // Undefined pixel. dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y), xo, 0); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::copyTexImage2D(uint32_t target, int level, uint32_t internalFormat, int x, int y, int width, int height, int border) { TextureUnit &unit = m_textureUnits[m_activeTexture]; TextureFormat storageFmt; rr::MultisampleConstPixelBufferAccess src = getReadColorbuffer(); RC_IF_ERROR(border != 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || height < 0 || level < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(isEmpty(src), GL_INVALID_OPERATION, RC_RET_VOID); // Map storage format. storageFmt = mapInternalFormat(internalFormat); RC_IF_ERROR(storageFmt.order == TextureFormat::CHANNELORDER_LAST || storageFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); if (target == GL_TEXTURE_2D) { // Validate size and level. RC_IF_ERROR(width > m_limits.maxTexture2DSize || height > m_limits.maxTexture2DSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTexture2DSize), GL_INVALID_VALUE, RC_RET_VOID); Texture2D *texture = unit.tex2DBinding ? unit.tex2DBinding : &unit.default2DTex; if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasLevel(level), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getLevel(level)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocLevel(level, storageFmt, width, height); // Copy from current framebuffer. PixelBufferAccess dst = texture->getLevel(level); for (int yo = 0; yo < height; yo++) for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight()) || !de::inBounds(y + yo, 0, src.raw().getDepth())) continue; // Undefined pixel. dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y + yo), xo, yo); } } else if (target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z) { // Validate size and level. RC_IF_ERROR(width != height || width > m_limits.maxTextureCubeSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(level > deLog2Floor32(m_limits.maxTextureCubeSize), GL_INVALID_VALUE, RC_RET_VOID); TextureCube *texture = unit.texCubeBinding ? unit.texCubeBinding : &unit.defaultCubeTex; tcu::CubeFace face = mapGLCubeFace(target); if (texture->isImmutable()) { RC_IF_ERROR(!texture->hasFace(level, face), GL_INVALID_OPERATION, RC_RET_VOID); ConstPixelBufferAccess dst(texture->getFace(level, face)); RC_IF_ERROR(storageFmt != dst.getFormat() || width != dst.getWidth() || height != dst.getHeight(), GL_INVALID_OPERATION, RC_RET_VOID); } else texture->allocFace(level, face, storageFmt, width, height); // Copy from current framebuffer. PixelBufferAccess dst = texture->getFace(level, face); for (int yo = 0; yo < height; yo++) for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight()) || !de::inBounds(y + yo, 0, src.raw().getDepth())) continue; // Undefined pixel. dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y + yo), xo, yo); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::copyTexSubImage1D(uint32_t target, int level, int xoffset, int x, int y, int width) { TextureUnit &unit = m_textureUnits[m_activeTexture]; rr::MultisampleConstPixelBufferAccess src = getReadColorbuffer(); RC_IF_ERROR(xoffset < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(isEmpty(src), GL_INVALID_OPERATION, RC_RET_VOID); if (target == GL_TEXTURE_1D) { Texture1D &texture = unit.tex1DBinding ? *unit.tex1DBinding : unit.default1DTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth(), GL_INVALID_VALUE, RC_RET_VOID); for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight())) continue; dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y), xo + xoffset, 0); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::copyTexSubImage2D(uint32_t target, int level, int xoffset, int yoffset, int x, int y, int width, int height) { TextureUnit &unit = m_textureUnits[m_activeTexture]; rr::MultisampleConstPixelBufferAccess src = getReadColorbuffer(); RC_IF_ERROR(xoffset < 0 || yoffset < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || height < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(isEmpty(src), GL_INVALID_OPERATION, RC_RET_VOID); if (target == GL_TEXTURE_2D) { Texture2D &texture = unit.tex2DBinding ? *unit.tex2DBinding : unit.default2DTex; RC_IF_ERROR(!texture.hasLevel(level), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getLevel(level); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight(), GL_INVALID_VALUE, RC_RET_VOID); for (int yo = 0; yo < height; yo++) for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight()) || !de::inBounds(y + yo, 0, src.raw().getDepth())) continue; dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y + yo), xo + xoffset, yo + yoffset); } } else if (target == GL_TEXTURE_CUBE_MAP_NEGATIVE_X || target == GL_TEXTURE_CUBE_MAP_POSITIVE_X || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Y || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Y || target == GL_TEXTURE_CUBE_MAP_NEGATIVE_Z || target == GL_TEXTURE_CUBE_MAP_POSITIVE_Z) { TextureCube &texture = unit.texCubeBinding ? *unit.texCubeBinding : unit.defaultCubeTex; tcu::CubeFace face = mapGLCubeFace(target); RC_IF_ERROR(!texture.hasFace(level, face), GL_INVALID_VALUE, RC_RET_VOID); PixelBufferAccess dst = texture.getFace(level, face); RC_IF_ERROR(xoffset + width > dst.getWidth() || yoffset + height > dst.getHeight(), GL_INVALID_VALUE, RC_RET_VOID); for (int yo = 0; yo < height; yo++) for (int xo = 0; xo < width; xo++) { if (!de::inBounds(x + xo, 0, src.raw().getHeight()) || !de::inBounds(y + yo, 0, src.raw().getDepth())) continue; dst.setPixel(rr::resolveMultisamplePixel(src, x + xo, y + yo), xo + xoffset, yo + yoffset); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::copyTexSubImage3D(uint32_t target, int level, int xoffset, int yoffset, int zoffset, int x, int y, int width, int height) { DE_UNREF(target && level && xoffset && yoffset && zoffset && x && y && width && height); DE_ASSERT(false); } void ReferenceContext::texStorage2D(uint32_t target, int levels, uint32_t internalFormat, int width, int height) { TextureUnit &unit = m_textureUnits[m_activeTexture]; TextureFormat storageFmt; RC_IF_ERROR(width <= 0 || height <= 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(!de::inRange(levels, 1, (int)deLog2Floor32(de::max(width, height)) + 1), GL_INVALID_VALUE, RC_RET_VOID); // Map storage format. storageFmt = mapInternalFormat(internalFormat); RC_IF_ERROR(storageFmt.order == TextureFormat::CHANNELORDER_LAST || storageFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); if (target == GL_TEXTURE_2D) { Texture2D &texture = unit.tex2DBinding ? *unit.tex2DBinding : unit.default2DTex; RC_IF_ERROR(width > m_limits.maxTexture2DSize || height >= m_limits.maxTexture2DSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(texture.isImmutable(), GL_INVALID_OPERATION, RC_RET_VOID); texture.clearLevels(); texture.setImmutable(); for (int level = 0; level < levels; level++) { int levelW = de::max(1, width >> level); int levelH = de::max(1, height >> level); texture.allocLevel(level, storageFmt, levelW, levelH); } } else if (target == GL_TEXTURE_CUBE_MAP) { TextureCube &texture = unit.texCubeBinding ? *unit.texCubeBinding : unit.defaultCubeTex; RC_IF_ERROR(width > m_limits.maxTextureCubeSize || height > m_limits.maxTextureCubeSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(texture.isImmutable(), GL_INVALID_OPERATION, RC_RET_VOID); texture.clearLevels(); texture.setImmutable(); for (int level = 0; level < levels; level++) { int levelW = de::max(1, width >> level); int levelH = de::max(1, height >> level); for (int face = 0; face < tcu::CUBEFACE_LAST; face++) texture.allocFace(level, (tcu::CubeFace)face, storageFmt, levelW, levelH); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } void ReferenceContext::texStorage3D(uint32_t target, int levels, uint32_t internalFormat, int width, int height, int depth) { TextureUnit &unit = m_textureUnits[m_activeTexture]; TextureFormat storageFmt; RC_IF_ERROR(width <= 0 || height <= 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(!de::inRange(levels, 1, (int)deLog2Floor32(de::max(width, height)) + 1), GL_INVALID_VALUE, RC_RET_VOID); // Map storage format. storageFmt = mapInternalFormat(internalFormat); RC_IF_ERROR(storageFmt.order == TextureFormat::CHANNELORDER_LAST || storageFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); if (target == GL_TEXTURE_2D_ARRAY) { Texture2DArray &texture = unit.tex2DArrayBinding ? *unit.tex2DArrayBinding : unit.default2DArrayTex; RC_IF_ERROR(width > m_limits.maxTexture2DSize || height >= m_limits.maxTexture2DSize || depth >= m_limits.maxTexture2DArrayLayers, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(texture.isImmutable(), GL_INVALID_OPERATION, RC_RET_VOID); texture.clearLevels(); texture.setImmutable(); for (int level = 0; level < levels; level++) { int levelW = de::max(1, width >> level); int levelH = de::max(1, height >> level); texture.allocLevel(level, storageFmt, levelW, levelH, depth); } } else if (target == GL_TEXTURE_3D) { Texture3D &texture = unit.tex3DBinding ? *unit.tex3DBinding : unit.default3DTex; RC_IF_ERROR(width > m_limits.maxTexture3DSize || height > m_limits.maxTexture3DSize || depth > m_limits.maxTexture3DSize, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(texture.isImmutable(), GL_INVALID_OPERATION, RC_RET_VOID); texture.clearLevels(); texture.setImmutable(); for (int level = 0; level < levels; level++) { int levelW = de::max(1, width >> level); int levelH = de::max(1, height >> level); int levelD = de::max(1, depth >> level); texture.allocLevel(level, storageFmt, levelW, levelH, levelD); } } else if (target == GL_TEXTURE_CUBE_MAP_ARRAY) { TextureCubeArray &texture = unit.texCubeArrayBinding ? *unit.texCubeArrayBinding : unit.defaultCubeArrayTex; RC_IF_ERROR(width != height || depth % 6 != 0 || width > m_limits.maxTexture2DSize || depth >= m_limits.maxTexture2DArrayLayers, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(texture.isImmutable(), GL_INVALID_OPERATION, RC_RET_VOID); texture.clearLevels(); texture.setImmutable(); for (int level = 0; level < levels; level++) { int levelW = de::max(1, width >> level); int levelH = de::max(1, height >> level); texture.allocLevel(level, storageFmt, levelW, levelH, depth); } } else RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } // \todo [2014-02-19 pyry] Duplicated with code in gluTextureUtil.hpp static inline tcu::Sampler::WrapMode mapGLWrapMode(int value) { switch (value) { case GL_CLAMP_TO_EDGE: return tcu::Sampler::CLAMP_TO_EDGE; case GL_REPEAT: return tcu::Sampler::REPEAT_GL; case GL_MIRRORED_REPEAT: return tcu::Sampler::MIRRORED_REPEAT_GL; default: return tcu::Sampler::WRAPMODE_LAST; } } static inline tcu::Sampler::FilterMode mapGLFilterMode(int value) { switch (value) { case GL_NEAREST: return tcu::Sampler::NEAREST; case GL_LINEAR: return tcu::Sampler::LINEAR; case GL_NEAREST_MIPMAP_NEAREST: return tcu::Sampler::NEAREST_MIPMAP_NEAREST; case GL_NEAREST_MIPMAP_LINEAR: return tcu::Sampler::NEAREST_MIPMAP_LINEAR; case GL_LINEAR_MIPMAP_NEAREST: return tcu::Sampler::LINEAR_MIPMAP_NEAREST; case GL_LINEAR_MIPMAP_LINEAR: return tcu::Sampler::LINEAR_MIPMAP_LINEAR; default: return tcu::Sampler::FILTERMODE_LAST; } } void ReferenceContext::texParameteri(uint32_t target, uint32_t pname, int value) { TextureUnit &unit = m_textureUnits[m_activeTexture]; Texture *texture = DE_NULL; switch (target) { case GL_TEXTURE_1D: texture = unit.tex1DBinding ? unit.tex1DBinding : &unit.default1DTex; break; case GL_TEXTURE_2D: texture = unit.tex2DBinding ? unit.tex2DBinding : &unit.default2DTex; break; case GL_TEXTURE_CUBE_MAP: texture = unit.texCubeBinding ? unit.texCubeBinding : &unit.defaultCubeTex; break; case GL_TEXTURE_2D_ARRAY: texture = unit.tex2DArrayBinding ? unit.tex2DArrayBinding : &unit.default2DArrayTex; break; case GL_TEXTURE_3D: texture = unit.tex3DBinding ? unit.tex3DBinding : &unit.default3DTex; break; case GL_TEXTURE_CUBE_MAP_ARRAY: texture = unit.texCubeArrayBinding ? unit.texCubeArrayBinding : &unit.defaultCubeArrayTex; break; default: RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } switch (pname) { case GL_TEXTURE_WRAP_S: { tcu::Sampler::WrapMode wrapS = mapGLWrapMode(value); RC_IF_ERROR(wrapS == tcu::Sampler::WRAPMODE_LAST, GL_INVALID_VALUE, RC_RET_VOID); texture->getSampler().wrapS = wrapS; break; } case GL_TEXTURE_WRAP_T: { tcu::Sampler::WrapMode wrapT = mapGLWrapMode(value); RC_IF_ERROR(wrapT == tcu::Sampler::WRAPMODE_LAST, GL_INVALID_VALUE, RC_RET_VOID); texture->getSampler().wrapT = wrapT; break; } case GL_TEXTURE_WRAP_R: { tcu::Sampler::WrapMode wrapR = mapGLWrapMode(value); RC_IF_ERROR(wrapR == tcu::Sampler::WRAPMODE_LAST, GL_INVALID_VALUE, RC_RET_VOID); texture->getSampler().wrapR = wrapR; break; } case GL_TEXTURE_MIN_FILTER: { tcu::Sampler::FilterMode minMode = mapGLFilterMode(value); RC_IF_ERROR(minMode == tcu::Sampler::FILTERMODE_LAST, GL_INVALID_VALUE, RC_RET_VOID); texture->getSampler().minFilter = minMode; break; } case GL_TEXTURE_MAG_FILTER: { tcu::Sampler::FilterMode magMode = mapGLFilterMode(value); RC_IF_ERROR(magMode != tcu::Sampler::LINEAR && magMode != tcu::Sampler::NEAREST, GL_INVALID_VALUE, RC_RET_VOID); texture->getSampler().magFilter = magMode; break; } case GL_TEXTURE_MAX_LEVEL: { RC_IF_ERROR(value < 0, GL_INVALID_VALUE, RC_RET_VOID); texture->setMaxLevel(value); break; } default: RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } } static inline Framebuffer::AttachmentPoint mapGLAttachmentPoint(uint32_t attachment) { switch (attachment) { case GL_COLOR_ATTACHMENT0: return Framebuffer::ATTACHMENTPOINT_COLOR0; case GL_DEPTH_ATTACHMENT: return Framebuffer::ATTACHMENTPOINT_DEPTH; case GL_STENCIL_ATTACHMENT: return Framebuffer::ATTACHMENTPOINT_STENCIL; default: return Framebuffer::ATTACHMENTPOINT_LAST; } } static inline Framebuffer::TexTarget mapGLFboTexTarget(uint32_t target) { switch (target) { case GL_TEXTURE_2D: return Framebuffer::TEXTARGET_2D; case GL_TEXTURE_CUBE_MAP_POSITIVE_X: return Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_X; case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: return Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_Y; case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: return Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_Z; case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: return Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_X; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: return Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Y; case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: return Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Z; default: return Framebuffer::TEXTARGET_LAST; } } void ReferenceContext::acquireFboAttachmentReference(const Framebuffer::Attachment &attachment) { switch (attachment.type) { case Framebuffer::ATTACHMENTTYPE_TEXTURE: { TCU_CHECK(attachment.name != 0); Texture *texture = m_textures.find(attachment.name); TCU_CHECK(texture); m_textures.acquireReference(texture); break; } case Framebuffer::ATTACHMENTTYPE_RENDERBUFFER: { TCU_CHECK(attachment.name != 0); Renderbuffer *rbo = m_renderbuffers.find(attachment.name); TCU_CHECK(rbo); m_renderbuffers.acquireReference(rbo); break; } default: break; // Silently ignore } } void ReferenceContext::releaseFboAttachmentReference(const Framebuffer::Attachment &attachment) { switch (attachment.type) { case Framebuffer::ATTACHMENTTYPE_TEXTURE: { TCU_CHECK(attachment.name != 0); Texture *texture = m_textures.find(attachment.name); TCU_CHECK(texture); m_textures.releaseReference(texture); break; } case Framebuffer::ATTACHMENTTYPE_RENDERBUFFER: { TCU_CHECK(attachment.name != 0); Renderbuffer *rbo = m_renderbuffers.find(attachment.name); TCU_CHECK(rbo); m_renderbuffers.releaseReference(rbo); break; } default: break; // Silently ignore } } void ReferenceContext::framebufferTexture2D(uint32_t target, uint32_t attachment, uint32_t textarget, uint32_t texture, int level) { if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { // Attach to both depth and stencil. framebufferTexture2D(target, GL_DEPTH_ATTACHMENT, textarget, texture, level); framebufferTexture2D(target, GL_STENCIL_ATTACHMENT, textarget, texture, level); } else { Framebuffer::AttachmentPoint point = mapGLAttachmentPoint(attachment); Texture *texObj = DE_NULL; Framebuffer::TexTarget fboTexTarget = mapGLFboTexTarget(textarget); RC_IF_ERROR(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(point == Framebuffer::ATTACHMENTPOINT_LAST, GL_INVALID_ENUM, RC_RET_VOID); // Select binding point. rc::Framebuffer *framebufferBinding = (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) ? m_drawFramebufferBinding : m_readFramebufferBinding; RC_IF_ERROR(!framebufferBinding, GL_INVALID_OPERATION, RC_RET_VOID); // If framebuffer object is bound for both reading and writing then we need to acquire/release multiple references. int bindingRefCount = (framebufferBinding == m_drawFramebufferBinding ? 1 : 0) + (framebufferBinding == m_readFramebufferBinding ? 1 : 0); if (texture != 0) { texObj = m_textures.find(texture); RC_IF_ERROR(!texObj, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(level != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-03-19 pyry] We should allow other levels as well. if (texObj->getType() == Texture::TYPE_2D) RC_IF_ERROR(fboTexTarget != Framebuffer::TEXTARGET_2D, GL_INVALID_OPERATION, RC_RET_VOID); else { TCU_CHECK(texObj->getType() == Texture::TYPE_CUBE_MAP); if (!deInRange32(fboTexTarget, Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_X, Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Z)) RC_ERROR_RET(GL_INVALID_OPERATION, RC_RET_VOID); } } Framebuffer::Attachment &fboAttachment = framebufferBinding->getAttachment(point); for (int ndx = 0; ndx < bindingRefCount; ndx++) releaseFboAttachmentReference(fboAttachment); fboAttachment = Framebuffer::Attachment(); if (texObj) { fboAttachment.type = Framebuffer::ATTACHMENTTYPE_TEXTURE; fboAttachment.name = texObj->getName(); fboAttachment.texTarget = fboTexTarget; fboAttachment.level = level; for (int ndx = 0; ndx < bindingRefCount; ndx++) acquireFboAttachmentReference(fboAttachment); } } } void ReferenceContext::framebufferTextureLayer(uint32_t target, uint32_t attachment, uint32_t texture, int level, int layer) { if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { // Attach to both depth and stencil. framebufferTextureLayer(target, GL_DEPTH_ATTACHMENT, texture, level, layer); framebufferTextureLayer(target, GL_STENCIL_ATTACHMENT, texture, level, layer); } else { Framebuffer::AttachmentPoint point = mapGLAttachmentPoint(attachment); Texture *texObj = DE_NULL; RC_IF_ERROR(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(point == Framebuffer::ATTACHMENTPOINT_LAST, GL_INVALID_ENUM, RC_RET_VOID); // Select binding point. rc::Framebuffer *framebufferBinding = (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) ? m_drawFramebufferBinding : m_readFramebufferBinding; RC_IF_ERROR(!framebufferBinding, GL_INVALID_OPERATION, RC_RET_VOID); // If framebuffer object is bound for both reading and writing then we need to acquire/release multiple references. int bindingRefCount = (framebufferBinding == m_drawFramebufferBinding ? 1 : 0) + (framebufferBinding == m_readFramebufferBinding ? 1 : 0); if (texture != 0) { texObj = m_textures.find(texture); RC_IF_ERROR(!texObj, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(level != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-03-19 pyry] We should allow other levels as well. RC_IF_ERROR(texObj->getType() != Texture::TYPE_2D_ARRAY && texObj->getType() != Texture::TYPE_3D && texObj->getType() != Texture::TYPE_CUBE_MAP_ARRAY, GL_INVALID_OPERATION, RC_RET_VOID); if (texObj->getType() == Texture::TYPE_2D_ARRAY || texObj->getType() == Texture::TYPE_CUBE_MAP_ARRAY) { RC_IF_ERROR((layer < 0) || (layer >= GL_MAX_ARRAY_TEXTURE_LAYERS), GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR((level < 0) || (level > deLog2Floor32(GL_MAX_TEXTURE_SIZE)), GL_INVALID_VALUE, RC_RET_VOID); } else if (texObj->getType() == Texture::TYPE_3D) { RC_IF_ERROR((layer < 0) || (layer >= GL_MAX_3D_TEXTURE_SIZE), GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR((level < 0) || (level > deLog2Floor32(GL_MAX_3D_TEXTURE_SIZE)), GL_INVALID_VALUE, RC_RET_VOID); } } Framebuffer::Attachment &fboAttachment = framebufferBinding->getAttachment(point); for (int ndx = 0; ndx < bindingRefCount; ndx++) releaseFboAttachmentReference(fboAttachment); fboAttachment = Framebuffer::Attachment(); if (texObj) { fboAttachment.type = Framebuffer::ATTACHMENTTYPE_TEXTURE; fboAttachment.name = texObj->getName(); fboAttachment.texTarget = texLayeredTypeToTarget(texObj->getType()); fboAttachment.level = level; fboAttachment.layer = layer; DE_ASSERT(fboAttachment.texTarget != Framebuffer::TEXTARGET_LAST); for (int ndx = 0; ndx < bindingRefCount; ndx++) acquireFboAttachmentReference(fboAttachment); } } } void ReferenceContext::framebufferRenderbuffer(uint32_t target, uint32_t attachment, uint32_t renderbuffertarget, uint32_t renderbuffer) { if (attachment == GL_DEPTH_STENCIL_ATTACHMENT) { // Attach both to depth and stencil. framebufferRenderbuffer(target, GL_DEPTH_ATTACHMENT, renderbuffertarget, renderbuffer); framebufferRenderbuffer(target, GL_STENCIL_ATTACHMENT, renderbuffertarget, renderbuffer); } else { Framebuffer::AttachmentPoint point = mapGLAttachmentPoint(attachment); Renderbuffer *rbo = DE_NULL; RC_IF_ERROR(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(point == Framebuffer::ATTACHMENTPOINT_LAST, GL_INVALID_ENUM, RC_RET_VOID); // Select binding point. rc::Framebuffer *framebufferBinding = (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) ? m_drawFramebufferBinding : m_readFramebufferBinding; RC_IF_ERROR(!framebufferBinding, GL_INVALID_OPERATION, RC_RET_VOID); // If framebuffer object is bound for both reading and writing then we need to acquire/release multiple references. int bindingRefCount = (framebufferBinding == m_drawFramebufferBinding ? 1 : 0) + (framebufferBinding == m_readFramebufferBinding ? 1 : 0); if (renderbuffer != 0) { rbo = m_renderbuffers.find(renderbuffer); RC_IF_ERROR(renderbuffertarget != GL_RENDERBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(!rbo, GL_INVALID_OPERATION, RC_RET_VOID); } Framebuffer::Attachment &fboAttachment = framebufferBinding->getAttachment(point); for (int ndx = 0; ndx < bindingRefCount; ndx++) releaseFboAttachmentReference(fboAttachment); fboAttachment = Framebuffer::Attachment(); if (rbo) { fboAttachment.type = Framebuffer::ATTACHMENTTYPE_RENDERBUFFER; fboAttachment.name = rbo->getName(); for (int ndx = 0; ndx < bindingRefCount; ndx++) acquireFboAttachmentReference(fboAttachment); } } } uint32_t ReferenceContext::checkFramebufferStatus(uint32_t target) { RC_IF_ERROR(target != GL_FRAMEBUFFER && target != GL_DRAW_FRAMEBUFFER && target != GL_READ_FRAMEBUFFER, GL_INVALID_ENUM, 0); // Select binding point. rc::Framebuffer *framebufferBinding = (target == GL_FRAMEBUFFER || target == GL_DRAW_FRAMEBUFFER) ? m_drawFramebufferBinding : m_readFramebufferBinding; // Default framebuffer is always complete. if (!framebufferBinding) return GL_FRAMEBUFFER_COMPLETE; int width = -1; int height = -1; bool hasAttachment = false; bool attachmentComplete = true; bool dimensionsOk = true; for (int point = 0; point < Framebuffer::ATTACHMENTPOINT_LAST; point++) { const Framebuffer::Attachment &attachment = framebufferBinding->getAttachment((Framebuffer::AttachmentPoint)point); int attachmentWidth = 0; int attachmentHeight = 0; tcu::TextureFormat attachmentFormat; if (attachment.type == Framebuffer::ATTACHMENTTYPE_TEXTURE) { const Texture *texture = m_textures.find(attachment.name); tcu::ConstPixelBufferAccess level; TCU_CHECK(texture); if (attachment.texTarget == Framebuffer::TEXTARGET_2D) { DE_ASSERT(texture->getType() == Texture::TYPE_2D); const Texture2D *tex2D = static_cast(texture); if (tex2D->hasLevel(attachment.level)) level = tex2D->getLevel(attachment.level); } else if (deInRange32(attachment.texTarget, Framebuffer::TEXTARGET_CUBE_MAP_POSITIVE_X, Framebuffer::TEXTARGET_CUBE_MAP_NEGATIVE_Z)) { DE_ASSERT(texture->getType() == Texture::TYPE_CUBE_MAP); const TextureCube *texCube = static_cast(texture); const tcu::CubeFace face = texTargetToFace(attachment.texTarget); TCU_CHECK(de::inBounds(face, 0, tcu::CUBEFACE_LAST)); if (texCube->hasFace(attachment.level, face)) level = texCube->getFace(attachment.level, face); } else if (attachment.texTarget == Framebuffer::TEXTARGET_2D_ARRAY) { DE_ASSERT(texture->getType() == Texture::TYPE_2D_ARRAY); const Texture2DArray *tex2DArr = static_cast(texture); if (tex2DArr->hasLevel(attachment.level)) level = tex2DArr->getLevel(attachment.level); // \note Slice doesn't matter here. } else if (attachment.texTarget == Framebuffer::TEXTARGET_3D) { DE_ASSERT(texture->getType() == Texture::TYPE_3D); const Texture3D *tex3D = static_cast(texture); if (tex3D->hasLevel(attachment.level)) level = tex3D->getLevel(attachment.level); // \note Slice doesn't matter here. } else if (attachment.texTarget == Framebuffer::TEXTARGET_CUBE_MAP_ARRAY) { DE_ASSERT(texture->getType() == Texture::TYPE_CUBE_MAP_ARRAY); const TextureCubeArray *texCubeArr = static_cast(texture); if (texCubeArr->hasLevel(attachment.level)) level = texCubeArr->getLevel(attachment.level); // \note Slice doesn't matter here. } else TCU_FAIL("Framebuffer attached to a texture but no valid target specified"); attachmentWidth = level.getWidth(); attachmentHeight = level.getHeight(); attachmentFormat = level.getFormat(); } else if (attachment.type == Framebuffer::ATTACHMENTTYPE_RENDERBUFFER) { const Renderbuffer *renderbuffer = m_renderbuffers.find(attachment.name); TCU_CHECK(renderbuffer); attachmentWidth = renderbuffer->getWidth(); attachmentHeight = renderbuffer->getHeight(); attachmentFormat = renderbuffer->getFormat(); } else { TCU_CHECK(attachment.type == Framebuffer::ATTACHMENTTYPE_LAST); continue; // Skip rest of checks. } if (!hasAttachment && attachmentWidth > 0 && attachmentHeight > 0) { width = attachmentWidth; height = attachmentHeight; hasAttachment = true; } else if (attachmentWidth != width || attachmentHeight != height) dimensionsOk = false; // Validate attachment point compatibility. switch (attachmentFormat.order) { case TextureFormat::R: case TextureFormat::RG: case TextureFormat::RGB: case TextureFormat::RGBA: case TextureFormat::sRGB: case TextureFormat::sRGBA: if (point != Framebuffer::ATTACHMENTPOINT_COLOR0) attachmentComplete = false; break; case TextureFormat::D: if (point != Framebuffer::ATTACHMENTPOINT_DEPTH) attachmentComplete = false; break; case TextureFormat::S: if (point != Framebuffer::ATTACHMENTPOINT_STENCIL) attachmentComplete = false; break; case TextureFormat::DS: if (point != Framebuffer::ATTACHMENTPOINT_DEPTH && point != Framebuffer::ATTACHMENTPOINT_STENCIL) attachmentComplete = false; break; default: TCU_FAIL("Unsupported attachment channel order"); } } if (!attachmentComplete) return GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT; else if (!hasAttachment) return GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT; else if (!dimensionsOk) return GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS; else return GL_FRAMEBUFFER_COMPLETE; } void ReferenceContext::getFramebufferAttachmentParameteriv(uint32_t target, uint32_t attachment, uint32_t pname, int *params) { DE_UNREF(target && attachment && pname && params); TCU_CHECK(false); // \todo [pyry] Implement } void ReferenceContext::renderbufferStorage(uint32_t target, uint32_t internalformat, int width, int height) { TextureFormat format = glu::mapGLInternalFormat(internalformat); RC_IF_ERROR(target != GL_RENDERBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(!m_renderbufferBinding, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(!deInRange32(width, 0, m_limits.maxRenderbufferSize) || !deInRange32(height, 0, m_limits.maxRenderbufferSize), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(format.order == TextureFormat::CHANNELORDER_LAST || format.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); m_renderbufferBinding->setStorage(format, (int)width, (int)height); } void ReferenceContext::renderbufferStorageMultisample(uint32_t target, int samples, uint32_t internalFormat, int width, int height) { // \todo [2012-04-07 pyry] Implement MSAA support. DE_UNREF(samples); renderbufferStorage(target, internalFormat, width, height); } tcu::PixelBufferAccess ReferenceContext::getFboAttachment(const rc::Framebuffer &framebuffer, rc::Framebuffer::AttachmentPoint point) { const Framebuffer::Attachment &attachment = framebuffer.getAttachment(point); switch (attachment.type) { case Framebuffer::ATTACHMENTTYPE_TEXTURE: { Texture *texture = m_textures.find(attachment.name); TCU_CHECK(texture); if (texture->getType() == Texture::TYPE_2D) { if (Texture2D *texture2D = dynamic_cast(texture)) return texture2D->getLevel(attachment.level); else return nullAccess(); } else if (texture->getType() == Texture::TYPE_CUBE_MAP) { if (TextureCube *cubeMap = dynamic_cast(texture)) return cubeMap->getFace(attachment.level, texTargetToFace(attachment.texTarget)); else return nullAccess(); } else if (texture->getType() == Texture::TYPE_2D_ARRAY || texture->getType() == Texture::TYPE_3D || texture->getType() == Texture::TYPE_CUBE_MAP_ARRAY) { tcu::PixelBufferAccess level; if (texture->getType() == Texture::TYPE_2D_ARRAY) { if (Texture2DArray *texture2DArray = dynamic_cast(texture)) level = texture2DArray->getLevel(attachment.level); } else if (texture->getType() == Texture::TYPE_3D) { if (Texture3D *texture3D = dynamic_cast(texture)) level = texture3D->getLevel(attachment.level); } else if (texture->getType() == Texture::TYPE_CUBE_MAP_ARRAY) { if (TextureCubeArray *cubeArray = dynamic_cast(texture)) level = cubeArray->getLevel(attachment.level); } void *layerData = static_cast(level.getDataPtr()) + level.getSlicePitch() * attachment.layer; return tcu::PixelBufferAccess(level.getFormat(), level.getWidth(), level.getHeight(), 1, level.getRowPitch(), 0, layerData); } else return nullAccess(); } case Framebuffer::ATTACHMENTTYPE_RENDERBUFFER: { Renderbuffer *rbo = m_renderbuffers.find(attachment.name); TCU_CHECK(rbo); return rbo->getAccess(); } default: return nullAccess(); } } const Texture2D &ReferenceContext::getTexture2D(int unitNdx) const { const TextureUnit &unit = m_textureUnits[unitNdx]; return unit.tex2DBinding ? *unit.tex2DBinding : unit.default2DTex; } const TextureCube &ReferenceContext::getTextureCube(int unitNdx) const { const TextureUnit &unit = m_textureUnits[unitNdx]; return unit.texCubeBinding ? *unit.texCubeBinding : unit.defaultCubeTex; } static bool isValidBufferTarget(uint32_t target) { switch (target) { case GL_ARRAY_BUFFER: case GL_COPY_READ_BUFFER: case GL_COPY_WRITE_BUFFER: case GL_DRAW_INDIRECT_BUFFER: case GL_ELEMENT_ARRAY_BUFFER: case GL_PIXEL_PACK_BUFFER: case GL_PIXEL_UNPACK_BUFFER: case GL_TRANSFORM_FEEDBACK_BUFFER: case GL_UNIFORM_BUFFER: return true; default: return false; } } void ReferenceContext::setBufferBinding(uint32_t target, DataBuffer *buffer) { DataBuffer **bindingPoint = DE_NULL; VertexArray *vertexArrayObject = (m_vertexArrayBinding) ? (m_vertexArrayBinding) : (&m_clientVertexArray); switch (target) { case GL_ARRAY_BUFFER: bindingPoint = &m_arrayBufferBinding; break; case GL_COPY_READ_BUFFER: bindingPoint = &m_copyReadBufferBinding; break; case GL_COPY_WRITE_BUFFER: bindingPoint = &m_copyWriteBufferBinding; break; case GL_DRAW_INDIRECT_BUFFER: bindingPoint = &m_drawIndirectBufferBinding; break; case GL_ELEMENT_ARRAY_BUFFER: bindingPoint = &vertexArrayObject->m_elementArrayBufferBinding; break; case GL_PIXEL_PACK_BUFFER: bindingPoint = &m_pixelPackBufferBinding; break; case GL_PIXEL_UNPACK_BUFFER: bindingPoint = &m_pixelUnpackBufferBinding; break; case GL_TRANSFORM_FEEDBACK_BUFFER: bindingPoint = &m_transformFeedbackBufferBinding; break; case GL_UNIFORM_BUFFER: bindingPoint = &m_uniformBufferBinding; break; default: DE_ASSERT(false); return; } if (*bindingPoint) { m_buffers.releaseReference(*bindingPoint); *bindingPoint = DE_NULL; } if (buffer) m_buffers.acquireReference(buffer); *bindingPoint = buffer; } DataBuffer *ReferenceContext::getBufferBinding(uint32_t target) const { const VertexArray *vertexArrayObject = (m_vertexArrayBinding) ? (m_vertexArrayBinding) : (&m_clientVertexArray); switch (target) { case GL_ARRAY_BUFFER: return m_arrayBufferBinding; case GL_COPY_READ_BUFFER: return m_copyReadBufferBinding; case GL_COPY_WRITE_BUFFER: return m_copyWriteBufferBinding; case GL_DRAW_INDIRECT_BUFFER: return m_drawIndirectBufferBinding; case GL_ELEMENT_ARRAY_BUFFER: return vertexArrayObject->m_elementArrayBufferBinding; case GL_PIXEL_PACK_BUFFER: return m_pixelPackBufferBinding; case GL_PIXEL_UNPACK_BUFFER: return m_pixelUnpackBufferBinding; case GL_TRANSFORM_FEEDBACK_BUFFER: return m_transformFeedbackBufferBinding; case GL_UNIFORM_BUFFER: return m_uniformBufferBinding; default: DE_ASSERT(false); return DE_NULL; } } void ReferenceContext::bindBuffer(uint32_t target, uint32_t buffer) { RC_IF_ERROR(!isValidBufferTarget(target), GL_INVALID_ENUM, RC_RET_VOID); rc::DataBuffer *bufObj = DE_NULL; if (buffer != 0) { bufObj = m_buffers.find(buffer); if (!bufObj) { bufObj = new DataBuffer(buffer); m_buffers.insert(bufObj); } } setBufferBinding(target, bufObj); } void ReferenceContext::genBuffers(int numBuffers, uint32_t *buffers) { RC_IF_ERROR(!buffers, GL_INVALID_VALUE, RC_RET_VOID); for (int ndx = 0; ndx < numBuffers; ndx++) buffers[ndx] = m_buffers.allocateName(); } void ReferenceContext::deleteBuffers(int numBuffers, const uint32_t *buffers) { RC_IF_ERROR(numBuffers < 0, GL_INVALID_VALUE, RC_RET_VOID); for (int ndx = 0; ndx < numBuffers; ndx++) { uint32_t buffer = buffers[ndx]; DataBuffer *bufObj = DE_NULL; if (buffer == 0) continue; bufObj = m_buffers.find(buffer); if (bufObj) deleteBuffer(bufObj); } } void ReferenceContext::deleteBuffer(DataBuffer *buffer) { static const uint32_t bindingPoints[] = { GL_ARRAY_BUFFER, GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, GL_DRAW_INDIRECT_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER, GL_PIXEL_UNPACK_BUFFER, GL_TRANSFORM_FEEDBACK_BUFFER, GL_UNIFORM_BUFFER}; for (int bindingNdx = 0; bindingNdx < DE_LENGTH_OF_ARRAY(bindingPoints); bindingNdx++) { if (getBufferBinding(bindingPoints[bindingNdx]) == buffer) setBufferBinding(bindingPoints[bindingNdx], DE_NULL); } { vector vertexArrays; m_vertexArrays.getAll(vertexArrays); vertexArrays.push_back(&m_clientVertexArray); for (vector::iterator i = vertexArrays.begin(); i != vertexArrays.end(); i++) { if ((*i)->m_elementArrayBufferBinding == buffer) { m_buffers.releaseReference(buffer); (*i)->m_elementArrayBufferBinding = DE_NULL; } for (size_t vertexAttribNdx = 0; vertexAttribNdx < (*i)->m_arrays.size(); ++vertexAttribNdx) { if ((*i)->m_arrays[vertexAttribNdx].bufferBinding == buffer) { m_buffers.releaseReference(buffer); (*i)->m_arrays[vertexAttribNdx].bufferDeleted = true; (*i)->m_arrays[vertexAttribNdx].bufferBinding = DE_NULL; } } } } DE_ASSERT(buffer->getRefCount() == 1); m_buffers.releaseReference(buffer); } void ReferenceContext::bufferData(uint32_t target, intptr_t size, const void *data, uint32_t usage) { RC_IF_ERROR(!isValidBufferTarget(target), GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(size < 0, GL_INVALID_VALUE, RC_RET_VOID); DE_UNREF(usage); DataBuffer *buffer = getBufferBinding(target); RC_IF_ERROR(!buffer, GL_INVALID_OPERATION, RC_RET_VOID); DE_ASSERT((intptr_t)(int)size == size); buffer->setStorage((int)size); if (data) deMemcpy(buffer->getData(), data, (int)size); } void ReferenceContext::bufferSubData(uint32_t target, intptr_t offset, intptr_t size, const void *data) { RC_IF_ERROR(!isValidBufferTarget(target), GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(offset < 0 || size < 0, GL_INVALID_VALUE, RC_RET_VOID); DataBuffer *buffer = getBufferBinding(target); RC_IF_ERROR(!buffer, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR((int)(offset + size) > buffer->getSize(), GL_INVALID_VALUE, RC_RET_VOID); deMemcpy(buffer->getData() + offset, data, (int)size); } void ReferenceContext::clearColor(float red, float green, float blue, float alpha) { m_clearColor = Vec4(de::clamp(red, 0.0f, 1.0f), de::clamp(green, 0.0f, 1.0f), de::clamp(blue, 0.0f, 1.0f), de::clamp(alpha, 0.0f, 1.0f)); } void ReferenceContext::clearDepthf(float depth) { m_clearDepth = de::clamp(depth, 0.0f, 1.0f); } void ReferenceContext::clearStencil(int stencil) { m_clearStencil = stencil; } void ReferenceContext::scissor(int x, int y, int width, int height) { RC_IF_ERROR(width < 0 || height < 0, GL_INVALID_VALUE, RC_RET_VOID); m_scissorBox = IVec4(x, y, width, height); } void ReferenceContext::enable(uint32_t cap) { switch (cap) { case GL_BLEND: m_blendEnabled = true; break; case GL_SCISSOR_TEST: m_scissorEnabled = true; break; case GL_DEPTH_TEST: m_depthTestEnabled = true; break; case GL_STENCIL_TEST: m_stencilTestEnabled = true; break; case GL_POLYGON_OFFSET_FILL: m_polygonOffsetFillEnabled = true; break; case GL_FRAMEBUFFER_SRGB: if (glu::isContextTypeGLCore(getType())) { m_sRGBUpdateEnabled = true; break; } setError(GL_INVALID_ENUM); break; case GL_DEPTH_CLAMP: if (glu::isContextTypeGLCore(getType())) { m_depthClampEnabled = true; break; } setError(GL_INVALID_ENUM); break; case GL_DITHER: // Not implemented - just ignored. break; case GL_PRIMITIVE_RESTART_FIXED_INDEX: if (!glu::isContextTypeGLCore(getType())) { m_primitiveRestartFixedIndex = true; break; } setError(GL_INVALID_ENUM); break; case GL_PRIMITIVE_RESTART: if (glu::isContextTypeGLCore(getType())) { m_primitiveRestartSettableIndex = true; break; } setError(GL_INVALID_ENUM); break; default: setError(GL_INVALID_ENUM); break; } } void ReferenceContext::disable(uint32_t cap) { switch (cap) { case GL_BLEND: m_blendEnabled = false; break; case GL_SCISSOR_TEST: m_scissorEnabled = false; break; case GL_DEPTH_TEST: m_depthTestEnabled = false; break; case GL_STENCIL_TEST: m_stencilTestEnabled = false; break; case GL_POLYGON_OFFSET_FILL: m_polygonOffsetFillEnabled = false; break; case GL_FRAMEBUFFER_SRGB: if (glu::isContextTypeGLCore(getType())) { m_sRGBUpdateEnabled = false; break; } setError(GL_INVALID_ENUM); break; case GL_DEPTH_CLAMP: if (glu::isContextTypeGLCore(getType())) { m_depthClampEnabled = false; break; } setError(GL_INVALID_ENUM); break; case GL_DITHER: break; case GL_PRIMITIVE_RESTART_FIXED_INDEX: if (!glu::isContextTypeGLCore(getType())) { m_primitiveRestartFixedIndex = false; break; } setError(GL_INVALID_ENUM); break; case GL_PRIMITIVE_RESTART: if (glu::isContextTypeGLCore(getType())) { m_primitiveRestartSettableIndex = false; break; } setError(GL_INVALID_ENUM); break; default: setError(GL_INVALID_ENUM); break; } } static bool isValidCompareFunc(uint32_t func) { switch (func) { case GL_NEVER: case GL_LESS: case GL_LEQUAL: case GL_GREATER: case GL_GEQUAL: case GL_EQUAL: case GL_NOTEQUAL: case GL_ALWAYS: return true; default: return false; } } static bool isValidStencilOp(uint32_t op) { switch (op) { case GL_KEEP: case GL_ZERO: case GL_REPLACE: case GL_INCR: case GL_INCR_WRAP: case GL_DECR: case GL_DECR_WRAP: case GL_INVERT: return true; default: return false; } } void ReferenceContext::stencilFunc(uint32_t func, int ref, uint32_t mask) { stencilFuncSeparate(GL_FRONT_AND_BACK, func, ref, mask); } void ReferenceContext::stencilFuncSeparate(uint32_t face, uint32_t func, int ref, uint32_t mask) { const bool setFront = face == GL_FRONT || face == GL_FRONT_AND_BACK; const bool setBack = face == GL_BACK || face == GL_FRONT_AND_BACK; RC_IF_ERROR(!isValidCompareFunc(func), GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(!setFront && !setBack, GL_INVALID_ENUM, RC_RET_VOID); for (int type = 0; type < rr::FACETYPE_LAST; ++type) { if ((type == rr::FACETYPE_FRONT && setFront) || (type == rr::FACETYPE_BACK && setBack)) { m_stencil[type].func = func; m_stencil[type].ref = ref; m_stencil[type].opMask = mask; } } } void ReferenceContext::stencilOp(uint32_t sfail, uint32_t dpfail, uint32_t dppass) { stencilOpSeparate(GL_FRONT_AND_BACK, sfail, dpfail, dppass); } void ReferenceContext::stencilOpSeparate(uint32_t face, uint32_t sfail, uint32_t dpfail, uint32_t dppass) { const bool setFront = face == GL_FRONT || face == GL_FRONT_AND_BACK; const bool setBack = face == GL_BACK || face == GL_FRONT_AND_BACK; RC_IF_ERROR(!isValidStencilOp(sfail) || !isValidStencilOp(dpfail) || !isValidStencilOp(dppass), GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(!setFront && !setBack, GL_INVALID_ENUM, RC_RET_VOID); for (int type = 0; type < rr::FACETYPE_LAST; ++type) { if ((type == rr::FACETYPE_FRONT && setFront) || (type == rr::FACETYPE_BACK && setBack)) { m_stencil[type].opStencilFail = sfail; m_stencil[type].opDepthFail = dpfail; m_stencil[type].opDepthPass = dppass; } } } void ReferenceContext::depthFunc(uint32_t func) { RC_IF_ERROR(!isValidCompareFunc(func), GL_INVALID_ENUM, RC_RET_VOID); m_depthFunc = func; } void ReferenceContext::depthRangef(float n, float f) { m_depthRangeNear = de::clamp(n, 0.0f, 1.0f); m_depthRangeFar = de::clamp(f, 0.0f, 1.0f); } void ReferenceContext::depthRange(double n, double f) { depthRangef((float)n, (float)f); } void ReferenceContext::polygonOffset(float factor, float units) { m_polygonOffsetFactor = factor; m_polygonOffsetUnits = units; } void ReferenceContext::provokingVertex(uint32_t convention) { // only in core DE_ASSERT(glu::isContextTypeGLCore(getType())); switch (convention) { case GL_FIRST_VERTEX_CONVENTION: m_provokingFirstVertexConvention = true; break; case GL_LAST_VERTEX_CONVENTION: m_provokingFirstVertexConvention = false; break; default: RC_ERROR_RET(GL_INVALID_ENUM, RC_RET_VOID); } } void ReferenceContext::primitiveRestartIndex(uint32_t index) { // only in core DE_ASSERT(glu::isContextTypeGLCore(getType())); m_primitiveRestartIndex = index; } static inline bool isValidBlendEquation(uint32_t mode) { return mode == GL_FUNC_ADD || mode == GL_FUNC_SUBTRACT || mode == GL_FUNC_REVERSE_SUBTRACT || mode == GL_MIN || mode == GL_MAX; } static bool isValidBlendFactor(uint32_t factor) { switch (factor) { case GL_ZERO: case GL_ONE: case GL_SRC_COLOR: case GL_ONE_MINUS_SRC_COLOR: case GL_DST_COLOR: case GL_ONE_MINUS_DST_COLOR: case GL_SRC_ALPHA: case GL_ONE_MINUS_SRC_ALPHA: case GL_DST_ALPHA: case GL_ONE_MINUS_DST_ALPHA: case GL_CONSTANT_COLOR: case GL_ONE_MINUS_CONSTANT_COLOR: case GL_CONSTANT_ALPHA: case GL_ONE_MINUS_CONSTANT_ALPHA: case GL_SRC_ALPHA_SATURATE: return true; default: return false; } } void ReferenceContext::blendEquation(uint32_t mode) { RC_IF_ERROR(!isValidBlendEquation(mode), GL_INVALID_ENUM, RC_RET_VOID); m_blendModeRGB = mode; m_blendModeAlpha = mode; } void ReferenceContext::blendEquationSeparate(uint32_t modeRGB, uint32_t modeAlpha) { RC_IF_ERROR(!isValidBlendEquation(modeRGB) || !isValidBlendEquation(modeAlpha), GL_INVALID_ENUM, RC_RET_VOID); m_blendModeRGB = modeRGB; m_blendModeAlpha = modeAlpha; } void ReferenceContext::blendFunc(uint32_t src, uint32_t dst) { RC_IF_ERROR(!isValidBlendFactor(src) || !isValidBlendFactor(dst), GL_INVALID_ENUM, RC_RET_VOID); m_blendFactorSrcRGB = src; m_blendFactorSrcAlpha = src; m_blendFactorDstRGB = dst; m_blendFactorDstAlpha = dst; } void ReferenceContext::blendFuncSeparate(uint32_t srcRGB, uint32_t dstRGB, uint32_t srcAlpha, uint32_t dstAlpha) { RC_IF_ERROR(!isValidBlendFactor(srcRGB) || !isValidBlendFactor(dstRGB) || !isValidBlendFactor(srcAlpha) || !isValidBlendFactor(dstAlpha), GL_INVALID_ENUM, RC_RET_VOID); m_blendFactorSrcRGB = srcRGB; m_blendFactorSrcAlpha = srcAlpha; m_blendFactorDstRGB = dstRGB; m_blendFactorDstAlpha = dstAlpha; } void ReferenceContext::blendColor(float red, float green, float blue, float alpha) { m_blendColor = Vec4(de::clamp(red, 0.0f, 1.0f), de::clamp(green, 0.0f, 1.0f), de::clamp(blue, 0.0f, 1.0f), de::clamp(alpha, 0.0f, 1.0f)); } void ReferenceContext::colorMask(bool r, bool g, bool b, bool a) { m_colorMask = tcu::BVec4(!!r, !!g, !!b, !!a); } void ReferenceContext::depthMask(bool mask) { m_depthMask = !!mask; } void ReferenceContext::stencilMask(uint32_t mask) { stencilMaskSeparate(GL_FRONT_AND_BACK, mask); } void ReferenceContext::stencilMaskSeparate(uint32_t face, uint32_t mask) { const bool setFront = face == GL_FRONT || face == GL_FRONT_AND_BACK; const bool setBack = face == GL_BACK || face == GL_FRONT_AND_BACK; RC_IF_ERROR(!setFront && !setBack, GL_INVALID_ENUM, RC_RET_VOID); if (setFront) m_stencil[rr::FACETYPE_FRONT].writeMask = mask; if (setBack) m_stencil[rr::FACETYPE_BACK].writeMask = mask; } static int getNumStencilBits(const tcu::TextureFormat &format) { switch (format.order) { case tcu::TextureFormat::S: switch (format.type) { case tcu::TextureFormat::UNSIGNED_INT8: return 8; case tcu::TextureFormat::UNSIGNED_INT16: return 16; case tcu::TextureFormat::UNSIGNED_INT32: return 32; default: DE_ASSERT(false); return 0; } case tcu::TextureFormat::DS: switch (format.type) { case tcu::TextureFormat::UNSIGNED_INT_24_8: return 8; case tcu::TextureFormat::FLOAT_UNSIGNED_INT_24_8_REV: return 8; default: DE_ASSERT(false); return 0; } default: DE_ASSERT(false); return 0; } } static inline uint32_t maskStencil(int numBits, uint32_t s) { return s & deBitMask32(0, numBits); } static inline void writeMaskedStencil(const rr::MultisamplePixelBufferAccess &access, int s, int x, int y, uint32_t stencil, uint32_t writeMask) { DE_ASSERT(access.raw().getFormat().order == tcu::TextureFormat::S); const uint32_t oldVal = access.raw().getPixelUint(s, x, y).x(); const uint32_t newVal = (oldVal & ~writeMask) | (stencil & writeMask); access.raw().setPixel(tcu::UVec4(newVal, 0u, 0u, 0u), s, x, y); } static inline void writeDepthOnly(const rr::MultisamplePixelBufferAccess &access, int s, int x, int y, float depth) { access.raw().setPixDepth(depth, s, x, y); } static rr::MultisamplePixelBufferAccess getDepthMultisampleAccess( const rr::MultisamplePixelBufferAccess &combinedDSaccess) { return rr::MultisamplePixelBufferAccess::fromMultisampleAccess( tcu::getEffectiveDepthStencilAccess(combinedDSaccess.raw(), tcu::Sampler::MODE_DEPTH)); } static rr::MultisamplePixelBufferAccess getStencilMultisampleAccess( const rr::MultisamplePixelBufferAccess &combinedDSaccess) { return rr::MultisamplePixelBufferAccess::fromMultisampleAccess( tcu::getEffectiveDepthStencilAccess(combinedDSaccess.raw(), tcu::Sampler::MODE_STENCIL)); } uint32_t ReferenceContext::blitResolveMultisampleFramebuffer(uint32_t mask, const IVec4 &srcRect, const IVec4 &dstRect, bool flipX, bool flipY) { if (mask & GL_COLOR_BUFFER_BIT) { rr::MultisampleConstPixelBufferAccess src = rr::getSubregion(getReadColorbuffer(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w()); tcu::PixelBufferAccess dst = tcu::getSubregion(getDrawColorbuffer().toSinglesampleAccess(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w()); tcu::TextureChannelClass dstClass = tcu::getTextureChannelClass(dst.getFormat().type); bool dstIsFloat = dstClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT || dstClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || dstClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT; bool srcIsSRGB = tcu::isSRGB(src.raw().getFormat()); bool dstIsSRGB = tcu::isSRGB(dst.getFormat()); const bool convertSRGB = m_sRGBUpdateEnabled && glu::isContextTypeES(getType()); if (!convertSRGB) { tcu::ConstPixelBufferAccess srcRaw = src.raw(); tcu::TextureFormat srcFmt = toNonSRGBFormat(srcRaw.getFormat()); srcRaw = tcu::ConstPixelBufferAccess(srcFmt, srcRaw.getWidth(), srcRaw.getHeight(), srcRaw.getDepth(), srcRaw.getRowPitch(), srcRaw.getSlicePitch(), srcRaw.getDataPtr()); src = rr::MultisampleConstPixelBufferAccess::fromMultisampleAccess(srcRaw); dst = tcu::PixelBufferAccess(toNonSRGBFormat(dst.getFormat()), dst.getWidth(), dst.getHeight(), dst.getDepth(), dst.getRowPitch(), dst.getSlicePitch(), dst.getDataPtr()); } for (int x = 0; x < dstRect.z(); ++x) for (int y = 0; y < dstRect.w(); ++y) { int srcX = (flipX) ? (srcRect.z() - x - 1) : (x); int srcY = (flipY) ? (srcRect.z() - y - 1) : (y); if (dstIsFloat || srcIsSRGB) { Vec4 p = src.raw().getPixel(0, srcX, srcY); dst.setPixel((dstIsSRGB && convertSRGB) ? tcu::linearToSRGB(p) : p, x, y); } else dst.setPixel(src.raw().getPixelInt(0, srcX, srcY), x, y); } } if (mask & GL_DEPTH_BUFFER_BIT) { rr::MultisampleConstPixelBufferAccess src = rr::getSubregion(getReadDepthbuffer(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w()); rr::MultisamplePixelBufferAccess dst = rr::getSubregion(getDrawDepthbuffer(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w()); for (int x = 0; x < dstRect.z(); ++x) for (int y = 0; y < dstRect.w(); ++y) { int srcX = (flipX) ? (srcRect.z() - x - 1) : (x); int srcY = (flipY) ? (srcRect.z() - y - 1) : (y); writeDepthOnly(dst, 0, x, y, src.raw().getPixel(0, srcX, srcY).x()); } } if (mask & GL_STENCIL_BUFFER_BIT) { rr::MultisampleConstPixelBufferAccess src = getStencilMultisampleAccess( rr::getSubregion(getReadStencilbuffer(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w())); rr::MultisamplePixelBufferAccess dst = getStencilMultisampleAccess( rr::getSubregion(getDrawStencilbuffer(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w())); for (int x = 0; x < dstRect.z(); ++x) for (int y = 0; y < dstRect.w(); ++y) { int srcX = (flipX) ? (srcRect.z() - x - 1) : (x); int srcY = (flipY) ? (srcRect.z() - y - 1) : (y); uint32_t srcStencil = src.raw().getPixelUint(0, srcX, srcY).x(); writeMaskedStencil(dst, 0, x, y, srcStencil, m_stencil[rr::FACETYPE_FRONT].writeMask); } } return GL_NO_ERROR; } void ReferenceContext::blitFramebuffer(int srcX0, int srcY0, int srcX1, int srcY1, int dstX0, int dstY0, int dstX1, int dstY1, uint32_t mask, uint32_t filter) { // p0 in inclusive, p1 exclusive. // Negative width/height means swap. bool swapSrcX = srcX1 < srcX0; bool swapSrcY = srcY1 < srcY0; bool swapDstX = dstX1 < dstX0; bool swapDstY = dstY1 < dstY0; int srcW = de::abs(srcX1 - srcX0); int srcH = de::abs(srcY1 - srcY0); int dstW = de::abs(dstX1 - dstX0); int dstH = de::abs(dstY1 - dstY0); bool scale = srcW != dstW || srcH != dstH; int srcOriginX = swapSrcX ? srcX1 : srcX0; int srcOriginY = swapSrcY ? srcY1 : srcY0; int dstOriginX = swapDstX ? dstX1 : dstX0; int dstOriginY = swapDstY ? dstY1 : dstY0; IVec4 srcRect = IVec4(srcOriginX, srcOriginY, srcW, srcH); IVec4 dstRect = IVec4(dstOriginX, dstOriginY, dstW, dstH); RC_IF_ERROR(filter != GL_NEAREST && filter != GL_LINEAR, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR((mask & (GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0 && filter != GL_NEAREST, GL_INVALID_OPERATION, RC_RET_VOID); // Validate that both targets are complete. RC_IF_ERROR(checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE || checkFramebufferStatus(GL_READ_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_OPERATION, RC_RET_VOID); // Check samples count is valid RC_IF_ERROR(getDrawColorbuffer().getNumSamples() != 1, GL_INVALID_OPERATION, RC_RET_VOID); // Check size restrictions of multisampled case if (getReadColorbuffer().getNumSamples() != 1) { // Src and Dst rect dimensions must be the same RC_IF_ERROR(srcW != dstW || srcH != dstH, GL_INVALID_OPERATION, RC_RET_VOID); // Framebuffer formats must match if (mask & GL_COLOR_BUFFER_BIT) RC_IF_ERROR(getReadColorbuffer().raw().getFormat() != getDrawColorbuffer().raw().getFormat(), GL_INVALID_OPERATION, RC_RET_VOID); if (mask & GL_DEPTH_BUFFER_BIT) RC_IF_ERROR(getReadDepthbuffer().raw().getFormat() != getDrawDepthbuffer().raw().getFormat(), GL_INVALID_OPERATION, RC_RET_VOID); if (mask & GL_STENCIL_BUFFER_BIT) RC_IF_ERROR(getReadStencilbuffer().raw().getFormat() != getDrawStencilbuffer().raw().getFormat(), GL_INVALID_OPERATION, RC_RET_VOID); } // Compute actual source rect. srcRect = (mask & GL_COLOR_BUFFER_BIT) ? intersect(srcRect, getBufferRect(getReadColorbuffer())) : srcRect; srcRect = (mask & GL_DEPTH_BUFFER_BIT) ? intersect(srcRect, getBufferRect(getReadDepthbuffer())) : srcRect; srcRect = (mask & GL_STENCIL_BUFFER_BIT) ? intersect(srcRect, getBufferRect(getReadStencilbuffer())) : srcRect; // Compute destination rect. dstRect = (mask & GL_COLOR_BUFFER_BIT) ? intersect(dstRect, getBufferRect(getDrawColorbuffer())) : dstRect; dstRect = (mask & GL_DEPTH_BUFFER_BIT) ? intersect(dstRect, getBufferRect(getDrawDepthbuffer())) : dstRect; dstRect = (mask & GL_STENCIL_BUFFER_BIT) ? intersect(dstRect, getBufferRect(getDrawStencilbuffer())) : dstRect; dstRect = m_scissorEnabled ? intersect(dstRect, m_scissorBox) : dstRect; if (isEmpty(srcRect) || isEmpty(dstRect)) return; // Don't attempt copy. // Multisampled read buffer is a special case if (getReadColorbuffer().getNumSamples() != 1) { uint32_t error = blitResolveMultisampleFramebuffer(mask, srcRect, dstRect, swapSrcX ^ swapDstX, swapSrcY ^ swapDstY); if (error != GL_NO_ERROR) setError(error); return; } // \note Multisample pixel buffers can now be accessed like non-multisampled because multisample read buffer case is already handled. => sample count must be 1 // Coordinate transformation: // Dst offset space -> dst rectangle space -> src rectangle space -> src offset space. tcu::Mat3 transform = tcu::translationMatrix(Vec2((float)(srcX0 - srcRect.x()), (float)(srcY0 - srcRect.y()))) * tcu::Mat3(Vec3((float)(srcX1 - srcX0) / (float)(dstX1 - dstX0), (float)(srcY1 - srcY0) / (float)(dstY1 - dstY0), 1.0f)) * tcu::translationMatrix(Vec2((float)(dstRect.x() - dstX0), (float)(dstRect.y() - dstY0))); if (mask & GL_COLOR_BUFFER_BIT) { tcu::ConstPixelBufferAccess src = tcu::getSubregion(getReadColorbuffer().toSinglesampleAccess(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w()); tcu::PixelBufferAccess dst = tcu::getSubregion(getDrawColorbuffer().toSinglesampleAccess(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w()); tcu::TextureChannelClass dstClass = tcu::getTextureChannelClass(dst.getFormat().type); bool dstIsFloat = dstClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT || dstClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT || dstClass == tcu::TEXTURECHANNELCLASS_SIGNED_FIXED_POINT; tcu::Sampler::FilterMode sFilter = (scale && filter == GL_LINEAR) ? tcu::Sampler::LINEAR : tcu::Sampler::NEAREST; tcu::Sampler sampler(tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, tcu::Sampler::CLAMP_TO_EDGE, sFilter, sFilter, 0.0f /* lod threshold */, false /* non-normalized coords */); bool srcIsSRGB = tcu::isSRGB(src.getFormat()); bool dstIsSRGB = tcu::isSRGB(dst.getFormat()); const bool convertSRGB = m_sRGBUpdateEnabled && glu::isContextTypeES(getType()); if (!convertSRGB) { src = tcu::ConstPixelBufferAccess(toNonSRGBFormat(src.getFormat()), src.getWidth(), src.getHeight(), src.getDepth(), src.getRowPitch(), src.getSlicePitch(), src.getDataPtr()); dst = tcu::PixelBufferAccess(toNonSRGBFormat(dst.getFormat()), dst.getWidth(), dst.getHeight(), dst.getDepth(), dst.getRowPitch(), dst.getSlicePitch(), dst.getDataPtr()); } // \note We don't check for unsupported conversions, unlike spec requires. for (int yo = 0; yo < dstRect.w(); yo++) { for (int xo = 0; xo < dstRect.z(); xo++) { float dX = (float)xo + 0.5f; float dY = (float)yo + 0.5f; // \note Only affine part is used. float sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); float sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); // do not copy pixels outside the modified source region (modified by buffer intersection) if (sX < 0.0f || sX >= (float)srcRect.z() || sY < 0.0f || sY >= (float)srcRect.w()) continue; if (dstIsFloat || srcIsSRGB || filter == tcu::Sampler::LINEAR) { Vec4 p = src.sample2D(sampler, sampler.minFilter, sX, sY, 0); dst.setPixel((dstIsSRGB && convertSRGB) ? tcu::linearToSRGB(p) : p, xo, yo); } else dst.setPixel(src.getPixelInt(deFloorFloatToInt32(sX), deFloorFloatToInt32(sY)), xo, yo); } } } if ((mask & GL_DEPTH_BUFFER_BIT) && m_depthMask) { rr::MultisampleConstPixelBufferAccess src = getDepthMultisampleAccess( rr::getSubregion(getReadDepthbuffer(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w())); rr::MultisamplePixelBufferAccess dst = getDepthMultisampleAccess( rr::getSubregion(getDrawDepthbuffer(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w())); for (int yo = 0; yo < dstRect.w(); yo++) { for (int xo = 0; xo < dstRect.z(); xo++) { const int sampleNdx = 0; // multisample read buffer case is already handled float dX = (float)xo + 0.5f; float dY = (float)yo + 0.5f; float sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); float sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); writeDepthOnly(dst, sampleNdx, xo, yo, src.raw().getPixDepth(sampleNdx, deFloorFloatToInt32(sX), deFloorFloatToInt32(sY))); } } } if (mask & GL_STENCIL_BUFFER_BIT) { rr::MultisampleConstPixelBufferAccess src = getStencilMultisampleAccess( rr::getSubregion(getReadStencilbuffer(), srcRect.x(), srcRect.y(), srcRect.z(), srcRect.w())); rr::MultisamplePixelBufferAccess dst = getStencilMultisampleAccess( rr::getSubregion(getDrawStencilbuffer(), dstRect.x(), dstRect.y(), dstRect.z(), dstRect.w())); for (int yo = 0; yo < dstRect.w(); yo++) { for (int xo = 0; xo < dstRect.z(); xo++) { const int sampleNdx = 0; // multisample read buffer case is already handled float dX = (float)xo + 0.5f; float dY = (float)yo + 0.5f; float sX = transform(0, 0) * dX + transform(0, 1) * dY + transform(0, 2); float sY = transform(1, 0) * dX + transform(1, 1) * dY + transform(1, 2); uint32_t srcStencil = src.raw().getPixelUint(sampleNdx, deFloorFloatToInt32(sX), deFloorFloatToInt32(sY)).x(); writeMaskedStencil(dst, sampleNdx, xo, yo, srcStencil, m_stencil[rr::FACETYPE_FRONT].writeMask); } } } } void ReferenceContext::invalidateSubFramebuffer(uint32_t target, int numAttachments, const uint32_t *attachments, int x, int y, int width, int height) { RC_IF_ERROR(target != GL_FRAMEBUFFER, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR((numAttachments < 0) || (numAttachments > 1 && attachments == DE_NULL), GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(width < 0 || height < 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-07-17 pyry] Support multiple color attachments. const Vec4 colorClearValue(0.0f); const float depthClearValue = 1.0f; const int stencilClearValue = 0; bool isFboBound = m_drawFramebufferBinding != DE_NULL; bool discardBuffers[3] = {false, false, false}; // Color, depth, stencil for (int attNdx = 0; attNdx < numAttachments; attNdx++) { bool isColor = attachments[attNdx] == (isFboBound ? GL_COLOR_ATTACHMENT0 : GL_COLOR); bool isDepth = attachments[attNdx] == (isFboBound ? GL_DEPTH_ATTACHMENT : GL_DEPTH); bool isStencil = attachments[attNdx] == (isFboBound ? GL_STENCIL_ATTACHMENT : GL_STENCIL); bool isDepthStencil = isFboBound && attachments[attNdx] == GL_DEPTH_STENCIL_ATTACHMENT; RC_IF_ERROR(!isColor && !isDepth && !isStencil && !isDepthStencil, GL_INVALID_VALUE, RC_RET_VOID); if (isColor) discardBuffers[0] = true; if (isDepth || isDepthStencil) discardBuffers[1] = true; if (isStencil || isDepthStencil) discardBuffers[2] = true; } for (int ndx = 0; ndx < 3; ndx++) { if (!discardBuffers[ndx]) continue; bool isColor = ndx == 0; bool isDepth = ndx == 1; bool isStencil = ndx == 2; rr::MultisamplePixelBufferAccess buf = isColor ? getDrawColorbuffer() : isDepth ? getDepthMultisampleAccess(getDrawDepthbuffer()) : getStencilMultisampleAccess(getDrawStencilbuffer()); if (isEmpty(buf)) continue; tcu::IVec4 area = intersect(tcu::IVec4(0, 0, buf.raw().getHeight(), buf.raw().getDepth()), tcu::IVec4(x, y, width, height)); rr::MultisamplePixelBufferAccess access = rr::getSubregion(buf, area.x(), area.y(), area.z(), area.w()); if (isColor) rr::clear(access, colorClearValue); else if (isDepth) rr::clear(access, tcu::Vec4(depthClearValue)); else if (isStencil) rr::clear(access, tcu::IVec4(stencilClearValue)); } } void ReferenceContext::invalidateFramebuffer(uint32_t target, int numAttachments, const uint32_t *attachments) { // \todo [2012-07-17 pyry] Support multiple color attachments. rr::MultisampleConstPixelBufferAccess colorBuf0 = getDrawColorbuffer(); rr::MultisampleConstPixelBufferAccess depthBuf = getDrawDepthbuffer(); rr::MultisampleConstPixelBufferAccess stencilBuf = getDrawStencilbuffer(); int width = 0; int height = 0; width = de::max(width, colorBuf0.raw().getHeight()); width = de::max(width, depthBuf.raw().getHeight()); width = de::max(width, stencilBuf.raw().getHeight()); height = de::max(height, colorBuf0.raw().getDepth()); height = de::max(height, depthBuf.raw().getDepth()); height = de::max(height, stencilBuf.raw().getDepth()); invalidateSubFramebuffer(target, numAttachments, attachments, 0, 0, width, height); } void ReferenceContext::clear(uint32_t buffers) { RC_IF_ERROR((buffers & ~(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT)) != 0, GL_INVALID_VALUE, RC_RET_VOID); rr::MultisamplePixelBufferAccess colorBuf0 = getDrawColorbuffer(); rr::MultisamplePixelBufferAccess depthBuf = getDrawDepthbuffer(); rr::MultisamplePixelBufferAccess stencilBuf = getDrawStencilbuffer(); IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff); IVec4 colorArea = intersect(baseArea, getBufferRect(colorBuf0)); IVec4 depthArea = intersect(baseArea, getBufferRect(depthBuf)); IVec4 stencilArea = intersect(baseArea, getBufferRect(stencilBuf)); bool hasColor0 = !isEmpty(colorArea); bool hasDepth = !isEmpty(depthArea); bool hasStencil = !isEmpty(stencilArea); if (hasColor0 && (buffers & GL_COLOR_BUFFER_BIT) != 0) { rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf0, colorArea.x(), colorArea.y(), colorArea.z(), colorArea.w()); bool isSRGB = tcu::isSRGB(colorBuf0.raw().getFormat()); Vec4 c = (isSRGB && m_sRGBUpdateEnabled) ? tcu::linearToSRGB(m_clearColor) : m_clearColor; bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3]; bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3]; if (!maskUsed) rr::clear(access, c); else if (!maskZero) { for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) access.raw().setPixel(tcu::select(c, access.raw().getPixel(s, x, y), m_colorMask), s, x, y); } // else all channels masked out } if (hasDepth && (buffers & GL_DEPTH_BUFFER_BIT) != 0 && m_depthMask) { rr::MultisamplePixelBufferAccess access = getDepthMultisampleAccess( rr::getSubregion(depthBuf, depthArea.x(), depthArea.y(), depthArea.z(), depthArea.w())); rr::clearDepth(access, m_clearDepth); } if (hasStencil && (buffers & GL_STENCIL_BUFFER_BIT) != 0) { rr::MultisamplePixelBufferAccess access = getStencilMultisampleAccess( rr::getSubregion(stencilBuf, stencilArea.x(), stencilArea.y(), stencilArea.z(), stencilArea.w())); int stencilBits = getNumStencilBits(stencilBuf.raw().getFormat()); int stencil = maskStencil(stencilBits, m_clearStencil); if ((m_stencil[rr::FACETYPE_FRONT].writeMask & ((1u << stencilBits) - 1u)) != ((1u << stencilBits) - 1u)) { // Slow path where depth or stencil is masked out in write. for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) writeMaskedStencil(access, s, x, y, stencil, m_stencil[rr::FACETYPE_FRONT].writeMask); } else rr::clearStencil(access, stencil); } } void ReferenceContext::clearBufferiv(uint32_t buffer, int drawbuffer, const int *value) { RC_IF_ERROR(buffer != GL_COLOR && buffer != GL_STENCIL, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support. IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff); if (buffer == GL_COLOR) { rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer(); bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3]; bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3]; IVec4 area = intersect(baseArea, getBufferRect(colorBuf)); if (!isEmpty(area) && !maskZero) { rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w()); IVec4 color(value[0], value[1], value[2], value[3]); if (!maskUsed) rr::clear(access, color); else { for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) access.raw().setPixel(tcu::select(color, access.raw().getPixelInt(s, x, y), m_colorMask), s, x, y); } } } else { TCU_CHECK_INTERNAL(buffer == GL_STENCIL); rr::MultisamplePixelBufferAccess stencilBuf = getDrawStencilbuffer(); IVec4 area = intersect(baseArea, getBufferRect(stencilBuf)); if (!isEmpty(area) && m_stencil[rr::FACETYPE_FRONT].writeMask != 0) { rr::MultisamplePixelBufferAccess access = getStencilMultisampleAccess(rr::getSubregion(stencilBuf, area.x(), area.y(), area.z(), area.w())); int stencil = value[0]; for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) writeMaskedStencil(access, s, x, y, stencil, m_stencil[rr::FACETYPE_FRONT].writeMask); } } } void ReferenceContext::clearBufferfv(uint32_t buffer, int drawbuffer, const float *value) { RC_IF_ERROR(buffer != GL_COLOR && buffer != GL_DEPTH, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support. IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff); if (buffer == GL_COLOR) { rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer(); bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3]; bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3]; IVec4 area = intersect(baseArea, getBufferRect(colorBuf)); if (!isEmpty(area) && !maskZero) { rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w()); Vec4 color(value[0], value[1], value[2], value[3]); if (m_sRGBUpdateEnabled && tcu::isSRGB(access.raw().getFormat())) color = tcu::linearToSRGB(color); if (!maskUsed) rr::clear(access, color); else { for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) access.raw().setPixel(tcu::select(color, access.raw().getPixel(s, x, y), m_colorMask), s, x, y); } } } else { TCU_CHECK_INTERNAL(buffer == GL_DEPTH); rr::MultisamplePixelBufferAccess depthBuf = getDrawDepthbuffer(); IVec4 area = intersect(baseArea, getBufferRect(depthBuf)); if (!isEmpty(area) && m_depthMask) { rr::MultisamplePixelBufferAccess access = rr::getSubregion(depthBuf, area.x(), area.y(), area.z(), area.w()); float depth = value[0]; rr::clearDepth(access, depth); } } } void ReferenceContext::clearBufferuiv(uint32_t buffer, int drawbuffer, const uint32_t *value) { RC_IF_ERROR(buffer != GL_COLOR, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support. IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff); TCU_CHECK_INTERNAL(buffer == GL_COLOR); { rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer(); bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3]; bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3]; IVec4 area = intersect(baseArea, getBufferRect(colorBuf)); if (!isEmpty(area) && !maskZero) { rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w()); tcu::UVec4 color(value[0], value[1], value[2], value[3]); if (!maskUsed) rr::clear(access, color.asInt()); else { for (int y = 0; y < access.raw().getDepth(); y++) for (int x = 0; x < access.raw().getHeight(); x++) for (int s = 0; s < access.getNumSamples(); s++) access.raw().setPixel(tcu::select(color, access.raw().getPixelUint(s, x, y), m_colorMask), s, x, y); } } } } void ReferenceContext::clearBufferfi(uint32_t buffer, int drawbuffer, float depth, int stencil) { RC_IF_ERROR(buffer != GL_DEPTH_STENCIL, GL_INVALID_ENUM, RC_RET_VOID); clearBufferfv(GL_DEPTH, drawbuffer, &depth); clearBufferiv(GL_STENCIL, drawbuffer, &stencil); } void ReferenceContext::bindVertexArray(uint32_t array) { rc::VertexArray *vertexArrayObject = DE_NULL; if (array != 0) { vertexArrayObject = m_vertexArrays.find(array); if (!vertexArrayObject) { vertexArrayObject = new rc::VertexArray(array, m_limits.maxVertexAttribs); m_vertexArrays.insert(vertexArrayObject); } } // Create new references if (vertexArrayObject) m_vertexArrays.acquireReference(vertexArrayObject); // Remove old references if (m_vertexArrayBinding) m_vertexArrays.releaseReference(m_vertexArrayBinding); m_vertexArrayBinding = vertexArrayObject; } void ReferenceContext::genVertexArrays(int numArrays, uint32_t *vertexArrays) { RC_IF_ERROR(!vertexArrays, GL_INVALID_VALUE, RC_RET_VOID); for (int ndx = 0; ndx < numArrays; ndx++) vertexArrays[ndx] = m_vertexArrays.allocateName(); } void ReferenceContext::deleteVertexArrays(int numArrays, const uint32_t *vertexArrays) { for (int i = 0; i < numArrays; i++) { uint32_t name = vertexArrays[i]; VertexArray *vertexArray = name ? m_vertexArrays.find(name) : DE_NULL; if (vertexArray) deleteVertexArray(vertexArray); } } void ReferenceContext::vertexAttribPointer(uint32_t index, int rawSize, uint32_t type, bool normalized, int stride, const void *pointer) { const bool allowBGRA = !glu::isContextTypeES(getType()); const int effectiveSize = (allowBGRA && rawSize == GL_BGRA) ? (4) : (rawSize); RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(effectiveSize <= 0 || effectiveSize > 4, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(type != GL_BYTE && type != GL_UNSIGNED_BYTE && type != GL_SHORT && type != GL_UNSIGNED_SHORT && type != GL_INT && type != GL_UNSIGNED_INT && type != GL_FIXED && type != GL_DOUBLE && type != GL_FLOAT && type != GL_HALF_FLOAT && type != GL_INT_2_10_10_10_REV && type != GL_UNSIGNED_INT_2_10_10_10_REV, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(normalized != GL_TRUE && normalized != GL_FALSE, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(stride < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR((type == GL_INT_2_10_10_10_REV || type == GL_UNSIGNED_INT_2_10_10_10_REV) && effectiveSize != 4, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(m_vertexArrayBinding != DE_NULL && m_arrayBufferBinding == DE_NULL && pointer != DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(allowBGRA && rawSize == GL_BGRA && type != GL_INT_2_10_10_10_REV && type != GL_UNSIGNED_INT_2_10_10_10_REV && type != GL_UNSIGNED_BYTE, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(allowBGRA && rawSize == GL_BGRA && normalized == GL_FALSE, GL_INVALID_OPERATION, RC_RET_VOID); rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vao.m_arrays[index].size = rawSize; vao.m_arrays[index].stride = stride; vao.m_arrays[index].type = type; vao.m_arrays[index].normalized = normalized == GL_TRUE; vao.m_arrays[index].integer = false; vao.m_arrays[index].pointer = pointer; // acquire new reference if (m_arrayBufferBinding) m_buffers.acquireReference(m_arrayBufferBinding); // release old reference if (vao.m_arrays[index].bufferBinding) m_buffers.releaseReference(vao.m_arrays[index].bufferBinding); vao.m_arrays[index].bufferDeleted = false; vao.m_arrays[index].bufferBinding = m_arrayBufferBinding; } void ReferenceContext::vertexAttribIPointer(uint32_t index, int size, uint32_t type, int stride, const void *pointer) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(size <= 0 || size > 4, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(type != GL_BYTE && type != GL_UNSIGNED_BYTE && type != GL_SHORT && type != GL_UNSIGNED_SHORT && type != GL_INT && type != GL_UNSIGNED_INT, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(stride < 0, GL_INVALID_VALUE, RC_RET_VOID); RC_IF_ERROR(m_vertexArrayBinding != DE_NULL && m_arrayBufferBinding == DE_NULL && pointer != DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vao.m_arrays[index].size = size; vao.m_arrays[index].stride = stride; vao.m_arrays[index].type = type; vao.m_arrays[index].normalized = false; vao.m_arrays[index].integer = true; vao.m_arrays[index].pointer = pointer; // acquire new reference if (m_arrayBufferBinding) m_buffers.acquireReference(m_arrayBufferBinding); // release old reference if (vao.m_arrays[index].bufferBinding) m_buffers.releaseReference(vao.m_arrays[index].bufferBinding); vao.m_arrays[index].bufferDeleted = false; vao.m_arrays[index].bufferBinding = m_arrayBufferBinding; } void ReferenceContext::enableVertexAttribArray(uint32_t index) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vao.m_arrays[index].enabled = true; } void ReferenceContext::disableVertexAttribArray(uint32_t index) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vao.m_arrays[index].enabled = false; } void ReferenceContext::vertexAttribDivisor(uint32_t index, uint32_t divisor) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vao.m_arrays[index].divisor = divisor; } void ReferenceContext::vertexAttrib1f(uint32_t index, float x) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, 0, 0, 1)); } void ReferenceContext::vertexAttrib2f(uint32_t index, float x, float y) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, 0, 1)); } void ReferenceContext::vertexAttrib3f(uint32_t index, float x, float y, float z) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, z, 1)); } void ReferenceContext::vertexAttrib4f(uint32_t index, float x, float y, float z, float w) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, z, w)); } void ReferenceContext::vertexAttribI4i(uint32_t index, int32_t x, int32_t y, int32_t z, int32_t w) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::IVec4(x, y, z, w)); } void ReferenceContext::vertexAttribI4ui(uint32_t index, uint32_t x, uint32_t y, uint32_t z, uint32_t w) { RC_IF_ERROR(index >= (uint32_t)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::UVec4(x, y, z, w)); } int32_t ReferenceContext::getAttribLocation(uint32_t program, const char *name) { ShaderProgramObjectContainer *shaderProg = m_programs.find(program); RC_IF_ERROR(shaderProg == DE_NULL, GL_INVALID_OPERATION, -1); if (name) { std::string nameString(name); for (size_t ndx = 0; ndx < shaderProg->m_program->m_attributeNames.size(); ++ndx) if (shaderProg->m_program->m_attributeNames[ndx] == nameString) return (int)ndx; } return -1; } void ReferenceContext::uniformv(int32_t location, glu::DataType type, int32_t count, const void *v) { RC_IF_ERROR(m_currentProgram == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); std::vector &uniforms = m_currentProgram->m_program->m_uniforms; if (location == -1) return; RC_IF_ERROR(location < 0 || (size_t)location >= uniforms.size(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(uniforms[location].type != type, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(count != 1, GL_INVALID_OPERATION, RC_RET_VOID); // \todo [2013-12-13 pyry] Array uniforms. { const int scalarSize = glu::getDataTypeScalarSize(type); DE_ASSERT(scalarSize * sizeof(uint32_t) <= sizeof(uniforms[location].value)); deMemcpy(&uniforms[location].value, v, scalarSize * (int)sizeof(uint32_t)); } } void ReferenceContext::uniform1iv(int32_t location, int32_t count, const int32_t *v) { RC_IF_ERROR(m_currentProgram == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); std::vector &uniforms = m_currentProgram->m_program->m_uniforms; if (location == -1) return; RC_IF_ERROR(location < 0 || (size_t)location >= uniforms.size(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(count != 1, GL_INVALID_OPERATION, RC_RET_VOID); // \todo [2013-12-13 pyry] Array uniforms. switch (uniforms[location].type) { case glu::TYPE_INT: uniforms[location].value.i = *v; return; // \note texture unit is stored to value case glu::TYPE_SAMPLER_2D: case glu::TYPE_UINT_SAMPLER_2D: case glu::TYPE_INT_SAMPLER_2D: case glu::TYPE_SAMPLER_CUBE: case glu::TYPE_UINT_SAMPLER_CUBE: case glu::TYPE_INT_SAMPLER_CUBE: case glu::TYPE_SAMPLER_2D_ARRAY: case glu::TYPE_UINT_SAMPLER_2D_ARRAY: case glu::TYPE_INT_SAMPLER_2D_ARRAY: case glu::TYPE_SAMPLER_3D: case glu::TYPE_UINT_SAMPLER_3D: case glu::TYPE_INT_SAMPLER_3D: case glu::TYPE_SAMPLER_CUBE_ARRAY: case glu::TYPE_UINT_SAMPLER_CUBE_ARRAY: case glu::TYPE_INT_SAMPLER_CUBE_ARRAY: uniforms[location].value.i = *v; return; default: setError(GL_INVALID_OPERATION); return; } } void ReferenceContext::uniform1f(int32_t location, const float v0) { uniform1fv(location, 1, &v0); } void ReferenceContext::uniform1i(int32_t location, int32_t v0) { uniform1iv(location, 1, &v0); } void ReferenceContext::uniform1fv(int32_t location, int32_t count, const float *v) { uniformv(location, glu::TYPE_FLOAT, count, v); } void ReferenceContext::uniform2fv(int32_t location, int32_t count, const float *v) { uniformv(location, glu::TYPE_FLOAT_VEC2, count, v); } void ReferenceContext::uniform3fv(int32_t location, int32_t count, const float *v) { uniformv(location, glu::TYPE_FLOAT_VEC3, count, v); } void ReferenceContext::uniform4fv(int32_t location, int32_t count, const float *v) { uniformv(location, glu::TYPE_FLOAT_VEC4, count, v); } void ReferenceContext::uniform2iv(int32_t location, int32_t count, const int32_t *v) { uniformv(location, glu::TYPE_INT_VEC2, count, v); } void ReferenceContext::uniform3iv(int32_t location, int32_t count, const int32_t *v) { uniformv(location, glu::TYPE_INT_VEC3, count, v); } void ReferenceContext::uniform4iv(int32_t location, int32_t count, const int32_t *v) { uniformv(location, glu::TYPE_INT_VEC4, count, v); } void ReferenceContext::uniformMatrix3fv(int32_t location, int32_t count, bool transpose, const float *value) { RC_IF_ERROR(m_currentProgram == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); std::vector &uniforms = m_currentProgram->m_program->m_uniforms; if (location == -1) return; RC_IF_ERROR(location < 0 || (size_t)location >= uniforms.size(), GL_INVALID_OPERATION, RC_RET_VOID); if (count == 0) return; RC_IF_ERROR(transpose != GL_TRUE && transpose != GL_FALSE, GL_INVALID_ENUM, RC_RET_VOID); switch (uniforms[location].type) { case glu::TYPE_FLOAT_MAT3: RC_IF_ERROR(count > 1, GL_INVALID_OPERATION, RC_RET_VOID); if (transpose == GL_FALSE) // input is column major => transpose from column major to internal row major for (int row = 0; row < 3; ++row) for (int col = 0; col < 3; ++col) uniforms[location].value.m3[row * 3 + col] = value[col * 3 + row]; else // input is row major for (int row = 0; row < 3; ++row) for (int col = 0; col < 3; ++col) uniforms[location].value.m3[row * 3 + col] = value[row * 3 + col]; break; default: setError(GL_INVALID_OPERATION); return; } } void ReferenceContext::uniformMatrix4fv(int32_t location, int32_t count, bool transpose, const float *value) { RC_IF_ERROR(m_currentProgram == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); std::vector &uniforms = m_currentProgram->m_program->m_uniforms; if (location == -1) return; RC_IF_ERROR(location < 0 || (size_t)location >= uniforms.size(), GL_INVALID_OPERATION, RC_RET_VOID); if (count == 0) return; RC_IF_ERROR(transpose != GL_TRUE && transpose != GL_FALSE, GL_INVALID_ENUM, RC_RET_VOID); switch (uniforms[location].type) { case glu::TYPE_FLOAT_MAT4: RC_IF_ERROR(count > 1, GL_INVALID_OPERATION, RC_RET_VOID); if (transpose == GL_FALSE) // input is column major => transpose from column major to internal row major for (int row = 0; row < 4; ++row) for (int col = 0; col < 4; ++col) uniforms[location].value.m4[row * 3 + col] = value[col * 3 + row]; else // input is row major for (int row = 0; row < 4; ++row) for (int col = 0; col < 4; ++col) uniforms[location].value.m4[row * 3 + col] = value[row * 3 + col]; break; default: setError(GL_INVALID_OPERATION); return; } } int32_t ReferenceContext::getUniformLocation(uint32_t program, const char *name) { ShaderProgramObjectContainer *shaderProg = m_programs.find(program); RC_IF_ERROR(shaderProg == DE_NULL, GL_INVALID_OPERATION, -1); std::vector &uniforms = shaderProg->m_program->m_uniforms; for (size_t i = 0; i < uniforms.size(); ++i) if (name && deStringEqual(uniforms[i].name.c_str(), name)) return (int)i; return -1; } void ReferenceContext::lineWidth(float w) { RC_IF_ERROR(w < 0.0f, GL_INVALID_VALUE, RC_RET_VOID); m_lineWidth = w; } void ReferenceContext::deleteVertexArray(rc::VertexArray *vertexArray) { if (m_vertexArrayBinding == vertexArray) bindVertexArray(0); if (vertexArray->m_elementArrayBufferBinding) m_buffers.releaseReference(vertexArray->m_elementArrayBufferBinding); for (size_t ndx = 0; ndx < vertexArray->m_arrays.size(); ++ndx) if (vertexArray->m_arrays[ndx].bufferBinding) m_buffers.releaseReference(vertexArray->m_arrays[ndx].bufferBinding); DE_ASSERT(vertexArray->getRefCount() == 1); m_vertexArrays.releaseReference(vertexArray); } void ReferenceContext::deleteProgramObject(rc::ShaderProgramObjectContainer *sp) { // Unbinding program will delete it if (m_currentProgram == sp && sp->m_deleteFlag) { useProgram(0); return; } // Unbinding program will NOT delete it if (m_currentProgram == sp) useProgram(0); DE_ASSERT(sp->getRefCount() == 1); m_programs.releaseReference(sp); } void ReferenceContext::drawArrays(uint32_t mode, int first, int count) { drawArraysInstanced(mode, first, count, 1); } void ReferenceContext::drawArraysInstanced(uint32_t mode, int first, int count, int instanceCount) { // Error conditions { RC_IF_ERROR(first < 0 || count < 0 || instanceCount < 0, GL_INVALID_VALUE, RC_RET_VOID); if (!predrawErrorChecks(mode)) return; } // All is ok { const rr::PrimitiveType primitiveType = sglr::rr_util::mapGLPrimitiveType(mode); drawWithReference(rr::PrimitiveList(primitiveType, count, first), instanceCount); } } void ReferenceContext::drawElements(uint32_t mode, int count, uint32_t type, const void *indices) { drawElementsInstanced(mode, count, type, indices, 1); } void ReferenceContext::drawElementsBaseVertex(uint32_t mode, int count, uint32_t type, const void *indices, int baseVertex) { drawElementsInstancedBaseVertex(mode, count, type, indices, 1, baseVertex); } void ReferenceContext::drawElementsInstanced(uint32_t mode, int count, uint32_t type, const void *indices, int instanceCount) { drawElementsInstancedBaseVertex(mode, count, type, indices, instanceCount, 0); } void ReferenceContext::drawElementsInstancedBaseVertex(uint32_t mode, int count, uint32_t type, const void *indices, int instanceCount, int baseVertex) { rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); // Error conditions { RC_IF_ERROR(type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(count < 0 || instanceCount < 0, GL_INVALID_VALUE, RC_RET_VOID); if (!predrawErrorChecks(mode)) return; } // All is ok { const rr::PrimitiveType primitiveType = sglr::rr_util::mapGLPrimitiveType(mode); const void *indicesPtr = (vao.m_elementArrayBufferBinding) ? (vao.m_elementArrayBufferBinding->getData() + reinterpret_cast(indices)) : (indices); drawWithReference( rr::PrimitiveList(primitiveType, count, rr::DrawIndices(indicesPtr, sglr::rr_util::mapGLIndexType(type), baseVertex)), instanceCount); } } void ReferenceContext::drawRangeElements(uint32_t mode, uint32_t start, uint32_t end, int count, uint32_t type, const void *indices) { RC_IF_ERROR(end < start, GL_INVALID_VALUE, RC_RET_VOID); drawElements(mode, count, type, indices); } void ReferenceContext::drawRangeElementsBaseVertex(uint32_t mode, uint32_t start, uint32_t end, int count, uint32_t type, const void *indices, int baseVertex) { RC_IF_ERROR(end < start, GL_INVALID_VALUE, RC_RET_VOID); drawElementsBaseVertex(mode, count, type, indices, baseVertex); } void ReferenceContext::drawArraysIndirect(uint32_t mode, const void *indirect) { struct DrawArraysIndirectCommand { uint32_t count; uint32_t primCount; uint32_t first; uint32_t reservedMustBeZero; }; const DrawArraysIndirectCommand *command; // Check errors if (!predrawErrorChecks(mode)) return; // Check pointer validity RC_IF_ERROR(m_drawIndirectBufferBinding == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(!deIsAlignedPtr(indirect, 4), GL_INVALID_OPERATION, RC_RET_VOID); // \note watch for overflows, indirect might be close to 0xFFFFFFFF and indirect+something might overflow RC_IF_ERROR((size_t) reinterpret_cast(indirect) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR((size_t) reinterpret_cast(indirect) + sizeof(DrawArraysIndirectCommand) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); // Check values command = (const DrawArraysIndirectCommand *)(m_drawIndirectBufferBinding->getData() + reinterpret_cast(indirect)); RC_IF_ERROR(command->reservedMustBeZero != 0, GL_INVALID_OPERATION, RC_RET_VOID); // draw drawArraysInstanced(mode, command->first, command->count, command->primCount); } void ReferenceContext::drawElementsIndirect(uint32_t mode, uint32_t type, const void *indirect) { struct DrawElementsIndirectCommand { uint32_t count; uint32_t primCount; uint32_t firstIndex; int32_t baseVertex; uint32_t reservedMustBeZero; }; const DrawElementsIndirectCommand *command; // Check errors if (!predrawErrorChecks(mode)) return; RC_IF_ERROR(type != GL_UNSIGNED_BYTE && type != GL_UNSIGNED_SHORT && type != GL_UNSIGNED_INT, GL_INVALID_ENUM, RC_RET_VOID); RC_IF_ERROR(!getBufferBinding(GL_ELEMENT_ARRAY_BUFFER), GL_INVALID_OPERATION, RC_RET_VOID); // Check pointer validity RC_IF_ERROR(m_drawIndirectBufferBinding == DE_NULL, GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(!deIsAlignedPtr(indirect, 4), GL_INVALID_OPERATION, RC_RET_VOID); // \note watch for overflows, indirect might be close to 0xFFFFFFFF and indirect+something might overflow RC_IF_ERROR((size_t) reinterpret_cast(indirect) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR((size_t) reinterpret_cast(indirect) + sizeof(DrawElementsIndirectCommand) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); // Check values command = (const DrawElementsIndirectCommand *)(m_drawIndirectBufferBinding->getData() + reinterpret_cast(indirect)); RC_IF_ERROR(command->reservedMustBeZero != 0, GL_INVALID_OPERATION, RC_RET_VOID); // Check command error conditions RC_IF_ERROR((int)command->count < 0 || (int)command->primCount < 0, GL_INVALID_VALUE, RC_RET_VOID); // Draw { const size_t sizeOfType = (type == GL_UNSIGNED_BYTE) ? (1) : ((type == GL_UNSIGNED_SHORT) ? (2) : (4)); const void *indicesPtr = glu::BufferOffsetAsPointer(command->firstIndex * sizeOfType); drawElementsInstancedBaseVertex(mode, (int)command->count, type, indicesPtr, (int)command->primCount, command->baseVertex); } } void ReferenceContext::multiDrawArrays(uint32_t mode, const int *first, const int *count, int primCount) { DE_UNREF(mode); DE_UNREF(first); DE_UNREF(count); DE_UNREF(primCount); // not supported in gles, prevent accidental use DE_ASSERT(false); } void ReferenceContext::multiDrawElements(uint32_t mode, const int *count, uint32_t type, const void **indices, int primCount) { DE_UNREF(mode); DE_UNREF(count); DE_UNREF(type); DE_UNREF(indices); DE_UNREF(primCount); // not supported in gles, prevent accidental use DE_ASSERT(false); } void ReferenceContext::multiDrawElementsBaseVertex(uint32_t mode, const int *count, uint32_t type, const void **indices, int primCount, const int *baseVertex) { DE_UNREF(mode); DE_UNREF(count); DE_UNREF(type); DE_UNREF(indices); DE_UNREF(primCount); DE_UNREF(baseVertex); // not supported in gles, prevent accidental use DE_ASSERT(false); } bool ReferenceContext::predrawErrorChecks(uint32_t mode) { RC_IF_ERROR(mode != GL_POINTS && mode != GL_LINE_STRIP && mode != GL_LINE_LOOP && mode != GL_LINES && mode != GL_TRIANGLE_STRIP && mode != GL_TRIANGLE_FAN && mode != GL_TRIANGLES && mode != GL_LINES_ADJACENCY && mode != GL_LINE_STRIP_ADJACENCY && mode != GL_TRIANGLES_ADJACENCY && mode != GL_TRIANGLE_STRIP_ADJACENCY, GL_INVALID_ENUM, false); // \todo [jarkko] Uncomment following code when the buffer mapping support is added //for (size_t ndx = 0; ndx < vao.m_arrays.size(); ++ndx) // if (vao.m_arrays[ndx].enabled && vao.m_arrays[ndx].bufferBinding && vao.m_arrays[ndx].bufferBinding->isMapped) // RC_ERROR_RET(GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR(checkFramebufferStatus(GL_DRAW_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE, GL_INVALID_FRAMEBUFFER_OPERATION, false); // Geometry shader checks if (m_currentProgram && m_currentProgram->m_program->m_hasGeometryShader) { RC_IF_ERROR(m_currentProgram->m_program->rr::GeometryShader::getInputType() == rr::GEOMETRYSHADERINPUTTYPE_POINTS && mode != GL_POINTS, GL_INVALID_OPERATION, false); RC_IF_ERROR(m_currentProgram->m_program->rr::GeometryShader::getInputType() == rr::GEOMETRYSHADERINPUTTYPE_LINES && (mode != GL_LINES && mode != GL_LINE_STRIP && mode != GL_LINE_LOOP), GL_INVALID_OPERATION, false); RC_IF_ERROR(m_currentProgram->m_program->rr::GeometryShader::getInputType() == rr::GEOMETRYSHADERINPUTTYPE_TRIANGLES && (mode != GL_TRIANGLES && mode != GL_TRIANGLE_STRIP && mode != GL_TRIANGLE_FAN), GL_INVALID_OPERATION, false); RC_IF_ERROR(m_currentProgram->m_program->rr::GeometryShader::getInputType() == rr::GEOMETRYSHADERINPUTTYPE_LINES_ADJACENCY && (mode != GL_LINES_ADJACENCY && mode != GL_LINE_STRIP_ADJACENCY), GL_INVALID_OPERATION, false); RC_IF_ERROR(m_currentProgram->m_program->rr::GeometryShader::getInputType() == rr::GEOMETRYSHADERINPUTTYPE_TRIANGLES_ADJACENCY && (mode != GL_TRIANGLES_ADJACENCY && mode != GL_TRIANGLE_STRIP_ADJACENCY), GL_INVALID_OPERATION, false); } return true; } static rr::PrimitiveType getPrimitiveBaseType(rr::PrimitiveType derivedType) { switch (derivedType) { case rr::PRIMITIVETYPE_TRIANGLES: case rr::PRIMITIVETYPE_TRIANGLE_STRIP: case rr::PRIMITIVETYPE_TRIANGLE_FAN: case rr::PRIMITIVETYPE_TRIANGLES_ADJACENCY: case rr::PRIMITIVETYPE_TRIANGLE_STRIP_ADJACENCY: return rr::PRIMITIVETYPE_TRIANGLES; case rr::PRIMITIVETYPE_LINES: case rr::PRIMITIVETYPE_LINE_STRIP: case rr::PRIMITIVETYPE_LINE_LOOP: case rr::PRIMITIVETYPE_LINES_ADJACENCY: case rr::PRIMITIVETYPE_LINE_STRIP_ADJACENCY: return rr::PRIMITIVETYPE_LINES; case rr::PRIMITIVETYPE_POINTS: return rr::PRIMITIVETYPE_POINTS; default: DE_ASSERT(false); return rr::PRIMITIVETYPE_LAST; } } static uint32_t getFixedRestartIndex(rr::IndexType indexType) { switch (indexType) { case rr::INDEXTYPE_UINT8: return 0xFF; case rr::INDEXTYPE_UINT16: return 0xFFFF; case rr::INDEXTYPE_UINT32: return 0xFFFFFFFFul; case rr::INDEXTYPE_LAST: default: DE_ASSERT(false); return 0; } } void ReferenceContext::drawWithReference(const rr::PrimitiveList &primitives, int instanceCount) { // undefined results if (m_currentProgram == DE_NULL) return; rr::MultisamplePixelBufferAccess colorBuf0 = getDrawColorbuffer(); rr::MultisamplePixelBufferAccess depthBuf = getDepthMultisampleAccess(getDrawDepthbuffer()); rr::MultisamplePixelBufferAccess stencilBuf = getStencilMultisampleAccess(getDrawStencilbuffer()); const bool hasStencil = !isEmpty(stencilBuf); const int stencilBits = (hasStencil) ? (getNumStencilBits(stencilBuf.raw().getFormat())) : (0); const rr::RenderTarget renderTarget(colorBuf0, depthBuf, stencilBuf); const rr::Program program( m_currentProgram->m_program->getVertexShader(), m_currentProgram->m_program->getFragmentShader(), (m_currentProgram->m_program->m_hasGeometryShader) ? (m_currentProgram->m_program->getGeometryShader()) : (DE_NULL)); rr::RenderState state((rr::ViewportState)(colorBuf0), m_limits.subpixelBits); const rr::Renderer referenceRenderer; std::vector vertexAttribs; // Gen state { const rr::PrimitiveType baseType = getPrimitiveBaseType(primitives.getPrimitiveType()); const bool polygonOffsetEnabled = (baseType == rr::PRIMITIVETYPE_TRIANGLES) ? (m_polygonOffsetFillEnabled) : (false); //state.cullMode = m_cullMode state.fragOps.scissorTestEnabled = m_scissorEnabled; state.fragOps.scissorRectangle = rr::WindowRectangle(m_scissorBox.x(), m_scissorBox.y(), m_scissorBox.z(), m_scissorBox.w()); state.fragOps.numStencilBits = stencilBits; state.fragOps.stencilTestEnabled = m_stencilTestEnabled; for (int faceType = 0; faceType < rr::FACETYPE_LAST; faceType++) { state.fragOps.stencilStates[faceType].compMask = m_stencil[faceType].opMask; state.fragOps.stencilStates[faceType].writeMask = m_stencil[faceType].writeMask; state.fragOps.stencilStates[faceType].ref = m_stencil[faceType].ref; state.fragOps.stencilStates[faceType].func = sglr::rr_util::mapGLTestFunc(m_stencil[faceType].func); state.fragOps.stencilStates[faceType].sFail = sglr::rr_util::mapGLStencilOp(m_stencil[faceType].opStencilFail); state.fragOps.stencilStates[faceType].dpFail = sglr::rr_util::mapGLStencilOp(m_stencil[faceType].opDepthFail); state.fragOps.stencilStates[faceType].dpPass = sglr::rr_util::mapGLStencilOp(m_stencil[faceType].opDepthPass); } state.fragOps.depthTestEnabled = m_depthTestEnabled; state.fragOps.depthFunc = sglr::rr_util::mapGLTestFunc(m_depthFunc); state.fragOps.depthMask = m_depthMask; state.fragOps.blendMode = m_blendEnabled ? rr::BLENDMODE_STANDARD : rr::BLENDMODE_NONE; state.fragOps.blendRGBState.equation = sglr::rr_util::mapGLBlendEquation(m_blendModeRGB); state.fragOps.blendRGBState.srcFunc = sglr::rr_util::mapGLBlendFunc(m_blendFactorSrcRGB); state.fragOps.blendRGBState.dstFunc = sglr::rr_util::mapGLBlendFunc(m_blendFactorDstRGB); state.fragOps.blendAState.equation = sglr::rr_util::mapGLBlendEquation(m_blendModeAlpha); state.fragOps.blendAState.srcFunc = sglr::rr_util::mapGLBlendFunc(m_blendFactorSrcAlpha); state.fragOps.blendAState.dstFunc = sglr::rr_util::mapGLBlendFunc(m_blendFactorDstAlpha); state.fragOps.blendColor = m_blendColor; state.fragOps.sRGBEnabled = m_sRGBUpdateEnabled; state.fragOps.colorMask = m_colorMask; state.fragOps.depthClampEnabled = m_depthClampEnabled; state.viewport.rect = rr::WindowRectangle(m_viewport.x(), m_viewport.y(), m_viewport.z(), m_viewport.w()); state.viewport.zn = m_depthRangeNear; state.viewport.zf = m_depthRangeFar; //state.point.pointSize = m_pointSize; state.line.lineWidth = m_lineWidth; state.fragOps.polygonOffsetEnabled = polygonOffsetEnabled; state.fragOps.polygonOffsetFactor = m_polygonOffsetFactor; state.fragOps.polygonOffsetUnits = m_polygonOffsetUnits; { const rr::IndexType indexType = primitives.getIndexType(); if (m_primitiveRestartFixedIndex && indexType != rr::INDEXTYPE_LAST) { state.restart.enabled = true; state.restart.restartIndex = getFixedRestartIndex(indexType); } else if (m_primitiveRestartSettableIndex) { // \note PRIMITIVE_RESTART is active for non-indexed (DrawArrays) operations too. state.restart.enabled = true; state.restart.restartIndex = m_primitiveRestartIndex; } else { state.restart.enabled = false; } } state.provokingVertexConvention = (m_provokingFirstVertexConvention) ? (rr::PROVOKINGVERTEX_FIRST) : (rr::PROVOKINGVERTEX_LAST); } // gen attributes { rc::VertexArray &vao = (m_vertexArrayBinding) ? (*m_vertexArrayBinding) : (m_clientVertexArray); vertexAttribs.resize(vao.m_arrays.size()); for (size_t ndx = 0; ndx < vao.m_arrays.size(); ++ndx) { if (!vao.m_arrays[ndx].enabled) { vertexAttribs[ndx].type = rr::VERTEXATTRIBTYPE_DONT_CARE; // reading with wrong type is allowed, but results are undefined vertexAttribs[ndx].generic = m_currentAttribs[ndx]; } else if (vao.m_arrays[ndx].bufferDeleted) { vertexAttribs[ndx].type = rr::VERTEXATTRIBTYPE_DONT_CARE; // reading from deleted buffer, output zeros vertexAttribs[ndx].generic = tcu::Vec4(0, 0, 0, 0); } else { vertexAttribs[ndx].type = (vao.m_arrays[ndx].integer) ? (sglr::rr_util::mapGLPureIntegerVertexAttributeType(vao.m_arrays[ndx].type)) : (sglr::rr_util::mapGLFloatVertexAttributeType(vao.m_arrays[ndx].type, vao.m_arrays[ndx].normalized, vao.m_arrays[ndx].size, this->getType())); vertexAttribs[ndx].size = sglr::rr_util::mapGLSize(vao.m_arrays[ndx].size); vertexAttribs[ndx].stride = vao.m_arrays[ndx].stride; vertexAttribs[ndx].instanceDivisor = vao.m_arrays[ndx].divisor; vertexAttribs[ndx].pointer = (vao.m_arrays[ndx].bufferBinding) ? (vao.m_arrays[ndx].bufferBinding->getData() + reinterpret_cast(vao.m_arrays[ndx].pointer)) : (vao.m_arrays[ndx].pointer); } } } // Set shader samplers for (size_t uniformNdx = 0; uniformNdx < m_currentProgram->m_program->m_uniforms.size(); ++uniformNdx) { const tcu::Sampler::DepthStencilMode depthStencilMode = tcu::Sampler::MODE_DEPTH; // \todo[jarkko] support sampler state const int texNdx = m_currentProgram->m_program->m_uniforms[uniformNdx].value.i; switch (m_currentProgram->m_program->m_uniforms[uniformNdx].type) { case glu::TYPE_SAMPLER_1D: case glu::TYPE_UINT_SAMPLER_1D: case glu::TYPE_INT_SAMPLER_1D: { rc::Texture1D *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].tex1DBinding) ? (m_textureUnits[texNdx].tex1DBinding) : (&m_textureUnits[texNdx].default1DTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex1D = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex1D = &m_emptyTex1D; break; } case glu::TYPE_SAMPLER_2D: case glu::TYPE_UINT_SAMPLER_2D: case glu::TYPE_INT_SAMPLER_2D: { rc::Texture2D *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].tex2DBinding) ? (m_textureUnits[texNdx].tex2DBinding) : (&m_textureUnits[texNdx].default2DTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex2D = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex2D = &m_emptyTex2D; break; } case glu::TYPE_SAMPLER_CUBE: case glu::TYPE_UINT_SAMPLER_CUBE: case glu::TYPE_INT_SAMPLER_CUBE: { rc::TextureCube *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].texCubeBinding) ? (m_textureUnits[texNdx].texCubeBinding) : (&m_textureUnits[texNdx].defaultCubeTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.texCube = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.texCube = &m_emptyTexCube; break; } case glu::TYPE_SAMPLER_2D_ARRAY: case glu::TYPE_UINT_SAMPLER_2D_ARRAY: case glu::TYPE_INT_SAMPLER_2D_ARRAY: { rc::Texture2DArray *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].tex2DArrayBinding) ? (m_textureUnits[texNdx].tex2DArrayBinding) : (&m_textureUnits[texNdx].default2DArrayTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex2DArray = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex2DArray = &m_emptyTex2DArray; break; } case glu::TYPE_SAMPLER_3D: case glu::TYPE_UINT_SAMPLER_3D: case glu::TYPE_INT_SAMPLER_3D: { rc::Texture3D *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].tex3DBinding) ? (m_textureUnits[texNdx].tex3DBinding) : (&m_textureUnits[texNdx].default3DTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex3D = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.tex3D = &m_emptyTex3D; break; } case glu::TYPE_SAMPLER_CUBE_ARRAY: case glu::TYPE_UINT_SAMPLER_CUBE_ARRAY: case glu::TYPE_INT_SAMPLER_CUBE_ARRAY: { rc::TextureCubeArray *tex = DE_NULL; if (texNdx >= 0 && (size_t)texNdx < m_textureUnits.size()) tex = (m_textureUnits[texNdx].texCubeArrayBinding) ? (m_textureUnits[texNdx].texCubeArrayBinding) : (&m_textureUnits[texNdx].defaultCubeArrayTex); if (tex && tex->isComplete()) { tex->updateView(depthStencilMode); m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.texCubeArray = tex; } else m_currentProgram->m_program->m_uniforms[uniformNdx].sampler.texCubeArray = &m_emptyTexCubeArray; break; } default: // nothing break; } } referenceRenderer.drawInstanced( rr::DrawCommand(state, renderTarget, program, (int)vertexAttribs.size(), &vertexAttribs[0], primitives), instanceCount); } uint32_t ReferenceContext::createProgram(ShaderProgram *program) { int name = m_programs.allocateName(); m_programs.insert(new rc::ShaderProgramObjectContainer(name, program)); return name; } void ReferenceContext::useProgram(uint32_t program) { rc::ShaderProgramObjectContainer *shaderProg = DE_NULL; rc::ShaderProgramObjectContainer *programToBeDeleted = DE_NULL; if (program) { shaderProg = m_programs.find(program); // shader has not been linked if (!shaderProg || shaderProg->m_deleteFlag) RC_ERROR_RET(GL_INVALID_OPERATION, RC_RET_VOID); } if (m_currentProgram && m_currentProgram->m_deleteFlag) programToBeDeleted = m_currentProgram; m_currentProgram = shaderProg; if (programToBeDeleted) { DE_ASSERT(programToBeDeleted->getRefCount() == 1); deleteProgramObject(programToBeDeleted); } } void ReferenceContext::deleteProgram(uint32_t program) { if (!program) return; rc::ShaderProgramObjectContainer *shaderProg = m_programs.find(program); if (shaderProg) { if (shaderProg == m_currentProgram) { m_currentProgram->m_deleteFlag = true; } else { DE_ASSERT(shaderProg->getRefCount() == 1); m_programs.releaseReference(shaderProg); } } } void ReferenceContext::readPixels(int x, int y, int width, int height, uint32_t format, uint32_t type, void *data) { rr::MultisamplePixelBufferAccess src = getReadColorbuffer(); TextureFormat transferFmt; // Map transfer format. transferFmt = glu::mapGLTransferFormat(format, type); RC_IF_ERROR(transferFmt.order == TextureFormat::CHANNELORDER_LAST || transferFmt.type == TextureFormat::CHANNELTYPE_LAST, GL_INVALID_ENUM, RC_RET_VOID); // Clamp input values const int copyX = deClamp32(x, 0, src.raw().getHeight()); const int copyY = deClamp32(y, 0, src.raw().getDepth()); const int copyWidth = deClamp32(width, 0, src.raw().getHeight() - x); const int copyHeight = deClamp32(height, 0, src.raw().getDepth() - y); PixelBufferAccess dst(transferFmt, width, height, 1, deAlign32(width * transferFmt.getPixelSize(), m_pixelPackAlignment), 0, getPixelPackPtr(data)); rr::resolveMultisampleColorBuffer(tcu::getSubregion(dst, 0, 0, copyWidth, copyHeight), rr::getSubregion(src, copyX, copyY, copyWidth, copyHeight)); } uint32_t ReferenceContext::getError(void) { uint32_t err = m_lastError; m_lastError = GL_NO_ERROR; return err; } void ReferenceContext::finish(void) { } inline void ReferenceContext::setError(uint32_t error) { if (m_lastError == GL_NO_ERROR) m_lastError = error; } void ReferenceContext::getIntegerv(uint32_t pname, int *param) { switch (pname) { case GL_MAX_TEXTURE_SIZE: *param = m_limits.maxTexture2DSize; break; case GL_MAX_CUBE_MAP_TEXTURE_SIZE: *param = m_limits.maxTextureCubeSize; break; case GL_MAX_ARRAY_TEXTURE_LAYERS: *param = m_limits.maxTexture2DArrayLayers; break; case GL_MAX_3D_TEXTURE_SIZE: *param = m_limits.maxTexture3DSize; break; case GL_MAX_RENDERBUFFER_SIZE: *param = m_limits.maxRenderbufferSize; break; case GL_MAX_TEXTURE_IMAGE_UNITS: *param = m_limits.maxTextureImageUnits; break; case GL_MAX_VERTEX_ATTRIBS: *param = m_limits.maxVertexAttribs; break; default: setError(GL_INVALID_ENUM); break; } } const char *ReferenceContext::getString(uint32_t pname) { switch (pname) { case GL_EXTENSIONS: return m_limits.extensionStr.c_str(); default: setError(GL_INVALID_ENUM); return DE_NULL; } } namespace rc { TextureLevelArray::TextureLevelArray(void) { } TextureLevelArray::~TextureLevelArray(void) { clear(); } void TextureLevelArray::clear(void) { DE_STATIC_ASSERT(DE_LENGTH_OF_ARRAY(m_data) == DE_LENGTH_OF_ARRAY(m_access)); for (int ndx = 0; ndx < DE_LENGTH_OF_ARRAY(m_data); ndx++) { m_data[ndx].clear(); m_access[ndx] = PixelBufferAccess(); } } void TextureLevelArray::allocLevel(int level, const tcu::TextureFormat &format, int width, int height, int depth) { const int dataSize = format.getPixelSize() * width * height * depth; DE_ASSERT(deInBounds32(level, 0, DE_LENGTH_OF_ARRAY(m_data))); if (hasLevel(level)) clearLevel(level); m_data[level].setStorage(dataSize); m_access[level] = PixelBufferAccess(format, width, height, depth, m_data[level].getPtr()); } void TextureLevelArray::clearLevel(int level) { DE_ASSERT(deInBounds32(level, 0, DE_LENGTH_OF_ARRAY(m_data))); m_data[level].clear(); m_access[level] = PixelBufferAccess(); } void TextureLevelArray::updateSamplerMode(tcu::Sampler::DepthStencilMode mode) { for (int levelNdx = 0; hasLevel(levelNdx); ++levelNdx) m_effectiveAccess[levelNdx] = tcu::getEffectiveDepthStencilAccess(m_access[levelNdx], mode); } Texture::Texture(uint32_t name, Type type, bool seamless) : NamedObject(name) , m_type(type) , m_immutable(false) , m_sampler(tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::REPEAT_GL, tcu::Sampler::NEAREST_MIPMAP_LINEAR, tcu::Sampler::LINEAR, 0.0f, // LOD threshold true, // normalized coords tcu::Sampler::COMPAREMODE_NONE, 0, // cmp channel ndx tcu::Vec4(0.0f), // border color seamless // seamless cube map, Default value is True. ) , m_baseLevel(0) , m_maxLevel(1000) { } Texture1D::Texture1D(uint32_t name) : Texture(name, TYPE_1D), m_view(0, DE_NULL) { } Texture1D::~Texture1D(void) { } void Texture1D::allocLevel(int level, const tcu::TextureFormat &format, int width) { m_levels.allocLevel(level, format, width, 1, 1); } bool Texture1D::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel)) { const tcu::ConstPixelBufferAccess &level0 = getLevel(baseLevel); const bool mipmap = isMipmapFilter(getSampler().minFilter); if (mipmap) { const TextureFormat &format = level0.getFormat(); const int w = level0.getWidth(); const int numLevels = de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels1D(w)); for (int levelNdx = 1; levelNdx < numLevels; levelNdx++) { if (hasLevel(baseLevel + levelNdx)) { const tcu::ConstPixelBufferAccess &level = getLevel(baseLevel + levelNdx); const int expectedW = getMipLevelSize(w, levelNdx); if (level.getWidth() != expectedW || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } tcu::Vec4 Texture1D::sample(float s, float lod) const { return m_view.sample(getSampler(), s, 0.0f, lod); } void Texture1D::sample4(tcu::Vec4 output[4], const float packetTexcoords[4], float lodBias) const { const float texWidth = (float)m_view.getWidth(); const float dFdx0 = packetTexcoords[1] - packetTexcoords[0]; const float dFdx1 = packetTexcoords[3] - packetTexcoords[2]; const float dFdy0 = packetTexcoords[2] - packetTexcoords[0]; const float dFdy1 = packetTexcoords[3] - packetTexcoords[1]; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const float &dFdx = (fragNdx > 2) ? dFdx1 : dFdx0; const float &dFdy = (fragNdx % 2) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx), de::abs(dFdy)); const float p = mu * texWidth; const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx], lod); } } void Texture1D::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel) && !isEmpty(getLevel(baseLevel))) { const int width = getLevel(baseLevel).getWidth(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels1D(width)) : 1; m_levels.updateSamplerMode(mode); m_view = tcu::Texture2DView(numLevels, m_levels.getEffectiveLevels() + baseLevel); } else m_view = tcu::Texture2DView(0, DE_NULL); } Texture2D::Texture2D(uint32_t name, bool es2) : Texture(name, TYPE_2D), m_view(0, DE_NULL, es2) { } Texture2D::~Texture2D(void) { } void Texture2D::allocLevel(int level, const tcu::TextureFormat &format, int width, int height) { m_levels.allocLevel(level, format, width, height, 1); } bool Texture2D::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel)) { const tcu::ConstPixelBufferAccess &level0 = getLevel(baseLevel); const bool mipmap = isMipmapFilter(getSampler().minFilter); if (mipmap) { const TextureFormat &format = level0.getFormat(); const int w = level0.getWidth(); const int h = level0.getHeight(); const int numLevels = de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(w, h)); for (int levelNdx = 1; levelNdx < numLevels; levelNdx++) { if (hasLevel(baseLevel + levelNdx)) { const tcu::ConstPixelBufferAccess &level = getLevel(baseLevel + levelNdx); const int expectedW = getMipLevelSize(w, levelNdx); const int expectedH = getMipLevelSize(h, levelNdx); if (level.getWidth() != expectedW || level.getHeight() != expectedH || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } void Texture2D::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel) && !isEmpty(getLevel(baseLevel))) { // Update number of levels in mipmap pyramid. const int width = getLevel(baseLevel).getWidth(); const int height = getLevel(baseLevel).getHeight(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(width, height)) : 1; m_levels.updateSamplerMode(mode); m_view = tcu::Texture2DView(numLevels, m_levels.getEffectiveLevels() + baseLevel); } else m_view = tcu::Texture2DView(0, DE_NULL); } tcu::Vec4 Texture2D::sample(float s, float t, float lod) const { return m_view.sample(getSampler(), s, t, lod); } void Texture2D::sample4(tcu::Vec4 output[4], const tcu::Vec2 packetTexcoords[4], float lodBias) const { const float texWidth = (float)m_view.getWidth(); const float texHeight = (float)m_view.getHeight(); const tcu::Vec2 dFdx0 = packetTexcoords[1] - packetTexcoords[0]; const tcu::Vec2 dFdx1 = packetTexcoords[3] - packetTexcoords[2]; const tcu::Vec2 dFdy0 = packetTexcoords[2] - packetTexcoords[0]; const tcu::Vec2 dFdy1 = packetTexcoords[3] - packetTexcoords[1]; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const tcu::Vec2 &dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; const tcu::Vec2 &dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx.x()), de::abs(dFdy.x())); const float mv = de::max(de::abs(dFdx.y()), de::abs(dFdy.y())); const float p = de::max(mu * texWidth, mv * texHeight); const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx].x(), packetTexcoords[fragNdx].y(), lod); } } TextureCube::TextureCube(uint32_t name, bool seamless) : Texture(name, TYPE_CUBE_MAP, seamless) { } TextureCube::~TextureCube(void) { } void TextureCube::clearLevels(void) { for (int face = 0; face < tcu::CUBEFACE_LAST; face++) m_levels[face].clear(); } void TextureCube::allocFace(int level, tcu::CubeFace face, const tcu::TextureFormat &format, int width, int height) { m_levels[face].allocLevel(level, format, width, height, 1); } bool TextureCube::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasFace(baseLevel, tcu::CUBEFACE_NEGATIVE_X)) { const int width = getFace(baseLevel, tcu::CUBEFACE_NEGATIVE_X).getWidth(); const int height = getFace(baseLevel, tcu::CUBEFACE_NEGATIVE_X).getHeight(); const tcu::TextureFormat &format = getFace(baseLevel, tcu::CUBEFACE_NEGATIVE_X).getFormat(); const bool mipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = mipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(width, height)) : 1; if (width != height) return false; // Non-square is not supported. // \note Level 0 is always checked for consistency for (int levelNdx = 0; levelNdx < numLevels; levelNdx++) { const int levelW = getMipLevelSize(width, levelNdx); const int levelH = getMipLevelSize(height, levelNdx); for (int face = 0; face < tcu::CUBEFACE_LAST; face++) { if (hasFace(baseLevel + levelNdx, (tcu::CubeFace)face)) { const tcu::ConstPixelBufferAccess &level = getFace(baseLevel + levelNdx, (tcu::CubeFace)face); if (level.getWidth() != levelW || level.getHeight() != levelH || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } void TextureCube::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); const tcu::ConstPixelBufferAccess *faces[tcu::CUBEFACE_LAST]; deMemset(&faces[0], 0, sizeof(faces)); if (isComplete()) { const int size = getFace(baseLevel, tcu::CUBEFACE_NEGATIVE_X).getWidth(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels1D(size)) : 1; for (int face = 0; face < tcu::CUBEFACE_LAST; face++) { m_levels[face].updateSamplerMode(mode); faces[face] = m_levels[face].getEffectiveLevels() + baseLevel; } m_view = tcu::TextureCubeView(numLevels, faces); } else m_view = tcu::TextureCubeView(0, faces); } tcu::Vec4 TextureCube::sample(float s, float t, float p, float lod) const { return m_view.sample(getSampler(), s, t, p, lod); } void TextureCube::sample4(tcu::Vec4 output[4], const tcu::Vec3 packetTexcoords[4], float lodBias) const { const float cubeSide = (float)m_view.getSize(); // Each tex coord might be in a different face. for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const tcu::CubeFace face = tcu::selectCubeFace(packetTexcoords[fragNdx]); const tcu::Vec2 coords[4] = { tcu::projectToFace(face, packetTexcoords[0]), tcu::projectToFace(face, packetTexcoords[1]), tcu::projectToFace(face, packetTexcoords[2]), tcu::projectToFace(face, packetTexcoords[3]), }; const tcu::Vec2 dFdx0 = coords[1] - coords[0]; const tcu::Vec2 dFdx1 = coords[3] - coords[2]; const tcu::Vec2 dFdy0 = coords[2] - coords[0]; const tcu::Vec2 dFdy1 = coords[3] - coords[1]; const tcu::Vec2 &dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; const tcu::Vec2 &dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx.x()), de::abs(dFdy.x())); const float mv = de::max(de::abs(dFdx.y()), de::abs(dFdy.y())); const float p = de::max(mu * cubeSide, mv * cubeSide); const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx].x(), packetTexcoords[fragNdx].y(), packetTexcoords[fragNdx].z(), lod); } } Texture2DArray::Texture2DArray(uint32_t name) : Texture(name, TYPE_2D_ARRAY), m_view(0, DE_NULL) { } Texture2DArray::~Texture2DArray(void) { } void Texture2DArray::allocLevel(int level, const tcu::TextureFormat &format, int width, int height, int numLayers) { m_levels.allocLevel(level, format, width, height, numLayers); } bool Texture2DArray::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel)) { const tcu::ConstPixelBufferAccess &level0 = getLevel(baseLevel); const bool mipmap = isMipmapFilter(getSampler().minFilter); if (mipmap) { const TextureFormat &format = level0.getFormat(); const int w = level0.getWidth(); const int h = level0.getHeight(); const int numLayers = level0.getDepth(); const int numLevels = de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(w, h)); for (int levelNdx = 1; levelNdx < numLevels; levelNdx++) { if (hasLevel(baseLevel + levelNdx)) { const tcu::ConstPixelBufferAccess &level = getLevel(baseLevel + levelNdx); const int expectedW = getMipLevelSize(w, levelNdx); const int expectedH = getMipLevelSize(h, levelNdx); if (level.getWidth() != expectedW || level.getHeight() != expectedH || level.getDepth() != numLayers || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } void Texture2DArray::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel) && !isEmpty(getLevel(baseLevel))) { const int width = getLevel(baseLevel).getWidth(); const int height = getLevel(baseLevel).getHeight(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(width, height)) : 1; m_levels.updateSamplerMode(mode); m_view = tcu::Texture2DArrayView(numLevels, m_levels.getEffectiveLevels() + baseLevel); } else m_view = tcu::Texture2DArrayView(0, DE_NULL); } tcu::Vec4 Texture2DArray::sample(float s, float t, float r, float lod) const { return m_view.sample(getSampler(), s, t, r, lod); } void Texture2DArray::sample4(tcu::Vec4 output[4], const tcu::Vec3 packetTexcoords[4], float lodBias) const { const float texWidth = (float)m_view.getWidth(); const float texHeight = (float)m_view.getHeight(); const tcu::Vec3 dFdx0 = packetTexcoords[1] - packetTexcoords[0]; const tcu::Vec3 dFdx1 = packetTexcoords[3] - packetTexcoords[2]; const tcu::Vec3 dFdy0 = packetTexcoords[2] - packetTexcoords[0]; const tcu::Vec3 dFdy1 = packetTexcoords[3] - packetTexcoords[1]; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const tcu::Vec3 &dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; const tcu::Vec3 &dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx.x()), de::abs(dFdy.x())); const float mv = de::max(de::abs(dFdx.y()), de::abs(dFdy.y())); const float p = de::max(mu * texWidth, mv * texHeight); const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx].x(), packetTexcoords[fragNdx].y(), packetTexcoords[fragNdx].z(), lod); } } TextureCubeArray::TextureCubeArray(uint32_t name) : Texture(name, TYPE_CUBE_MAP_ARRAY), m_view(0, DE_NULL) { } TextureCubeArray::~TextureCubeArray(void) { } void TextureCubeArray::allocLevel(int level, const tcu::TextureFormat &format, int width, int height, int numLayers) { DE_ASSERT(numLayers % 6 == 0); m_levels.allocLevel(level, format, width, height, numLayers); } bool TextureCubeArray::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel)) { const tcu::ConstPixelBufferAccess &level0 = getLevel(baseLevel); const bool mipmap = isMipmapFilter(getSampler().minFilter); if (mipmap) { const TextureFormat &format = level0.getFormat(); const int w = level0.getWidth(); const int h = level0.getHeight(); const int numLayers = level0.getDepth(); const int numLevels = de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(w, h)); for (int levelNdx = 1; levelNdx < numLevels; levelNdx++) { if (hasLevel(baseLevel + levelNdx)) { const tcu::ConstPixelBufferAccess &level = getLevel(baseLevel + levelNdx); const int expectedW = getMipLevelSize(w, levelNdx); const int expectedH = getMipLevelSize(h, levelNdx); if (level.getWidth() != expectedW || level.getHeight() != expectedH || level.getDepth() != numLayers || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } void TextureCubeArray::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel) && !isEmpty(getLevel(baseLevel))) { const int width = getLevel(baseLevel).getWidth(); const int height = getLevel(baseLevel).getHeight(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels2D(width, height)) : 1; m_levels.updateSamplerMode(mode); m_view = tcu::TextureCubeArrayView(numLevels, m_levels.getEffectiveLevels() + baseLevel); } else m_view = tcu::TextureCubeArrayView(0, DE_NULL); } tcu::Vec4 TextureCubeArray::sample(float s, float t, float r, float q, float lod) const { return m_view.sample(getSampler(), s, t, r, q, lod); } void TextureCubeArray::sample4(tcu::Vec4 output[4], const tcu::Vec4 packetTexcoords[4], float lodBias) const { const float cubeSide = (float)m_view.getSize(); const tcu::Vec3 cubeCoords[4] = {packetTexcoords[0].toWidth<3>(), packetTexcoords[1].toWidth<3>(), packetTexcoords[2].toWidth<3>(), packetTexcoords[3].toWidth<3>()}; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const tcu::CubeFace face = tcu::selectCubeFace(cubeCoords[fragNdx]); const tcu::Vec2 faceCoords[4] = { tcu::projectToFace(face, cubeCoords[0]), tcu::projectToFace(face, cubeCoords[1]), tcu::projectToFace(face, cubeCoords[2]), tcu::projectToFace(face, cubeCoords[3]), }; const tcu::Vec2 dFdx0 = faceCoords[1] - faceCoords[0]; const tcu::Vec2 dFdx1 = faceCoords[3] - faceCoords[2]; const tcu::Vec2 dFdy0 = faceCoords[2] - faceCoords[0]; const tcu::Vec2 dFdy1 = faceCoords[3] - faceCoords[1]; const tcu::Vec2 &dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; const tcu::Vec2 &dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx.x()), de::abs(dFdy.x())); const float mv = de::max(de::abs(dFdx.y()), de::abs(dFdy.y())); const float p = de::max(mu * cubeSide, mv * cubeSide); const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx].x(), packetTexcoords[fragNdx].y(), packetTexcoords[fragNdx].z(), packetTexcoords[fragNdx].w(), lod); } } Texture3D::Texture3D(uint32_t name) : Texture(name, TYPE_3D), m_view(0, DE_NULL) { } Texture3D::~Texture3D(void) { } void Texture3D::allocLevel(int level, const tcu::TextureFormat &format, int width, int height, int depth) { m_levels.allocLevel(level, format, width, height, depth); } bool Texture3D::isComplete(void) const { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel)) { const tcu::ConstPixelBufferAccess &level0 = getLevel(baseLevel); const bool mipmap = isMipmapFilter(getSampler().minFilter); if (mipmap) { const TextureFormat &format = level0.getFormat(); const int w = level0.getWidth(); const int h = level0.getHeight(); const int d = level0.getDepth(); const int numLevels = de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels3D(w, h, d)); for (int levelNdx = 1; levelNdx < numLevels; levelNdx++) { if (hasLevel(baseLevel + levelNdx)) { const tcu::ConstPixelBufferAccess &level = getLevel(baseLevel + levelNdx); const int expectedW = getMipLevelSize(w, levelNdx); const int expectedH = getMipLevelSize(h, levelNdx); const int expectedD = getMipLevelSize(d, levelNdx); if (level.getWidth() != expectedW || level.getHeight() != expectedH || level.getDepth() != expectedD || level.getFormat() != format) return false; } else return false; } } return true; } else return false; } tcu::Vec4 Texture3D::sample(float s, float t, float r, float lod) const { return m_view.sample(getSampler(), s, t, r, lod); } void Texture3D::sample4(tcu::Vec4 output[4], const tcu::Vec3 packetTexcoords[4], float lodBias) const { const float texWidth = (float)m_view.getWidth(); const float texHeight = (float)m_view.getHeight(); const float texDepth = (float)m_view.getDepth(); const tcu::Vec3 dFdx0 = packetTexcoords[1] - packetTexcoords[0]; const tcu::Vec3 dFdx1 = packetTexcoords[3] - packetTexcoords[2]; const tcu::Vec3 dFdy0 = packetTexcoords[2] - packetTexcoords[0]; const tcu::Vec3 dFdy1 = packetTexcoords[3] - packetTexcoords[1]; for (int fragNdx = 0; fragNdx < 4; ++fragNdx) { const tcu::Vec3 &dFdx = (fragNdx & 2) ? dFdx1 : dFdx0; const tcu::Vec3 &dFdy = (fragNdx & 1) ? dFdy1 : dFdy0; const float mu = de::max(de::abs(dFdx.x()), de::abs(dFdy.x())); const float mv = de::max(de::abs(dFdx.y()), de::abs(dFdy.y())); const float mw = de::max(de::abs(dFdx.z()), de::abs(dFdy.z())); const float p = de::max(de::max(mu * texWidth, mv * texHeight), mw * texDepth); const float lod = deFloatLog2(p) + lodBias; output[fragNdx] = sample(packetTexcoords[fragNdx].x(), packetTexcoords[fragNdx].y(), packetTexcoords[fragNdx].z(), lod); } } void Texture3D::updateView(tcu::Sampler::DepthStencilMode mode) { const int baseLevel = getBaseLevel(); if (hasLevel(baseLevel) && !isEmpty(getLevel(baseLevel))) { const int width = getLevel(baseLevel).getWidth(); const int height = getLevel(baseLevel).getHeight(); const int depth = getLevel(baseLevel).getDepth(); const bool isMipmap = isMipmapFilter(getSampler().minFilter); const int numLevels = isMipmap ? de::min(getMaxLevel() - baseLevel + 1, getNumMipLevels3D(width, height, depth)) : 1; m_levels.updateSamplerMode(mode); m_view = tcu::Texture3DView(numLevels, m_levels.getEffectiveLevels() + baseLevel); } else m_view = tcu::Texture3DView(0, DE_NULL); } Renderbuffer::Renderbuffer(uint32_t name) : NamedObject(name) { } Renderbuffer::~Renderbuffer(void) { } void Renderbuffer::setStorage(const TextureFormat &format, int width, int height) { m_data.setStorage(format, width, height); } Framebuffer::Framebuffer(uint32_t name) : NamedObject(name) { } Framebuffer::~Framebuffer(void) { } VertexArray::VertexArray(uint32_t name, int maxVertexAttribs) : NamedObject(name) , m_elementArrayBufferBinding(DE_NULL) , m_arrays(maxVertexAttribs) { for (int i = 0; i < maxVertexAttribs; ++i) { m_arrays[i].enabled = false; m_arrays[i].size = 4; m_arrays[i].stride = 0; m_arrays[i].type = GL_FLOAT; m_arrays[i].normalized = false; m_arrays[i].integer = false; m_arrays[i].divisor = 0; m_arrays[i].bufferDeleted = false; m_arrays[i].bufferBinding = DE_NULL; m_arrays[i].pointer = DE_NULL; } } ShaderProgramObjectContainer::ShaderProgramObjectContainer(uint32_t name, ShaderProgram *program) : NamedObject(name) , m_program(program) , m_deleteFlag(false) { } ShaderProgramObjectContainer::~ShaderProgramObjectContainer(void) { } } // namespace rc } // namespace sglr