Bug 586811 - Handle incomplete and NPOT textures - r=vladimir a=blocking2.0

This commit is contained in:
Benoit Jacob 2010-08-23 17:03:53 -04:00
Родитель a6b4fe7683
Коммит b8ecdcd363
3 изменённых файлов: 495 добавлений и 26 удалений

Просмотреть файл

@ -99,6 +99,9 @@ WebGLContext::WebGLContext()
mMapShaders.Init();
mMapFramebuffers.Init();
mMapRenderbuffers.Init();
mBlackTexturesAreInitialized = PR_FALSE;
mFakeBlackStatus = DoNotNeedFakeBlack;
}
WebGLContext::~WebGLContext()
@ -198,6 +201,12 @@ WebGLContext::DestroyResourcesAndContext()
mMapRenderbuffers.EnumerateRead(DeleteRenderbufferFunction, gl);
mMapRenderbuffers.Clear();
if (mBlackTexturesAreInitialized) {
gl->fDeleteTextures(1, &mBlackTexture2D);
gl->fDeleteTextures(1, &mBlackTextureCubeMap);
mBlackTexturesAreInitialized = PR_FALSE;
}
// We just got rid of everything, so the context had better
// have been going away.
printf_stderr("--- WebGL context destroyed: %p\n", gl.get());

Просмотреть файл

