Bug 1048108 - Exclude RGBA configs when alpha:false. - r=kamidphish

This commit is contained in:
Jeff Gilbert 2014-09-24 16:42:27 -07:00
parent 1ec62ca2c2
commit feb7643d1e
6 changed files with 411 additions and 98 deletions

View File

@ -3,6 +3,8 @@ support-files =
webgl-mochitest/driver-info.js
webgl-mochitest/webgl-util.js
[webgl-mochitest/test-backbuffer-channels.html]
[webgl-mochitest/test-hidden-alpha.html]
[webgl-mochitest/test_depth_readpixels.html]
[webgl-mochitest/test_draw.html]
[webgl-mochitest/test_fb_param.html]

View File

@ -9,7 +9,7 @@ extDotPos = mochiPath.find('.html')
assert extDotPos != -1, 'mochitest target must be an html doc.'
testPath = mochiPath[:extDotPos] + '.solo.html'
def ReadLocalFile(include):
incPath = os.path.dirname(mochiPath)
filePath = os.path.join(incPath, include)
@ -31,22 +31,29 @@ def ReadLocalFile(include):
kSimpleTestReplacement = '''\n
<script>
// SimpleTest.js replacement
function ok(val, text) {
function debug(text) {
var elem = document.getElementById('mochi-to-testcase-output');
elem.innerHTML += '\\n<br/>\\n' + text;
}
function ok(val, text) {
var status = val ? 'Test <font color=\\'green\\'>passed</font>: '
: 'Test <font color=\\'red\\' >FAILED</font>: ';
elem.innerHTML += '\\n<br/>\\n' + status + text;
debug(status + text);
}
function todo(val, text) {
ok(!val, 'Todo: ' + text);
var status = val ? 'Test <font color=\\'orange\\'>UNEXPECTED PASS</font>: '
: 'Test <font color=\\'blue\\' >todo</font>: ';
debug(status + text);
}
</script>
<div id='mochi-to-testcase-output'></div>
\n'''
fin = open(mochiPath, 'r')
fout = open(testPath, 'w')
fin = open(mochiPath, 'rb')
fout = open(testPath, 'wb')
includePattern = re.compile('<script\\s*src=[\'"](.*)\\.js[\'"]>\\s*</script>')
cssPattern = re.compile('<link\\s*rel=[\'"]stylesheet[\'"]\\s*href=[\'"]([^=>]*)[\'"]>')
for line in fin:
@ -54,7 +61,7 @@ for line in fin:
for css in cssPattern.findall(line):
skipLine = True
print('Ignoring stylesheet: ' + css)
for inc in includePattern.findall(line):
skipLine = True
if inc == '/MochiKit/MochiKit':
@ -64,7 +71,7 @@ for line in fin:
print('Injecting SimpleTest replacement')
fout.write(kSimpleTestReplacement);
continue
incData = ReadLocalFile(inc + '.js')
if not incData:
print('Warning: Unknown JS file ignored: ' + inc + '.js')
@ -78,7 +85,7 @@ for line in fin:
if skipLine:
continue
fout.write(line)
continue

View File

