| // |
| // Copyright 2022 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. |
| // |
| |
| #include "test_utils/ANGLETest.h" |
| |
| #include <vector> |
| |
| #include "GLSLANG/ShaderLang.h" |
| #include "test_utils/gl_raii.h" |
| |
| using namespace angle; |
| |
| class ShaderBinaryTest : public ANGLETest<> |
| { |
| protected: |
| ShaderBinaryTest() |
| { |
| setWindowWidth(128); |
| setWindowHeight(128); |
| setConfigRedBits(8); |
| setConfigGreenBits(8); |
| setConfigBlueBits(8); |
| setConfigAlphaBits(8); |
| |
| // Test flakiness was noticed when reusing displays. |
| forceNewDisplay(); |
| } |
| |
| void testSetUp() override |
| { |
| ASSERT_EQ(sh::Initialize(), true); |
| |
| if (!supported()) |
| { |
| // Must return early because the initialization below will crash otherwise. |
| // Individal tests will skip themselves as well. |
| return; |
| } |
| |
| mCompileOptions.objectCode = true; |
| mCompileOptions.emulateGLDrawID = true; |
| mCompileOptions.initializeUninitializedLocals = true; |
| |
| sh::InitBuiltInResources(&mResources); |
| |
| // Generate a shader binary: |
| ShShaderSpec spec = SH_GLES2_SPEC; |
| ShShaderOutput output = SH_SPIRV_VULKAN_OUTPUT; |
| |
| // Vertex shader: |
| const char *source = essl1_shaders::vs::Simple(); |
| ShHandle vertexCompiler = |
| sh::ConstructCompiler(GL_VERTEX_SHADER, spec, output, &mResources); |
| bool compileResult = |
| sh::GetShaderBinary(vertexCompiler, &source, 1, mCompileOptions, &mVertexShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (mVertexShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating vertex shader binary failed."; |
| } |
| |
| // Fragment shader: |
| source = essl1_shaders::fs::Red(); |
| ShHandle fragmentCompiler = |
| sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &mResources); |
| compileResult = sh::GetShaderBinary(fragmentCompiler, &source, 1, mCompileOptions, |
| &mFragmentShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (mFragmentShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating fragment shader binary failed."; |
| } |
| } |
| |
| void testTearDown() override |
| { |
| sh::Finalize(); |
| |
| if (!supported()) |
| { |
| // Return early because the initialization didn't complete. |
| return; |
| } |
| |
| glDeleteBuffers(1, &mBuffer); |
| } |
| |
| bool supported() const |
| { |
| GLint formatCount; |
| glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &formatCount); |
| if (formatCount == 0) |
| { |
| std::cout << "Test skipped because no program binary formats are available." |
| << std::endl; |
| return false; |
| } |
| std::vector<GLint> formats(formatCount); |
| glGetIntegerv(GL_SHADER_BINARY_FORMATS, formats.data()); |
| |
| ASSERT(formats[0] == GL_SHADER_BINARY_ANGLE); |
| |
| return true; |
| } |
| |
| ShCompileOptions mCompileOptions = {}; |
| ShBuiltInResources mResources; |
| GLuint mBuffer; |
| sh::ShaderBinaryBlob mVertexShaderBinary; |
| sh::ShaderBinaryBlob mFragmentShaderBinary; |
| }; |
| |
| // This tests the ability to successfully create and load a shader binary. |
| TEST_P(ShaderBinaryTest, CreateAndLoadBinary) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| |
| GLint compileResult; |
| // Create vertex shader and load binary |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| // Create fragment shader and load binary |
| GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderBinary(1, &fragShader, GL_SHADER_BINARY_ANGLE, mFragmentShaderBinary.data(), |
| mFragmentShaderBinary.size()); |
| glGetShaderiv(fragShader, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| // Create program from the shaders |
| GLuint newProgram = glCreateProgram(); |
| glAttachShader(newProgram, vertShader); |
| glAttachShader(newProgram, fragShader); |
| glLinkProgram(newProgram); |
| newProgram = CheckLinkStatusAndReturnProgram(newProgram, true); |
| |
| // Test with a basic draw |
| drawQuad(newProgram, "a_position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Check invalid gl call parameters, such as providing a GL type when a shader handle is expected. |
| TEST_P(ShaderBinaryTest, InvalidCallParams) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| |
| GLuint vertShader[2]; |
| vertShader[0] = glCreateShader(GL_VERTEX_SHADER); |
| GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); |
| |
| // Invalid shader |
| vertShader[1] = -1; |
| glShaderBinary(1, &vertShader[1], GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // GL_INVALID_ENUM is generated if binaryFormat is not an accepted value. |
| glShaderBinary(1, &vertShader[0], GL_INVALID_ENUM, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_ENUM); |
| |
| // GL_INVALID_VALUE is generated if n or length is negative |
| glShaderBinary(-1, &vertShader[0], GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| glShaderBinary(1, &vertShader[0], GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), -1); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // GL_INVALID_OPERATION is generated if any value in shaders is not a shader object. |
| GLuint program = glCreateProgram(); |
| glShaderBinary(1, &program, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // GL_INVALID_OPERATION is generated if more than one of the handles in shaders refers to the |
| // same shader object. |
| vertShader[1] = vertShader[0]; |
| glShaderBinary(2, &vertShader[0], GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| |
| // GL_INVALID_VALUE is generated if the data pointed to by binary does not match the format |
| // specified by binaryFormat. |
| std::string invalid("Invalid Shader Blob."); |
| glShaderBinary(1, &vertShader[0], GL_SHADER_BINARY_ANGLE, invalid.data(), invalid.size()); |
| EXPECT_GL_ERROR(GL_INVALID_VALUE); |
| |
| // Try loading vertex shader binary into fragment shader |
| glShaderBinary(1, &fragShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| } |
| |
| // Check attempting to get source code from a shader that was loaded with glShaderBinary. |
| TEST_P(ShaderBinaryTest, GetSourceFromBinaryShader) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| |
| GLint compileResult; |
| // Create vertex shader and load binary |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| GLsizei length = 0; |
| glGetShaderSource(vertShader, 0, &length, nullptr); |
| |
| EXPECT_EQ(length, 0); |
| } |
| |
| // Create a program from both shader source code and a binary blob. |
| TEST_P(ShaderBinaryTest, CombineSourceAndBinaryShaders) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| |
| GLint compileResult; |
| // Create vertex shader and load binary |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| // Create fragment shader |
| GLuint fragShader = CompileShader(GL_FRAGMENT_SHADER, essl1_shaders::fs::Red()); |
| |
| GLuint newProgram = glCreateProgram(); |
| glAttachShader(newProgram, vertShader); |
| glAttachShader(newProgram, fragShader); |
| glLinkProgram(newProgram); |
| newProgram = CheckLinkStatusAndReturnProgram(newProgram, true); |
| |
| // Test with a basic draw |
| drawQuad(newProgram, "a_position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test that shaders loaded with glShaderBinary do not cause false hits in the program cache. |
| TEST_P(ShaderBinaryTest, ProgramCacheWithShaderBinary) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_get_program_binary")); |
| |
| GLint compileResult; |
| // Create vertex shader that will be shared between the programs |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| // Create a program with a red vertex shader |
| GLuint fragShaderRed = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderBinary(1, &fragShaderRed, GL_SHADER_BINARY_ANGLE, mFragmentShaderBinary.data(), |
| mFragmentShaderBinary.size()); |
| glGetShaderiv(fragShaderRed, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| GLuint programRed = glCreateProgram(); |
| glAttachShader(programRed, vertShader); |
| glAttachShader(programRed, fragShaderRed); |
| glLinkProgram(programRed); |
| programRed = CheckLinkStatusAndReturnProgram(programRed, true); |
| |
| // Test with a basic draw |
| drawQuad(programRed, "a_position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Create a program with a blue fragment shader, also loaded from a binary |
| ShShaderSpec spec = SH_GLES2_SPEC; |
| ShShaderOutput output = SH_SPIRV_VULKAN_OUTPUT; |
| |
| const char *source = essl1_shaders::fs::Blue(); |
| sh::ShaderBinaryBlob fragShaderBlueData; |
| ShHandle fragmentCompiler = |
| sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &mResources); |
| bool binaryCompileResult = |
| sh::GetShaderBinary(fragmentCompiler, &source, 1, mCompileOptions, &fragShaderBlueData); |
| ASSERT_TRUE(binaryCompileResult); |
| if (fragShaderBlueData.size() == 0) |
| { |
| FAIL() << "Creating fragment shader binary failed."; |
| } |
| |
| GLuint fragShaderBlue = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderBinary(1, &fragShaderBlue, GL_SHADER_BINARY_ANGLE, fragShaderBlueData.data(), |
| fragShaderBlueData.size()); |
| glGetShaderiv(fragShaderBlue, GL_COMPILE_STATUS, &compileResult); |
| ASSERT_GL_TRUE(compileResult); |
| |
| GLuint programBlue = glCreateProgram(); |
| glAttachShader(programBlue, vertShader); |
| glAttachShader(programBlue, fragShaderBlue); |
| glLinkProgram(programBlue); |
| programBlue = CheckLinkStatusAndReturnProgram(programBlue, true); |
| |
| // The program cache should miss and create a new program |
| drawQuad(programBlue, "a_position", 0.5f); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| |
| class ShaderBinaryTestES31 : public ShaderBinaryTest |
| { |
| protected: |
| void testSetUp() override |
| { |
| ASSERT_EQ(sh::Initialize(), true); |
| |
| mCompileOptions.objectCode = true; |
| mCompileOptions.emulateGLDrawID = true; |
| mCompileOptions.initializeUninitializedLocals = true; |
| |
| sh::InitBuiltInResources(&mResources); |
| mResources.EXT_geometry_shader = 1; |
| mResources.EXT_tessellation_shader = 1; |
| |
| // Generate a shader binary: |
| ShShaderSpec spec = SH_GLES3_1_SPEC; |
| ShShaderOutput output = SH_SPIRV_VULKAN_OUTPUT; |
| |
| // Vertex shader: |
| const char *source = essl31_shaders::vs::Simple(); |
| ShHandle vertexCompiler = |
| sh::ConstructCompiler(GL_VERTEX_SHADER, spec, output, &mResources); |
| bool compileResult = |
| sh::GetShaderBinary(vertexCompiler, &source, 1, mCompileOptions, &mVertexShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (mVertexShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating vertex shader binary failed."; |
| } |
| |
| // Fragment shader: |
| source = essl31_shaders::fs::Red(); |
| ShHandle fragmentCompiler = |
| sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &mResources); |
| compileResult = sh::GetShaderBinary(fragmentCompiler, &source, 1, mCompileOptions, |
| &mFragmentShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (mFragmentShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating fragment shader binary failed."; |
| } |
| } |
| }; |
| |
| // Test all shader stages |
| TEST_P(ShaderBinaryTestES31, AllShaderStages) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_geometry_shader")); |
| ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_tessellation_shader")); |
| |
| const char *kGS = R"(#version 310 es |
| #extension GL_EXT_geometry_shader : require |
| precision mediump float; |
| |
| layout (triangles) in; |
| layout (triangle_strip, max_vertices = 3) out; |
| |
| void main() { |
| gl_Position = gl_in[0].gl_Position; |
| EmitVertex(); |
| |
| gl_Position = gl_in[1].gl_Position; |
| EmitVertex(); |
| |
| gl_Position = gl_in[2].gl_Position; |
| EmitVertex(); |
| |
| EndPrimitive(); |
| } |
| )"; |
| |
| const char *kTCS = R"(#version 310 es |
| #extension GL_EXT_tessellation_shader : require |
| precision mediump float; |
| |
| layout (vertices = 1) out; |
| |
| void main() |
| { |
| gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; |
| gl_TessLevelInner[0] = 1.0; |
| gl_TessLevelInner[1] = 1.0; |
| gl_TessLevelOuter[0] = 1.0; |
| gl_TessLevelOuter[1] = 1.0; |
| gl_TessLevelOuter[2] = 1.0; |
| gl_TessLevelOuter[3] = 1.0; |
| } |
| |
| )"; |
| |
| const char *kTES = R"(#version 310 es |
| #extension GL_EXT_tessellation_shader : require |
| precision mediump float; |
| |
| layout (quads, cw, fractional_odd_spacing) in; |
| |
| void main() |
| { |
| gl_Position = vec4(gl_TessCoord.xy * 2. - 1., 0, 1); |
| } |
| )"; |
| |
| // Generate a shader binary for geo, tcs, tes: |
| ShShaderSpec spec = SH_GLES3_1_SPEC; |
| ShShaderOutput output = SH_SPIRV_VULKAN_OUTPUT; |
| mResources.EXT_geometry_shader = 1; |
| mResources.EXT_tessellation_shader = 1; |
| |
| // Geometry shader: |
| sh::ShaderBinaryBlob geometryShaderBinary; |
| ShHandle geometryCompiler = |
| sh::ConstructCompiler(GL_GEOMETRY_SHADER, spec, output, &mResources); |
| bool compileResult = |
| sh::GetShaderBinary(geometryCompiler, &kGS, 1, mCompileOptions, &geometryShaderBinary); |
| ASSERT_TRUE(compileResult); |
| if (geometryShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating geometry shader binary failed."; |
| } |
| |
| // tesselation control shader: |
| sh::ShaderBinaryBlob tessControlShaderBinary; |
| ShHandle tessControlCompiler = |
| sh::ConstructCompiler(GL_TESS_CONTROL_SHADER, spec, output, &mResources); |
| compileResult = sh::GetShaderBinary(tessControlCompiler, &kTCS, 1, mCompileOptions, |
| &tessControlShaderBinary); |
| ASSERT_TRUE(compileResult); |
| if (tessControlShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating tesselation control shader binary failed."; |
| } |
| |
| // tesselation evaluation shader: |
| sh::ShaderBinaryBlob tessEvaluationShaderBinary; |
| ShHandle tessEvaluationCompiler = |
| sh::ConstructCompiler(GL_TESS_EVALUATION_SHADER, spec, output, &mResources); |
| compileResult = sh::GetShaderBinary(tessEvaluationCompiler, &kTES, 1, mCompileOptions, |
| &tessEvaluationShaderBinary); |
| ASSERT_TRUE(compileResult); |
| if (tessEvaluationShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating tesselation evaluation shader binary failed."; |
| } |
| |
| GLint loadResult; |
| // Create vertex shader and load binary |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, mVertexShaderBinary.data(), |
| mVertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create geometry shader and load binary |
| GLuint geoShader = glCreateShader(GL_GEOMETRY_SHADER); |
| glShaderBinary(1, &geoShader, GL_SHADER_BINARY_ANGLE, geometryShaderBinary.data(), |
| geometryShaderBinary.size()); |
| glGetShaderiv(geoShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create tesselation control shader and load binary |
| GLuint tcShader = glCreateShader(GL_TESS_CONTROL_SHADER); |
| glShaderBinary(1, &tcShader, GL_SHADER_BINARY_ANGLE, tessControlShaderBinary.data(), |
| tessControlShaderBinary.size()); |
| glGetShaderiv(tcShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create tesselation evaluation and load binary |
| GLuint teShader = glCreateShader(GL_TESS_EVALUATION_SHADER); |
| glShaderBinary(1, &teShader, GL_SHADER_BINARY_ANGLE, tessEvaluationShaderBinary.data(), |
| tessEvaluationShaderBinary.size()); |
| glGetShaderiv(teShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create fragment shader and load binary |
| GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderBinary(1, &fragShader, GL_SHADER_BINARY_ANGLE, mFragmentShaderBinary.data(), |
| mFragmentShaderBinary.size()); |
| glGetShaderiv(fragShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create program from the shaders |
| GLuint newProgram = glCreateProgram(); |
| glAttachShader(newProgram, vertShader); |
| glAttachShader(newProgram, geoShader); |
| glAttachShader(newProgram, tcShader); |
| glAttachShader(newProgram, teShader); |
| glAttachShader(newProgram, fragShader); |
| glLinkProgram(newProgram); |
| newProgram = CheckLinkStatusAndReturnProgram(newProgram, true); |
| |
| // Test with a basic draw |
| drawPatches(newProgram, "a_position", 0.5f, 1.0f, GL_FALSE); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| } |
| |
| // Test glShaderBinary with complex shaders |
| TEST_P(ShaderBinaryTestES31, ComplexShader) |
| { |
| ANGLE_SKIP_TEST_IF(!supported()); |
| |
| const char *kVertexShader = R"(#version 310 es |
| uniform vec2 table[4]; |
| |
| in vec2 position; |
| in vec4 aTest; |
| |
| out vec2 texCoord; |
| out vec4 vTest; |
| |
| void main() |
| { |
| gl_Position = vec4(position + table[gl_InstanceID], 0, 1); |
| vTest = aTest; |
| texCoord = gl_Position.xy * 0.5 + vec2(0.5); |
| })"; |
| |
| const char *kFragmentShader = R"(#version 310 es |
| precision mediump float; |
| |
| struct S { sampler2D sampler; }; |
| uniform S uStruct; |
| |
| layout (binding = 0, std430) buffer Input { |
| float sampledInput; |
| }; |
| |
| in vec2 texCoord; |
| in vec4 vTest; |
| out vec4 my_FragColor; |
| |
| void main() |
| { |
| if (sampledInput == 1.0) |
| { |
| my_FragColor = texture(uStruct.sampler, texCoord); |
| } |
| else |
| { |
| my_FragColor = vTest; |
| } |
| })"; |
| |
| // Generate shader binaries: |
| ShShaderSpec spec = SH_GLES3_1_SPEC; |
| ShShaderOutput output = SH_SPIRV_VULKAN_OUTPUT; |
| |
| // Vertex shader: |
| sh::ShaderBinaryBlob vertexShaderBinary; |
| ShHandle vertexCompiler = sh::ConstructCompiler(GL_VERTEX_SHADER, spec, output, &mResources); |
| bool compileResult = sh::GetShaderBinary(vertexCompiler, &kVertexShader, 1, mCompileOptions, |
| &vertexShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (vertexShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating vertex shader binary failed."; |
| } |
| |
| // Fragment shader: |
| sh::ShaderBinaryBlob fragmentShaderBinary; |
| ShHandle fragmentCompiler = |
| sh::ConstructCompiler(GL_FRAGMENT_SHADER, spec, output, &mResources); |
| compileResult = sh::GetShaderBinary(fragmentCompiler, &kFragmentShader, 1, mCompileOptions, |
| &fragmentShaderBinary); |
| ASSERT_TRUE(compileResult); |
| |
| if (fragmentShaderBinary.size() == 0) |
| { |
| FAIL() << "Creating fragment shader binary failed."; |
| } |
| |
| GLint loadResult; |
| // Create vertex shader and load binary |
| GLuint vertShader = glCreateShader(GL_VERTEX_SHADER); |
| glShaderBinary(1, &vertShader, GL_SHADER_BINARY_ANGLE, vertexShaderBinary.data(), |
| vertexShaderBinary.size()); |
| glGetShaderiv(vertShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create fragment shader and load binary |
| GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER); |
| glShaderBinary(1, &fragShader, GL_SHADER_BINARY_ANGLE, fragmentShaderBinary.data(), |
| fragmentShaderBinary.size()); |
| glGetShaderiv(fragShader, GL_COMPILE_STATUS, &loadResult); |
| ASSERT_GL_TRUE(loadResult); |
| |
| // Create program from the shaders |
| GLuint newProgram = glCreateProgram(); |
| glAttachShader(newProgram, vertShader); |
| glAttachShader(newProgram, fragShader); |
| glLinkProgram(newProgram); |
| newProgram = CheckLinkStatusAndReturnProgram(newProgram, true); |
| glUseProgram(newProgram); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Setup instance offset table |
| constexpr GLfloat table[] = {-1, -1, -1, 1, 1, -1, 1, 1}; |
| GLint tableMemberLoc = glGetUniformLocation(newProgram, "table"); |
| ASSERT_NE(-1, tableMemberLoc); |
| glUniform2fv(tableMemberLoc, 4, table); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Setup red testure and sampler uniform |
| GLTexture tex; |
| glActiveTexture(GL_TEXTURE0); |
| glBindTexture(GL_TEXTURE_2D, tex); |
| GLubyte texData[] = {255u, 0u, 0u, 255u}; |
| glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData); |
| |
| GLint samplerMemberLoc = glGetUniformLocation(newProgram, "uStruct.sampler"); |
| ASSERT_NE(-1, samplerMemberLoc); |
| glUniform1i(samplerMemberLoc, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Setup the `aTest` attribute to blue |
| std::vector<Vector4> kInputAttribute(6, Vector4(0.0f, 0.0f, 1.0f, 1.0f)); |
| GLint positionLocation = glGetAttribLocation(newProgram, "aTest"); |
| glVertexAttribPointer(positionLocation, 4, GL_FLOAT, GL_FALSE, 0, kInputAttribute.data()); |
| glEnableVertexAttribArray(positionLocation); |
| |
| // Setup 'sampledInput' storage buffer to 1 |
| constexpr GLfloat kInputDataOne = 1.0f; |
| GLBuffer input; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, input); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLfloat), &kInputDataOne, GL_STATIC_COPY); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, input); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Test sampling texture with an instanced draw |
| drawQuadInstanced(newProgram, "position", 0.5f, 0.5f, GL_FALSE, 4); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red); |
| |
| // Setup 'sampledInput' storage buffer to 0 |
| constexpr GLfloat kInputDataZero = 0.0f; |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, input); |
| glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(GLfloat), &kInputDataZero, GL_STATIC_COPY); |
| glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, input); |
| glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); |
| ASSERT_GL_NO_ERROR(); |
| |
| // Test color attribute with an instanced draw |
| drawQuadInstanced(newProgram, "position", 0.5f, 0.5f, GL_FALSE, 4); |
| ASSERT_GL_NO_ERROR(); |
| EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue); |
| } |
| |
| // Use this to select which configurations (e.g. which renderer, which GLES major version) these |
| // tests should be run against. |
| ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND_ES31(ShaderBinaryTest); |
| |
| GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShaderBinaryTestES31); |
| ANGLE_INSTANTIATE_TEST_ES31(ShaderBinaryTestES31); |