/*------------------------------------------------------------------------- * 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" namespace sglr { using std::vector; using std::map; using tcu::Vec2; using tcu::Vec3; using tcu::Vec4; using tcu::IVec2; using tcu::IVec4; using tcu::RGBA; // Reference context implementation using namespace rc; using tcu::TextureFormat; using tcu::PixelBufferAccess; using tcu::ConstPixelBufferAccess; // Utilities for ReferenceContext #define RC_RET_VOID #define RC_ERROR_RET(ERR, RET) \ do { \ setError(ERR); \ return RET; \ } while (deGetFalse()) #define RC_IF_ERROR(COND, ERR, RET) \ do { \ if (COND) \ RC_ERROR_RET(ERR, RET); \ } while (deGetFalse()) 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 (deUint32 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 (deUint32 texture) { if (deInBounds32(texture, GL_TEXTURE0, GL_TEXTURE0 + (deUint32)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 (deUint32 target, deUint32 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, deUint32* textures) { while (numTextures--) *textures++ = m_textures.allocateName(); } void ReferenceContext::deleteTextures (int numTextures, const deUint32* textures) { for (int i = 0; i < numTextures; i++) { deUint32 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 (deUint32 target, deUint32 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++) { deUint32 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, deUint32* 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 deUint32* framebuffers) { for (int i = 0; i < numFramebuffers; i++) { deUint32 name = framebuffers[i]; Framebuffer* framebuffer = name ? m_framebuffers.find(name) : DE_NULL; if (framebuffer) deleteFramebuffer(framebuffer); } } void ReferenceContext::bindRenderbuffer (deUint32 target, deUint32 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, deUint32* 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 deUint32* renderbuffers) { for (int i = 0; i < numRenderbuffers; i++) { deUint32 name = renderbuffers[i]; Renderbuffer* renderbuffer = name ? m_renderbuffers.find(name) : DE_NULL; if (renderbuffer) deleteRenderbuffer(renderbuffer); } } void ReferenceContext::pixelStorei (deUint32 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 deUint8* ptr = (const deUint8*)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 deUint8* ptr = (const deUint8*)data + m_pixelUnpackSkipImages*slicePitch + m_pixelUnpackSkipRows*rowPitch + m_pixelUnpackSkipPixels*pixelSize; return tcu::ConstPixelBufferAccess(format, width, height, depth, rowPitch, slicePitch, ptr); } static tcu::TextureFormat mapInternalFormat (deUint32 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 (deUint32 target, int level, deUint32 internalFormat, int width, int border, deUint32 format, deUint32 type, const void* data) { texImage2D(target, level, internalFormat, width, 1, border, format, type, data); } void ReferenceContext::texImage2D (deUint32 target, int level, deUint32 internalFormat, int width, int height, int border, deUint32 format, deUint32 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 (deUint32 target, int level, deUint32 internalFormat, int width, int height, int depth, int border, deUint32 format, deUint32 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 (deUint32 target, int level, int xoffset, int width, deUint32 format, deUint32 type, const void* data) { texSubImage2D(target, level, xoffset, 0, width, 1, format, type, data); } void ReferenceContext::texSubImage2D (deUint32 target, int level, int xoffset, int yoffset, int width, int height, deUint32 format, deUint32 type, const void* data) { texSubImage3D(target, level, xoffset, yoffset, 0, width, height, 1, format, type, data); } void ReferenceContext::texSubImage3D (deUint32 target, int level, int xoffset, int yoffset, int zoffset, int width, int height, int depth, deUint32 format, deUint32 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 (deUint32 target, int level, deUint32 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 (deUint32 target, int level, deUint32 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 (deUint32 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 (deUint32 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 (deUint32 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 (deUint32 target, int levels, deUint32 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 (deUint32 target, int levels, deUint32 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 (deUint32 target, deUint32 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 (deUint32 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 (deUint32 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 (deUint32 target, deUint32 attachment, deUint32 textarget, deUint32 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 (deUint32 target, deUint32 attachment, deUint32 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 (deUint32 target, deUint32 attachment, deUint32 renderbuffertarget, deUint32 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); } } } deUint32 ReferenceContext::checkFramebufferStatus (deUint32 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 (deUint32 target, deUint32 attachment, deUint32 pname, int* params) { DE_UNREF(target && attachment && pname && params); TCU_CHECK(false); // \todo [pyry] Implement } void ReferenceContext::renderbufferStorage (deUint32 target, deUint32 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 (deUint32 target, int samples, deUint32 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) return dynamic_cast(texture)->getLevel(attachment.level); else if (texture->getType() == Texture::TYPE_CUBE_MAP) return dynamic_cast(texture)->getFace(attachment.level, texTargetToFace(attachment.texTarget)); 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) level = dynamic_cast(texture)->getLevel(attachment.level); else if (texture->getType() == Texture::TYPE_3D) level = dynamic_cast(texture)->getLevel(attachment.level); else if (texture->getType() == Texture::TYPE_CUBE_MAP_ARRAY) level = dynamic_cast(texture)->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 (deUint32 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 (deUint32 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 (deUint32 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 (deUint32 target, deUint32 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, deUint32* 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 deUint32* buffers) { RC_IF_ERROR(numBuffers < 0, GL_INVALID_VALUE, RC_RET_VOID); for (int ndx = 0; ndx < numBuffers; ndx++) { deUint32 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 deUint32 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 (deUint32 target, deIntptr size, const void* data, deUint32 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((deIntptr)(int)size == size); buffer->setStorage((int)size); if (data) deMemcpy(buffer->getData(), data, (int)size); } void ReferenceContext::bufferSubData (deUint32 target, deIntptr offset, deIntptr 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 (deUint32 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 (deUint32 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 (deUint32 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 (deUint32 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 (deUint32 func, int ref, deUint32 mask) { stencilFuncSeparate(GL_FRONT_AND_BACK, func, ref, mask); } void ReferenceContext::stencilFuncSeparate (deUint32 face, deUint32 func, int ref, deUint32 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 (deUint32 sfail, deUint32 dpfail, deUint32 dppass) { stencilOpSeparate(GL_FRONT_AND_BACK, sfail, dpfail, dppass); } void ReferenceContext::stencilOpSeparate (deUint32 face, deUint32 sfail, deUint32 dpfail, deUint32 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 (deUint32 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 (deUint32 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 (deUint32 index) { // only in core DE_ASSERT(glu::isContextTypeGLCore(getType())); m_primitiveRestartIndex = index; } static inline bool isValidBlendEquation (deUint32 mode) { return mode == GL_FUNC_ADD || mode == GL_FUNC_SUBTRACT || mode == GL_FUNC_REVERSE_SUBTRACT || mode == GL_MIN || mode == GL_MAX; } static bool isValidBlendFactor (deUint32 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 (deUint32 mode) { RC_IF_ERROR(!isValidBlendEquation(mode), GL_INVALID_ENUM, RC_RET_VOID); m_blendModeRGB = mode; m_blendModeAlpha = mode; } void ReferenceContext::blendEquationSeparate (deUint32 modeRGB, deUint32 modeAlpha) { RC_IF_ERROR(!isValidBlendEquation(modeRGB) || !isValidBlendEquation(modeAlpha), GL_INVALID_ENUM, RC_RET_VOID); m_blendModeRGB = modeRGB; m_blendModeAlpha = modeAlpha; } void ReferenceContext::blendFunc (deUint32 src, deUint32 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 (deUint32 srcRGB, deUint32 dstRGB, deUint32 srcAlpha, deUint32 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 (deBool r, deBool g, deBool b, deBool a) { m_colorMask = tcu::BVec4(!!r, !!g, !!b, !!a); } void ReferenceContext::depthMask (deBool mask) { m_depthMask = !!mask; } void ReferenceContext::stencilMask (deUint32 mask) { stencilMaskSeparate(GL_FRONT_AND_BACK, mask); } void ReferenceContext::stencilMaskSeparate (deUint32 face, deUint32 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 deUint32 maskStencil (int numBits, deUint32 s) { return s & deBitMask32(0, numBits); } static inline void writeMaskedStencil (const rr::MultisamplePixelBufferAccess& access, int s, int x, int y, deUint32 stencil, deUint32 writeMask) { DE_ASSERT(access.raw().getFormat().order == tcu::TextureFormat::S); const deUint32 oldVal = access.raw().getPixelUint(s, x, y).x(); const deUint32 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)); } deUint32 ReferenceContext::blitResolveMultisampleFramebuffer (deUint32 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); deUint32 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, deUint32 mask, deUint32 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) { deUint32 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); deUint32 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 (deUint32 target, int numAttachments, const deUint32* 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 (deUint32 target, int numAttachments, const deUint32* 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 (deUint32 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<= (deUint32)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 (deUint32 index, int size, deUint32 type, int stride, const void *pointer) { RC_IF_ERROR(index >= (deUint32)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 (deUint32 index) { RC_IF_ERROR(index >= (deUint32)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 (deUint32 index) { RC_IF_ERROR(index >= (deUint32)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 (deUint32 index, deUint32 divisor) { RC_IF_ERROR(index >= (deUint32)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 (deUint32 index, float x) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, 0, 0, 1)); } void ReferenceContext::vertexAttrib2f (deUint32 index, float x, float y) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, 0, 1)); } void ReferenceContext::vertexAttrib3f (deUint32 index, float x, float y, float z) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, z, 1)); } void ReferenceContext::vertexAttrib4f (deUint32 index, float x, float y, float z, float w) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::Vec4(x, y, z, w)); } void ReferenceContext::vertexAttribI4i (deUint32 index, deInt32 x, deInt32 y, deInt32 z, deInt32 w) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::IVec4(x, y, z, w)); } void ReferenceContext::vertexAttribI4ui (deUint32 index, deUint32 x, deUint32 y, deUint32 z, deUint32 w) { RC_IF_ERROR(index >= (deUint32)m_limits.maxVertexAttribs, GL_INVALID_VALUE, RC_RET_VOID); m_currentAttribs[index] = rr::GenericVec4(tcu::UVec4(x, y, z, w)); } deInt32 ReferenceContext::getAttribLocation (deUint32 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 (deInt32 location, glu::DataType type, deInt32 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(deUint32) <= sizeof(uniforms[location].value)); deMemcpy(&uniforms[location].value, v, scalarSize*(int)sizeof(deUint32)); } } void ReferenceContext::uniform1iv (deInt32 location, deInt32 count, const deInt32* 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 (deInt32 location, const float v0) { uniform1fv(location, 1, &v0); } void ReferenceContext::uniform1i (deInt32 location, deInt32 v0) { uniform1iv(location, 1, &v0); } void ReferenceContext::uniform1fv (deInt32 location, deInt32 count, const float* v) { uniformv(location, glu::TYPE_FLOAT, count, v); } void ReferenceContext::uniform2fv (deInt32 location, deInt32 count, const float* v) { uniformv(location, glu::TYPE_FLOAT_VEC2, count, v); } void ReferenceContext::uniform3fv (deInt32 location, deInt32 count, const float* v) { uniformv(location, glu::TYPE_FLOAT_VEC3, count, v); } void ReferenceContext::uniform4fv (deInt32 location, deInt32 count, const float* v) { uniformv(location, glu::TYPE_FLOAT_VEC4, count, v); } void ReferenceContext::uniform2iv (deInt32 location, deInt32 count, const deInt32* v) { uniformv(location, glu::TYPE_INT_VEC2, count, v); } void ReferenceContext::uniform3iv (deInt32 location, deInt32 count, const deInt32* v) { uniformv(location, glu::TYPE_INT_VEC3, count, v); } void ReferenceContext::uniform4iv (deInt32 location, deInt32 count, const deInt32* v) { uniformv(location, glu::TYPE_INT_VEC4, count, v); } void ReferenceContext::uniformMatrix3fv (deInt32 location, deInt32 count, deBool 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 (deInt32 location, deInt32 count, deBool 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; } } deInt32 ReferenceContext::getUniformLocation (deUint32 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 (deUint32 mode, int first, int count) { drawArraysInstanced(mode, first, count, 1); } void ReferenceContext::drawArraysInstanced (deUint32 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 (deUint32 mode, int count, deUint32 type, const void *indices) { drawElementsInstanced(mode, count, type, indices, 1); } void ReferenceContext::drawElementsBaseVertex (deUint32 mode, int count, deUint32 type, const void *indices, int baseVertex) { drawElementsInstancedBaseVertex(mode, count, type, indices, 1, baseVertex); } void ReferenceContext::drawElementsInstanced (deUint32 mode, int count, deUint32 type, const void *indices, int instanceCount) { drawElementsInstancedBaseVertex(mode, count, type, indices, instanceCount, 0); } void ReferenceContext::drawElementsInstancedBaseVertex (deUint32 mode, int count, deUint32 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() + ((const deUint8*)indices - (const deUint8*)DE_NULL)) : (indices); drawWithReference(rr::PrimitiveList(primitiveType, count, rr::DrawIndices(indicesPtr, sglr::rr_util::mapGLIndexType(type), baseVertex)), instanceCount); } } void ReferenceContext::drawRangeElements (deUint32 mode, deUint32 start, deUint32 end, int count, deUint32 type, const void *indices) { RC_IF_ERROR(end < start, GL_INVALID_VALUE, RC_RET_VOID); drawElements(mode, count, type, indices); } void ReferenceContext::drawRangeElementsBaseVertex (deUint32 mode, deUint32 start, deUint32 end, int count, deUint32 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 (deUint32 mode, const void *indirect) { struct DrawArraysIndirectCommand { deUint32 count; deUint32 primCount; deUint32 first; deUint32 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)((const char*)indirect - (const char*)DE_NULL) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR((size_t)((const char*)indirect - (const char*)DE_NULL) + sizeof(DrawArraysIndirectCommand) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); // Check values command = (const DrawArraysIndirectCommand*)(m_drawIndirectBufferBinding->getData() + ((const char*)indirect - (const char*)DE_NULL)); RC_IF_ERROR(command->reservedMustBeZero != 0, GL_INVALID_OPERATION, RC_RET_VOID); // draw drawArraysInstanced(mode, command->first, command->count, command->primCount); } void ReferenceContext::drawElementsIndirect (deUint32 mode, deUint32 type, const void *indirect) { struct DrawElementsIndirectCommand { deUint32 count; deUint32 primCount; deUint32 firstIndex; deInt32 baseVertex; deUint32 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)((const char*)indirect - (const char*)DE_NULL) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); RC_IF_ERROR((size_t)((const char*)indirect - (const char*)DE_NULL) + sizeof(DrawElementsIndirectCommand) > (size_t)m_drawIndirectBufferBinding->getSize(), GL_INVALID_OPERATION, RC_RET_VOID); // Check values command = (const DrawElementsIndirectCommand*)(m_drawIndirectBufferBinding->getData() + ((const char*)indirect - (const char*)DE_NULL)); 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 (deUint32 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 (deUint32 mode, const int* count, deUint32 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 (deUint32 mode, const int* count, deUint32 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 (deUint32 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 deUint32 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() + ((const deUint8*)vao.m_arrays[ndx].pointer - (const deUint8*)DE_NULL)) : (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); } deUint32 ReferenceContext::createProgram (ShaderProgram* program) { int name = m_programs.allocateName(); m_programs.insert(new rc::ShaderProgramObjectContainer(name, program)); return name; } void ReferenceContext::useProgram (deUint32 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 (deUint32 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, deUint32 format, deUint32 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)); } deUint32 ReferenceContext::getError (void) { deUint32 err = m_lastError; m_lastError = GL_NO_ERROR; return err; } void ReferenceContext::finish (void) { } inline void ReferenceContext::setError (deUint32 error) { if (m_lastError == GL_NO_ERROR) m_lastError = error; } void ReferenceContext::getIntegerv (deUint32 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 (deUint32 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 (deUint32 name, Type type, deBool 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 (deUint32 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 (deUint32 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 (deUint32 name, deBool 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 (deUint32 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 (deUint32 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 (deUint32 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 (deUint32 name) : NamedObject (name) { } Renderbuffer::~Renderbuffer (void) { } void Renderbuffer::setStorage (const TextureFormat& format, int width, int height) { m_data.setStorage(format, width, height); } Framebuffer::Framebuffer (deUint32 name) : NamedObject(name) { } Framebuffer::~Framebuffer (void) { } VertexArray::VertexArray (deUint32 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 (deUint32 name, ShaderProgram* program) : NamedObject (name) , m_program (program) , m_deleteFlag (false) { } ShaderProgramObjectContainer::~ShaderProgramObjectContainer (void) { } } // rc } // sglr