@ -0,0 +1,111 @@
<!DOCTYPE HTML>
<title>WebGL test: bug 958723</title>
<script src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script src="driver-info.js"></script>
<script src="webgl-util.js"></script>
<body>
<script>
function TestAttribs(attribs) {
debug('Testing attribs: ' + JSON.stringify(attribs));
var canvas = document.createElement('canvas');
var gl = canvas.getContext('experimental-webgl', attribs);
ok(gl, 'No tested attribs should result in failure to create a context');
if (!gl)
return;
var actual = gl.getContextAttributes();
ok(actual.alpha == attribs.alpha,
'Resulting `alpha` should match request.');
ok(actual.premultipliedAlpha == attribs.premultipliedAlpha,
'Resulting `premultipliedAlpha` should match request.');
ok(actual.preserveDrawingBuffer == attribs.preserveDrawingBuffer,
'Resulting `preserveDrawingBuffer` should match request.');
// "The depth, stencil and antialias attributes, when set to true, are
// requests, not requirements."
if (!attribs.antialias) {
ok(!actual.antialias, 'No `antialias` if not requested.');
}
if (!attribs.depth) {
ok(!actual.depth, 'No `depth` if not requested.');
}
if (!attribs.stencil) {
ok(!actual.stencil, 'No `stencil` if not requested.');
}
var hasAlpha = !!gl.getParameter(gl.ALPHA_BITS);
var hasDepth = !!gl.getParameter(gl.DEPTH_BITS);
var hasStencil = !!gl.getParameter(gl.STENCIL_BITS);
var hasAntialias = !!gl.getParameter(gl.SAMPLES);
ok(hasAlpha == actual.alpha, 'Bits should match `alpha` attrib.');
ok(hasAntialias == actual.antialias, 'Bits should match `antialias` attrib.');
ok(hasDepth == actual.depth, 'Bits should match `depth` attrib.');
ok(hasStencil == actual.stencil, 'Bits should match `stencil` attrib.');
}
function CloneAttribs(attribs) {
return {
alpha: attribs.alpha,
antialias: attribs.antialias,
depth: attribs.depth,
premultipliedAlpha: attribs.premultipliedAlpha,
preserveDrawingBuffer: attribs.preserveDrawingBuffer,
stencil: attribs.stencil,
};
}
function SplitForAttrib(list, attrib) {
var ret = [];
for (var i in list) {
var cur = list[i];
if (cur[attrib])
throw 'Attrib is already true.';
var clone = CloneAttribs(cur);
clone[attrib] = true;
ret.push(cur);
ret.push(clone);
}
return ret;
}
function GenAttribList() {
var base = {
alpha: false,
antialias: false,
depth: false,
premultipliedAlpha: false,
preserveDrawingBuffer: false,
stencil: false,
};
var list = [base];
list = SplitForAttrib(list, 'alpha');
list = SplitForAttrib(list, 'antialias');
list = SplitForAttrib(list, 'depth');
list = SplitForAttrib(list, 'premultipliedAlpha');
list = SplitForAttrib(list, 'preserveDrawingBuffer');
list = SplitForAttrib(list, 'stencil');
if (list.length != 1<<6)
throw 'Attribs list length wrong: ' + list.length;
return list;
}
var list = GenAttribList();
for (var i in list) {
var attribs = list[i];
TestAttribs(attribs);
}
ok(true, 'Test complete.');
</script>

View File

