blob: 01efe1d0379a38c1f84db2a24e8fedc023cba86d [file] [log] [blame]
/*-------------------------------------------------------------------------
* 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 "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();
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 ()
, m_emptyTexCube ()
, 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);
if (glu::isContextTypeGLCore(getType()))
m_sRGBUpdateEnabled = false;
}
ReferenceContext::~ReferenceContext (void)
{
// Destroy all objects -- verifies that ref counting works
{
vector<VertexArray*> vertexArrays;
m_vertexArrays.getAll(vertexArrays);
for (vector<VertexArray*>::iterator i = vertexArrays.begin(); i != vertexArrays.end(); i++)
deleteVertexArray(*i);
DE_ASSERT(m_clientVertexArray.getRefCount() == 1);
}
{
vector<Texture*> textures;
m_textures.getAll(textures);
for (vector<Texture*>::iterator i = textures.begin(); i != textures.end(); i++)
deleteTexture(*i);
}
{
vector<Framebuffer*> framebuffers;
m_framebuffers.getAll(framebuffers);
for (vector<Framebuffer*>::iterator i = framebuffers.begin(); i != framebuffers.end(); i++)
deleteFramebuffer(*i);
}
{
vector<Renderbuffer*> renderbuffers;
m_renderbuffers.getAll(renderbuffers);
for (vector<Renderbuffer*>::iterator i = renderbuffers.begin(); i != renderbuffers.end(); i++)
deleteRenderbuffer(*i);
}
{
vector<DataBuffer*> buffers;
m_buffers.getAll(buffers);
for (vector<DataBuffer*>::iterator i = buffers.begin(); i != buffers.end(); i++)
deleteBuffer(*i);
}
{
vector<ShaderProgramObjectContainer*> programs;
m_programs.getAll(programs);
for (vector<ShaderProgramObjectContainer*>::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.
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); 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<Texture1D*> (texObj)); break;
case GL_TEXTURE_2D: setTex2DBinding (unitNdx, static_cast<Texture2D*> (texObj)); break;
case GL_TEXTURE_CUBE_MAP: setTexCubeBinding (unitNdx, static_cast<TextureCube*> (texObj)); break;
case GL_TEXTURE_2D_ARRAY: setTex2DArrayBinding (unitNdx, static_cast<Texture2DArray*> (texObj)); break;
case GL_TEXTURE_3D: setTex3DBinding (unitNdx, static_cast<Texture3D*> (texObj)); break;
case GL_TEXTURE_CUBE_MAP_ARRAY: setTexCubeArrayBinding (unitNdx, static_cast<TextureCubeArray*> (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<const Texture2D*>(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<const TextureCube*>(texture);
const tcu::CubeFace face = texTargetToFace(attachment.texTarget);
TCU_CHECK(de::inBounds<int>(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<const Texture2DArray*>(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<const Texture3D*>(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<const TextureCubeArray*>(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<Texture2D*>(texture)->getLevel(attachment.level);
else if (texture->getType() == Texture::TYPE_CUBE_MAP)
return dynamic_cast<TextureCube*>(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<Texture2DArray*>(texture)->getLevel(attachment.level);
else if (texture->getType() == Texture::TYPE_3D)
level = dynamic_cast<Texture3D*>(texture)->getLevel(attachment.level);
else if (texture->getType() == Texture::TYPE_CUBE_MAP_ARRAY)
level = dynamic_cast<TextureCubeArray*>(texture)->getLevel(attachment.level);
void* layerData = static_cast<deUint8*>(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<VertexArray*> vertexArrays;
m_vertexArrays.getAll(vertexArrays);
vertexArrays.push_back(&m_clientVertexArray);
for (vector<VertexArray*>::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