You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
5467 lines
183 KiB
5467 lines
183 KiB
/*-------------------------------------------------------------------------
|
|
* 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<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.
|
|
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<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 = 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<<stencilBits)-1u)) != ((1u<<stencilBits)-1u))
|
|
{
|
|
// Slow path where depth or stencil is masked out in write.
|
|
for (int y = 0; y < access.raw().getDepth(); y++)
|
|
for (int x = 0; x < access.raw().getHeight(); x++)
|
|
for (int s = 0; s < access.getNumSamples(); s++)
|
|
writeMaskedStencil(access, s, x, y, stencil, m_stencil[rr::FACETYPE_FRONT].writeMask);
|
|
}
|
|
else
|
|
rr::clearStencil(access, stencil);
|
|
}
|
|
}
|
|
|
|
void ReferenceContext::clearBufferiv (deUint32 buffer, int drawbuffer, const int* value)
|
|
{
|
|
RC_IF_ERROR(buffer != GL_COLOR && buffer != GL_STENCIL, GL_INVALID_ENUM, RC_RET_VOID);
|
|
RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support.
|
|
|
|
IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff);
|
|
|
|
if (buffer == GL_COLOR)
|
|
{
|
|
rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer();
|
|
bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3];
|
|
bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3];
|
|
IVec4 area = intersect(baseArea, getBufferRect(colorBuf));
|
|
|
|
if (!isEmpty(area) && !maskZero)
|
|
{
|
|
rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w());
|
|
IVec4 color (value[0], value[1], value[2], value[3]);
|
|
|
|
if (!maskUsed)
|
|
rr::clear(access, color);
|
|
else
|
|
{
|
|
for (int y = 0; y < access.raw().getDepth(); y++)
|
|
for (int x = 0; x < access.raw().getHeight(); x++)
|
|
for (int s = 0; s < access.getNumSamples(); s++)
|
|
access.raw().setPixel(tcu::select(color, access.raw().getPixelInt(s, x, y), m_colorMask), s, x, y);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TCU_CHECK_INTERNAL(buffer == GL_STENCIL);
|
|
|
|
rr::MultisamplePixelBufferAccess stencilBuf = getDrawStencilbuffer();
|
|
IVec4 area = intersect(baseArea, getBufferRect(stencilBuf));
|
|
|
|
if (!isEmpty(area) && m_stencil[rr::FACETYPE_FRONT].writeMask != 0)
|
|
{
|
|
rr::MultisamplePixelBufferAccess access = getStencilMultisampleAccess(rr::getSubregion(stencilBuf, area.x(), area.y(), area.z(), area.w()));
|
|
int stencil = value[0];
|
|
|
|
for (int y = 0; y < access.raw().getDepth(); y++)
|
|
for (int x = 0; x < access.raw().getHeight(); x++)
|
|
for (int s = 0; s < access.getNumSamples(); s++)
|
|
writeMaskedStencil(access, s, x, y, stencil, m_stencil[rr::FACETYPE_FRONT].writeMask);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReferenceContext::clearBufferfv (deUint32 buffer, int drawbuffer, const float* value)
|
|
{
|
|
RC_IF_ERROR(buffer != GL_COLOR && buffer != GL_DEPTH, GL_INVALID_ENUM, RC_RET_VOID);
|
|
RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support.
|
|
|
|
IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff);
|
|
|
|
if (buffer == GL_COLOR)
|
|
{
|
|
rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer();
|
|
bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3];
|
|
bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3];
|
|
IVec4 area = intersect(baseArea, getBufferRect(colorBuf));
|
|
|
|
if (!isEmpty(area) && !maskZero)
|
|
{
|
|
rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w());
|
|
Vec4 color (value[0], value[1], value[2], value[3]);
|
|
|
|
if (m_sRGBUpdateEnabled && tcu::isSRGB(access.raw().getFormat()))
|
|
color = tcu::linearToSRGB(color);
|
|
|
|
if (!maskUsed)
|
|
rr::clear(access, color);
|
|
else
|
|
{
|
|
for (int y = 0; y < access.raw().getDepth(); y++)
|
|
for (int x = 0; x < access.raw().getHeight(); x++)
|
|
for (int s = 0; s < access.getNumSamples(); s++)
|
|
access.raw().setPixel(tcu::select(color, access.raw().getPixel(s, x, y), m_colorMask), s, x, y);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
TCU_CHECK_INTERNAL(buffer == GL_DEPTH);
|
|
|
|
rr::MultisamplePixelBufferAccess depthBuf = getDrawDepthbuffer();
|
|
IVec4 area = intersect(baseArea, getBufferRect(depthBuf));
|
|
|
|
if (!isEmpty(area) && m_depthMask)
|
|
{
|
|
rr::MultisamplePixelBufferAccess access = rr::getSubregion(depthBuf, area.x(), area.y(), area.z(), area.w());
|
|
float depth = value[0];
|
|
|
|
rr::clearDepth(access, depth);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReferenceContext::clearBufferuiv (deUint32 buffer, int drawbuffer, const deUint32* value)
|
|
{
|
|
RC_IF_ERROR(buffer != GL_COLOR, GL_INVALID_ENUM, RC_RET_VOID);
|
|
RC_IF_ERROR(drawbuffer != 0, GL_INVALID_VALUE, RC_RET_VOID); // \todo [2012-04-06 pyry] MRT support.
|
|
|
|
IVec4 baseArea = m_scissorEnabled ? m_scissorBox : IVec4(0, 0, 0x7fffffff, 0x7fffffff);
|
|
|
|
TCU_CHECK_INTERNAL(buffer == GL_COLOR);
|
|
{
|
|
rr::MultisamplePixelBufferAccess colorBuf = getDrawColorbuffer();
|
|
bool maskUsed = !m_colorMask[0] || !m_colorMask[1] || !m_colorMask[2] || !m_colorMask[3];
|
|
bool maskZero = !m_colorMask[0] && !m_colorMask[1] && !m_colorMask[2] && !m_colorMask[3];
|
|
IVec4 area = intersect(baseArea, getBufferRect(colorBuf));
|
|
|
|
if (!isEmpty(area) && !maskZero)
|
|
{
|
|
rr::MultisamplePixelBufferAccess access = rr::getSubregion(colorBuf, area.x(), area.y(), area.z(), area.w());
|
|
tcu::UVec4 color (value[0], value[1], value[2], value[3]);
|
|
|
|
if (!maskUsed)
|
|
rr::clear(access, color.asInt());
|
|
else
|
|
{
|
|
for (int y = 0; y < access.raw().getDepth(); y++)
|
|
for (int x = 0; x < access.raw().getHeight(); x++)
|
|
for (int s = 0; s < access.getNumSamples(); s++)
|
|
access.raw().setPixel(tcu::select(color, access.raw().getPixelUint(s, x, y), m_colorMask), s, x, y);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReferenceContext::clearBufferfi (deUint32 buffer, int drawbuffer, float depth, int stencil)
|
|
{
|
|
RC_IF_ERROR(buffer != GL_DEPTH_STENCIL, GL_INVALID_ENUM, RC_RET_VOID);
|
|
clearBufferfv(GL_DEPTH, drawbuffer, &depth);
|
|
clearBufferiv(GL_STENCIL, drawbuffer, &stencil);
|
|
}
|
|
|
|
void ReferenceContext::bindVertexArray (deUint32 array)
|
|
{
|
|
rc::VertexArray* vertexArrayObject = DE_NULL;
|
|
|
|
if (array != 0)
|
|
{
|
|
vertexArrayObject = m_vertexArrays.find(array);
|
|
if (!vertexArrayObject)
|
|
{
|
|
vertexArrayObject = new rc::VertexArray(array, m_limits.maxVertexAttribs);
|
|
m_vertexArrays.insert(vertexArrayObject);
|
|
}
|
|
}
|
|
|
|
// Create new references
|
|
if (vertexArrayObject)
|
|
m_vertexArrays.acquireReference(vertexArrayObject);
|
|
|
|
// Remove old references
|
|
if (m_vertexArrayBinding)
|
|
m_vertexArrays.releaseReference(m_vertexArrayBinding);
|
|
|
|
m_vertexArrayBinding = vertexArrayObject;
|
|
}
|
|
|
|
void ReferenceContext::genVertexArrays (int numArrays, deUint32* vertexArrays)
|
|
{
|
|
RC_IF_ERROR(!vertexArrays, GL_INVALID_VALUE, RC_RET_VOID);
|
|
|
|
for (int ndx = 0; ndx < numArrays; ndx++)
|
|
vertexArrays[ndx] = m_vertexArrays.allocateName();
|
|
}
|
|
|
|
void ReferenceContext::deleteVertexArrays (int numArrays, const deUint32* vertexArrays)
|
|
{
|
|
for (int i = 0; i < numArrays; i++)
|
|
{
|
|
deUint32 name = vertexArrays[i];
|
|
VertexArray* vertexArray = name ? m_vertexArrays.find(name) : DE_NULL;
|
|
|
|
if (vertexArray)
|
|
deleteVertexArray(vertexArray);
|
|
}
|
|
}
|
|
|
|
void ReferenceContext::vertexAttribPointer (deUint32 index, int rawSize, deUint32 type, deBool normalized, int stride, const void *pointer)
|
|
{
|
|
const bool allowBGRA = !glu::isContextTypeES(getType());
|
|
const int effectiveSize = (allowBGRA && rawSize == GL_BGRA) ? (4) : (rawSize);
|
|
|
|
RC_IF_ERROR(index >= (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<sglr::UniformSlot>& 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<sglr::UniformSlot>& 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<sglr::UniformSlot>& 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<sglr::UniformSlot>& 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<sglr::UniformSlot>& 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<rr::VertexAttrib> 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
|