@ -81,6 +81,13 @@ class WebGLUniformLocation;
class WebGLZeroingObject;
class WebGLContextBoundObject;
enum FakeBlackStatus { DoNotNeedFakeBlack, DoNeedFakeBlack, DontKnowIfNeedFakeBlack };
inline PRBool is_pot_assuming_nonnegative(WebGLsizei x)
{
return (x & (x-1)) == 0;
}
class WebGLObjectBaseRefPtr
{
protected:
@ -318,6 +325,16 @@ public:
// a number that increments every time we have an event that causes
// all context resources to be lost.
PRUint32 Generation() { return mGeneration.value(); }
void SetDontKnowIfNeedFakeBlack() {
mFakeBlackStatus = DontKnowIfNeedFakeBlack;
}
PRBool NeedFakeBlack();
void BindFakeBlackTextures();
void UnbindFakeBlackTextures();
protected:
nsCOMPtr<nsIDOMHTMLCanvasElement> mCanvasElement;
nsHTMLCanvasElement *HTMLCanvasElement() {
@ -453,10 +470,17 @@ protected:
// WebGL-specific PixelStore parameters
PRBool mPixelStoreFlipY, mPixelStorePremultiplyAlpha;
FakeBlackStatus mFakeBlackStatus;
WebGLuint mBlackTexture2D, mBlackTextureCubeMap;
PRBool mBlackTexturesAreInitialized;
public:
// console logging helpers
static void LogMessage (const char *fmt, ...);
static void LogMessage(const char *fmt, va_list ap);
friend class WebGLTexture;
};
// this class is a mixin for the named type wrappers, and is used
@ -652,8 +676,17 @@ public:
WebGLTexture(WebGLContext *context, WebGLuint name) :
WebGLContextBoundObject(context),
mName(name), mDeleted(PR_FALSE)
{ }
mDeleted(PR_FALSE), mName(name),
mTarget(0),
mMinFilter(LOCAL_GL_NEAREST_MIPMAP_LINEAR),
mMagFilter(LOCAL_GL_LINEAR),
mWrapS(LOCAL_GL_REPEAT),
mWrapT(LOCAL_GL_REPEAT),
mFacesCount(0),
mMaxLevelWithCustomImages(0),
mHaveGeneratedMipmap(PR_FALSE),
mFakeBlackStatus(DontKnowIfNeedFakeBlack)
{}
void Delete() {
if (mDeleted)
@ -667,9 +700,323 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIWEBGLTEXTURE
protected:
WebGLuint mName;
PRBool mDeleted;
WebGLuint mName;
////////////////////////////////////////////////////////////////////////////////////////////////////
/////// everything below that point is only used for the texture completeness/npot business
/////// (sections 3.7.10 and 3.8.2 in GL ES 2.0.24 spec)
////////////////////////////////////////////////////////////////////////////////////////////////////
struct ImageInfo {
ImageInfo() : mWidth(0), mHeight(0), mFormat(0), mType(0), mIsDefined(PR_FALSE) {}
PRBool operator==(const ImageInfo& a) const {
return mWidth == a.mWidth && mHeight == a.mHeight &&
mFormat == a.mFormat && mType == a.mType;
}
PRBool operator!=(const ImageInfo& a) const {
return !(*this == a);
}
PRBool IsSquare() const {
return mWidth == mHeight;
}
PRBool IsPositive() const {
return mWidth > 0 && mHeight > 0;
}
PRBool IsPowerOfTwo() const {
return is_pot_assuming_nonnegative(mWidth) &&
is_pot_assuming_nonnegative(mHeight); // negative sizes should never happen (caught in texImage2D...)
}
WebGLsizei mWidth, mHeight;
WebGLenum mFormat, mType;
PRBool mIsDefined;
};
ImageInfo& ImageInfoAt(size_t level, size_t face) {
#ifdef DEBUG
if (face >= mFacesCount)
NS_ERROR("wrong face index, must be 0 for TEXTURE_2D and at most 5 for cube maps");
#endif
// no need to check level as a wrong value would be caught by ElementAt().
return mImageInfos.ElementAt(level * mFacesCount + face);
}
const ImageInfo& ImageInfoAt(size_t level, size_t face) const {
return const_cast<WebGLTexture*>(this)->ImageInfoAt(level, face);
}
WebGLenum mTarget;
WebGLenum mMinFilter, mMagFilter, mWrapS, mWrapT;
size_t mFacesCount, mMaxLevelWithCustomImages;
nsTArray<ImageInfo> mImageInfos;
PRBool mHaveGeneratedMipmap;
FakeBlackStatus mFakeBlackStatus;
void EnsureMaxLevelWithCustomImagesAtLeast(size_t aMaxLevelWithCustomImages) {
mMaxLevelWithCustomImages = PR_MAX(mMaxLevelWithCustomImages, aMaxLevelWithCustomImages);
mImageInfos.EnsureLengthAtLeast((mMaxLevelWithCustomImages + 1) * mFacesCount);
}
PRBool DoesMinFilterRequireMipmap() const {
return !(mMinFilter == LOCAL_GL_NEAREST || mMinFilter == LOCAL_GL_LINEAR);
}
PRBool AreBothWrapModesClampToEdge() const {
return mWrapS == LOCAL_GL_CLAMP_TO_EDGE && mWrapT == LOCAL_GL_CLAMP_TO_EDGE;
}
PRBool DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(size_t face) const {
if (mHaveGeneratedMipmap)
return PR_TRUE;
ImageInfo expected = ImageInfoAt(0, face);
// checks if custom level>0 images are all defined up to the highest level defined
// and have the expected dimensions
for (size_t level = 0; level <= mMaxLevelWithCustomImages; ++level) {
const ImageInfo& actual = ImageInfoAt(level, face);
if (actual != expected)
return PR_FALSE;
expected.mWidth = PR_MAX(1, expected.mWidth >> 1);
expected.mHeight = PR_MAX(1, expected.mHeight >> 1);
// if the current level has size 1x1, we can stop here: the spec doesn't seem to forbid the existence
// of extra useless levels.
if (actual.mWidth == 1 && actual.mHeight == 1)
return PR_TRUE;
}
// if we're here, we've exhausted all levels without finding a 1x1 image
return PR_FALSE;
}
public:
void SetDontKnowIfNeedFakeBlack() {
mFakeBlackStatus = DontKnowIfNeedFakeBlack;
mContext->SetDontKnowIfNeedFakeBlack();
}
void Bind(WebGLenum aTarget) {
// this function should only be called by bindTexture().
// it assumes that the GL context is already current.
PRBool firstTimeThisTextureIsBound = mTarget == 0;
if (!firstTimeThisTextureIsBound && aTarget != mTarget) {
mContext->ErrorInvalidOperation("bindTexture: this texture has already been bound to a different target");
// very important to return here before modifying texture state! This was the place when I lost a whole day figuring
// very strange 'invalid write' crashes.
return;
}
mTarget = aTarget;
mContext->gl->fBindTexture(mTarget, mName);
if (firstTimeThisTextureIsBound) {
mFacesCount = (mTarget == LOCAL_GL_TEXTURE_2D) ? 1 : 6;
EnsureMaxLevelWithCustomImagesAtLeast(0);
SetDontKnowIfNeedFakeBlack();
// thanks to the WebKit people for finding this out: GL_TEXTURE_WRAP_R is not
// present in GLES 2, but is present in GL and it seems as if for cube maps
// we need to set it to GL_CLAMP_TO_EDGE to get the expected GLES behavior.
if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP && !mContext->gl->IsGLES2())
mContext->gl->fTexParameteri(mTarget, LOCAL_GL_TEXTURE_WRAP_R, LOCAL_GL_CLAMP_TO_EDGE);
}
}
void SetImageInfo(WebGLenum aTarget, WebGLint aLevel,
WebGLsizei aWidth, WebGLsizei aHeight,
WebGLenum aFormat = 0, WebGLenum aType = 0) {
size_t face = 0;
if (aTarget == LOCAL_GL_TEXTURE_2D) {
if (mTarget != LOCAL_GL_TEXTURE_2D) return;
} else {
if (mTarget == LOCAL_GL_TEXTURE_2D) return;
face = aTarget - LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X;
}
EnsureMaxLevelWithCustomImagesAtLeast(aLevel);
ImageInfo& imageInfo = ImageInfoAt(aLevel, face);
imageInfo.mWidth = aWidth;
imageInfo.mHeight = aHeight;
if (aFormat)
imageInfo.mFormat = aFormat;
if (aType)
imageInfo.mType = aType;
imageInfo.mIsDefined = PR_TRUE;
if (aLevel > 0)
SetCustomMipmap();
SetDontKnowIfNeedFakeBlack();
}
void SetMinFilter(WebGLenum aMinFilter) {
mMinFilter = aMinFilter;
SetDontKnowIfNeedFakeBlack();
}
void SetMagFilter(WebGLenum aMagFilter) {
mMagFilter = aMagFilter;
SetDontKnowIfNeedFakeBlack();
}
void SetWrapS(WebGLenum aWrapS) {
mWrapS = aWrapS;
SetDontKnowIfNeedFakeBlack();
}
void SetWrapT(WebGLenum aWrapT) {
mWrapT = aWrapT;
SetDontKnowIfNeedFakeBlack();
}
void SetGeneratedMipmap() {
if (!mHaveGeneratedMipmap) {
mHaveGeneratedMipmap = PR_TRUE;
SetDontKnowIfNeedFakeBlack();
}
}
void SetCustomMipmap() {
if (mHaveGeneratedMipmap) {
// if we were in GeneratedMipmap mode and are now switching to CustomMipmap mode,
// we need to compute now all the mipmap image info.
// since we were in GeneratedMipmap mode, we know that the level 0 images all have the same info,
// and are power-of-two.
ImageInfo imageInfo = ImageInfoAt(0, 0);
NS_ASSERTION(imageInfo.IsPowerOfTwo(), "this texture is NPOT, so how could GenerateMipmap() ever accept it?");
WebGLsizei size = PR_MAX(imageInfo.mWidth, imageInfo.mHeight);
// so, the size is a power of two, let's find its log in base 2.
size_t maxLevel = 0;
for (WebGLsizei n = size; n > 1; n >>= 1)
++maxLevel;
EnsureMaxLevelWithCustomImagesAtLeast(maxLevel);
for (size_t level = 1; level <= maxLevel; ++level) {
// again, since the sizes are powers of two, no need for any max(1,x) computation
imageInfo.mWidth >>= 1;
imageInfo.mHeight >>= 1;
for(size_t face = 0; face < mFacesCount; ++face)
ImageInfoAt(level, face) = imageInfo;
}
}
mHaveGeneratedMipmap = PR_FALSE;
}
PRBool IsGenerateMipmapAllowed() const {
const ImageInfo &first = ImageInfoAt(0, 0);
if (!first.IsPowerOfTwo())
return PR_FALSE;
for (size_t face = 0; face < mFacesCount; ++face) {
if (ImageInfoAt(0, face) != first)
return PR_FALSE;
}
return PR_TRUE;
}
PRBool IsMipmapTexture2DComplete() const {
if (mTarget != LOCAL_GL_TEXTURE_2D)
return PR_FALSE;
if (!mImageInfos[0].IsPositive())
return PR_FALSE;
if (mHaveGeneratedMipmap)
return PR_TRUE;
return DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(0);
}
PRBool IsCubeComplete() const {
if (mTarget != LOCAL_GL_TEXTURE_CUBE_MAP)
return PR_FALSE;
const ImageInfo &first = ImageInfoAt(0, 0);
if (!first.IsPositive() || !first.IsSquare())
return PR_FALSE;
for (size_t face = 0; face < mFacesCount; ++face) {
if (ImageInfoAt(0, face) != first)
return PR_FALSE;
}
return PR_TRUE;
}
PRBool IsMipmapCubeComplete() const {
if (!IsCubeComplete()) // in particular, this checks that this is a cube map
return PR_FALSE;
for (size_t face = 0; face < mFacesCount; ++face) {
if (!DoesTexture2DMipmapHaveAllLevelsConsistentlyDefined(face))
return PR_FALSE;
}
return PR_TRUE;
}
PRBool NeedFakeBlack() {
// handle this case first, it's the generic case
if (mFakeBlackStatus == DoNotNeedFakeBlack)
return PR_FALSE;
if (mFakeBlackStatus == DontKnowIfNeedFakeBlack) {
// Determine if the texture needs to be faked as a black texture.
// See 3.8.2 Shader Execution in the OpenGL ES 2.0.24 spec.
if (mTarget == LOCAL_GL_TEXTURE_2D)
{
if (DoesMinFilterRequireMipmap())
{
if (!IsMipmapTexture2DComplete() ||
!mImageInfos[0].IsPowerOfTwo())
{
mFakeBlackStatus = DoNeedFakeBlack;
}
}
else // no mipmap required
{
if (!mImageInfos[0].IsPositive() ||
(!AreBothWrapModesClampToEdge() && !mImageInfos[0].IsPowerOfTwo()))
{
mFakeBlackStatus = DoNeedFakeBlack;
}
}
}
else if (mTarget == LOCAL_GL_TEXTURE_CUBE_MAP)
{
PRBool areAllLevel0ImagesPOT = PR_TRUE;
for (size_t face = 0; face < mFacesCount; ++face)
areAllLevel0ImagesPOT &= ImageInfoAt(0, face).IsPowerOfTwo();
if (DoesMinFilterRequireMipmap())
{
if (!IsMipmapCubeComplete() ||
!areAllLevel0ImagesPOT)
{
mFakeBlackStatus = DoNeedFakeBlack;
}
}
else // no mipmap required
{
if (!IsCubeComplete() ||
(!AreBothWrapModesClampToEdge() && !areAllLevel0ImagesPOT))
{
mFakeBlackStatus = DoNeedFakeBlack;
}
}
}
// we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
// that means that we do NOT need it.
if (mFakeBlackStatus == DontKnowIfNeedFakeBlack)
mFakeBlackStatus = DoNotNeedFakeBlack;
}
return mFakeBlackStatus == DoNeedFakeBlack;
}
};
NS_DEFINE_STATIC_IID_ACCESSOR(WebGLTexture, WEBGLTEXTURE_PRIVATE_IID)