@ -0,0 +1,153 @@
<!DOCTYPE HTML>
<title>WebGL test: Hidden alpha on no-alpha contexts</title>
<script src='/tests/SimpleTest/SimpleTest.js'></script>
<link rel='stylesheet' href='/tests/SimpleTest/test.css'>
<script src='driver-info.js'></script>
<script src='webgl-util.js'></script>
<body>
<script id='vs' type='x-shader/x-vertex'>
attribute vec2 aPosCoord;
void main(void) {
gl_Position = vec4(aPosCoord, 0.0, 1.0);
}
</script>
<script id='fs' type='x-shader/x-fragment'>
precision mediump float;
void main(void) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
</script>
<canvas id='canvas' style='border: none;' width='100' height='100'></canvas>
<script>
var posCoords_arr = new Float32Array(2 * 4);
var posCoords_buff = null;
function DrawQuad(gl, prog, x0, y0, x1, y1) {
gl.useProgram(prog);
if (!posCoords_buff) {
posCoords_buff = gl.createBuffer();
}
gl.bindBuffer(gl.ARRAY_BUFFER, posCoords_buff);
posCoords_arr[0] = x0;
posCoords_arr[1] = y0;
posCoords_arr[2] = x1;
posCoords_arr[3] = y0;
posCoords_arr[4] = x0;
posCoords_arr[5] = y1;
posCoords_arr[6] = x1;
posCoords_arr[7] = y1;
gl.bufferData(gl.ARRAY_BUFFER, posCoords_arr, gl.STREAM_DRAW);
gl.enableVertexAttribArray(prog.aPosCoord);
gl.vertexAttribPointer(prog.aPosCoord, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
}
function DrawSquare(gl, prog, size) {
DrawQuad(gl, prog, -size, -size, size, size);
}
function Reset(gl) {
gl.canvas.width += 1;
gl.canvas.width -= 1;
}
function ReadCenterPixel(gl) {
var w = gl.drawingbufferWidth;
var h = gl.drawingbufferHeight;
var ret = new Uint8Array(4);
gl.readPixels(w/2, h/2, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, ret);
return ret;
}
function Test(gl, prog) {
gl.enable(gl.BLEND);
gl.blendFunc(gl.ZERO, gl.DST_ALPHA);
var iColor = 64;
var fColor = iColor / 255.0;
//////////////////
debug('clear(R,G,B,0)');
Reset(gl);
gl.clearColor(fColor, fColor, fColor, 0.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var dataURL_pre = gl.canvas.toDataURL();
//console.log('Before blending: ' + dataURL_pre);
DrawSquare(gl, prog, 0.7);
var pixel = ReadCenterPixel(gl);
ok(pixel[0] == iColor &&
pixel[1] == iColor &&
pixel[2] == iColor, 'Color should be the same.');
ok(pixel[3] == 255, 'No-alpha should always readback as 1.0 alpha.');
var dataURL_post = gl.canvas.toDataURL();
//console.log('After blending: ' + dataURL_post);
ok(dataURL_post == dataURL_pre,
'toDataURL should be unchanged after blending.');
//////////////////
debug('mask(R,G,B,0), clear(R,G,B,1)');
Reset(gl);
gl.colorMask(true, true, true, false);
gl.clearColor(fColor, fColor, fColor, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.colorMask(true, true, true, true);
dataURL_pre = gl.canvas.toDataURL();
//console.log('Before blending: ' + dataURL_pre);
DrawSquare(gl, prog, 0.7);
var pixel = ReadCenterPixel(gl);
ok(pixel[0] == iColor &&
pixel[1] == iColor &&
pixel[2] == iColor, 'Color should be the same.');
ok(pixel[3] == 255, 'No-alpha should always readback as 1.0 alpha.');
ok(gl.getError() == 0, 'Should have no errors.');
dataURL_post = gl.canvas.toDataURL();
//console.log('After blending: ' + dataURL_post);
ok(dataURL_post == dataURL_pre,
'toDataURL should be unchanged after blending.');
ok(true, 'Test complete.');
}
(function(){
var canvas = document.getElementById('canvas');
var attribs = {
alpha: false,
antialias: false,
premultipliedAlpha: false,
};
var gl = canvas.getContext('experimental-webgl', attribs);
ok(gl, 'WebGL should work.');
ok(gl.getParameter(gl.ALPHA_BITS) == 0, 'Shouldn\'t have alpha bits.');
var prog = WebGLUtil.createProgramByIds(gl, 'vs', 'fs');
ok(prog, 'Program should link.');
prog.aPosCoord = gl.getAttribLocation(prog, 'aPosCoord');
setTimeout(function(){ Test(gl, prog); }, 500);
})();
</script>
</body>

View File

@ -11,6 +11,73 @@
namespace mozilla {
namespace gl {
// Returns `EGL_NO_SURFACE` (`0`) on error.
static EGLSurface
CreatePBufferSurface(GLLibraryEGL* egl,
EGLDisplay display,
EGLConfig config,
const gfx::IntSize& size)
{
auto width = size.width;
auto height = size.height;
EGLint attribs[] = {
LOCAL_EGL_WIDTH, width,
LOCAL_EGL_HEIGHT, height,
LOCAL_EGL_NONE
};
DebugOnly<EGLint> preCallErr = egl->fGetError();
MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
EGLint err = egl->fGetError();
if (err != LOCAL_EGL_SUCCESS)
return 0;
return surface;
}
/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
EGLContext context, EGLConfig config,
const gfx::IntSize& size, bool hasAlpha)
{
GLLibraryEGL* egl = &sEGLLibrary;
MOZ_ASSERT(egl);
MOZ_ASSERT(egl->IsExtensionSupported(
GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
if (!context || !config)
return nullptr;
EGLDisplay display = egl->Display();
EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
if (!pbuffer)
return nullptr;
// Declare everything before 'goto's.
HANDLE shareHandle = nullptr;
bool ok = egl->fQuerySurfacePointerANGLE(display,
pbuffer,
LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
&shareHandle);
if (!ok) {
egl->fDestroySurface(egl->Display(), pbuffer);
return nullptr;
}
GLuint fence = 0;
if (gl->IsExtensionSupported(GLContext::NV_fence)) {
gl->MakeCurrent();
gl->fGenFences(1, &fence);
}
typedef SharedSurface_ANGLEShareHandle ptrT;
UniquePtr<ptrT> ret( new ptrT(gl, egl, size, hasAlpha, context,
pbuffer, shareHandle, fence) );
return Move(ret);
}
EGLDisplay
SharedSurface_ANGLEShareHandle::Display()
{
@ -114,6 +181,9 @@ SharedSurface_ANGLEShareHandle::PollSync_ContentThread_Impl()
return PollSync();
}
////////////////////////////////////////////////////////////////////////////////
// Factory
static void
FillPBufferAttribs_ByBits(nsTArray<EGLint>& aAttrs,
int redBits, int greenBits,
@ -169,15 +239,25 @@ FillPBufferAttribs_BySizes(nsTArray<EGLint>& attribs,
alpha = 8;
}
FillPBufferAttribs_ByBits(attribs,
red, green, blue, alpha,
depthBits, stencilBits);
FillPBufferAttribs_ByBits(attribs, red, green, blue, alpha, depthBits,
stencilBits);
}
static bool
DoesAttribBitsMatchCapBool(GLLibraryEGL* egl, EGLConfig config, EGLint attrib,
bool capBool)
{
EGLint bits = 0;
egl->fGetConfigAttrib(egl->Display(), config, attrib, &bits);
MOZ_ASSERT(egl->fGetError() == LOCAL_EGL_SUCCESS);
bool hasBits = !!bits;
return hasBits == capBool;
}
static EGLConfig
ChooseConfig(GLContext* gl,
GLLibraryEGL* egl,
const SurfaceCaps& caps)
ChooseConfig(GLContext* gl, GLLibraryEGL* egl, const SurfaceCaps& caps)
{
MOZ_ASSERT(egl);
MOZ_ASSERT(caps.color);
@ -188,28 +268,43 @@ ChooseConfig(GLContext* gl,
// Ok, now we have everything.
nsTArray<EGLint> attribs(32);
FillPBufferAttribs_BySizes(attribs,
caps.bpp16, caps.alpha,
depthBits, stencilBits);
FillPBufferAttribs_BySizes(attribs, caps.bpp16, caps.alpha, depthBits,
stencilBits);
// Time to try to get this config:
EGLConfig configs[64];
int numConfigs = sizeof(configs)/sizeof(EGLConfig);
int foundConfigs = 0;
if (!egl->fChooseConfig(egl->Display(),
attribs.Elements(),
configs, numConfigs,
&foundConfigs) ||
if (!egl->fChooseConfig(egl->Display(), attribs.Elements(), configs,
numConfigs, &foundConfigs) ||
!foundConfigs)
{
NS_WARNING("No configs found for the requested formats.");
return EGL_NO_CONFIG;
}
// TODO: Pick a config progamatically instead of hoping that
// the first config will be minimally matching our request.
EGLConfig config = configs[0];
// The requests passed to ChooseConfig are treated as minimums. If you ask
// for 0 bits of alpha, we might still get 8 bits.
EGLConfig config = EGL_NO_CONFIG;
for (int i = 0; i < foundConfigs; i++) {
EGLConfig cur = configs[0];
if (DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_ALPHA_SIZE,
caps.alpha) &&
DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_DEPTH_SIZE,
caps.depth) &&
DoesAttribBitsMatchCapBool(egl, cur, LOCAL_EGL_STENCIL_SIZE,
caps.stencil))
{
config = cur;
break;
}
}
if (config == EGL_NO_CONFIG) {
NS_WARNING("No acceptable EGLConfig found.");
return EGL_NO_CONFIG;
}
if (gl->DebugMode()) {
egl->DumpEGLConfig(config);
@ -218,73 +313,6 @@ ChooseConfig(GLContext* gl,
return config;
}
// Returns `EGL_NO_SURFACE` (`0`) on error.
static EGLSurface
CreatePBufferSurface(GLLibraryEGL* egl,
EGLDisplay display,
EGLConfig config,
const gfx::IntSize& size)
{
auto width = size.width;
auto height = size.height;
EGLint attribs[] = {
LOCAL_EGL_WIDTH, width,
LOCAL_EGL_HEIGHT, height,
LOCAL_EGL_NONE
};
DebugOnly<EGLint> preCallErr = egl->fGetError();
MOZ_ASSERT(preCallErr == LOCAL_EGL_SUCCESS);
EGLSurface surface = egl->fCreatePbufferSurface(display, config, attribs);
EGLint err = egl->fGetError();
if (err != LOCAL_EGL_SUCCESS)
return 0;
return surface;
}
/*static*/ UniquePtr<SharedSurface_ANGLEShareHandle>
SharedSurface_ANGLEShareHandle::Create(GLContext* gl,
EGLContext context, EGLConfig config,
const gfx::IntSize& size, bool hasAlpha)
{
GLLibraryEGL* egl = &sEGLLibrary;
MOZ_ASSERT(egl);
MOZ_ASSERT(egl->IsExtensionSupported(
GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle));
if (!context || !config)
return nullptr;
EGLDisplay display = egl->Display();
EGLSurface pbuffer = CreatePBufferSurface(egl, display, config, size);
if (!pbuffer)
return nullptr;
// Declare everything before 'goto's.
HANDLE shareHandle = nullptr;
bool ok = egl->fQuerySurfacePointerANGLE(display,
pbuffer,
LOCAL_EGL_D3D_TEXTURE_2D_SHARE_HANDLE_ANGLE,
&shareHandle);
if (!ok) {
egl->fDestroySurface(egl->Display(), pbuffer);
return nullptr;
}
GLuint fence = 0;
if (gl->IsExtensionSupported(GLContext::NV_fence)) {
gl->MakeCurrent();
gl->fGenFences(1, &fence);
}
typedef SharedSurface_ANGLEShareHandle ptrT;
UniquePtr<ptrT> ret( new ptrT(gl, egl, size, hasAlpha, context,
pbuffer, shareHandle, fence) );
return Move(ret);
}
/*static*/ UniquePtr<SurfaceFactory_ANGLEShareHandle>
SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl,
const SurfaceCaps& caps)
@ -295,25 +323,36 @@ SurfaceFactory_ANGLEShareHandle::Create(GLContext* gl,
auto ext = GLLibraryEGL::ANGLE_surface_d3d_texture_2d_share_handle;
if (!egl->IsExtensionSupported(ext))
{
return nullptr;
}
bool success;
typedef SurfaceFactory_ANGLEShareHandle ptrT;
UniquePtr<ptrT> ret( new ptrT(gl, egl, caps) );
UniquePtr<ptrT> ret( new ptrT(gl, egl, caps, &success) );
if (!success)
return nullptr;
return Move(ret);
}
SurfaceFactory_ANGLEShareHandle::SurfaceFactory_ANGLEShareHandle(GLContext* gl,
GLLibraryEGL* egl,
const SurfaceCaps& caps)
const SurfaceCaps& caps,
bool* const out_success)
: SurfaceFactory(gl, SharedSurfaceType::EGLSurfaceANGLE, caps)
, mProdGL(gl)
, mEGL(egl)
{
mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps);
MOZ_ASSERT(out_success);
*out_success = false;
mContext = GLContextEGL::Cast(mProdGL)->GetEGLContext();
mConfig = ChooseConfig(mProdGL, mEGL, mReadCaps);
if (mConfig == EGL_NO_CONFIG)
return;
MOZ_ASSERT(mConfig && mContext);
*out_success = true;
}
} /* namespace gl */

View File

@ -88,7 +88,8 @@ public:
protected:
SurfaceFactory_ANGLEShareHandle(GLContext* gl,
GLLibraryEGL* egl,
const SurfaceCaps& caps);
const SurfaceCaps& caps,
bool* const out_success);
virtual UniquePtr<SharedSurface> CreateShared(const gfx::IntSize& size) MOZ_OVERRIDE {
bool hasAlpha = mReadCaps.alpha;