blob: 1136ea3fc6bebf874ca25536bb96338a345e5f49 [file] [log] [blame]
//
// Copyright 2021 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// FramebufferFetchTest:
// Tests the correctness of the EXT_shader_framebuffer_fetch and the
// EXT_shader_framebuffer_fetch_non_coherent extensions.
//
#include "common/debug.h"
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include "util/EGLWindow.h"
namespace angle
{
//
// Shared Vertex Shaders for the tests below
//
// A 1.0 GLSL vertex shader
static constexpr char k100VS[] = R"(#version 100
attribute vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
// A 3.1 GLSL vertex shader
static constexpr char k310VS[] = R"(#version 310 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
// Shared simple (i.e. no framebuffer fetch) Fragment Shaders for the tests below
//
// Simple (i.e. no framebuffer fetch) 3.1 GLSL fragment shader that writes to 1 attachment
static constexpr char k310NoFetch1AttachmentFS[] = R"(#version 310 es
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color;
})";
// Shared Coherent Fragment Shaders for the tests below
//
// Coherent version of a 1.0 GLSL fragment shader that uses gl_LastFragData
static constexpr char k100CoherentFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
mediump vec4 gl_LastFragData[gl_MaxDrawBuffers];
uniform highp vec4 u_color;
void main (void)
{
gl_FragColor = u_color + gl_LastFragData[0];
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 1 attachment
static constexpr char k310Coherent1AttachmentFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes the output to a storage buffer.
static constexpr char k310CoherentStorageBuffer[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
layout(std140, binding = 0) buffer outBlock {
highp vec4 data[256];
};
uniform highp vec4 u_color;
void main (void)
{
uint index = uint(gl_FragCoord.y) * 16u + uint(gl_FragCoord.x);
data[index] = o_color;
o_color += u_color;
})";
// Coherent version of a 1.0 GLSL fragment shader that writes to 4 attachments with constant indices
static constexpr char k100Coherent4AttachmentFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_EXT_draw_buffers : require
uniform highp vec4 u_color;
void main (void)
{
gl_FragData[0] = gl_LastFragData[0] + u_color;
gl_FragData[1] = gl_LastFragData[1] + u_color;
gl_FragData[2] = gl_LastFragData[2] + u_color;
gl_FragData[3] = gl_LastFragData[3] + u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments
static constexpr char k310Coherent4AttachmentFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color0;
layout(location = 1) inout highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2;
layout(location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 += u_color;
o_color2 += u_color;
o_color3 += u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments via an inout
// array
static constexpr char k310Coherent4AttachmentArrayFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
inout highp vec4 o_color[4];
uniform highp vec4 u_color;
void main (void)
{
o_color[0] += u_color;
o_color[1] += u_color;
o_color[2] += u_color;
o_color[3] += u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments with the order of
// non-fetch program and fetch program with different attachments (version 1)
static constexpr char k310CoherentDifferent4AttachmentFS1[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2;
layout(location = 3) out highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 = u_color;
o_color2 += u_color;
o_color3 = u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments with the order
// of non-fetch program and fetch program with different attachments (version 2)
static constexpr char k310CoherentDifferent4AttachmentFS2[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(location = 2) out highp vec4 o_color2;
layout(location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 = u_color;
o_color2 = u_color;
o_color3 += u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments, fetching from
// different indices (version 3)
static constexpr char k310CoherentDifferent4AttachmentFS3[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color0;
layout(location = 1) inout highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2;
layout(location = 3) out highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 = u_color;
o_color1 += u_color;
o_color2 += u_color;
o_color3 = u_color;
})";
// Coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments, fetching from
// different indices (version 4)
static constexpr char k310CoherentDifferent4AttachmentFS4[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2;
layout(location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 = u_color;
o_color1 = u_color;
o_color2 += u_color;
o_color3 += u_color;
})";
// Coherent version of a 1.0 GLSL fragment shader with complex interactions
static constexpr char k100CoherentComplexFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_EXT_draw_buffers : require
precision highp float;
uniform vec4 u_color;
vec4 addColor(vec4 lastFragData, vec4 color)
{
return lastFragData + color;
}
void addLastFragData(inout vec4 outVar, vec4 lastFragData)
{
outVar += lastFragData;
}
void main (void)
{
// Leave gl_LastFragData[0] unused, as well as gl_LastFragData[2]
gl_FragData[0] = u_color;
gl_FragData[1] = addColor(gl_LastFragData[1], u_color);
gl_FragData[2] = u_color;
gl_FragData[3] = addColor(gl_LastFragData[3], u_color);
// Make sure gl_LastFragData is not clobbered by a write to gl_FragData.
gl_FragData[1] -= gl_LastFragData[1];
gl_FragData[3] -= gl_LastFragData[3];
// Test passing to inout variables.
addLastFragData(gl_FragData[1], gl_LastFragData[1]);
addLastFragData(gl_FragData[3], gl_LastFragData[3]);
})";
// Coherent version of a 3.1 GLSL fragment shader with complex interactions
static constexpr char k310CoherentComplexFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
precision highp float;
layout(location = 0) inout highp vec4 o_color0;
layout(location = 1) inout highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2[2];
uniform vec4 u_color;
vec4 addColor(vec4 lastValue, vec4 color)
{
return lastValue + color;
}
vec4 getColor2_1()
{
return o_color2[1];
}
void addUniform(inout vec4 outVar)
{
outVar += u_color;
}
void main (void)
{
// o_color0 and o_color2[0] don't use the input value.
o_color0 = u_color;
o_color2[0] = u_color;
addUniform(o_color1);
addUniform(o_color2[1]);
// Make sure reading back from the output variables returns the latest value and not the
// original input value.
vec4 temp1 = o_color1;
vec4 temp3 = getColor2_1();
o_color1 = temp1;
o_color2[1] = temp3;
})";
// Shared Non-Coherent Fragment Shaders for the tests below
//
// Non-coherent version of a 1.0 GLSL fragment shader that uses gl_LastFragData
static constexpr char k100NonCoherentFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent) mediump vec4 gl_LastFragData[gl_MaxDrawBuffers];
uniform highp vec4 u_color;
void main (void)
{
gl_FragColor = u_color + gl_LastFragData[0];
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 1 attachment
static constexpr char k310NonCoherent1AttachmentFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes the output to a storage buffer.
static constexpr char k310NonCoherentStorageBuffer[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent) inout highp vec4 o_color;
layout(std140, binding = 0) buffer outBlock {
highp vec4 data[256];
};
uniform highp vec4 u_color;
void main (void)
{
uint index = uint(gl_FragCoord.y) * 16u + uint(gl_FragCoord.x);
data[index] = o_color;
o_color += u_color;
})";
// Non-coherent version of a 1.0 GLSL fragment shader that writes to 4 attachments with constant
// indices
static constexpr char k100NonCoherent4AttachmentFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
#extension GL_EXT_draw_buffers : require
layout(noncoherent) mediump vec4 gl_LastFragData[gl_MaxDrawBuffers];
uniform highp vec4 u_color;
void main (void)
{
gl_FragData[0] = gl_LastFragData[0] + u_color;
gl_FragData[1] = gl_LastFragData[1] + u_color;
gl_FragData[2] = gl_LastFragData[2] + u_color;
gl_FragData[3] = gl_LastFragData[3] + u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments
static constexpr char k310NonCoherent4AttachmentFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color0;
layout(noncoherent, location = 1) inout highp vec4 o_color1;
layout(noncoherent, location = 2) inout highp vec4 o_color2;
layout(noncoherent, location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 += u_color;
o_color2 += u_color;
o_color3 += u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments via an inout
// array
static constexpr char k310NonCoherent4AttachmentArrayFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color[4];
uniform highp vec4 u_color;
void main (void)
{
o_color[0] += u_color;
o_color[1] += u_color;
o_color[2] += u_color;
o_color[3] += u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments with the order
// of non-fetch program and fetch program with different attachments (version 1)
static constexpr char k310NonCoherentDifferent4AttachmentFS1[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(noncoherent, location = 2) inout highp vec4 o_color2;
layout(location = 3) out highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 = u_color;
o_color2 += u_color;
o_color3 = u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments with the order
// of non-fetch program and fetch program with different attachments (version 2)
static constexpr char k310NonCoherentDifferent4AttachmentFS2[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(location = 2) out highp vec4 o_color2;
layout(noncoherent, location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 += u_color;
o_color1 = u_color;
o_color2 = u_color;
o_color3 += u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments, fetching from
// different indices (version 3)
static constexpr char k310NonCoherentDifferent4AttachmentFS3[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(location = 0) out highp vec4 o_color0;
layout(noncoherent, location = 1) inout highp vec4 o_color1;
layout(noncoherent, location = 2) inout highp vec4 o_color2;
layout(location = 3) out highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 = u_color;
o_color1 += u_color;
o_color2 += u_color;
o_color3 = u_color;
})";
// Non-coherent version of a 3.1 GLSL fragment shader that writes to 4 attachments, fetching from
// different indices (version 4)
static constexpr char k310NonCoherentDifferent4AttachmentFS4[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(location = 0) out highp vec4 o_color0;
layout(location = 1) out highp vec4 o_color1;
layout(noncoherent, location = 2) inout highp vec4 o_color2;
layout(noncoherent, location = 3) inout highp vec4 o_color3;
uniform highp vec4 u_color;
void main (void)
{
o_color0 = u_color;
o_color1 = u_color;
o_color2 += u_color;
o_color3 += u_color;
})";
// Non-coherent version of a 1.0 GLSL fragment shader with complex interactions
static constexpr char k100NonCoherentComplexFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
#extension GL_EXT_draw_buffers : require
precision highp float;
layout(noncoherent) mediump vec4 gl_LastFragData[gl_MaxDrawBuffers];
uniform vec4 u_color;
vec4 addColor(vec4 lastFragData, vec4 color)
{
return lastFragData + color;
}
void addLastFragData(inout vec4 outVar, vec4 lastFragData)
{
outVar += lastFragData;
}
void main (void)
{
// Leave gl_LastFragData[0] unused, as well as gl_LastFragData[2]
gl_FragData[0] = u_color;
gl_FragData[1] = addColor(gl_LastFragData[1], u_color);
gl_FragData[2] = u_color;
gl_FragData[3] = addColor(gl_LastFragData[3], u_color);
// Make sure gl_LastFragData is not clobbered by a write to gl_FragData.
gl_FragData[1] -= gl_LastFragData[1];
gl_FragData[3] -= gl_LastFragData[3];
// Test passing to inout variables.
addLastFragData(gl_FragData[1], gl_LastFragData[1]);
addLastFragData(gl_FragData[3], gl_LastFragData[3]);
})";
// Non-coherent version of a 3.1 GLSL fragment shader with complex interactions
static constexpr char k310NonCoherentComplexFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
precision highp float;
layout(location = 0) out highp vec4 o_color0;
layout(noncoherent, location = 1) inout highp vec4 o_color1;
layout(noncoherent, location = 2) inout highp vec4 o_color2[2];
uniform vec4 u_color;
vec4 addColor(vec4 lastValue, vec4 color)
{
return lastValue + color;
}
vec4 getColor2_1()
{
return o_color2[1];
}
void addUniform(inout vec4 outVar)
{
outVar += u_color;
}
void main (void)
{
// o_color0 and o_color2[0] don't use the input value.
o_color0 = u_color;
o_color2[0] = u_color;
addUniform(o_color1);
addUniform(o_color2[1]);
// Make sure reading back from the output variables returns the latest value and not the
// original input value.
vec4 temp1 = o_color1;
vec4 temp3 = getColor2_1();
o_color1 = temp1;
o_color2[1] = temp3;
})";
// Shared Coherent Fragment Shaders for the tests below
//
// Coherent version of a 1.0 GLSL fragment shader that uses gl_LastFragColorARM
static constexpr char k100ARMFS[] = R"(#version 100
#extension GL_ARM_shader_framebuffer_fetch : require
mediump vec4 gl_LastFragColorARM;
uniform highp vec4 u_color;
void main (void)
{
gl_FragColor = u_color + gl_LastFragColorARM;
})";
// ARM version of a 3.1 GLSL fragment shader that writes to 1 attachment
static constexpr char k310ARM1AttachmentFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + gl_LastFragColorARM;
})";
// ARM version of a 3.1 GLSL fragment shader that writes the output to a storage buffer.
static constexpr char k310ARMStorageBuffer[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color;
layout(std140, binding = 0) buffer outBlock {
highp vec4 data[256];
};
uniform highp vec4 u_color;
void main (void)
{
uint index = uint(gl_FragCoord.y) * 16u + uint(gl_FragCoord.x);
data[index] = gl_LastFragColorARM;
o_color = u_color + gl_LastFragColorARM;
})";
// Variants that use both EXT and ARM simultaneously. At least one app has been observed to do
// this.
static constexpr char k100BothFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch : require
uniform highp vec4 u_color;
void main (void)
{
gl_FragColor = u_color + (gl_LastFragColorARM + gl_LastFragData[0]) / 2.;
})";
static constexpr char k310Both1AttachmentFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch : require
inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + (o_color + gl_LastFragColorARM) / 2.;
})";
static constexpr char k100Both4AttachmentFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch : require
#extension GL_EXT_draw_buffers : require
uniform highp vec4 u_color;
void main (void)
{
gl_FragData[0] = (gl_LastFragData[0] + gl_LastFragColorARM) / 2. + u_color;
gl_FragData[1] = gl_LastFragData[1] + u_color;
gl_FragData[2] = gl_LastFragData[2] + u_color;
gl_FragData[3] = gl_LastFragData[3] + u_color;
})";
static constexpr char k100BothComplexFS[] = R"(#version 100
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch : require
#extension GL_EXT_draw_buffers : require
precision highp float;
uniform vec4 u_color;
vec4 addColor(vec4 lastFragData, vec4 color)
{
return lastFragData + color;
}
void addLastFragData(inout vec4 outVar, vec4 lastFragData)
{
outVar += lastFragData;
}
void main (void)
{
// Leave gl_LastFragData[1] unused, as well as gl_LastFragData[3]
gl_FragData[0] = addColor((gl_LastFragData[0] + gl_LastFragColorARM) / 2., u_color);
gl_FragData[1] = u_color;
gl_FragData[2] = addColor(gl_LastFragData[2], u_color);
gl_FragData[3] = u_color;
// Make sure gl_LastFragData is not clobbered by a write to gl_FragData.
gl_FragData[0] -= gl_LastFragColorARM;
gl_FragData[2] -= gl_LastFragData[2];
// Test passing to inout variables.
addLastFragData(gl_FragData[0], gl_LastFragData[0]);
addLastFragData(gl_FragData[2], gl_LastFragData[2]);
})";
static constexpr char k310BothComplexFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch : require
precision highp float;
layout(location = 0) inout highp vec4 o_color0;
layout(location = 1) inout highp vec4 o_color1;
layout(location = 2) inout highp vec4 o_color2[2];
uniform vec4 u_color;
vec4 addColor(vec4 lastValue, vec4 color)
{
return lastValue + color;
}
vec4 getColor2_0()
{
return o_color2[0];
}
void addUniform(inout vec4 outVar)
{
outVar += u_color;
}
void main (void)
{
// o_color1 and o_color2[1] don't use the input value.
o_color1 = u_color;
o_color2[1] = u_color;
o_color0 = gl_LastFragColorARM + u_color;
addUniform(o_color2[0]);
// Make sure reading back from the output variables returns the latest value and not the
// original input value.
vec4 temp0 = o_color0;
vec4 temp2 = getColor2_0();
o_color0 = temp0;
o_color2[0] = temp2;
// Make sure gl_LastFragColorARM is not clobberred by the write to o_color0
if (gl_LastFragColorARM == o_color0)
o_color0 = vec4(0);
})";
class FramebufferFetchES31 : public ANGLETest<>
{
protected:
static constexpr GLuint kMaxColorBuffer = 4u;
static constexpr GLuint kViewportWidth = 16u;
static constexpr GLuint kViewportHeight = 16u;
static constexpr GLenum kDSFormat[6] = {GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT24,
GL_DEPTH24_STENCIL8, GL_DEPTH_COMPONENT32F,
GL_DEPTH32F_STENCIL8, GL_STENCIL_INDEX8};
FramebufferFetchES31()
{
setWindowWidth(16);
setWindowHeight(16);
setConfigRedBits(8);
setConfigGreenBits(8);
setConfigBlueBits(8);
setConfigAlphaBits(8);
setConfigDepthBits(24);
setConfigStencilBits(8);
mCoherentExtension = false;
mARMExtension = false;
mBothExtensions = false;
}
enum WhichExtension
{
COHERENT,
NON_COHERENT,
ARM,
BOTH,
};
void setWhichExtension(WhichExtension whichExtension)
{
mCoherentExtension = whichExtension != NON_COHERENT;
mARMExtension = whichExtension == ARM;
mBothExtensions = whichExtension == BOTH;
}
enum WhichFragmentShader
{
GLSL100,
GLSL310_NO_FETCH_1ATTACHMENT,
GLSL310_1ATTACHMENT,
GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER,
GLSL100_4ATTACHMENT,
GLSL100_COMPLEX,
GLSL310_4ATTACHMENT,
GLSL310_4ATTACHMENT_ARRAY,
GLSL310_4ATTACHMENT_DIFFERENT1,
GLSL310_4ATTACHMENT_DIFFERENT2,
GLSL310_4ATTACHMENT_DIFFERENT3,
GLSL310_4ATTACHMENT_DIFFERENT4,
GLSL310_COMPLEX,
};
const char *getFragmentShader(WhichFragmentShader whichFragmentShader)
{
if (mBothExtensions)
{
switch (whichFragmentShader)
{
case GLSL100:
return k100BothFS;
case GLSL310_NO_FETCH_1ATTACHMENT:
return k310NoFetch1AttachmentFS;
case GLSL310_1ATTACHMENT:
return k310Both1AttachmentFS;
case GLSL100_4ATTACHMENT:
return k100Both4AttachmentFS;
case GLSL100_COMPLEX:
return k100BothComplexFS;
case GLSL310_COMPLEX:
return k310BothComplexFS;
default:
UNREACHABLE();
return nullptr;
}
}
else if (mARMExtension)
{
// gl_LastFragColorARM cannot support multiple attachments
switch (whichFragmentShader)
{
case GLSL100:
return k100ARMFS;
case GLSL310_NO_FETCH_1ATTACHMENT:
return k310NoFetch1AttachmentFS;
case GLSL310_1ATTACHMENT:
return k310ARM1AttachmentFS;
case GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER:
return k310ARMStorageBuffer;
default:
UNREACHABLE();
return nullptr;
}
}
else if (mCoherentExtension)
{
switch (whichFragmentShader)
{
case GLSL100:
return k100CoherentFS;
case GLSL310_NO_FETCH_1ATTACHMENT:
return k310NoFetch1AttachmentFS;
case GLSL310_1ATTACHMENT:
return k310Coherent1AttachmentFS;
case GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER:
return k310CoherentStorageBuffer;
case GLSL100_4ATTACHMENT:
return k100Coherent4AttachmentFS;
case GLSL310_4ATTACHMENT:
return k310Coherent4AttachmentFS;
case GLSL310_4ATTACHMENT_ARRAY:
return k310Coherent4AttachmentArrayFS;
case GLSL310_4ATTACHMENT_DIFFERENT1:
return k310CoherentDifferent4AttachmentFS1;
case GLSL310_4ATTACHMENT_DIFFERENT2:
return k310CoherentDifferent4AttachmentFS2;
case GLSL310_4ATTACHMENT_DIFFERENT3:
return k310CoherentDifferent4AttachmentFS3;
case GLSL310_4ATTACHMENT_DIFFERENT4:
return k310CoherentDifferent4AttachmentFS4;
case GLSL100_COMPLEX:
return k100CoherentComplexFS;
case GLSL310_COMPLEX:
return k310CoherentComplexFS;
default:
UNREACHABLE();
return nullptr;
}
}
else
{
switch (whichFragmentShader)
{
case GLSL100:
return k100NonCoherentFS;
case GLSL310_NO_FETCH_1ATTACHMENT:
return k310NoFetch1AttachmentFS;
case GLSL310_1ATTACHMENT:
return k310NonCoherent1AttachmentFS;
case GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER:
return k310NonCoherentStorageBuffer;
case GLSL100_4ATTACHMENT:
return k100NonCoherent4AttachmentFS;
case GLSL310_4ATTACHMENT:
return k310NonCoherent4AttachmentFS;
case GLSL310_4ATTACHMENT_ARRAY:
return k310NonCoherent4AttachmentArrayFS;
case GLSL310_4ATTACHMENT_DIFFERENT1:
return k310NonCoherentDifferent4AttachmentFS1;
case GLSL310_4ATTACHMENT_DIFFERENT2:
return k310NonCoherentDifferent4AttachmentFS2;
case GLSL310_4ATTACHMENT_DIFFERENT3:
return k310NonCoherentDifferent4AttachmentFS3;
case GLSL310_4ATTACHMENT_DIFFERENT4:
return k310NonCoherentDifferent4AttachmentFS4;
case GLSL100_COMPLEX:
return k100NonCoherentComplexFS;
case GLSL310_COMPLEX:
return k310NonCoherentComplexFS;
default:
UNREACHABLE();
return nullptr;
}
}
}
void render(GLuint coordLoc, GLboolean needsFramebufferFetchBarrier)
{
const GLfloat coords[] = {
-1.0f, -1.0f, +1.0f, -1.0f, +1.0f, +1.0f, -1.0f, +1.0f,
};
const GLushort indices[] = {
0, 1, 2, 2, 3, 0,
};
glViewport(0, 0, kViewportWidth, kViewportHeight);
GLBuffer coordinatesBuffer;
GLBuffer elementsBuffer;
glBindBuffer(GL_ARRAY_BUFFER, coordinatesBuffer);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)sizeof(coords), coords, GL_STATIC_DRAW);
glEnableVertexAttribArray(coordLoc);
glVertexAttribPointer(coordLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementsBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)sizeof(indices), &indices[0],
GL_STATIC_DRAW);
if (needsFramebufferFetchBarrier)
{
glFramebufferFetchBarrierEXT();
}
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, nullptr);
ASSERT_GL_NO_ERROR();
}
void BasicTest(GLProgram &program)
{
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glBindTexture(GL_TEXTURE_2D, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
float color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void MultipleRenderTargetTest(GLProgram &program, WhichFragmentShader whichFragmentShader)
{
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> color0(kViewportWidth * kViewportHeight, GLColor::cyan);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
std::vector<GLColor> color2(kViewportWidth * kViewportHeight, GLColor::blue);
std::vector<GLColor> color3(kViewportWidth * kViewportHeight, GLColor::black);
GLTexture colorBufferTex[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glBindTexture(GL_TEXTURE_2D, colorBufferTex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color0.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color3.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
float color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
// All fragment shaders add the input color with the uniform. Except the COMPLEX shaders
// which initialize attachments 0 and 2, or 1 and 3 with the uniform only (and don't use
// input attachments for these indices).
GLColor expect0 = GLColor::white;
GLColor expect1 = GLColor::yellow;
GLColor expect2 = GLColor::magenta;
GLColor expect3 = GLColor::red;
switch (whichFragmentShader)
{
case GLSL100_COMPLEX:
case GLSL310_COMPLEX:
if (mBothExtensions)
{
expect1 = GLColor::red;
expect3 = GLColor::red;
}
else
{
expect0 = GLColor::red;
expect2 = GLColor::red;
}
break;
default:
break;
}
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, expect0);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, expect1);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, expect2);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, expect3);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void MultipleRenderTargetArrayTest(GLProgram &program)
{
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> color0(kViewportWidth * kViewportHeight, GLColor::black);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
std::vector<GLColor> color2(kViewportWidth * kViewportHeight, GLColor::blue);
std::vector<GLColor> color3(kViewportWidth * kViewportHeight, GLColor::cyan);
GLTexture colorBufferTex[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glBindTexture(GL_TEXTURE_2D, colorBufferTex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color0.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color3.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
float color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::white);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void MultipleDrawTest(GLProgram &program)
{
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
float color1[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color1);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, !mCoherentExtension);
float color2[4] = {0.0f, 0.0f, 1.0f, 1.0f};
glUniform4fv(colorLocation, 1, color2);
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::white);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawNonFetchDrawFetchTest(GLProgram &programNonFetch, GLProgram &programFetch)
{
glUseProgram(programNonFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
GLint positionLocationNonFetch = glGetAttribLocation(programNonFetch, "a_position");
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgram(programFetch);
float colorGreen[4] = {0.0f, 1.0f, 0.0f, 1.0f};
GLint colorLocationFetch = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocationFetch, 1, colorGreen);
GLint positionLocationFetch = glGetAttribLocation(programFetch, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocationFetch, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glUseProgram(programNonFetch);
glUniform4fv(colorLocationNonFetch, 1, colorRed);
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgram(programFetch);
glUniform4fv(colorLocationFetch, 1, colorGreen);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocationFetch, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawFetchDrawNonFetchTest(GLProgram &programNonFetch, GLProgram &programFetch)
{
glUseProgram(programFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationFetch = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocationFetch, 1, colorRed);
GLint positionLocationFetch = glGetAttribLocation(programFetch, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocationFetch, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glUseProgram(programNonFetch);
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
GLint positionLocationNonFetch = glGetAttribLocation(programNonFetch, "a_position");
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
float colorGreen[4] = {0.0f, 1.0f, 0.0f, 1.0f};
glUseProgram(programFetch);
glUniform4fv(colorLocationFetch, 1, colorGreen);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocationFetch, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glUseProgram(programNonFetch);
glUniform4fv(colorLocationNonFetch, 1, colorRed);
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
enum class StorageBufferTestPostFetchAction
{
Nothing,
Clear,
};
void DrawNonFetchDrawFetchInStorageBufferTest(GLProgram &programNonFetch,
GLProgram &programFetch,
StorageBufferTestPostFetchAction postFetchAction)
{
// Create output buffer
constexpr GLsizei kBufferSize = kViewportWidth * kViewportHeight * sizeof(float[4]);
GLBuffer buffer;
glBindBuffer(GL_SHADER_STORAGE_BUFFER, buffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, kBufferSize, nullptr, GL_STATIC_DRAW);
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, 0, buffer, 0, kBufferSize);
// Zero-initialize it
void *bufferData = glMapBufferRange(
GL_SHADER_STORAGE_BUFFER, 0, kBufferSize,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
memset(bufferData, 0, kBufferSize);
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glUseProgram(programNonFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> initColor(kViewportWidth * kViewportHeight, GLColor{10, 20, 30, 40});
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, initColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
GLint positionLocationNonFetch = glGetAttribLocation(programNonFetch, "a_position");
// Mask color output. The no-fetch draw call should be a no-op, and the fetch draw-call
// should only output to the storage buffer, but not the color attachment.
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
glUseProgram(programFetch);
float colorBlue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
GLint colorLocationFetch = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocationFetch, 1, colorBlue);
GLint positionLocationFetch = glGetAttribLocation(programFetch, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocationFetch, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
// Enable the color mask and clear the alpha channel. This shouldn't be reordered with the
// fetch draw.
GLColor expect = initColor[0];
if (postFetchAction == StorageBufferTestPostFetchAction::Clear)
{
expect.A = 200;
glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glClearColor(0.5, 0.6, 0.7, expect.A / 255.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
// Since color is completely masked out, the texture should retain its original green color.
EXPECT_PIXEL_COLOR_NEAR(kViewportWidth / 2, kViewportHeight / 2, expect, 1);
// Read back the storage buffer and make sure framebuffer fetch worked as intended despite
// masked color.
glMemoryBarrier(GL_BUFFER_UPDATE_BARRIER_BIT);
const float *colorData = static_cast<const float *>(
glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, kBufferSize, GL_MAP_READ_BIT));
for (uint32_t y = 0; y < kViewportHeight; ++y)
{
for (uint32_t x = 0; x < kViewportWidth; ++x)
{
uint32_t ssboIndex = (y * kViewportWidth + x) * 4;
EXPECT_NEAR(colorData[ssboIndex + 0], initColor[0].R / 255.0, 0.05);
EXPECT_NEAR(colorData[ssboIndex + 1], initColor[0].G / 255.0, 0.05);
EXPECT_NEAR(colorData[ssboIndex + 2], initColor[0].B / 255.0, 0.05);
EXPECT_NEAR(colorData[ssboIndex + 3], initColor[0].A / 255.0, 0.05);
}
}
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawNonFetchDrawFetchWithDifferentAttachmentsTest(GLProgram &programNonFetch,
GLProgram &programFetch)
{
glUseProgram(programNonFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorTex;
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
GLint positionLocationNonFetch = glGetAttribLocation(programNonFetch, "a_position");
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgram(programFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebufferMRT1;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT1);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
std::vector<GLColor> color2(kViewportWidth * kViewportHeight, GLColor::blue);
GLTexture colorBufferTex1[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex1[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
GLint colorLocation = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocation, 1, colorRed);
GLint positionLocation = glGetAttribLocation(programFetch, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
GLFramebuffer framebufferMRT2;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT2);
GLTexture colorBufferTex2[kMaxColorBuffer];
glBindTexture(GL_TEXTURE_2D, colorBufferTex2[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex2[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex2[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex2[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex2[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
glUniform4fv(colorLocation, 1, colorRed);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawNonFetchDrawFetchWithDifferentProgramsTest(GLProgram &programNonFetch,
GLProgram &programFetch1,
GLProgram &programFetch2)
{
glUseProgram(programNonFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorTex;
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
GLint positionLocationNonFetch = glGetAttribLocation(programNonFetch, "a_position");
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocationNonFetch, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgram(programFetch1);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebufferMRT1;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT1);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex1[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex1[i], 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
GLint colorLocation = glGetUniformLocation(programFetch1, "u_color");
glUniform4fv(colorLocation, 1, colorRed);
GLint positionLocation = glGetAttribLocation(programFetch1, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgram(programFetch2);
ASSERT_GL_NO_ERROR();
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint colorLocation1 = glGetUniformLocation(programFetch2, "u_color");
glUniform4fv(colorLocation1, 1, colorRed);
GLint positionLocation1 = glGetAttribLocation(programFetch2, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation1, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawFetchWithDifferentIndicesInSameRenderPassTest(GLProgram &programFetch1,
GLProgram &programFetch2)
{
GLFramebuffer framebufferMRT1;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT1);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex1[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex1[i], 0);
}
glBindTexture(GL_TEXTURE_2D, 0);
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
glUseProgram(programFetch1);
ASSERT_GL_NO_ERROR();
GLint colorLocation = glGetUniformLocation(programFetch1, "u_color");
const float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
glUniform4fv(colorLocation, 1, colorRed);
GLint positionLocation = glGetAttribLocation(programFetch1, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
//
// Attachments are red, yellow, yellow, red
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glUseProgram(programFetch2);
ASSERT_GL_NO_ERROR();
GLint colorLocation1 = glGetUniformLocation(programFetch2, "u_color");
const float colorBlue[4] = {0.0f, 0.0f, 1.0f, 1.0f};
glUniform4fv(colorLocation1, 1, colorBlue);
GLint positionLocation1 = glGetAttribLocation(programFetch2, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
//
// Attachments are blue, blue, white, magenta
render(positionLocation1, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::white);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void DrawFetchBlitDrawFetchTest(GLProgram &programNonFetch, GLProgram &programFetch)
{
glUseProgram(programFetch);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebufferMRT1;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT1);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
std::vector<GLColor> color2(kViewportWidth * kViewportHeight, GLColor::blue);
GLTexture colorBufferTex1[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex1[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex1[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocation, 1, colorRed);
GLint positionLocation = glGetAttribLocation(programFetch, "a_position");
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
GLFramebuffer framebufferColor;
glBindFramebuffer(GL_FRAMEBUFFER, framebufferColor);
GLTexture colorTex;
glBindTexture(GL_TEXTURE_2D, colorTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTex, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, framebufferColor);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, framebufferMRT1);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth,
kViewportHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
ASSERT_GL_NO_ERROR();
glBindFramebuffer(GL_FRAMEBUFFER, framebufferMRT1);
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
float colorGreen[4] = {0.0f, 1.0f, 0.0f, 1.0f};
glUniform4fv(colorLocation, 1, colorGreen);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
glReadBuffer(colorAttachments[0]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::cyan);
glReadBuffer(colorAttachments[1]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
glReadBuffer(colorAttachments[2]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::cyan);
glReadBuffer(colorAttachments[3]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void makeProgramPipeline(GLProgramPipeline &pipeline, const char *vs, const char *fs)
{
GLProgram programVS, programFS;
GLShader vertShader(GL_VERTEX_SHADER);
glShaderSource(vertShader, 1, &vs, nullptr);
glCompileShader(vertShader);
glProgramParameteri(programVS, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(programVS, vertShader);
glLinkProgram(programVS);
ASSERT_GL_NO_ERROR();
GLShader fragShader(GL_FRAGMENT_SHADER);
glShaderSource(fragShader, 1, &fs, nullptr);
glCompileShader(fragShader);
glProgramParameteri(programFS, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(programFS, fragShader);
glLinkProgram(programFS);
ASSERT_GL_NO_ERROR();
glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, programVS);
glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, programFS);
glUseProgram(0);
glBindProgramPipeline(pipeline);
ASSERT_GL_NO_ERROR();
}
void ProgramPipelineTest(const char *kVS, const char *kFS1, const char *kFS2)
{
GLProgram programVert, programNonFetch, programFetch;
const char *sourceArray[3] = {kVS, kFS1, kFS2};
GLShader vertShader(GL_VERTEX_SHADER);
glShaderSource(vertShader, 1, &sourceArray[0], nullptr);
glCompileShader(vertShader);
glProgramParameteri(programVert, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(programVert, vertShader);
glLinkProgram(programVert);
ASSERT_GL_NO_ERROR();
GLShader fragShader1(GL_FRAGMENT_SHADER);
glShaderSource(fragShader1, 1, &sourceArray[1], nullptr);
glCompileShader(fragShader1);
glProgramParameteri(programNonFetch, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(programNonFetch, fragShader1);
glLinkProgram(programNonFetch);
ASSERT_GL_NO_ERROR();
GLShader fragShader2(GL_FRAGMENT_SHADER);
glShaderSource(fragShader2, 1, &sourceArray[2], nullptr);
glCompileShader(fragShader2);
glProgramParameteri(programFetch, GL_PROGRAM_SEPARABLE, GL_TRUE);
glAttachShader(programFetch, fragShader2);
glLinkProgram(programFetch);
ASSERT_GL_NO_ERROR();
GLProgramPipeline pipeline1, pipeline2, pipeline3, pipeline4;
glUseProgramStages(pipeline1, GL_VERTEX_SHADER_BIT, programVert);
glUseProgramStages(pipeline1, GL_FRAGMENT_SHADER_BIT, programNonFetch);
glBindProgramPipeline(pipeline1);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex,
0);
ASSERT_GL_NO_ERROR();
glActiveShaderProgram(pipeline1, programNonFetch);
float colorRed[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
ASSERT_GL_NO_ERROR();
glActiveShaderProgram(pipeline1, programVert);
GLint positionLocation = glGetAttribLocation(programVert, "a_position");
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocation, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgramStages(pipeline2, GL_VERTEX_SHADER_BIT, programVert);
glUseProgramStages(pipeline2, GL_FRAGMENT_SHADER_BIT, programFetch);
glBindProgramPipeline(pipeline2);
ASSERT_GL_NO_ERROR();
glActiveShaderProgram(pipeline2, programFetch);
float colorGreen[4] = {0.0f, 1.0f, 0.0f, 1.0f};
GLint colorLocationFetch = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocationFetch, 1, colorGreen);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glUseProgramStages(pipeline3, GL_VERTEX_SHADER_BIT, programVert);
glUseProgramStages(pipeline3, GL_FRAGMENT_SHADER_BIT, programNonFetch);
glBindProgramPipeline(pipeline3);
ASSERT_GL_NO_ERROR();
glActiveShaderProgram(pipeline3, programNonFetch);
colorLocationNonFetch = glGetUniformLocation(programNonFetch, "u_color");
glUniform4fv(colorLocationNonFetch, 1, colorRed);
ASSERT_GL_NO_ERROR();
// Render without regard to glFramebufferFetchBarrierEXT()
render(positionLocation, GL_FALSE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
glUseProgramStages(pipeline4, GL_VERTEX_SHADER_BIT, programVert);
glUseProgramStages(pipeline4, GL_FRAGMENT_SHADER_BIT, programFetch);
glBindProgramPipeline(pipeline4);
ASSERT_GL_NO_ERROR();
glActiveShaderProgram(pipeline4, programFetch);
colorLocationFetch = glGetUniformLocation(programFetch, "u_color");
glUniform4fv(colorLocationFetch, 1, colorGreen);
// Render potentially with a glFramebufferFetchBarrierEXT() depending on the [non-]coherent
// extension being used
render(positionLocation, !mCoherentExtension);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void framebufferFetchDepthStencilDetachSeparately(GLenum depthStencilFormat)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
layout(location=0) out highp vec4 color;
highp float gl_LastFragDepthARM;
highp int gl_LastFragStencilARM;
void main()
{
color = vec4(float(gl_LastFragStencilARM)/255.0, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color[4], depthStencil;
GLFramebuffer fbo;
stateReset();
// Create FBO with depth/stencil
createFboWithDepthStencilAndMRT(1, 1, 0, depthStencilFormat, &fbo, color, &depthStencil);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
ASSERT_GL_NO_ERROR();
glEnable(GL_DEPTH_TEST);
glEnable(GL_STENCIL_TEST);
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glStencilFunc(GL_LESS, 40, 0xFF);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.4f);
EXPECT_PIXEL_RECT_EQ(0, 0, 1, 1, GLColor(60, 204, 0, 255));
// CASE 1: Detach stencil, depth is still attached
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glStencilFunc(GL_LESS, 30, 0xFF);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.3f);
ASSERT_GL_NO_ERROR();
GLColor actual0;
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual0.R);
EXPECT_EQ(178, actual0.G);
// CASE 2: Detach depth and attach old stencil
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glUseProgram(program);
glStencilFunc(GL_LESS, 20, 0xFF);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.2f);
ASSERT_GL_NO_ERROR();
GLColor actual1;
glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &actual1.R);
EXPECT_EQ(40, actual1.R);
// CASE 3: Attach old depth
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
glUseProgram(program);
glStencilFunc(GL_LESS, 10, 0xFF);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.1f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_RECT_EQ(0, 0, 1, 1, GLColor(20, 166, 0, 255));
}
const char *getFragShaderName(GLenum depthStencilFormat)
{
static const char kFS1[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp float gl_LastFragDepthARM;
highp int gl_LastFragStencilARM;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
fragColor0 = fragColor1 = fragColor2 = fragColor3 = vec4(gl_LastFragDepthARM,
float(gl_LastFragStencilARM)/255.0, gl_FragCoord.z, 1.0);
})";
static const char kFS2[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp float gl_LastFragDepthARM;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
fragColor0 = fragColor1 = fragColor2 = fragColor3 = vec4(gl_LastFragDepthARM, 0.0,
gl_FragCoord.z, 1.0);
})";
static const char kFS3[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp int gl_LastFragStencilARM;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
fragColor0 = fragColor1 = fragColor2 = fragColor3 = vec4(0.0,
float(gl_LastFragStencilARM)/255.0, gl_FragCoord.z, 1.0);
})";
bool depth = depthFormatBitCount(depthStencilFormat) > 0;
bool stencil = stencilFormatBitCount(depthStencilFormat) > 0;
if (depth && stencil)
{
return kFS1;
}
else if (depth)
{
return kFS2;
}
else
{
return kFS3;
}
}
void stateReset()
{
glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glDisable(GL_FETCH_PER_SAMPLE_ARM);
glBlendFunc(GL_ONE, GL_ZERO);
glDepthFunc(GL_LEQUAL);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glStencilFunc(GL_ALWAYS, 0, 0xFFFFu);
glClearDepthf(1.f);
glClearStencil(0);
glClearColor(0.f, 0.f, 0.f, 0.f);
}
void createFboWithDepthStencilAndMRT(int width,
int height,
int samples,
GLenum depthStencilFormat,
GLFramebuffer *fbo,
GLRenderbuffer *color,
GLRenderbuffer *depthStencil)
{
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
ASSERT_GL_NO_ERROR();
for (GLuint i = 0; i < kMaxColorBuffer; ++i)
{
glBindRenderbuffer(GL_RENDERBUFFER, color[i]);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_RENDERBUFFER,
color[i]);
ASSERT_GL_NO_ERROR();
}
glBindRenderbuffer(GL_RENDERBUFFER, *depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthStencilFormat, width,
height);
if (depthFormatBitCount(depthStencilFormat) > 0)
{
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
*depthStencil);
}
if (stencilFormatBitCount(depthStencilFormat) > 0)
{
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
*depthStencil);
}
ASSERT_GL_NO_ERROR();
GLenum drawBuffers[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glDrawBuffers(kMaxColorBuffer, drawBuffers);
glViewport(0, 0, width, height);
ASSERT_GL_NO_ERROR();
}
GLColor filterDepthStencilColor(GLColor color, GLenum depthStencilFormat)
{
if (depthFormatBitCount(depthStencilFormat) == 0)
{
color.R = 0;
}
if (stencilFormatBitCount(depthStencilFormat) == 0)
{
color.G = 0;
}
return color;
}
int depthFormatBitCount(GLenum format)
{
switch (format)
{
case GL_DEPTH_COMPONENT16:
return 16;
case GL_DEPTH_COMPONENT24:
case GL_DEPTH24_STENCIL8:
return 24;
case GL_DEPTH32F_STENCIL8:
case GL_DEPTH_COMPONENT32F:
return 32;
default:
return 0;
}
}
int stencilFormatBitCount(GLenum format)
{
switch (format)
{
case GL_DEPTH24_STENCIL8:
case GL_DEPTH32F_STENCIL8:
case GL_STENCIL_INDEX8:
return 8;
default:
return 0;
}
}
int maxSamplesSupported(GLenum format)
{
GLint samples = 0;
glGetInternalformativ(GL_RENDERBUFFER, format, GL_SAMPLES, 1, &samples);
return samples;
}
bool sampleCountSupported(GLenum target, GLenum format, int sampleCount)
{
GLint numSupportedSampleCounts = 0;
GLint supportedSampleCounts[8] = {0};
glGetInternalformativ(target, format, GL_NUM_SAMPLE_COUNTS, 1, &numSupportedSampleCounts);
glGetInternalformativ(target, format, GL_SAMPLES, numSupportedSampleCounts,
supportedSampleCounts);
for (int i = 0; i < numSupportedSampleCounts; ++i)
{
if (supportedSampleCounts[i] == sampleCount || sampleCount == 0)
{
return true;
}
}
return false;
}
void bindResolveFboAndVerify(GLRenderbuffer *resolve,
GLFramebuffer *resolveFbo,
GLsizei width,
GLsizei height,
bool isBlit,
bool isDiscard,
GLFramebuffer *fbo,
GLenum depthStencilFormat)
{
if (!isBlit)
{
glBindRenderbuffer(GL_RENDERBUFFER, *resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glBindFramebuffer(GL_FRAMEBUFFER, *resolveFbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
*resolve);
ASSERT_GL_NO_ERROR();
}
else
{
for (GLuint index = 0; index < kMaxColorBuffer; ++index)
{
glReadBuffer(GL_COLOR_ATTACHMENT0 + index);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, *resolveFbo);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, *resolveFbo);
ASSERT_GL_NO_ERROR();
if (!isDiscard)
{
EXPECT_PIXEL_RECT_EQ(
0, 0, width, height,
filterDepthStencilColor(GLColor(255, 70, 191, 255), depthStencilFormat));
}
else
{
for (GLsizei x = 0; x < width; ++x)
{
for (GLsizei y = 0; y < height; ++y)
{
if ((x + y) % 2 != 0)
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::blue);
}
else
{
EXPECT_PIXEL_COLOR_EQ(x, y, GLColor::white);
}
}
}
}
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
}
}
}
void clearAndDrawQuad(GLuint program, bool isDiscard)
{
glClearDepthf(1.f);
glClearStencil(70);
glClearColor(1.f, 1.f, 1.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ASSERT_GL_NO_ERROR();
if (!isDiscard)
{
glUseProgram(program);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
}
else
{
glUseProgram(program);
GLint colorLocation = glGetUniformLocation(program, "color");
glUniform4fv(colorLocation, 1, GLColor::blue.toNormalizedVector().data());
drawQuad(program, essl31_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
}
}
void createFramebufferWithDepthStencil(GLRenderbuffer *color,
GLRenderbuffer *depthStencil,
GLFramebuffer *fbo);
// Helpers for tests that don't care whether coherent or non-coherent framebuffer fetch is
// enabled, because they are testing something orthogonal to coherence. They only account for
// GL_EXT_shader_framebuffer_fetch and GL_EXT_shader_framebuffer_fetch_non_coherent, not the ARM
// variant or depth/stencil.
WhichExtension chooseBetweenCoherentOrIncoherent();
std::string makeShaderPreamble(WhichExtension whichExtension,
const char *otherExtensions,
uint32_t colorAttachmentCount);
bool mCoherentExtension;
bool mARMExtension;
bool mBothExtensions;
};
class FramebufferFetchAndAdvancedBlendES31 : public FramebufferFetchES31
{};
void FramebufferFetchES31::createFramebufferWithDepthStencil(GLRenderbuffer *color,
GLRenderbuffer *depthStencil,
GLFramebuffer *fbo)
{
glBindFramebuffer(GL_FRAMEBUFFER, *fbo);
glBindRenderbuffer(GL_RENDERBUFFER, *color);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *color);
glBindRenderbuffer(GL_RENDERBUFFER, *depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
*depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
}
FramebufferFetchES31::WhichExtension FramebufferFetchES31::chooseBetweenCoherentOrIncoherent()
{
const bool isCoherent = IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch");
EXPECT_TRUE(isCoherent || IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
return isCoherent ? COHERENT : NON_COHERENT;
}
std::string FramebufferFetchES31::makeShaderPreamble(WhichExtension whichExtension,
const char *otherExtensions,
uint32_t colorAttachmentCount)
{
std::ostringstream fs;
fs << "#version 310 es\n";
switch (whichExtension)
{
case COHERENT:
fs << "#extension GL_EXT_shader_framebuffer_fetch : require\n";
break;
case NON_COHERENT:
fs << "#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n";
break;
default:
UNREACHABLE();
break;
}
if (otherExtensions != nullptr)
{
fs << otherExtensions << "\n";
}
for (uint32_t location = 0; location < colorAttachmentCount; ++location)
{
fs << "layout(";
if (whichExtension == NON_COHERENT)
{
fs << "noncoherent, ";
}
fs << "location = " << location << ") inout highp vec4 color" << location << ";\n";
}
return fs.str();
}
// Test coherent extension with inout qualifier
TEST_P(FramebufferFetchES31, BasicInout_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Test non-coherent extension with inout qualifier
TEST_P(FramebufferFetchES31, BasicInout_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Test coherent extension with gl_LastFragData
TEST_P(FramebufferFetchES31, BasicLastFragData_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Test non-coherent extension with gl_LastFragData
TEST_P(FramebufferFetchES31, BasicLastFragData_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Testing coherent extension with multiple render target, using gl_FragData with constant indices
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Coherent_FragData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_4ATTACHMENT);
}
// Testing coherent extension with multiple render target, using gl_FragData with complex
// expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Coherent_FragData_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_COMPLEX);
}
// Testing coherent extension with multiple render target, using inouts with complex expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Coherent_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_COMPLEX);
}
// Testing coherent extension with multiple render target
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_4ATTACHMENT);
}
// Testing non-coherent extension with multiple render target, using gl_FragData with constant
// indices
TEST_P(FramebufferFetchES31, MultipleRenderTarget_NonCoherent_FragData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_4ATTACHMENT);
}
// Testing non-coherent extension with multiple render target, using gl_FragData with complex
// expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_NonCoherent_FragData_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_COMPLEX);
}
// Testing non-coherent extension with multiple render target, using inouts with complex expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_NonCoherent_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_COMPLEX);
}
// Testing non-coherent extension with multiple render target
TEST_P(FramebufferFetchES31, MultipleRenderTarget_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_4ATTACHMENT);
}
// Testing non-coherent extension with multiple render target using inout array
TEST_P(FramebufferFetchES31, MultipleRenderTargetWithInoutArray_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_4ATTACHMENT);
}
// Testing coherent extension with multiple render target using inout array
TEST_P(FramebufferFetchES31, MultipleRenderTargetWithInoutArray_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_4ATTACHMENT);
}
// Test coherent extension with multiple draw
TEST_P(FramebufferFetchES31, MultipleDraw_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleDrawTest(program);
}
// Test non-coherent extension with multiple draw
TEST_P(FramebufferFetchES31, MultipleDraw_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleDrawTest(program);
}
// Testing coherent extension with the order of non-fetch program and fetch program
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetch_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchTest(programNonFetch, programFetch);
}
// Testing non-coherent extension with the order of non-fetch program and fetch program
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetch_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchTest(programNonFetch, programFetch);
}
// Testing coherent extension with the order of fetch program and non-fetch program
TEST_P(FramebufferFetchES31, DrawFetchDrawNonFetch_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawFetchDrawNonFetchTest(programNonFetch, programFetch);
}
// Testing non-coherent extension with the order of fetch program and non-fetch program
TEST_P(FramebufferFetchES31, DrawFetchDrawNonFetch_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawFetchDrawNonFetchTest(programNonFetch, programFetch);
}
// Testing coherent extension with framebuffer fetch read in combination with color attachment mask
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBuffer_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Nothing);
}
// Testing non-coherent extension with framebuffer fetch read in combination with color attachment
// mask
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBuffer_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Nothing);
}
// Testing coherent extension with the order of non-fetch program and fetch program with
// different attachments
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchWithDifferentAttachments_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchWithDifferentAttachmentsTest(programNonFetch, programFetch);
}
// Testing coherent extension with framebuffer fetch read in combination with color attachment mask
// and clear
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBufferThenClear_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Clear);
}
// Testing non-coherent extension with framebuffer fetch read in combination with color attachment
// mask and clear
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBufferThenClear_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Clear);
}
// Testing non-coherent extension with the order of non-fetch program and fetch program with
// different attachments
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchWithDifferentAttachments_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchWithDifferentAttachmentsTest(programNonFetch, programFetch);
}
// Testing coherent extension with the order of non-fetch program and fetch with different
// programs
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchWithDifferentPrograms_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programNonFetch, programFetch1, programFetch2;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch1.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
programFetch2.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT2));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchWithDifferentProgramsTest(programNonFetch, programFetch1, programFetch2);
}
// Testing non-coherent extension with the order of non-fetch program and fetch with different
// programs
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchWithDifferentPrograms_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programNonFetch, programFetch1, programFetch2;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch1.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
programFetch2.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT2));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchWithDifferentProgramsTest(programNonFetch, programFetch1, programFetch2);
}
// Testing coherent extension with two fetch programs using different attachments. The different
// sets of attachments start at different non-zero indices.
TEST_P(FramebufferFetchES31, DrawFetchWithDifferentIndicesInSameRenderPass_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programFetch1, programFetch2;
programFetch1.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT3));
programFetch2.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT4));
ASSERT_GL_NO_ERROR();
DrawFetchWithDifferentIndicesInSameRenderPassTest(programFetch1, programFetch2);
}
// Testing non-coherent extension with two fetch programs using different attachments. The
// different sets of attachments start at different non-zero indices.
TEST_P(FramebufferFetchES31, DrawFetchWithDifferentIndicesInSameRenderPass_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programFetch1, programFetch2;
programFetch1.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT3));
programFetch2.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT4));
ASSERT_GL_NO_ERROR();
DrawFetchWithDifferentIndicesInSameRenderPassTest(programFetch1, programFetch2);
}
// Testing coherent extension with the order of draw fetch, blit and draw fetch
TEST_P(FramebufferFetchES31, DrawFetchBlitDrawFetch_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
ASSERT_GL_NO_ERROR();
DrawFetchBlitDrawFetchTest(programNonFetch, programFetch);
}
// Testing non-coherent extension with the order of draw fetch, blit and draw fetch
TEST_P(FramebufferFetchES31, DrawFetchBlitDrawFetch_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_4ATTACHMENT_DIFFERENT1));
ASSERT_GL_NO_ERROR();
DrawFetchBlitDrawFetchTest(programNonFetch, programFetch);
}
// Testing coherent extension with program pipeline
TEST_P(FramebufferFetchES31, ProgramPipeline_Coherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
ProgramPipelineTest(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT),
getFragmentShader(GLSL310_1ATTACHMENT));
}
// Testing non-coherent extension with program pipeline
TEST_P(FramebufferFetchES31, ProgramPipeline_NonCoherent)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
setWhichExtension(NON_COHERENT);
ProgramPipelineTest(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT),
getFragmentShader(GLSL310_1ATTACHMENT));
}
// Verify that sample shading is automatically enabled when framebuffer fetch is used with
// multisampling.
TEST_P(FramebufferFetchES31, MultiSampled)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_sample_variables"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
// Create a single-sampled framebuffer as the resolve target
GLRenderbuffer resolve;
glBindRenderbuffer(GL_RENDERBUFFER, resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_FRAMEBUFFER, resolveFbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolve);
// Create a multisampled framebuffer
GLRenderbuffer rbo;
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo);
// Initialize every sample differently with per-sample shading.
constexpr char kPrimeFS[] = R"(#version 310 es
#extension GL_OES_sample_variables : require
out highp vec4 color;
void main (void)
{
switch (gl_SampleID)
{
case 0:
color = vec4(1.0, 0.9, 0.8, 0.7);
break;
case 1:
color = vec4(0.0, 0.1, 0.2, 0.3);
break;
case 2:
color = vec4(0.5, 0.25, 0.75, 1.0);
break;
default:
color = vec4(0.4, 0.6, 0.2, 0.8);
break;
}
})";
ANGLE_GL_PROGRAM(prime, essl31_shaders::vs::Passthrough(), kPrimeFS);
glViewport(0, 0, kViewportWidth, kViewportHeight);
drawQuad(prime, essl31_shaders::PositionAttrib(), 0.0f);
// Break the render pass to make sure sample shading is not left enabled by accident.
// The expected value is the average of the values set by the shader.
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_NEAR(0, 0, 121, 118, 124, 178, 1);
ASSERT_GL_NO_ERROR();
// Use framebuffer fetch to read the value of each sample, and store the square of that value.
// Because square is non-linear, applied to the average value it would produce a different
// result compared with it being applied to individual samples and then averaged. The test thus
// ensures that framebuffer fetch on a multisampled framebuffer implicitly enables sample
// shading.
std::ostringstream fs;
fs << makeShaderPreamble(whichExtension, nullptr, 1);
fs << R"(void main()
{
color0 *= color0;
})";
ANGLE_GL_PROGRAM(square, essl31_shaders::vs::Passthrough(), fs.str().c_str());
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
drawQuad(square, essl31_shaders::PositionAttrib(), 0.0f);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
// Verify that the result is average(square(samples)) and not square(average(samples)).
EXPECT_PIXEL_NEAR(0, 0, 90, 79, 82, 141, 1);
// For debugging purposes, the following would be true if framebuffer fetch _didn't_ implicitly
// enable sample shading.
// EXPECT_PIXEL_NEAR(0, 0, 57, 54, 60, 125, 1);
ASSERT_GL_NO_ERROR();
}
// Test recovering a supposedly closed render pass that used framebuffer fetch.
TEST_P(FramebufferFetchES31, ReopenRenderPass)
{
const bool isCoherent = IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch");
ANGLE_SKIP_TEST_IF(!isCoherent &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
// Create two framebuffers
GLRenderbuffer color[2];
GLFramebuffer fbo[2];
for (uint32_t i = 0; i < 2; ++i)
{
glBindRenderbuffer(GL_RENDERBUFFER, color[i]);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glBindFramebuffer(GL_FRAMEBUFFER, fbo[i]);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color[i]);
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Use a framebuffer fetch program.
std::ostringstream fs;
fs << "#version 310 es\n";
if (isCoherent)
{
fs << "#extension GL_EXT_shader_framebuffer_fetch : require\n";
}
else
{
fs << "#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n";
fs << "layout(noncoherent) ";
}
fs << R"(inout highp vec4 color;
void main (void)
{
color += vec4(0.25, 0.125, 0.5, 0.0);
})";
ANGLE_GL_PROGRAM(ff, essl31_shaders::vs::Passthrough(), fs.str().c_str());
drawQuad(ff, essl31_shaders::PositionAttrib(), 0.0f);
// Switch to another framebuffer and do a clear. In the Vulkan backend, the previous render
// pass stays around.
glBindFramebuffer(GL_FRAMEBUFFER, fbo[1]);
glClearColor(1.0f, 0.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// Switch back to the original framebuffer and do a non-framebuffer fetch draw
ANGLE_GL_PROGRAM(drawRed, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
glBindFramebuffer(GL_FRAMEBUFFER, fbo[0]);
glEnable(GL_SCISSOR_TEST);
glScissor(kViewportWidth / 2, kViewportHeight / 2, kViewportWidth - kViewportWidth / 2,
kViewportHeight - kViewportHeight / 2);
drawQuad(drawRed, essl31_shaders::PositionAttrib(), 0.0f);
// Verify the results
EXPECT_PIXEL_NEAR(0, 0, 191, 159, 255, 255, 1);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth - 1, kViewportHeight - 1, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test opening a render pass with a scissored clear
TEST_P(FramebufferFetchES31, StartWithScissoredClear)
{
const bool isCoherent = IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch");
ANGLE_SKIP_TEST_IF(!isCoherent &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
std::ostringstream fs;
fs << "#version 310 es\n";
if (isCoherent)
{
fs << "#extension GL_EXT_shader_framebuffer_fetch : require\n";
}
else
{
fs << "#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n";
fs << "layout(noncoherent) ";
}
fs << R"(inout highp vec4 color;
void main (void)
{
color += vec4(0.25, 0.125, 0.5, 0.0);
})";
ANGLE_GL_PROGRAM(ff, kVS, fs.str().c_str());
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLRenderbuffer color;
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
// Draw once for the program to be processed, so the draw after clear would not have executable
// related dirty bits.
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(ff);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Clear value (0.5, 0.5, 0.5, 1.0f) + shader's addition (0.25, 0.125, 0.5, 0.0)
EXPECT_PIXEL_NEAR(0, 0, 191, 159, 255, 255, 1);
// Start rendering with a scissored clear, then do a draw call
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kViewportWidth / 2, kViewportHeight / 2);
glClearColor(0.125f, 0.75f, 0.25f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
if (!isCoherent)
{
glFramebufferFetchBarrierEXT();
}
// Don't use drawQuad, as it reinstalls the program, adding additional dirty bits.
glDrawArrays(GL_TRIANGLES, 0, 3);
// Verify the results
// Clear value (0.125, 0.75, 0.25, 1.0f) + shader's addition (0.25, 0.125, 0.5, 0.0)
EXPECT_PIXEL_NEAR(0, 0, 96, 223, 191, 255, 1);
// The rest of the image should be left untouched due to scissor
EXPECT_PIXEL_NEAR(kViewportWidth - 1, kViewportHeight - 1, 191, 159, 255, 255, 1);
ASSERT_GL_NO_ERROR();
}
// Test opening a render pass with a masked clear
TEST_P(FramebufferFetchES31, StartWithMaskedClear)
{
const bool isCoherent = IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch");
ANGLE_SKIP_TEST_IF(!isCoherent &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
std::ostringstream fs;
fs << "#version 310 es\n";
if (isCoherent)
{
fs << "#extension GL_EXT_shader_framebuffer_fetch : require\n";
}
else
{
fs << "#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require\n";
fs << "layout(noncoherent) ";
}
fs << R"(inout highp vec4 color;
void main (void)
{
color += vec4(0.25, 0.125, 0.5, 0.0);
})";
ANGLE_GL_PROGRAM(ff, kVS, fs.str().c_str());
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLRenderbuffer color;
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
// Draw once for the program to be processed, so the draw after clear would not have executable
// related dirty bits.
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glUseProgram(ff);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Clear value (0.5, 0.5, 0.5, 1.0f) + shader's addition (0.25, 0.125, 0.5, 0.0)
EXPECT_PIXEL_NEAR(0, 0, 191, 159, 255, 255, 1);
// Start rendering with a scissored clear, then do a draw call
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kViewportWidth / 2, kViewportHeight / 2);
glClearColor(0.125f, 0.75f, 0.25f, 1.0f);
glColorMask(GL_TRUE, GL_FALSE, GL_FALSE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
if (!isCoherent)
{
glFramebufferFetchBarrierEXT();
}
// Don't use drawQuad, as it reinstalls the program, adding additional dirty bits.
glDrawArrays(GL_TRIANGLES, 0, 3);
// Verify the results
// Clear value (0.125, 0.75, 0.25, 1.0f) + shader's addition (0.25, 0.125, 0.5, 0.0)
EXPECT_PIXEL_NEAR(0, 0, 96, 191, 255, 255, 1);
// The rest of the image should be left untouched due to scissor
EXPECT_PIXEL_NEAR(kViewportWidth - 1, kViewportHeight - 1, 191, 159, 255, 255, 1);
ASSERT_GL_NO_ERROR();
}
// Test combination of inout and samplers.
TEST_P(FramebufferFetchES31, UniformUsageCombinations)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 a_position;
out highp vec2 texCoord;
void main()
{
gl_Position = a_position;
texCoord = (a_position.xy * 0.5) + 0.5;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(binding=0, offset=0) uniform atomic_uint atDiff;
uniform sampler2D tex;
layout(noncoherent, location = 0) inout highp vec4 o_color[4];
in highp vec2 texCoord;
void main()
{
highp vec4 texColor = texture(tex, texCoord);
if (texColor != o_color[0])
{
atomicCounterIncrement(atDiff);
o_color[0] = texColor;
}
else
{
if (atomicCounter(atDiff) > 0u)
{
atomicCounterDecrement(atDiff);
}
}
if (texColor != o_color[1])
{
atomicCounterIncrement(atDiff);
o_color[1] = texColor;
}
else
{
if (atomicCounter(atDiff) > 0u)
{
atomicCounterDecrement(atDiff);
}
}
if (texColor != o_color[2])
{
atomicCounterIncrement(atDiff);
o_color[2] = texColor;
}
else
{
if (atomicCounter(atDiff) > 0u)
{
atomicCounterDecrement(atDiff);
}
}
if (texColor != o_color[3])
{
atomicCounterIncrement(atDiff);
o_color[3] = texColor;
}
else
{
if (atomicCounter(atDiff) > 0u)
{
atomicCounterDecrement(atDiff);
}
}
})";
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> color0(kViewportWidth * kViewportHeight, GLColor::cyan);
std::vector<GLColor> color1(kViewportWidth * kViewportHeight, GLColor::green);
std::vector<GLColor> color2(kViewportWidth * kViewportHeight, GLColor::blue);
std::vector<GLColor> color3(kViewportWidth * kViewportHeight, GLColor::black);
GLTexture colorBufferTex[kMaxColorBuffer];
GLenum colorAttachments[kMaxColorBuffer] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,
GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3};
glBindTexture(GL_TEXTURE_2D, colorBufferTex[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color0.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color1.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color2.data());
glBindTexture(GL_TEXTURE_2D, colorBufferTex[3]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, color3.data());
glBindTexture(GL_TEXTURE_2D, 0);
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glFramebufferTexture2D(GL_FRAMEBUFFER, colorAttachments[i], GL_TEXTURE_2D,
colorBufferTex[i], 0);
}
glDrawBuffers(kMaxColorBuffer, &colorAttachments[0]);
ASSERT_GL_NO_ERROR();
GLBuffer atomicBuffer;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicBuffer);
glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_DYNAMIC_DRAW);
// Reset atomic counter buffer
GLuint *userCounters;
userCounters = static_cast<GLuint *>(glMapBufferRange(
GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint),
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT));
memset(userCounters, 0, sizeof(GLuint));
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, atomicBuffer);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
float color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, GL_TRUE);
ASSERT_GL_NO_ERROR();
// Because no texture is bound, the shader samples black, increments the counter for every pixel
// and sets all attachments to black.
for (unsigned int i = 0; i < kMaxColorBuffer; i++)
{
glReadBuffer(colorAttachments[i]);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::black);
}
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicBuffer);
userCounters = static_cast<GLuint *>(
glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), GL_MAP_READ_BIT));
EXPECT_EQ(*userCounters, kViewportWidth * kViewportHeight * 2);
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Testing that binding the location value using GLES API is conflicted to the location value of the
// fragment inout.
TEST_P(FramebufferFetchES31, FixedUniformLocation)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch_non_coherent : require
layout(noncoherent, location = 0) inout highp vec4 o_color;
layout(location = 0) uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
std::vector<GLColor> greenColor(kViewportWidth * kViewportHeight, GLColor::green);
GLTexture colorBufferTex;
glBindTexture(GL_TEXTURE_2D, colorBufferTex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, greenColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex, 0);
ASSERT_GL_NO_ERROR();
float color[4] = {1.0f, 0.0f, 0.0f, 1.0f};
GLint colorLocation = glGetUniformLocation(program, "u_color");
glUniform4fv(colorLocation, 1, color);
GLint positionLocation = glGetAttribLocation(program, "a_position");
render(positionLocation, GL_TRUE);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// Verify we can use inout with the default framebuffer
// http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, DefaultFramebufferTest)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// Ensure that we're rendering to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Start with a clear buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
// Draw once with red
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
// Draw again with blue, adding it to the existing red, ending up with magenta
glUniform4fv(colorLocation, 1, GLColor::blue.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
}
// Verify we can render to the default framebuffer without fetch, then switch to a program
// that does fetch.
// http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, DefaultFramebufferMixedProgramsTest)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color;
})";
constexpr char kFetchFS[] = R"(#version 300 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
// Create a program that simply writes out a color, no fetching
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// Ensure that we're rendering to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Start with a clear buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
// Draw once with red
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
// Create another program that DOES fetch from the framebuffer
GLProgram program2;
program2.makeRaster(kVS, kFetchFS);
glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "a_position");
GLint colorLocation2 = glGetUniformLocation(program2, "u_color");
// Draw again with blue, fetching red from the framebuffer, adding it together
glUniform4fv(colorLocation2, 1, GLColor::blue.toNormalizedVector().data());
render(positionLocation2, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
// Switch back to the non-fetched framebuffer, and render green
glUseProgram(program);
glUniform4fv(colorLocation, 1, GLColor::green.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
ASSERT_GL_NO_ERROR();
}
// Verify we can render to a framebuffer with fetch, then switch to another framebuffer (without
// changing programs) http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, FramebufferMixedFetchTest)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color;
})";
constexpr char kFetchFS[] = R"(#version 300 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color += u_color;
})";
// Create a program that simply writes out a color, no fetching
GLProgram program;
program.makeRaster(kVS, kFS);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
ASSERT_GL_NO_ERROR();
// Create a program that DOES fetch from the framebuffer
GLProgram fetchProgram;
fetchProgram.makeRaster(kVS, kFetchFS);
GLint fetchPositionLocation = glGetAttribLocation(fetchProgram, "a_position");
GLint fetchColorLocation = glGetUniformLocation(fetchProgram, "u_color");
ASSERT_GL_NO_ERROR();
// Create an empty framebuffer to use without fetch
GLFramebuffer framebuffer1;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
std::vector<GLColor> clearColor(kViewportWidth * kViewportHeight, GLColor::transparentBlack);
GLTexture colorBufferTex1;
glBindTexture(GL_TEXTURE_2D, colorBufferTex1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, clearColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex1, 0);
ASSERT_GL_NO_ERROR();
// Draw to it with green, without using fetch, overwriting any contents
glUseProgram(program);
glUniform4fv(colorLocation, 1, GLColor::green.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
ASSERT_GL_NO_ERROR();
// Create another framebuffer to use WITH fetch, and initialize it with blue
GLFramebuffer framebuffer2;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
std::vector<GLColor> blueColor(kViewportWidth * kViewportHeight, GLColor::blue);
GLTexture colorBufferTex2;
glBindTexture(GL_TEXTURE_2D, colorBufferTex2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, blueColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex2, 0);
ASSERT_GL_NO_ERROR();
// Draw once with red, fetching blue from the framebuffer, adding it together
glUseProgram(fetchProgram);
glUniform4fv(fetchColorLocation, 1, GLColor::red.toNormalizedVector().data());
render(fetchPositionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
// Now use the same program (WITH fetch) and render to the other framebuffer that was NOT used
// with fetch. This verifies the framebuffer state is appropriately updated to match the
// program.
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
render(fetchPositionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
ASSERT_GL_NO_ERROR();
}
// Verify that switching between single sampled framebuffer fetch and multi sampled framebuffer
// fetch works fine
TEST_P(FramebufferFetchES31, SingleSampledMultiSampledMixedTest)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(COHERENT);
// Create a program that fetches from the framebuffer
GLProgram fetchProgram;
fetchProgram.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
GLint positionLocation = glGetAttribLocation(fetchProgram, "a_position");
GLint colorLocation = glGetUniformLocation(fetchProgram, "u_color");
ASSERT_GL_NO_ERROR();
// Create two single sampled framebuffer
GLRenderbuffer singleSampledRenderbuffer1;
glBindRenderbuffer(GL_RENDERBUFFER, singleSampledRenderbuffer1);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer singleSampledFramebuffer1;
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
singleSampledRenderbuffer1);
GLRenderbuffer singleSampledRenderbuffer2;
glBindRenderbuffer(GL_RENDERBUFFER, singleSampledRenderbuffer2);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer singleSampledFramebuffer2;
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
singleSampledRenderbuffer2);
// Create one multi sampled framebuffer
GLRenderbuffer multiSampledRenderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, multiSampledRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer multiSampledFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
multiSampledRenderbuffer);
// Create a singlesampled render buffer for blit and read
GLRenderbuffer resolvedRbo;
glBindRenderbuffer(GL_RENDERBUFFER, resolvedRbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer resolvedFbo;
glBindFramebuffer(GL_FRAMEBUFFER, resolvedFbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolvedRbo);
// Clear three Framebuffers with different colors
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::black);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
// Bind first single sampled framebuffer, draw once with red, fetching black from the
// framebuffer
glUseProgram(fetchProgram);
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
render(positionLocation, false);
ASSERT_GL_NO_ERROR();
// Bind the multi sampled framebuffer, draw once with red, fetching green from the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
render(positionLocation, false);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
ASSERT_GL_NO_ERROR();
// Bind the single sampled framebuffer, draw once with red, fetching blue from the framebuffer
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
render(positionLocation, false);
ASSERT_GL_NO_ERROR();
// Verify the rendering result on all three framebuffers
// Verify the last framebuffer being drawn: singleSampledFramebuffer2
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
// Verify the second last framebuffer being drawn: multisampledFramebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, multiSampledFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
// Verify the first framebuffer being drawn: singleSampledFramebuffer1
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
}
// Verify that calling glFramebufferFetchBarrierEXT without an open render pass is ok.
TEST_P(FramebufferFetchES31, BarrierBeforeDraw)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") ||
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
glFramebufferFetchBarrierEXT();
drawQuad(program, essl1_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// Test ARM extension with gl_LastFragColorARM
TEST_P(FramebufferFetchES31, BasicLastFragData_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Test ARM extension with multiple draw
TEST_P(FramebufferFetchES31, MultipleDraw_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleDrawTest(program);
}
// Testing ARM extension with the order of non-fetch program and fetch program
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetch_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchTest(programNonFetch, programFetch);
}
// Testing ARM extension with the order of fetch program and non-fetch program
TEST_P(FramebufferFetchES31, DrawFetchDrawNonFetch_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawFetchDrawNonFetchTest(programNonFetch, programFetch);
}
// Testing ARM extension with framebuffer fetch read in combination with color attachment mask
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBuffer_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Nothing);
}
// Testing ARM extension with framebuffer fetch read in combination with color attachment mask
// and clear
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetchInStorageBufferThenClear_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
GLint maxFragmentShaderStorageBlocks = 0;
glGetIntegerv(GL_MAX_FRAGMENT_SHADER_STORAGE_BLOCKS, &maxFragmentShaderStorageBlocks);
ANGLE_SKIP_TEST_IF(maxFragmentShaderStorageBlocks == 0);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT_WITH_STORAGE_BUFFER));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchInStorageBufferTest(programNonFetch, programFetch,
StorageBufferTestPostFetchAction::Clear);
}
// Testing ARM extension with program pipeline
TEST_P(FramebufferFetchES31, ProgramPipeline_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
ProgramPipelineTest(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT),
getFragmentShader(GLSL310_1ATTACHMENT));
}
// Verify we can use the default framebuffer
// http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, DefaultFramebufferTest_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
#extension GL_ARM_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + gl_LastFragColorARM;
})";
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// Ensure that we're rendering to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Start with a clear buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
// Draw once with red
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
// Draw again with blue, adding it to the existing red, ending up with magenta
glUniform4fv(colorLocation, 1, GLColor::blue.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
}
// Verify we can redeclare gl_LastFragColorARM with a new precision
// http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, NondefaultPrecisionTest_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
#extension GL_ARM_shader_framebuffer_fetch : require
highp vec4 gl_LastFragColorARM;
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + gl_LastFragColorARM;
})";
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// Ensure that we're rendering to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Start with a clear buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
// Draw once with red
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
// Draw again with blue, adding it to the existing red, ending up with magenta
glUniform4fv(colorLocation, 1, GLColor::blue.toNormalizedVector().data());
render(positionLocation, GL_FALSE);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
}
// Verify we can render to the default framebuffer without fetch, then switch to a program
// that does fetch.
// http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, DefaultFramebufferMixedProgramsTest_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color;
})";
constexpr char kFetchFS[] = R"(#version 300 es
#extension GL_ARM_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + gl_LastFragColorARM;
})";
// Create a program that simply writes out a color, no fetching
GLProgram program;
program.makeRaster(kVS, kFS);
glUseProgram(program);
ASSERT_GL_NO_ERROR();
// Ensure that we're rendering to the default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Start with a clear buffer
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
// Draw once with red
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
ASSERT_GL_NO_ERROR();
// Create another program that DOES fetch from the framebuffer
GLProgram program2;
program2.makeRaster(kVS, kFetchFS);
glUseProgram(program2);
GLint positionLocation2 = glGetAttribLocation(program2, "a_position");
GLint colorLocation2 = glGetUniformLocation(program2, "u_color");
// Draw again with blue, fetching red from the framebuffer, adding it together
glUniform4fv(colorLocation2, 1, GLColor::blue.toNormalizedVector().data());
render(positionLocation2, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
// Switch back to the non-fetched framebuffer, and render green
glUseProgram(program);
glUniform4fv(colorLocation, 1, GLColor::green.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
ASSERT_GL_NO_ERROR();
}
// Verify we can render to a framebuffer with fetch, then switch to another framebuffer (without
// changing programs) http://anglebug.com/42265386
TEST_P(FramebufferFetchES31, FramebufferMixedFetchTest_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 300 es
in highp vec4 a_position;
void main (void)
{
gl_Position = a_position;
})";
constexpr char kFS[] = R"(#version 300 es
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color;
})";
constexpr char kFetchFS[] = R"(#version 300 es
#extension GL_ARM_shader_framebuffer_fetch : require
layout(location = 0) out highp vec4 o_color;
uniform highp vec4 u_color;
void main (void)
{
o_color = u_color + gl_LastFragColorARM;
})";
// Create a program that simply writes out a color, no fetching
GLProgram program;
program.makeRaster(kVS, kFS);
GLint positionLocation = glGetAttribLocation(program, "a_position");
GLint colorLocation = glGetUniformLocation(program, "u_color");
ASSERT_GL_NO_ERROR();
// Create a program that DOES fetch from the framebuffer
GLProgram fetchProgram;
fetchProgram.makeRaster(kVS, kFetchFS);
GLint fetchPositionLocation = glGetAttribLocation(fetchProgram, "a_position");
GLint fetchColorLocation = glGetUniformLocation(fetchProgram, "u_color");
ASSERT_GL_NO_ERROR();
// Create an empty framebuffer to use without fetch
GLFramebuffer framebuffer1;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
std::vector<GLColor> clearColor(kViewportWidth * kViewportHeight, GLColor::transparentBlack);
GLTexture colorBufferTex1;
glBindTexture(GL_TEXTURE_2D, colorBufferTex1);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, clearColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex1, 0);
ASSERT_GL_NO_ERROR();
// Draw to it with green, without using fetch, overwriting any contents
glUseProgram(program);
glUniform4fv(colorLocation, 1, GLColor::green.toNormalizedVector().data());
render(positionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
ASSERT_GL_NO_ERROR();
// Create another framebuffer to use WITH fetch, and initialize it with blue
GLFramebuffer framebuffer2;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer2);
std::vector<GLColor> blueColor(kViewportWidth * kViewportHeight, GLColor::blue);
GLTexture colorBufferTex2;
glBindTexture(GL_TEXTURE_2D, colorBufferTex2);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kViewportWidth, kViewportHeight, 0, GL_RGBA,
GL_UNSIGNED_BYTE, blueColor.data());
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorBufferTex2, 0);
ASSERT_GL_NO_ERROR();
// Draw once with red, fetching blue from the framebuffer, adding it together
glUseProgram(fetchProgram);
glUniform4fv(fetchColorLocation, 1, GLColor::red.toNormalizedVector().data());
render(fetchPositionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
ASSERT_GL_NO_ERROR();
// Now use the same program (WITH fetch) and render to the other framebuffer that was NOT used
// with fetch. This verifies the framebuffer state is appropriately updated to match the
// program.
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer1);
render(fetchPositionLocation, false);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
ASSERT_GL_NO_ERROR();
}
// Verify that switching between single sampled framebuffer fetch and multi sampled framebuffer
// fetch works fine
TEST_P(FramebufferFetchES31, SingleSampledMultiSampledMixedTest_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
setWhichExtension(ARM);
// Create a program that fetches from the framebuffer
GLProgram fetchProgram;
fetchProgram.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
GLint positionLocation = glGetAttribLocation(fetchProgram, "a_position");
GLint colorLocation = glGetUniformLocation(fetchProgram, "u_color");
ASSERT_GL_NO_ERROR();
// Create two single sampled framebuffer
GLRenderbuffer singleSampledRenderbuffer1;
glBindRenderbuffer(GL_RENDERBUFFER, singleSampledRenderbuffer1);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer singleSampledFramebuffer1;
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
singleSampledRenderbuffer1);
GLRenderbuffer singleSampledRenderbuffer2;
glBindRenderbuffer(GL_RENDERBUFFER, singleSampledRenderbuffer2);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer singleSampledFramebuffer2;
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
singleSampledRenderbuffer2);
// Create one multi sampled framebuffer
GLRenderbuffer multiSampledRenderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, multiSampledRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer multiSampledFramebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
multiSampledRenderbuffer);
// Create a singlesampled render buffer for blit and read
GLRenderbuffer resolvedRbo;
glBindRenderbuffer(GL_RENDERBUFFER, resolvedRbo);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer resolvedFbo;
glBindFramebuffer(GL_FRAMEBUFFER, resolvedFbo);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolvedRbo);
// Clear three Framebuffers with different colors
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::black);
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
glClear(GL_COLOR_BUFFER_BIT);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::blue);
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
glClear(GL_COLOR_BUFFER_BIT);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::green);
// Bind first single sampled framebuffer, draw once with red, fetching black from the
// framebuffer
glUseProgram(fetchProgram);
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
render(positionLocation, false);
ASSERT_GL_NO_ERROR();
// Bind the multi sampled framebuffer, draw once with red, fetching green from the framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, multiSampledFramebuffer);
render(positionLocation, false);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
ASSERT_GL_NO_ERROR();
// Bind the single sampled framebuffer, draw once with red, fetching blue from the framebuffer
glUniform4fv(colorLocation, 1, GLColor::red.toNormalizedVector().data());
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer2);
render(positionLocation, false);
ASSERT_GL_NO_ERROR();
// Verify the rendering result on all three framebuffers
// Verify the last framebuffer being drawn: singleSampledFramebuffer2
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::magenta);
// Verify the second last framebuffer being drawn: multisampledFramebuffer
glBindFramebuffer(GL_READ_FRAMEBUFFER, multiSampledFramebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolvedFbo);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolvedFbo);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::yellow);
// Verify the first framebuffer being drawn: singleSampledFramebuffer1
glBindFramebuffer(GL_FRAMEBUFFER, singleSampledFramebuffer1);
EXPECT_PIXEL_COLOR_EQ(kViewportWidth / 2, kViewportHeight / 2, GLColor::red);
}
// Test ARM extension with new tokens
TEST_P(FramebufferFetchES31, BasicTokenUsage_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
// GL_FETCH_PER_SAMPLE_ARM can be set and queried
GLboolean isFetchPerSampleEnabledBool = false;
GLint isFetchPerSampleEnabledInt = -1;
GLfloat isFetchPerSampleEnabledFloat = -1.0f;
// Set GL_FETCH_PER_SAMPLE_ARM true
glEnable(GL_FETCH_PER_SAMPLE_ARM);
EXPECT_GL_TRUE(glIsEnabled(GL_FETCH_PER_SAMPLE_ARM));
// Ensure it returns true
glGetBooleanv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledBool);
EXPECT_GL_TRUE(isFetchPerSampleEnabledBool);
glGetIntegerv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledInt);
ASSERT_EQ(isFetchPerSampleEnabledInt, 1);
glGetFloatv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledFloat);
ASSERT_EQ(isFetchPerSampleEnabledFloat, 1.0);
// Set GL_FETCH_PER_SAMPLE_ARM false
glDisable(GL_FETCH_PER_SAMPLE_ARM);
EXPECT_GL_FALSE(glIsEnabled(GL_FETCH_PER_SAMPLE_ARM));
// Ensure it returns false
glGetBooleanv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledBool);
EXPECT_GL_FALSE(isFetchPerSampleEnabledBool);
glGetIntegerv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledInt);
ASSERT_EQ(isFetchPerSampleEnabledInt, 0);
glGetFloatv(GL_FETCH_PER_SAMPLE_ARM, &isFetchPerSampleEnabledFloat);
ASSERT_EQ(isFetchPerSampleEnabledFloat, 0.0);
ASSERT_GL_NO_ERROR();
// GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM can only be queried
GLboolean isFragmentShaderFramebufferFetchMrtBool = false;
GLint isFragmentShaderFramebufferFetchMrtInt = -1;
GLfloat isFragmentShaderFramebufferFetchMrtFloat = -1.0f;
// Try to set it, ensure we can't
glEnable(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
glDisable(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Ensure we can't query its state with isEnabled
// Commented out due to http://anglebug.com/42266484
// glIsEnabled(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM);
// EXPECT_GL_ERROR(GL_INVALID_ENUM);
// Ensure GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM returns consistent values
glGetBooleanv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrtBool);
glGetIntegerv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrtInt);
ASSERT_EQ(isFragmentShaderFramebufferFetchMrtInt,
static_cast<GLint>(isFragmentShaderFramebufferFetchMrtBool));
glGetFloatv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrtFloat);
ASSERT_EQ(isFragmentShaderFramebufferFetchMrtFloat,
static_cast<GLfloat>(isFragmentShaderFramebufferFetchMrtBool));
ASSERT_GL_NO_ERROR();
}
// Test that depth/stencil framebuffer fetch with early_fragment_tests is disallowed
TEST_P(FramebufferFetchES31, NoEarlyFragmentTestsWithDepthStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kDepthFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
layout(early_fragment_tests) in;
highp out vec4 color;
void main()
{
color = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
GLuint shader = CompileShader(GL_FRAGMENT_SHADER, kDepthFS);
EXPECT_EQ(0u, shader);
const char kStencilFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
layout(early_fragment_tests) in;
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0xE5;
color = vec4(correct, 0, 0, 1);
})";
shader = CompileShader(GL_FRAGMENT_SHADER, kStencilFS);
EXPECT_EQ(0u, shader);
}
// Test using both extensions simultaneously with gl_LastFragData and gl_LastFragColorARM
TEST_P(FramebufferFetchES31, BasicLastFragData_Both)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(BOTH);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
BasicTest(program);
}
// Test using both extentions simultaneously with multiple draw
TEST_P(FramebufferFetchES31, MultipleDraw_Both)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(BOTH);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleDrawTest(program);
}
// Testing using both extentions simultaneously with the order of non-fetch program and fetch
// program
TEST_P(FramebufferFetchES31, DrawNonFetchDrawFetch_Both)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(BOTH);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawNonFetchDrawFetchTest(programNonFetch, programFetch);
}
// Testing using both extentions simultaneously with the order of fetch program and non-fetch
// program
TEST_P(FramebufferFetchES31, DrawFetchDrawNonFetch_Both)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
setWhichExtension(BOTH);
GLProgram programNonFetch, programFetch;
programNonFetch.makeRaster(k310VS, getFragmentShader(GLSL310_NO_FETCH_1ATTACHMENT));
programFetch.makeRaster(k310VS, getFragmentShader(GLSL310_1ATTACHMENT));
ASSERT_GL_NO_ERROR();
DrawFetchDrawNonFetchTest(programNonFetch, programFetch);
}
// Testing using both extentions simultaneously with multiple render target, using gl_FragData with
// constant indices
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Both_FragData)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
GLboolean isFragmentShaderFramebufferFetchMrt = false;
glGetBooleanv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrt);
ANGLE_SKIP_TEST_IF(!isFragmentShaderFramebufferFetchMrt);
setWhichExtension(BOTH);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_4ATTACHMENT));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_4ATTACHMENT);
}
// Testing using both extentions simultaneously with multiple render target, using gl_FragData with
// complex expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Both_FragData_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_draw_buffers"));
GLboolean isFragmentShaderFramebufferFetchMrt = false;
glGetBooleanv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrt);
ANGLE_SKIP_TEST_IF(!isFragmentShaderFramebufferFetchMrt);
setWhichExtension(BOTH);
GLProgram program;
program.makeRaster(k100VS, getFragmentShader(GLSL100_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL100_COMPLEX);
}
// Testing using both extentions simultaneously with multiple render target, using inouts with
// complex expressions
TEST_P(FramebufferFetchES31, MultipleRenderTarget_Both_Complex)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
GLboolean isFragmentShaderFramebufferFetchMrt = false;
glGetBooleanv(GL_FRAGMENT_SHADER_FRAMEBUFFER_FETCH_MRT_ARM,
&isFragmentShaderFramebufferFetchMrt);
ANGLE_SKIP_TEST_IF(!isFragmentShaderFramebufferFetchMrt);
setWhichExtension(BOTH);
GLProgram program;
program.makeRaster(k310VS, getFragmentShader(GLSL310_COMPLEX));
glUseProgram(program);
ASSERT_GL_NO_ERROR();
MultipleRenderTargetTest(program, GLSL310_COMPLEX);
}
// Test that using the maximum number of color attachments works.
TEST_P(FramebufferFetchES31, MaximumColorAttachments)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
GLint maxDrawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
std::vector<GLTexture> color(maxDrawBuffers);
std::vector<GLenum> buffers(maxDrawBuffers);
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
buffers[index] = GL_COLOR_ATTACHMENT0 + index;
glBindTexture(GL_TEXTURE_2D, color[index]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, buffers[index], GL_TEXTURE_2D, color[index], 0);
}
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glDrawBuffers(maxDrawBuffers, buffers.data());
// Create two programs, one to initialize the attachments and another to read back the contents
// with framebuffer fetch and blend.
std::ostringstream initFs;
initFs << "#version 310 es\n";
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
initFs << "layout(location=" << index << ") out highp vec4 color" << index << ";\n";
}
std::ostringstream fetchFs;
fetchFs << makeShaderPreamble(whichExtension, nullptr, maxDrawBuffers);
initFs << R"(void main()
{
)";
fetchFs << R"(void main()
{
)";
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
initFs << " color" << index << " = vec4(" << ((index % 5) / 8.0) << ", "
<< ((index % 4) / 6.0) << ", " << ((index % 3) / 4.0) << ", " << ((index % 2) / 2.0)
<< ");\n";
fetchFs << " color" << index << " += vec4(" << (((index + 1) % 2) / 2.0) << ", "
<< (((index + 1) % 3) / 4.0) << ", " << (((index + 1) % 4) / 6.0) << ", "
<< (((index + 1) % 5) / 8.0) << ");\n";
}
initFs << "}\n";
fetchFs << "}\n";
ANGLE_GL_PROGRAM(init, essl31_shaders::vs::Passthrough(), initFs.str().c_str());
ANGLE_GL_PROGRAM(fetch, essl31_shaders::vs::Passthrough(), fetchFs.str().c_str());
drawQuad(init, essl31_shaders::PositionAttrib(), 0.0f);
if (whichExtension == NON_COHERENT)
{
glFramebufferFetchBarrierEXT();
}
drawQuad(fetch, essl31_shaders::PositionAttrib(), 0.0f);
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
glReadBuffer(buffers[index]);
uint32_t expectR = (255 * (index % 5) + 4) / 8;
uint32_t expectG = (255 * (index % 4) + 3) / 6;
uint32_t expectB = (255 * (index % 3) + 2) / 4;
uint32_t expectA = (255 * (index % 2) + 1) / 2;
expectR += (255 * ((index + 1) % 2) + 1) / 2;
expectG += (255 * ((index + 1) % 3) + 2) / 4;
expectB += (255 * ((index + 1) % 4) + 3) / 6;
expectA += (255 * ((index + 1) % 5) + 4) / 8;
EXPECT_PIXEL_NEAR(0, 0, expectR, expectG, expectB, expectA, 2);
}
ASSERT_GL_NO_ERROR();
}
// Test that depth framebuffer fetch works.
TEST_P(FramebufferFetchES31, Depth)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
color = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
GLRenderbuffer color, depth;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
glBindRenderbuffer(GL_RENDERBUFFER, depth);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depth);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.4f);
glClear(GL_DEPTH_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(102, 0, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that stencil framebuffer fetch works.
TEST_P(FramebufferFetchES31, Stencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0xE5;
color = vec4(correct, 0, 0, 1);
})";
GLRenderbuffer color, stencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
glBindRenderbuffer(GL_RENDERBUFFER, stencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, stencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearStencil(0xE5);
glClear(GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch work simultaneously and with the built-ins
// redeclared in the shader.
TEST_P(FramebufferFetchES31, DepthStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
highp float gl_LastFragDepthARM;
highp int gl_LastFragStencilARM;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with MSAA.
TEST_P(FramebufferFetchES31, DepthStencilMultisampled)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kViewportWidth,
kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
GLRenderbuffer resolveColor;
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
glBindRenderbuffer(GL_RENDERBUFFER, resolveColor);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
resolveColor);
glBlitFramebuffer(0, 0, kViewportWidth, kViewportHeight, 0, 0, kViewportWidth, kViewportHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with MSRTT textures.
TEST_P(FramebufferFetchES31, DepthStencilMSRTT)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLTexture color, depthStencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindTexture(GL_TEXTURE_2D, color);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color,
0, 4);
glBindTexture(GL_TEXTURE_2D, depthStencil);
glTexStorage2D(GL_TEXTURE_2D, 2, GL_DEPTH24_STENCIL8, 2 * kViewportWidth, 2 * kViewportHeight);
glFramebufferTexture2DMultisampleEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D,
depthStencil, 1, 4);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with MSRTT renderbuffers.
TEST_P(FramebufferFetchES31, DepthStencilMSRTTRenderbuffer)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_multisampled_render_to_texture"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindRenderbuffer(GL_RENDERBUFFER, color);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_RGBA8, kViewportWidth,
kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, color);
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisampleEXT(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kViewportWidth,
kViewportHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with textures
TEST_P(FramebufferFetchES31, DepthStencilTexture)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLTexture color, depthStencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindTexture(GL_TEXTURE_2D, color);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
glBindTexture(GL_TEXTURE_2D, depthStencil);
glTexStorage2D(GL_TEXTURE_2D, 2, GL_DEPTH24_STENCIL8, 2 * kViewportWidth, 2 * kViewportHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthStencil,
1);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with layered framebuffers
TEST_P(FramebufferFetchES31, DepthStencilLayered)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader") &&
!IsGLExtensionEnabled("GL_OES_geometry_shader"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLTexture color, depthStencil;
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glBindTexture(GL_TEXTURE_2D_ARRAY, color);
glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA8, kViewportWidth, kViewportHeight, 7);
glBindTexture(GL_TEXTURE_2D_ARRAY, depthStencil);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_DEPTH24_STENCIL8, 3 * kViewportWidth,
3 * kViewportHeight, 5, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, nullptr);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 1, GL_DEPTH24_STENCIL8, 2 * kViewportWidth,
2 * kViewportHeight, 7, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, nullptr);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 2, GL_DEPTH24_STENCIL8, kViewportWidth, kViewportHeight, 7, 0,
GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8_OES, nullptr);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 1);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 2);
if (IsGLExtensionEnabled("GL_OES_geometry_shader"))
{
glFramebufferTextureOES(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color, 0);
glFramebufferTextureOES(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStencil, 2);
}
else
{
glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, color, 0);
glFramebufferTextureEXT(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, depthStencil, 2);
}
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with default framebuffer
TEST_P(FramebufferFetchES31, DepthStencilSurface)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Tests that accessing gl_LastFragDepthARM or gl_LastFragStencilARM without attached depth or
// stencil attachments produces undefined results without generating an error.
TEST_P(FramebufferFetchES31, DrawWithoutDepthAndStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, getWindowWidth(), getWindowHeight());
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
// Values are undefined
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithoutDepthAndStencil, but with a draw call before that does have D/S attachment.
TEST_P(FramebufferFetchES31, DrawWithAndWithoutDepthAndStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Issue a draw call that correctly renders to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Remove the depth attachment, and issue another draw call to the other half.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// The bottom half has undefined values, but the top half can be verified.
EXPECT_PIXEL_NEAR(0, 0, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight / 2 - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithoutDepthAndStencil, but with a draw call after that does have D/S attachment.
TEST_P(FramebufferFetchES31, DrawWithoutAndWithDepthAndStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Issue a draw call with undefined render to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Add a depth attachment, and issue another draw call to the other half.
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
// The top half has undefined values, but the bottom half can be verified.
EXPECT_PIXEL_NEAR(0, kHeight / 2, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithAndWithoutDepthAndStencil, but with a framebuffer change instead of attachment
// change.
TEST_P(FramebufferFetchES31, DrawWithAndWithoutDepthAndStencilNewFBO)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo2;
glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Issue a draw call that correctly renders to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Switch framebuffers to the one that doesn't have a depth/stencil attachment.
glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// The bottom half has undefined values, but the top half can be verified.
EXPECT_PIXEL_NEAR(0, 0, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight / 2 - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithoutAndWithDepthAndStencil, but with a framebuffer change instead of attachment
// change.
TEST_P(FramebufferFetchES31, DrawWithoutAndWithDepthAndStencilNewFBO)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo2;
glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
glClearColor(0, 0, 0, 0);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
// Issue a draw call with undefined render to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Switch framebuffers to the one that does have a depth/stencil attachment.
glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// The top half has undefined values, but the bottom half can be verified.
EXPECT_PIXEL_NEAR(0, kHeight / 2, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithAndWithoutDepthAndStencil, but framebuffer is MSAA
TEST_P(FramebufferFetchES31, DrawWithAndWithoutDepthAndStencilMSAA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Issue a draw call that correctly renders to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Remove the depth attachment, and issue another draw call to the other half.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer resolve;
glBindRenderbuffer(GL_RENDERBUFFER, resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolve);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// The bottom half has undefined values, but the top half can be verified.
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_NEAR(0, 0, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight / 2 - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch work simultaneously and
// verify whether detaching the depth attachment and stencil attachment separately
// works correctly when the renderbuffer internalformat is set to GL_DEPTH24_STENCIL8.
TEST_P(FramebufferFetchES31, DepthStencilDetachSeparatelyD24S8_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
framebufferFetchDepthStencilDetachSeparately(GL_DEPTH24_STENCIL8);
ASSERT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch work simultaneously and
// verify whether detaching the depth attachment and stencil attachment separately
// works correctly when the renderbuffer internalformat is set to GL_DEPTH32F_STENCIL8.
TEST_P(FramebufferFetchES31, DepthStencilDetachSeparatelyD32FS8_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
framebufferFetchDepthStencilDetachSeparately(GL_DEPTH32F_STENCIL8);
ASSERT_GL_NO_ERROR();
}
// Test that framebuffer fetch works as expected when GL_FETCH_PER_SAMPLE_ARM is disabled.
TEST_P(FramebufferFetchES31, DrawFetchPerFragmentAndWriteOut_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
for (auto depthStencilFormat : kDSFormat)
{
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, false,
false, &fbo, depthStencilFormat);
stateReset();
createFboWithDepthStencilAndMRT(kViewportWidth, kViewportHeight, 0, depthStencilFormat,
&fbo, color, &depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programDS, essl31_shaders::vs::Passthrough(),
getFragShaderName(depthStencilFormat));
ASSERT_GL_NO_ERROR();
glDisable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programDS, false);
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, true, false,
&fbo, depthStencilFormat);
}
}
// Test that the discard functionality works as expected during framebuffer fetch when
// GL_FETCH_PER_SAMPLE_ARM is disabled.
TEST_P(FramebufferFetchES31, DrawFetchPerFragmentAndWriteOutWithDiscard_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char colorDiscard[] = R"(#version 310 es
precision highp float;
uniform vec4 color;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
ivec2 fragCoord = ivec2(gl_FragCoord.xy);
if (0 == ((fragCoord.x + fragCoord.y) % 2)) discard;
fragColor0 = fragColor1 = fragColor2 = fragColor3 = color;
})";
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, false, true, &fbo, GL_DEPTH_COMPONENT24);
stateReset();
createFboWithDepthStencilAndMRT(2, 2, 0, GL_DEPTH_COMPONENT24, &fbo, color, &depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programColorDiscard, essl31_shaders::vs::Passthrough(), colorDiscard);
ASSERT_GL_NO_ERROR();
glDisable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programColorDiscard, true);
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, true, true, &fbo, GL_DEPTH_COMPONENT24);
}
// Test that framebuffer fetch works as expected under the conditions of multisample and with
// GL_FETCH_PER_SAMPLE_ARM disabled.
TEST_P(FramebufferFetchES31, DrawFetchPerFragmentAndWriteOutWithMultisample_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
for (auto depthStencilFormat : kDSFormat)
{
for (int samples = 2; samples <= maxSamplesSupported(depthStencilFormat); samples *= 2)
{
if (!sampleCountSupported(GL_RENDERBUFFER, depthStencilFormat, samples))
{
continue;
}
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, false,
false, &fbo, depthStencilFormat);
stateReset();
createFboWithDepthStencilAndMRT(kViewportWidth, kViewportHeight, samples,
depthStencilFormat, &fbo, color, &depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programDS, essl31_shaders::vs::Passthrough(),
getFragShaderName(depthStencilFormat));
ASSERT_GL_NO_ERROR();
glDisable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programDS, false);
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, true,
false, &fbo, depthStencilFormat);
}
}
}
// Test that framebuffer fetch works as expected under the conditions of multisample and with
// GL_FETCH_PER_SAMPLE_ARM enabled.
TEST_P(FramebufferFetchES31, DrawFetchPerSampleAndWriteOutWithMultisample_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
for (auto depthStencilFormat : kDSFormat)
{
for (int samples = 2; samples <= maxSamplesSupported(depthStencilFormat); samples *= 2)
{
if (!sampleCountSupported(GL_RENDERBUFFER, depthStencilFormat, samples))
{
continue;
}
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, false,
false, &fbo, depthStencilFormat);
stateReset();
createFboWithDepthStencilAndMRT(kViewportWidth, kViewportHeight, samples,
depthStencilFormat, &fbo, color, &depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programDS, essl31_shaders::vs::Passthrough(),
getFragShaderName(depthStencilFormat));
ASSERT_GL_NO_ERROR();
glEnable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programDS, false);
bindResolveFboAndVerify(&resolve, &resolveFbo, kViewportWidth, kViewportHeight, true,
false, &fbo, depthStencilFormat);
}
}
}
// Test that the discard functionality works as expected during framebuffer fetch with multisample
// when GL_FETCH_PER_SAMPLE_ARM is disabled.
TEST_P(FramebufferFetchES31, DrawFetchPerFragmentAndWriteOutWithDiscardAndMultisample_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char colorDiscard[] = R"(#version 310 es
precision highp float;
uniform vec4 color;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
ivec2 fragCoord = ivec2(gl_FragCoord.xy);
if (0 == ((fragCoord.x + fragCoord.y) % 2)) discard;
fragColor0 = fragColor1 = fragColor2 = fragColor3 = color;
})";
for (int samples = 2; samples <= maxSamplesSupported(GL_DEPTH_COMPONENT16); samples *= 2)
{
if (!sampleCountSupported(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, samples))
{
continue;
}
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, false, true, &fbo,
GL_DEPTH_COMPONENT24);
stateReset();
createFboWithDepthStencilAndMRT(2, 2, samples, GL_DEPTH_COMPONENT24, &fbo, color,
&depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programColorDiscard, essl31_shaders::vs::Passthrough(), colorDiscard);
ASSERT_GL_NO_ERROR();
glDisable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programColorDiscard, true);
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, true, true, &fbo,
GL_DEPTH_COMPONENT24);
}
}
// Test that the discard functionality works as expected during framebuffer fetch with multisample
// when GL_FETCH_PER_SAMPLE_ARM is enabled.
TEST_P(FramebufferFetchES31, DrawFetchPerSampleAndWriteOutWithDiscardAndMultisample_ARM)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char colorDiscard[] = R"(#version 310 es
precision highp float;
uniform vec4 color;
layout(location=0) out highp vec4 fragColor0;
layout(location=1) out highp vec4 fragColor1;
layout(location=2) out highp vec4 fragColor2;
layout(location=3) out highp vec4 fragColor3;
void main()
{
ivec2 fragCoord = ivec2(gl_FragCoord.xy);
if (0 == ((fragCoord.x + fragCoord.y) % 2)) discard;
fragColor0 = fragColor1 = fragColor2 = fragColor3 = color;
})";
for (int samples = 2; samples <= maxSamplesSupported(GL_DEPTH_COMPONENT16); samples *= 2)
{
if (!sampleCountSupported(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, samples))
{
continue;
}
GLFramebuffer fbo, resolveFbo;
GLRenderbuffer color[kMaxColorBuffer], depthStencil, resolve;
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, false, true, &fbo,
GL_DEPTH_COMPONENT24);
stateReset();
createFboWithDepthStencilAndMRT(2, 2, samples, GL_DEPTH_COMPONENT24, &fbo, color,
&depthStencil);
ASSERT_GL_NO_ERROR();
ANGLE_GL_PROGRAM(programColorDiscard, essl31_shaders::vs::Passthrough(), colorDiscard);
ASSERT_GL_NO_ERROR();
glEnable(GL_FETCH_PER_SAMPLE_ARM);
clearAndDrawQuad(programColorDiscard, true);
bindResolveFboAndVerify(&resolve, &resolveFbo, 2, 2, true, true, &fbo,
GL_DEPTH_COMPONENT24);
}
}
// Similar to DrawWithoutAndWithDepthAndStencil, but framebuffer is MSAA
TEST_P(FramebufferFetchES31, DrawWithoutAndWithDepthAndStencilMSAA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Issue a draw call with undefined render to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Add a depth attachment, and issue another draw call to the other half.
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer resolve;
glBindRenderbuffer(GL_RENDERBUFFER, resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolve);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// The top half has undefined values, but the bottom half can be verified.
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_NEAR(0, kHeight / 2, 255, 153, 0, 255, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight - 1, 255, 153, 0, 255, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithAndWithoutDepthAndStencilMSAA, but color framebuffer fetch is simultaneously
// used.
TEST_P(FramebufferFetchES31, DrawWithAndWithoutDepthAndStencilAndColorMSAA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp inout vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color.xy = vec2(correct, gl_LastFragDepthARM);
color.zw += vec2(0.25, 0.5);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Issue a draw call that correctly renders to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Remove the depth attachment, and issue another draw call to the other half.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer resolve;
glBindRenderbuffer(GL_RENDERBUFFER, resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolve);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// The bottom half has undefined values, but the top half can be verified.
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_NEAR(0, 0, 255, 153, 63, 127, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight / 2 - 1, 255, 153, 63, 127, 1);
EXPECT_GL_NO_ERROR();
}
// Similar to DrawWithoutAndWithDepthAndStencilMSAA, but color framebuffer fetch is simultaneously
// used.
TEST_P(FramebufferFetchES31, DrawWithoutAndWithDepthAndStencilAndColorMSAA)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp inout vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color.xy = vec2(correct, gl_LastFragDepthARM);
color.zw += vec2(0.25, 0.5);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glUseProgram(program);
EXPECT_GL_NO_ERROR();
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
EXPECT_GL_NO_ERROR();
constexpr GLint kWidth = 37;
constexpr GLint kHeight = 52;
GLRenderbuffer renderbuffer;
glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderbuffer);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glViewport(0, 0, kWidth, kHeight);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Issue a draw call with undefined render to half of the framebuffer.
glEnable(GL_SCISSOR_TEST);
glScissor(0, 0, kWidth, kHeight / 2);
glDrawArrays(GL_TRIANGLES, 0, 3);
// Add a depth attachment, and issue another draw call to the other half.
GLRenderbuffer depthStencil;
glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
depthStencil);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glScissor(0, kHeight / 2, kWidth, kHeight - kHeight / 2);
glClearDepthf(0.6);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
GLFramebuffer resolveFbo;
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFbo);
EXPECT_GL_NO_ERROR();
GLRenderbuffer resolve;
glBindRenderbuffer(GL_RENDERBUFFER, resolve);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kWidth, kHeight);
glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, resolve);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_DRAW_FRAMEBUFFER);
EXPECT_GL_NO_ERROR();
glBlitFramebuffer(0, 0, kWidth, kHeight, 0, 0, kWidth, kHeight, GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// The top half has undefined values, but the bottom half can be verified.
glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFbo);
EXPECT_PIXEL_NEAR(0, kHeight / 2, 255, 153, 63, 127, 1);
EXPECT_PIXEL_NEAR(kWidth - 1, kHeight - 1, 255, 153, 63, 127, 1);
EXPECT_GL_NO_ERROR();
}
// Test that depth and stencil framebuffer fetch works with pbuffers
TEST_P(FramebufferFetchES31, DepthStencilPbuffer)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x3C;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
EGLWindow *window = getEGLWindow();
ASSERT(window);
EGLConfig config = window->getConfig();
EGLContext context = window->getContext();
EGLDisplay dpy = window->getDisplay();
EGLint surfaceType = 0;
// Skip if pbuffer surface is not supported
eglGetConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType);
ANGLE_SKIP_TEST_IF((surfaceType & EGL_PBUFFER_BIT) == 0);
const EGLint surfaceWidth = static_cast<EGLint>(getWindowWidth());
const EGLint surfaceHeight = static_cast<EGLint>(getWindowHeight());
const EGLint pBufferAttributes[] = {
EGL_WIDTH, surfaceWidth, EGL_HEIGHT, surfaceHeight, EGL_NONE,
};
// Create Pbuffer surface
EGLSurface pbufferSurface = eglCreatePbufferSurface(dpy, config, pBufferAttributes);
ASSERT_NE(pbufferSurface, EGL_NO_SURFACE);
ASSERT_EGL_SUCCESS();
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, pbufferSurface, pbufferSurface, context));
ASSERT_EGL_SUCCESS();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearDepthf(0.8f);
glClearStencil(0x3C);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
// Switch back to the window surface and destroy the pbuffer
EXPECT_EGL_TRUE(eglMakeCurrent(dpy, window->getSurface(), window->getSurface(), context));
ASSERT_EGL_SUCCESS();
EXPECT_EGL_TRUE(eglDestroySurface(dpy, pbufferSurface));
ASSERT_EGL_SUCCESS();
}
// Test that depth framebuffer fetch works with color framebuffer fetch
TEST_P(FramebufferFetchES31, DepthAndColor)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
std::ostringstream fs;
fs << makeShaderPreamble(
whichExtension, "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require", 1);
fs << R"(void main()
{
color0 = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.4f);
glClear(GL_DEPTH_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), fs.str().c_str());
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(102, 0, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that stencil framebuffer fetch works with color framebuffer fetch
TEST_P(FramebufferFetchES31, StencilAndColor)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
std::ostringstream fs;
fs << makeShaderPreamble(
whichExtension, "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require", 1);
fs << R"(void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
color0 = vec4(correct, 0, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearStencil(0x7D);
glClear(GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), fs.str().c_str());
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 0, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth/stencil framebuffer fetch works with color framebuffer fetch
TEST_P(FramebufferFetchES31, DepthStencilAndColor)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
std::ostringstream fs;
fs << makeShaderPreamble(
whichExtension, "#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require", 1);
fs << R"(void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
color0 = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.8f);
glClearStencil(0x7D);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), fs.str().c_str());
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 0, 255));
ASSERT_GL_NO_ERROR();
}
// Test that mixing depth-only and stencil-only framebuffer fetch programs work
TEST_P(FramebufferFetchES31, DepthThenStencilThenNone)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kDepthFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
color = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
const char kStencilFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0xE5;
color = vec4(0, correct, 0, 1);
})";
const char kNoneFS[] = R"(#version 310 es
highp out vec4 color;
void main()
{
color = vec4(0, 0, 1, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.8f);
glClearStencil(0xE5);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(depth, essl31_shaders::vs::Passthrough(), kDepthFS);
ANGLE_GL_PROGRAM(stencil, essl31_shaders::vs::Passthrough(), kStencilFS);
ANGLE_GL_PROGRAM(none, essl31_shaders::vs::Passthrough(), kNoneFS);
drawQuad(depth, essl31_shaders::PositionAttrib(), 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
drawQuad(stencil, essl31_shaders::PositionAttrib(), 0.0f);
drawQuad(none, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(204, 255, 255, 255));
ASSERT_GL_NO_ERROR();
}
// Test that starting without framebuffer fetch, then doing framebuffer fetch works.
TEST_P(FramebufferFetchES31, NoneThenDepthThenStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kDepthFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
color = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
const char kStencilFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0xE5;
color = vec4(0, correct, 0, 1);
})";
const char kNoneFS[] = R"(#version 310 es
highp out vec4 color;
void main()
{
color = vec4(0, 0, 1, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.4f);
glClearStencil(0xE5);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(depth, essl31_shaders::vs::Passthrough(), kDepthFS);
ANGLE_GL_PROGRAM(stencil, essl31_shaders::vs::Passthrough(), kStencilFS);
ANGLE_GL_PROGRAM(none, essl31_shaders::vs::Passthrough(), kNoneFS);
drawQuad(none, essl31_shaders::PositionAttrib(), 0.0f);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
drawQuad(depth, essl31_shaders::PositionAttrib(), 0.0f);
drawQuad(stencil, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(102, 255, 255, 255));
ASSERT_GL_NO_ERROR();
}
// Test that depth/stencil framebuffer fetch is actually coherent by writing to depth/stencil in one
// draw call and reading from it in another.
TEST_P(FramebufferFetchES31, DepthStencilDrawThenRead)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kWriteDepthFS[] = R"(#version 310 es
highp out vec4 color;
void main()
{
if (gl_FragCoord.x < 8.)
gl_FragDepth = 0.4f;
else
gl_FragDepth = 0.8f;
color = vec4(0, 0, 1, 1);
})";
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x5B;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0);
glClearStencil(0);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(writeDepth, essl31_shaders::vs::Passthrough(), kWriteDepthFS);
ANGLE_GL_PROGRAM(read, essl31_shaders::vs::Passthrough(), kFS);
// Write depth (0.4 or 0.8 by the shader) and stencil (0x5B) in one draw call
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 0x5B, 0xFF);
glStencilOp(GL_REPLACE, GL_REPLACE, GL_REPLACE);
glStencilMask(0xFF);
drawQuad(writeDepth, essl31_shaders::PositionAttrib(), 0.0f);
// Read them in the next draw call to verify
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
drawQuad(read, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth / 2, kViewportHeight, GLColor(255, 102, 255, 255));
EXPECT_PIXEL_RECT_EQ(kViewportWidth / 2, 0, kViewportWidth - kViewportWidth, kViewportHeight,
GLColor(255, 204, 255, 255));
ASSERT_GL_NO_ERROR();
}
// Test that writing to gl_FragDepth does not affect gl_LastFragDepthARM.
TEST_P(FramebufferFetchES31, DepthWriteAndReadInSameShader)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
gl_FragDepth = 0.9;
color = vec4(gl_LastFragDepthARM, 0, 0, 1);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.4f);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_ALWAYS);
glDepthMask(GL_TRUE);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(102, 0, 0, 255));
ASSERT_GL_NO_ERROR();
// For completeness, verify that gl_FragDepth did write to depth.
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glDepthMask(GL_FALSE);
ANGLE_GL_PROGRAM(red, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
ANGLE_GL_PROGRAM(green, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
drawQuad(red, essl1_shaders::PositionAttrib(), 0.79f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor::red);
drawQuad(green, essl1_shaders::PositionAttrib(), 0.81f);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor::red);
ASSERT_GL_NO_ERROR();
}
// Test that render pass can start with D/S framebuffer fetch, then color framebuffer fetch is used.
TEST_P(FramebufferFetchES31, DepthStencilThenColor)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
const char kDepthStencilFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
std::ostringstream colorFS;
colorFS << makeShaderPreamble(whichExtension, nullptr, 1);
colorFS << R"(void main()
{
color0.x /= 2.;
color0.y *= 2.;
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearDepthf(0.4f);
glClearStencil(0x7D);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
ANGLE_GL_PROGRAM(readDepthStencil, essl31_shaders::vs::Passthrough(), kDepthStencilFS);
ANGLE_GL_PROGRAM(readColor, essl31_shaders::vs::Passthrough(), colorFS.str().c_str());
drawQuad(readDepthStencil, essl31_shaders::PositionAttrib(), 0.0f);
drawQuad(readColor, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(127, 204, 0, 255), 1);
EXPECT_PIXEL_COLOR_NEAR(kViewportWidth - 1, kViewportHeight - 1, GLColor(127, 204, 0, 255), 1);
ASSERT_GL_NO_ERROR();
}
// Test that render pass can start without framebuffer fetch, then do D/S framebuffer fetch, then
// color framebuffer fetch. This test uses PPOs.
TEST_P(FramebufferFetchES31, NoneThenDepthStencilThenColorPPO)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
constexpr char kVS[] = R"(#version 310 es
void main()
{
vec2 pos = vec2(0.0);
switch (gl_VertexID) {
case 0: pos = vec2(-1.0, -1.0); break;
case 1: pos = vec2(3.0, -1.0); break;
case 2: pos = vec2(-1.0, 3.0); break;
};
gl_Position = vec4(pos, 0.0, 1.0);
})";
const char kNoneFS[] = R"(#version 310 es
highp out vec4 color;
void main()
{
color = vec4(0, 0, 1, 1);
})";
const char kDepthStencilFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
highp out vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
color = vec4(correct, gl_LastFragDepthARM, 0, 1);
})";
std::ostringstream colorFS;
colorFS << makeShaderPreamble(whichExtension, nullptr, 1);
colorFS << R"(void main()
{
color0.x /= 2.;
color0.y *= 2.;
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
GLProgramPipeline nonePPO, depthStencilPPO, colorPPO;
makeProgramPipeline(nonePPO, kVS, kNoneFS);
makeProgramPipeline(depthStencilPPO, kVS, kDepthStencilFS);
makeProgramPipeline(colorPPO, kVS, colorFS.str().c_str());
glClearDepthf(0.4f);
glClearStencil(0x7D);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glBindProgramPipeline(nonePPO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glBindProgramPipeline(depthStencilPPO);
glDrawArrays(GL_TRIANGLES, 0, 3);
glDisable(GL_BLEND);
glBindProgramPipeline(colorPPO);
glDrawArrays(GL_TRIANGLES, 0, 3);
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(127, 204, 255, 255), 1);
EXPECT_PIXEL_COLOR_NEAR(kViewportWidth - 1, kViewportHeight - 1, GLColor(127, 204, 255, 255),
1);
ASSERT_GL_NO_ERROR();
}
// Test that using the maximum number of color attachments works in conjunction with depth/stencil
// framebuffer fetch.
TEST_P(FramebufferFetchES31, MaximumColorAttachmentsAndDepthStencil)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch") &&
!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch_non_coherent"));
const WhichExtension whichExtension = chooseBetweenCoherentOrIncoherent();
GLint maxDrawBuffers = 0;
glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuffers);
GLFramebuffer fbo;
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
GLTexture depthStencil;
std::vector<GLTexture> color(maxDrawBuffers);
std::vector<GLenum> buffers(maxDrawBuffers);
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
buffers[index] = GL_COLOR_ATTACHMENT0 + index;
glBindTexture(GL_TEXTURE_2D, color[index]);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kViewportWidth, kViewportHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, buffers[index], GL_TEXTURE_2D, color[index], 0);
}
glBindTexture(GL_TEXTURE_2D, depthStencil);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, kViewportWidth, kViewportHeight);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_TEXTURE_2D, depthStencil,
0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glDrawBuffers(maxDrawBuffers, buffers.data());
glClearColor(0, 0, 1, 0);
glClearDepthf(0.8f);
glClearStencil(0x7D);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
std::ostringstream fs;
fs << makeShaderPreamble(whichExtension,
"#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require",
maxDrawBuffers);
fs << R"(void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
)";
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
fs << " color" << index << " += vec4(correct, gl_LastFragDepthARM, 0, 1);\n";
}
fs << "}\n";
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), fs.str().c_str());
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
for (GLint index = 0; index < maxDrawBuffers; ++index)
{
glReadBuffer(buffers[index]);
EXPECT_PIXEL_RECT_EQ(0, 0, kViewportWidth, kViewportHeight, GLColor(255, 204, 255, 255));
}
ASSERT_GL_NO_ERROR();
}
// Test that depth/stencil framebuffer fetch works with advanced blend
TEST_P(FramebufferFetchAndAdvancedBlendES31, DepthStencilAndAdvancedBlend)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_ARM_shader_framebuffer_fetch_depth_stencil"));
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_KHR_blend_equation_advanced"));
const char kFS[] = R"(#version 310 es
#extension GL_ARM_shader_framebuffer_fetch_depth_stencil : require
#extension GL_KHR_blend_equation_advanced : require
layout(blend_support_multiply) out;
layout(location = 0) out mediump vec4 color;
void main()
{
bool correct = gl_LastFragStencilARM == 0x7D;
color = vec4(correct, gl_LastFragDepthARM, 0, 0.5);
})";
GLRenderbuffer color, depthStencil;
GLFramebuffer fbo;
createFramebufferWithDepthStencil(&color, &depthStencil, &fbo);
glClearColor(0.5, 0.2, 0.4, 0.6);
glClearDepthf(0.8f);
glClearStencil(0x7D);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glEnable(GL_BLEND);
glBlendEquation(GL_MULTIPLY_KHR);
ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), kFS);
drawQuad(program, essl31_shaders::PositionAttrib(), 0.0f);
EXPECT_PIXEL_NEAR(0, 0, 255, 148, 51, 204, 1);
EXPECT_PIXEL_NEAR(kViewportWidth - 1, kViewportHeight - 1, 255, 148, 51, 204, 1);
ASSERT_GL_NO_ERROR();
}
// Test switching between framebuffer fetch and non framebuffer fetch draw calls, with multiple
// calls in each mode in between. Tests Vulkan backend's emulation of coherent framebuffer fetch
// over non-coherent hardware. While this is untestable without adding counters, the test should
// generate implicit framebuffer fetch barriers only when the current program uses framebuffer
// fetch. This can be observed in RenderDoc.
TEST_P(FramebufferFetchES31, SwitchWithAndWithoutFramebufferFetchPrograms)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
void main()
{
// gl_VertexID x y
// 0 -1 -1
// 1 1 -1
// 2 -1 1
// 3 1 1
int bit0 = gl_VertexID & 1;
int bit1 = gl_VertexID >> 1;
gl_Position = vec4(bit0 * 2 - 1, bit1 * 2 - 1, 0, 1);
})";
// Program without framebuffer fetch
constexpr char kFS1[] = R"(#version 310 es
layout(location = 0) out highp vec4 o_color;
uniform mediump vec4 u_color;
void main (void)
{
o_color = u_color;
})";
ANGLE_GL_PROGRAM(drawColor, kVS, kFS1);
glUseProgram(drawColor);
GLint uniLoc = glGetUniformLocation(drawColor, "u_color");
ASSERT_NE(uniLoc, -1);
// Program with framebuffer fetch
constexpr char kFS2[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 o_color;
void main (void)
{
o_color = o_color * o_color + vec4(0.1, 0.2, 0.3, 0.2);
})";
ANGLE_GL_PROGRAM(ff, kVS, kFS2);
GLTexture color;
glBindTexture(GL_TEXTURE_2D, color);
glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, kViewportWidth, kViewportHeight);
GLFramebuffer framebuffer;
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
ASSERT_GL_NO_ERROR();
glClearColor(0, 0, 0.5, 0);
glClear(GL_COLOR_BUFFER_BIT);
// Start without framebuffer fetch.
glUseProgram(drawColor);
glEnable(GL_BLEND);
glBlendFunc(GL_ONE, GL_ONE);
glUniform4f(uniLoc, 0.7, 0, 0, 0.3);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glUniform4f(uniLoc, 0.1, 0.4, 0, 0);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Switch to framebuffer fetch mode, and draw a few times
glDisable(GL_BLEND);
glUseProgram(ff);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Break the render pass. Later continue drawing in framebuffer fetch mode without changing
// programs to ensure that framebuffer fetch barrier is still added.
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(165, 84, 153, 72), 3);
// More FF calls
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Back to no FF calls, no barrier should be added.
glEnable(GL_BLEND);
glUseProgram(drawColor);
glUniform4f(uniLoc, 0.2, 0.1, 0.05, 0.15);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// Verify results
EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(145, 100, 201, 109), 3);
}
// Test that declaring inout variables but only ever writing to them works.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariable)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void main (void)
{
color = vec4(1, 0, 0, 1);
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 1, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::red);
}
// Test that declaring inout variables but only ever writing to them works. This test writes to
// different channels of the variable separately.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariableSplit)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void main (void)
{
color.xz = vec2(1, 0);
color.wy = vec2(1, 1);
return;
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::yellow);
}
// Verify that partial writes to an |inout| variable don't make ANGLE consider it as an |out|
// variable.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariablePartial)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void main (void)
{
color.x = 1.;
color.wy = vec2(1, 0);
return;
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 0, 1, 0);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), GLColor::magenta);
}
// Verify that conditional writes to an |inout| variable don't make ANGLE consider it as an |out|
// variable.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariableConditional)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void main (void)
{
if (gl_FragCoord.x < 8.)
{
color.yz = vec2(1, 0);
}
color.x = 1.;
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, 8, getWindowHeight(), GLColor::yellow);
EXPECT_PIXEL_RECT_EQ(8, 0, getWindowWidth() - 8, getWindowHeight(), GLColor::magenta);
}
// Verify that early out from main stops write-only |inout| variables from turning into |out|
// variables.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariableEarlyReturn)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void main (void)
{
if (gl_FragCoord.x < 8.)
{
return;
}
color.x = 1.;
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, 8, getWindowHeight(), GLColor::blue);
EXPECT_PIXEL_RECT_EQ(8, 0, getWindowWidth() - 8, getWindowHeight(), GLColor::magenta);
}
// Verify that discard in the shader stops write-only |inout| variables from turning into |out|
// variables.
TEST_P(FramebufferFetchES31, WriteOnlyInOutVariableDiscard)
{
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_framebuffer_fetch"));
constexpr char kVS[] = R"(#version 310 es
in highp vec4 position;
void main (void)
{
gl_Position = position;
})";
constexpr char kFS[] = R"(#version 310 es
#extension GL_EXT_shader_framebuffer_fetch : require
layout(location = 0) inout highp vec4 color;
void f()
{
if (gl_FragCoord.x < 8.)
{
discard;
}
}
void main (void)
{
f();
color.x = 1.;
})";
ANGLE_GL_PROGRAM(program, kVS, kFS);
glClearColor(0, 0, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
drawQuad(program, "position", 0);
EXPECT_PIXEL_RECT_EQ(0, 0, 8, getWindowHeight(), GLColor::blue);
EXPECT_PIXEL_RECT_EQ(8, 0, getWindowWidth() - 8, getWindowHeight(), GLColor::magenta);
}
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FramebufferFetchES31);
ANGLE_INSTANTIATE_TEST_ES31_AND(FramebufferFetchES31,
ES31_VULKAN().disable(Feature::SupportsSPIRV14));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FramebufferFetchAndAdvancedBlendES31);
ANGLE_INSTANTIATE_TEST_ES31_AND(FramebufferFetchAndAdvancedBlendES31,
ES31_VULKAN_SWIFTSHADER()
.disable(Feature::SupportsBlendOperationAdvanced)
.enable(Feature::EmulateAdvancedBlendEquations));
} // namespace angle