Просмотреть файл

@ -62,15 +62,6 @@
using namespace mozilla;
template<typename T>
bool is_power_of_two(T x)
{
if (x <= 0)
return false;
else
return (x & (x-1)) == 0;
}
static PRBool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize);
/* Helper macros for when we're just wrapping a gl method, so that
@ -272,7 +263,7 @@ WebGLContext::BindTexture(WebGLenum target, nsIWebGLTexture *tobj)
{
WebGLuint texturename;
WebGLTexture *tex;
PRBool isNull;
PRBool isNull; // allow null object
if (!GetConcreteObjectAndGLName("bindTexture", tobj, &tex, &texturename, &isNull))
return NS_OK;
@ -286,7 +277,10 @@ WebGLContext::BindTexture(WebGLenum target, nsIWebGLTexture *tobj)
MakeContextCurrent();
gl->fBindTexture(target, texturename);
if (tex)
tex->Bind(target);
else
gl->fBindTexture(target, 0 /* == texturename */);
return NS_OK;
}
@ -573,16 +567,21 @@ WebGLContext::CopyTexImage2D(WebGLenum target,
return ErrorInvalidEnumInfo("CopyTexImage2D: internal format", internalformat);
}
if (border != 0) {
return ErrorInvalidValue("CopyTexImage2D: border != 0");
}
if (border != 0)
return ErrorInvalidValue("copyTexImage2D: border must be 0");
if (level < 0)
return ErrorInvalidValue("copyTexImage2D: level may not be negative");
if (!CanvasUtils::CheckSaneSubrectSize(x,y,width, height, mWidth, mHeight))
return ErrorInvalidOperation("CopyTexImage2D: copied rectangle out of bounds");
if (!activeBoundTextureForTarget(target))
WebGLTexture *tex = activeBoundTextureForTarget(target);
if (!tex)
return ErrorInvalidOperation("copyTexImage2D: no texture bound to this target");
tex->SetImageInfo(target, level, width, height);
MakeContextCurrent();
gl->fCopyTexImage2D(target, level, internalformat, x, y, width, height, border);
@ -856,6 +855,94 @@ WebGLContext::DisableVertexAttribArray(WebGLuint index)
return NS_OK;
}
PRBool
WebGLContext::NeedFakeBlack()
{
// handle this case first, it's the generic case
if (mFakeBlackStatus == DoNotNeedFakeBlack)
return PR_FALSE;
if (mFakeBlackStatus == DontKnowIfNeedFakeBlack) {
for (PRInt32 i = 0; i < mGLMaxTextureImageUnits; ++i) {
if ((mBound2DTextures[i] && mBound2DTextures[i]->NeedFakeBlack()) ||
(mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->NeedFakeBlack()))
{
mFakeBlackStatus = DoNeedFakeBlack;
break;
}
}
// we have exhausted all cases where we do need fakeblack, so if the status is still unknown,
// that means that we do NOT need it.
if (mFakeBlackStatus == DontKnowIfNeedFakeBlack)
mFakeBlackStatus = DoNotNeedFakeBlack;
}
return mFakeBlackStatus == DoNeedFakeBlack;
}
void
WebGLContext::BindFakeBlackTextures()
{
// this is the generic case: try to return early
if (!NeedFakeBlack())
return;
if (!mBlackTexturesAreInitialized) {
const PRUint8 black[] = {0, 0, 0, 255};
gl->fGenTextures(1, &mBlackTexture2D);
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBlackTexture2D);
gl->fTexImage2D(LOCAL_GL_TEXTURE_2D, 0, LOCAL_GL_RGBA, 1, 1,
0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, &black);
gl->fGenTextures(1, &mBlackTextureCubeMap);
gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBlackTextureCubeMap);
for (WebGLuint i = 0; i < 6; ++i) {
gl->fTexImage2D(LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, LOCAL_GL_RGBA, 1, 1,
0, LOCAL_GL_RGBA, LOCAL_GL_UNSIGNED_BYTE, &black);
}
// return the texture bindings to the 0 texture to prevent the user from modifying our black textures
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, 0);
gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, 0);
mBlackTexturesAreInitialized = PR_TRUE;
}
for (PRInt32 i = 0; i < mGLMaxTextureImageUnits; ++i) {
if (mBound2DTextures[i] && mBound2DTextures[i]->NeedFakeBlack()) {
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBlackTexture2D);
}
if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->NeedFakeBlack()) {
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBlackTextureCubeMap);
}
}
}
void
WebGLContext::UnbindFakeBlackTextures()
{
// this is the generic case: try to return early
if (!NeedFakeBlack())
return;
for (PRInt32 i = 0; i < mGLMaxTextureImageUnits; ++i) {
if (mBound2DTextures[i] && mBound2DTextures[i]->NeedFakeBlack()) {
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
gl->fBindTexture(LOCAL_GL_TEXTURE_2D, mBound2DTextures[i]->GLName());
}
if (mBoundCubeMapTextures[i] && mBoundCubeMapTextures[i]->NeedFakeBlack()) {
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + i);
gl->fBindTexture(LOCAL_GL_TEXTURE_CUBE_MAP, mBoundCubeMapTextures[i]->GLName());
}
}
gl->fActiveTexture(LOCAL_GL_TEXTURE0 + mActiveTexture);
}
NS_IMETHODIMP
WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
{
@ -884,7 +971,9 @@ WebGLContext::DrawArrays(GLenum mode, WebGLint first, WebGLsizei count)
MakeContextCurrent();
BindFakeBlackTextures();
gl->fDrawArrays(mode, first, count);
UnbindFakeBlackTextures();
Invalidate();
@ -955,7 +1044,9 @@ WebGLContext::DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, Web
MakeContextCurrent();
BindFakeBlackTextures();
gl->fDrawElements(mode, count, type, (GLvoid*) (byteOffset));
UnbindFakeBlackTextures();
Invalidate();
@ -1144,11 +1235,15 @@ WebGLContext::GenerateMipmap(WebGLenum target)
WebGLTexture *tex = activeBoundTextureForTarget(target);
if (!(is_power_of_two(tex->width()) ||
is_power_of_two(tex->height()))) {
return ErrorInvalidOperation("generateMipmap: texture width and height must be powers of two");
if (!tex)
return ErrorInvalidOperation("generateMipmap: no texture is bound to this target");
if (!tex->IsGenerateMipmapAllowed()) {
return ErrorInvalidOperation("generateMipmap: texture does not satisfy requirements for generateMipmap");
}
tex->SetGeneratedMipmap();
MakeContextCurrent();
gl->fGenerateMipmap(target);
return NS_OK;
@ -1791,6 +1886,10 @@ nsresult WebGLContext::TexParameter_base(WebGLenum target, WebGLenum pname,
if (!ValidateTextureTargetEnum(target, "texParameter: target"))
return NS_OK;
WebGLTexture *tex = activeBoundTextureForTarget(target);
if (!tex)
return ErrorInvalidOperation("texParameter: no texture is bound to this target");
PRBool pnameAndParamAreIncompatible = PR_FALSE;
switch (pname) {
@ -1802,6 +1901,7 @@ nsresult WebGLContext::TexParameter_base(WebGLenum target, WebGLenum pname,
case LOCAL_GL_LINEAR_MIPMAP_NEAREST:
case LOCAL_GL_NEAREST_MIPMAP_LINEAR:
case LOCAL_GL_LINEAR_MIPMAP_LINEAR:
tex->SetMinFilter(intParam);
break;
default:
pnameAndParamAreIncompatible = PR_TRUE;
@ -1811,17 +1911,29 @@ nsresult WebGLContext::TexParameter_base(WebGLenum target, WebGLenum pname,
switch (intParam) {
case LOCAL_GL_NEAREST:
case LOCAL_GL_LINEAR:
tex->SetMagFilter(intParam);
break;
default:
pnameAndParamAreIncompatible = PR_TRUE;
}
break;
case LOCAL_GL_TEXTURE_WRAP_S:
switch (intParam) {
case LOCAL_GL_CLAMP_TO_EDGE:
case LOCAL_GL_MIRRORED_REPEAT:
case LOCAL_GL_REPEAT:
tex->SetWrapS(intParam);
break;
default:
pnameAndParamAreIncompatible = PR_TRUE;
}
break;
case LOCAL_GL_TEXTURE_WRAP_T:
switch (intParam) {
case LOCAL_GL_CLAMP_TO_EDGE:
case LOCAL_GL_MIRRORED_REPEAT:
case LOCAL_GL_REPEAT:
tex->SetWrapT(intParam);
break;
default:
pnameAndParamAreIncompatible = PR_TRUE;
@ -1840,9 +1952,6 @@ nsresult WebGLContext::TexParameter_base(WebGLenum target, WebGLenum pname,
pname, floatParam);
}
if (!activeBoundTextureForTarget(target))
return ErrorInvalidOperation("texParameter: no texture is bound to this target");
MakeContextCurrent();
if (intParamPtr)
gl->fTexParameteri(target, pname, intParam);
@ -3078,7 +3187,8 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern
return ErrorInvalidValue("texImage2D: width or height exceeds maximum texture size");
if (level >= 1) {
if (!(is_power_of_two(width) && is_power_of_two(height)))
if (!(is_pot_assuming_nonnegative(width) &&
is_pot_assuming_nonnegative(height)))
return ErrorInvalidValue("texImage2D: with level > 0, width and height must be powers of two");
}
@ -3105,6 +3215,8 @@ WebGLContext::TexImage2D_base(WebGLenum target, WebGLint level, WebGLenum intern
if (!tex)
return ErrorInvalidOperation("texImage2D: no texture is bound to this target");
tex->SetImageInfo(target, level, width, height, format, type);
MakeContextCurrent();
if (byteLength) {
@ -3211,7 +3323,8 @@ WebGLContext::TexSubImage2D_base(WebGLenum target, WebGLint level,
return ErrorInvalidValue("texSubImage2D: width or height exceeds maximum texture size");
if (level >= 1) {
if (!(is_power_of_two(width) && is_power_of_two(height)))
if (!(is_pot_assuming_nonnegative(width) &&
is_pot_assuming_nonnegative(height)))
return ErrorInvalidValue("texSubImage2D: with level > 0, width and height must be powers of two");
}