diff --git a/BUILD.gn b/BUILD.gn index 707cdb37f..7a5411452 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -654,7 +654,10 @@ angle_static_library("angle_gpu_info_util") { angle_source_set("angle_translator_headers") { sources = angle_translator_exported_headers - public_deps = [ ":includes" ] + public_deps = [ + ":angle_common_shader_state", + ":includes", + ] } template("translator_lib") { @@ -749,6 +752,7 @@ template("translator_lib") { public_deps = [ ":angle_common", ":angle_translator_headers", + ":angle_version_info", ] if (is_win) { diff --git a/doc/ExtensionSupport.md b/doc/ExtensionSupport.md index c654b9595..c56da0032 100644 --- a/doc/ExtensionSupport.md +++ b/doc/ExtensionSupport.md @@ -227,6 +227,7 @@ using data from registry_xml.py and gl.xml. | [GL_ANGLE_robust_fragment_shader_output](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_robust_fragment_shader_output.txt) | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | ✔ | | [GL_ANGLE_robust_resource_initialization](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_robust_resource_initialization.txt) | | | | | | | | | [GL_ANGLE_semaphore_fuchsia](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_semaphore_fuchsia.txt) | | | | | | | | +| [GL_ANGLE_shader_binary](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_shader_binary.txt) | | | | | | | | | [GL_ANGLE_shader_pixel_local_storage](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_shader_pixel_local_storage.txt) | | | | | | | | | [GL_ANGLE_shader_pixel_local_storage_coherent](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/ANGLE_shader_pixel_local_storage.txt) | | | | | | | | | [GL_CHROMIUM_sync_query](https://chromium.googlesource.com/angle/angle/+/refs/heads/main/extensions/CHROMIUM_sync_query.txt) | | | | | | | | diff --git a/extensions/ANGLE_shader_binary.txt b/extensions/ANGLE_shader_binary.txt new file mode 100644 index 000000000..1ef484101 --- /dev/null +++ b/extensions/ANGLE_shader_binary.txt @@ -0,0 +1,74 @@ +Name + + ANGLE_shader_binary + +Name Strings + + GL_ANGLE_shader_binary + +Contributors + + Jonah Taylor, Samsung + Mahmoud Kharsa, Samsung + +Contacts + + Mahmoud Kharsa, Samsung (m.kharsa 'at' samsung 'dot' com) + +Notice + + Copyright (c) 2023 The Khronos Group Inc. Copyright terms at + http://www.khronos.org/registry/speccopyright.html + +Status + + Draft. + +Version + + Version 1, January 03, 2023 + +Number + + OpenGL ES Extension ### + +Dependencies + + OpenGL ES 2.0 is required. + +Overview + + This extension makes available a shader binary format, SHADER_BINARY_ANGLE. + It enables the loading of shader binaries pre-compiled with ANGLE. + +New Types + + None. + +New Procedures and Functions + + None. + +New Tokens + + Accepted by the parameter of ShaderBinary: + + GL_SHADER_BINARY_ANGLE 0x96BB + +Errors + + An INVALID_VALUE is generated if the binary points to an incompatible + shader binary that was generated using a different version of ANGLE. + +New State + + None. + +Issues + + None. + +Revision History + + #1 - (January 03, 2023) Mahmoud Kharsa + Original draft diff --git a/include/GLES2/gl2ext_angle.h b/include/GLES2/gl2ext_angle.h index ea9f3d187..37b86898c 100644 --- a/include/GLES2/gl2ext_angle.h +++ b/include/GLES2/gl2ext_angle.h @@ -671,6 +671,14 @@ GL_APICALL void GL_APIENTRY glGetFramebufferPixelLocalStorageParameterivANGLE (G #endif /* GL_ANGLE_rgbx_internal_format */ +#ifndef GL_ANGLE_shader_binary +#define GL_ANGLE_shader_binary + +// General shader binary format +#define GL_SHADER_BINARY_ANGLE 0x96BB + +#endif /* GL_ANGLE_shader_binary */ + #ifndef GL_ANGLE_logic_op #define GL_ANGLE_logic_op diff --git a/include/GLSLANG/ShaderLang.h b/include/GLSLANG/ShaderLang.h index 8218556e6..93b227a64 100644 --- a/include/GLSLANG/ShaderLang.h +++ b/include/GLSLANG/ShaderLang.h @@ -26,7 +26,7 @@ // Version number for shader translation API. // It is incremented every time the API changes. -#define ANGLE_SH_VERSION 319 +#define ANGLE_SH_VERSION 320 enum ShShaderSpec { @@ -684,7 +684,8 @@ using ShHandle = void *; namespace sh { -using BinaryBlob = std::vector; +using BinaryBlob = std::vector; +using ShaderBinaryBlob = std::vector; // // Driver must call this first, once, before doing any other compiler operations. @@ -774,6 +775,15 @@ const std::string &GetObjectCode(const ShHandle handle); // handle: Specifies the compiler const BinaryBlob &GetObjectBinaryBlob(const ShHandle handle); +// Returns a full binary for a compiled shader, to be loaded with glShaderBinary during runtime. +// Parameters: +// handle: Specifies the compiler +bool GetShaderBinary(const ShHandle handle, + const char *const shaderStrings[], + size_t numStrings, + const ShCompileOptions &compileOptions, + ShaderBinaryBlob *const binaryOut); + // Returns a (original_name, hash) map containing all the user defined names in the shader, // including variable names, function names, struct names, and struct field names. // Parameters: diff --git a/scripts/code_generation_hashes/Extension_files.json b/scripts/code_generation_hashes/Extension_files.json index 6e0114300..877b45ee3 100644 --- a/scripts/code_generation_hashes/Extension_files.json +++ b/scripts/code_generation_hashes/Extension_files.json @@ -1,6 +1,6 @@ { "doc/ExtensionSupport.md": - "5d87ec1dddf7731ef8f72cc0ab29f4ea", + "6b7a5bb0491ee22595003dab0717feef", "scripts/egl_angle_ext.xml": "9fbe802e21374f4c0bff6f7a68bb928c", "scripts/extension_data/intel_630_linux.json": @@ -22,13 +22,13 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "src/libANGLE/gen_extensions.py": "6ea1cb1733c4df98b527bbf2752e118b", "src/libANGLE/gles_extensions_autogen.cpp": - "eddad048db9e3d8650c61d8cfdfb0455", + "d4b08b0863c635ff36b6f4d772ef0aef", "src/libANGLE/gles_extensions_autogen.h": - "75f21c904b9ef3d7202db7cb96dfe7bf", + "f38310dfbf41a17079b546b2fff599fe", "third_party/EGL-Registry/src/api/egl.xml": "2056d54ea07156f1988ca1366bdee21a", "third_party/OpenCL-Docs/src/xml/cl.xml": diff --git a/scripts/code_generation_hashes/GL_EGL_WGL_loader.json b/scripts/code_generation_hashes/GL_EGL_WGL_loader.json index 9e7149046..6e67e18c0 100644 --- a/scripts/code_generation_hashes/GL_EGL_WGL_loader.json +++ b/scripts/code_generation_hashes/GL_EGL_WGL_loader.json @@ -6,7 +6,7 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "src/libEGL/egl_loader_autogen.cpp": "8a743c210f7dbe7e306849203dff7889", "src/libEGL/egl_loader_autogen.h": diff --git a/scripts/code_generation_hashes/GL_EGL_entry_points.json b/scripts/code_generation_hashes/GL_EGL_entry_points.json index 7a5bc50e9..601a7b9c3 100644 --- a/scripts/code_generation_hashes/GL_EGL_entry_points.json +++ b/scripts/code_generation_hashes/GL_EGL_entry_points.json @@ -10,7 +10,7 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "src/common/entry_points_enum_autogen.cpp": "a60e991004c77e81390a1f75b0471a92", "src/common/entry_points_enum_autogen.h": @@ -38,7 +38,7 @@ "src/libANGLE/Context_gles_3_2_autogen.h": "48567dca16fd881dfe6d61fee0e3106f", "src/libANGLE/Context_gles_ext_autogen.h": - "157ca9e862b7375c1c9c91234ca0516e", + "1dd20f23334ce6eca927de9fa68270b4", "src/libANGLE/capture/capture_egl_autogen.cpp": "ab06f4f9d65450de5a474218d8765ff2", "src/libANGLE/capture/capture_egl_autogen.h": diff --git a/scripts/code_generation_hashes/GLenum_value_to_string_map.json b/scripts/code_generation_hashes/GLenum_value_to_string_map.json index 9a922607d..4fb0069b8 100644 --- a/scripts/code_generation_hashes/GLenum_value_to_string_map.json +++ b/scripts/code_generation_hashes/GLenum_value_to_string_map.json @@ -4,7 +4,7 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "src/common/gl_enum_utils_autogen.cpp": "fd5198762b90cadf7e682ffb89ae379c", "src/common/gl_enum_utils_autogen.h": diff --git a/scripts/code_generation_hashes/interpreter_utils.json b/scripts/code_generation_hashes/interpreter_utils.json index fee656afe..77dcd664f 100644 --- a/scripts/code_generation_hashes/interpreter_utils.json +++ b/scripts/code_generation_hashes/interpreter_utils.json @@ -6,7 +6,7 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "third_party/EGL-Registry/src/api/egl.xml": "2056d54ea07156f1988ca1366bdee21a", "third_party/OpenCL-Docs/src/xml/cl.xml": diff --git a/scripts/code_generation_hashes/proc_table.json b/scripts/code_generation_hashes/proc_table.json index b3aa69107..0d02d10a4 100644 --- a/scripts/code_generation_hashes/proc_table.json +++ b/scripts/code_generation_hashes/proc_table.json @@ -6,7 +6,7 @@ "scripts/gl_angle_ext.xml": "e42890bf014f4d4b7ebc07112bbb9764", "scripts/registry_xml.py": - "3d4835d0da60f1d8b76a8ab14f791e31", + "63aa0c1fab6c330985a239ab95945a7d", "src/libGLESv2/proc_table_cl_autogen.cpp": "ed003b0f041aaaa35b67d3fe07e61f91", "src/libGLESv2/proc_table_egl_autogen.cpp": diff --git a/scripts/registry_xml.py b/scripts/registry_xml.py index 3c2ac2c75..1c0a063fc 100644 --- a/scripts/registry_xml.py +++ b/scripts/registry_xml.py @@ -225,6 +225,7 @@ angle_es_only_extensions = [ "GL_ANGLE_rgbx_internal_format", "GL_ANGLE_robust_client_memory", "GL_ANGLE_robust_resource_initialization", + "GL_ANGLE_shader_binary", "GL_ANGLE_shader_pixel_local_storage", "GL_ANGLE_shader_pixel_local_storage_coherent", "GL_ANGLE_webgl_compatibility", diff --git a/src/compiler/fuzz/translator_fuzzer.cpp b/src/compiler/fuzz/translator_fuzzer.cpp index 48dcb8448..266aba3bc 100644 --- a/src/compiler/fuzz/translator_fuzzer.cpp +++ b/src/compiler/fuzz/translator_fuzzer.cpp @@ -151,6 +151,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) validOutputs.push_back(SH_GLSL_430_CORE_OUTPUT); validOutputs.push_back(SH_GLSL_440_CORE_OUTPUT); validOutputs.push_back(SH_GLSL_450_CORE_OUTPUT); + validOutputs.push_back(SH_SPIRV_VULKAN_OUTPUT); validOutputs.push_back(SH_HLSL_3_0_OUTPUT); validOutputs.push_back(SH_HLSL_4_1_OUTPUT); validOutputs.push_back(SH_HLSL_4_0_FL9_3_OUTPUT); diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp index 1a6ab8bd8..37ca80a28 100644 --- a/src/compiler/translator/Compiler.cpp +++ b/src/compiler/translator/Compiler.cpp @@ -9,7 +9,12 @@ #include #include "angle_gl.h" -#include "common/utilities.h" + +#include "common/BinaryStream.h" +#include "common/CompiledShaderState.h" +#include "common/PackedEnums.h" +#include "common/angle_version_info.h" + #include "compiler/translator/CallDAG.h" #include "compiler/translator/CollectVariables.h" #include "compiler/translator/Initialize.h" @@ -181,7 +186,8 @@ bool RemoveInvariant(sh::GLenum shaderType, ShShaderOutput outputType, const ShCompileOptions &compileOptions) { - if (shaderType == GL_FRAGMENT_SHADER && IsGLSL420OrNewer(outputType)) + if (shaderType == GL_FRAGMENT_SHADER && + (IsGLSL420OrNewer(outputType) || IsOutputVulkan(outputType))) return true; if (compileOptions.removeInvariantAndCentroidForESSL3 && shaderVersion >= 300 && @@ -625,6 +631,47 @@ unsigned int TCompiler::getSharedMemorySize() const return sharedMemSize; } +bool TCompiler::getShaderBinary(const ShHandle compilerHandle, + const char *const shaderStrings[], + size_t numStrings, + const ShCompileOptions &compileOptions, + ShaderBinaryBlob *const binaryOut) +{ + if (!compile(shaderStrings, numStrings, compileOptions)) + { + return false; + } + + gl::BinaryOutputStream stream; + gl::ShaderType shaderType = gl::FromGLenum(mShaderType); + gl::CompiledShaderState state(shaderType); + state.buildCompiledShaderState(compilerHandle, IsOutputVulkan(mOutputType)); + + stream.writeBytes( + reinterpret_cast(angle::GetANGLEShaderProgramVersion()), + angle::GetANGLEShaderProgramVersionHashSize()); + stream.writeEnum(shaderType); + stream.writeEnum(mOutputType); + + // Serialize the full source string for the shader. Ignore the source path if it is provided. + std::string sourceString; + size_t startingIndex = compileOptions.sourcePath ? 1 : 0; + for (size_t i = startingIndex; i < numStrings; ++i) + { + sourceString.append(shaderStrings[i]); + } + stream.writeString(sourceString); + + stream.writeBytes(reinterpret_cast(&compileOptions), sizeof(compileOptions)); + stream.writeBytes(reinterpret_cast(&mResources), sizeof(mResources)); + + state.serialize(stream); + + ASSERT(binaryOut); + *binaryOut = std::move(stream.getData()); + return true; +} + bool TCompiler::validateAST(TIntermNode *root) { if (mCompileOptions.validateAST) diff --git a/src/compiler/translator/Compiler.h b/src/compiler/translator/Compiler.h index 77d1bf8f6..6a976c707 100644 --- a/src/compiler/translator/Compiler.h +++ b/src/compiler/translator/Compiler.h @@ -195,6 +195,13 @@ class TCompiler : public TShHandleBase sh::GLenum getShaderType() const { return mShaderType; } + // Generate a self-contained binary representation of the shader. + bool getShaderBinary(const ShHandle compilerHandle, + const char *const shaderStrings[], + size_t numStrings, + const ShCompileOptions &compileOptions, + ShaderBinaryBlob *const binaryOut); + // Validate the AST and produce errors if it is inconsistent. bool validateAST(TIntermNode *root); // Some transformations may need to temporarily disable validation until they are complete. A diff --git a/src/compiler/translator/ShaderLang.cpp b/src/compiler/translator/ShaderLang.cpp index 74ecb20c8..a622cc192 100644 --- a/src/compiler/translator/ShaderLang.cpp +++ b/src/compiler/translator/ShaderLang.cpp @@ -463,6 +463,18 @@ const BinaryBlob &GetObjectBinaryBlob(const ShHandle handle) return infoSink.obj.getBinary(); } +bool GetShaderBinary(const ShHandle handle, + const char *const shaderStrings[], + size_t numStrings, + const ShCompileOptions &compileOptions, + ShaderBinaryBlob *const binaryOut) +{ + TCompiler *compiler = GetCompilerFromHandle(handle); + ASSERT(compiler); + + return compiler->getShaderBinary(handle, shaderStrings, numStrings, compileOptions, binaryOut); +} + const std::map *GetNameHashingMap(const ShHandle handle) { TCompiler *compiler = GetCompilerFromHandle(handle); diff --git a/src/compiler/translator/TranslatorVulkan.cpp b/src/compiler/translator/TranslatorVulkan.cpp index 3af080daa..6517e5a9b 100644 --- a/src/compiler/translator/TranslatorVulkan.cpp +++ b/src/compiler/translator/TranslatorVulkan.cpp @@ -159,8 +159,8 @@ bool DeclareDefaultUniforms(TCompiler *compiler, TLayoutQualifier layoutQualifier = TLayoutQualifier::Create(); layoutQualifier.blockStorage = EbsStd140; const TVariable *uniformBlock = DeclareInterfaceBlock( - root, symbolTable, uniformList, EvqUniform, layoutQualifier, TMemoryQualifier::Create(), 0, - ImmutableString(kDefaultUniformNames[shaderType]), ImmutableString("")); + root, symbolTable, uniformList, EvqUniform, layoutQualifier, TMemoryQualifier::Create(), 0, + ImmutableString(kDefaultUniformNames[shaderType]), ImmutableString("")); // Create a map from the uniform variables to new variables that reference the fields of the // block. @@ -641,7 +641,7 @@ bool HasFramebufferFetch(const TExtensionBehavior &extBehavior, } // anonymous namespace TranslatorVulkan::TranslatorVulkan(sh::GLenum type, ShShaderSpec spec) - : TCompiler(type, spec, SH_GLSL_450_CORE_OUTPUT) + : TCompiler(type, spec, SH_SPIRV_VULKAN_OUTPUT) {} bool TranslatorVulkan::translateImpl(TIntermBlock *root, diff --git a/src/libANGLE/Context.cpp b/src/libANGLE/Context.cpp index 79691bb05..380e0f2cc 100644 --- a/src/libANGLE/Context.cpp +++ b/src/libANGLE/Context.cpp @@ -7573,8 +7573,9 @@ void Context::shaderBinary(GLsizei n, const void *binary, GLsizei length) { - // No binary shader formats are supported. - UNIMPLEMENTED(); + Shader *shaderObject = getShader(*shaders); + ASSERT(shaderObject != nullptr); + ANGLE_CONTEXT_TRY(shaderObject->loadShaderBinary(this, binary, length)); } void Context::bindFragDataLocationIndexed(ShaderProgramID program, diff --git a/src/libANGLE/Context_gles_ext_autogen.h b/src/libANGLE/Context_gles_ext_autogen.h index 50402f434..922d09d8c 100644 --- a/src/libANGLE/Context_gles_ext_autogen.h +++ b/src/libANGLE/Context_gles_ext_autogen.h @@ -569,6 +569,7 @@ /* GL_ANGLE_semaphore_fuchsia */ \ void importSemaphoreZirconHandle(SemaphoreID semaphorePacked, HandleType handleTypePacked, \ GLuint handle); \ + /* GL_ANGLE_shader_binary */ \ /* GL_ANGLE_shader_pixel_local_storage */ \ void framebufferMemorylessPixelLocalStorage(GLint plane, GLenum internalformat); \ void framebufferTexturePixelLocalStorage(GLint plane, TextureID backingtexturePacked, \ diff --git a/src/libANGLE/ErrorStrings.h b/src/libANGLE/ErrorStrings.h index d365b62ed..a0e2e2138 100644 --- a/src/libANGLE/ErrorStrings.h +++ b/src/libANGLE/ErrorStrings.h @@ -357,6 +357,9 @@ MSG kInvalidSampleMaskNumber = "MaskNumber cannot be greater than or equal to th MSG kInvalidSampler = "Sampler is not valid"; MSG kInvalidSamplerName = "name is not a valid sampler."; MSG kInvalidShaderBinaryFormat = "Invalid shader binary format."; +MSG kInvalidShaderBinary = "Invalid or incompatible shader binary."; +MSG kMismatchedShaderBinaryType = "Mismatched shader binary and shader object type."; +MSG kInvalidShaderCount = "Invalid Shader count."; MSG kInvalidShaderName = "Shader object expected."; MSG kInvalidShaderType = "Invalid shader type."; MSG kInvalidShadingModel = "Invalid shading model."; diff --git a/src/libANGLE/Shader.cpp b/src/libANGLE/Shader.cpp index 0da60e6ee..35b799121 100644 --- a/src/libANGLE/Shader.cpp +++ b/src/libANGLE/Shader.cpp @@ -361,7 +361,8 @@ void Shader::compile(const Context *context) ASSERT(compilerHandle); // Find a shader in Blob Cache - setShaderKey(context, options, compilerInstance); + setShaderKey(context, options, compilerInstance.getShaderOutputType(), + compilerInstance.getBuiltInResources()); ASSERT(!mShaderHash.empty()); MemoryShaderCache *shaderCache = context->getMemoryShaderCache(); if (shaderCache) @@ -739,24 +740,81 @@ angle::Result Shader::serialize(const Context *context, angle::MemoryBuffer *bin return angle::Result::Continue; } -angle::Result Shader::deserialize(const Context *context, BinaryInputStream &stream) +angle::Result Shader::deserialize(BinaryInputStream &stream) { - if (stream.readInt() != kShaderCacheIdentifier) + mState.mCompiledShaderState.deserialize(stream); + + if (stream.error()) { + // Error while deserializing binary stream return angle::Result::Stop; } - mState.mCompiledShaderState.deserialize(stream); return angle::Result::Continue; } angle::Result Shader::loadBinary(const Context *context, const void *binary, GLsizei length) { - BinaryInputStream stream(binary, length); - ANGLE_TRY(deserialize(context, stream)); + return loadBinaryImpl(context, binary, length, false); +} - // Only successfully-compiled shaders are serialized. If deserialization is successful, - // we can assume the CompileStatus. +angle::Result Shader::loadShaderBinary(const Context *context, const void *binary, GLsizei length) +{ + return loadBinaryImpl(context, binary, length, true); +} + +angle::Result Shader::loadBinaryImpl(const Context *context, + const void *binary, + GLsizei length, + bool generatedWithOfflineCompiler) +{ + BinaryInputStream stream(binary, length); + + // Shader binaries generated with offline compiler have additional fields + if (generatedWithOfflineCompiler) + { + // Load binary from a glShaderBinary call. + // Validation layer should have already verified that the shader program version and shader + // type match + std::vector commitString(angle::GetANGLEShaderProgramVersionHashSize(), 0); + stream.readBytes(commitString.data(), commitString.size()); + ASSERT(memcmp(commitString.data(), angle::GetANGLEShaderProgramVersion(), + commitString.size()) == 0); + + gl::ShaderType shaderType; + stream.readEnum(&shaderType); + ASSERT(mType == shaderType); + + // Get fields needed to generate the key for memory caches. + ShShaderOutput outputType; + stream.readEnum(&outputType); + + // Get the shader's source string. + mState.mSource = stream.readString(); + + // In the absence of element-by-element serialize/deserialize functions, read + // ShCompileOptions and ShBuiltInResources as raw binary blobs. + ShCompileOptions compileOptions; + stream.readBytes(reinterpret_cast(&compileOptions), sizeof(ShCompileOptions)); + + ShBuiltInResources resources; + stream.readBytes(reinterpret_cast(&resources), sizeof(ShBuiltInResources)); + + setShaderKey(context, compileOptions, outputType, resources); + } + else + { + // Load binary from shader cache. + if (stream.readInt() != kShaderCacheIdentifier) + { + return angle::Result::Stop; + } + } + + ANGLE_TRY(deserialize(stream)); + + // Only successfully-compiled shaders are serialized. If deserialization is successful, we can + // assume the CompileStatus. mState.mCompileStatus = CompileStatus::COMPILED; return angle::Result::Continue; @@ -764,7 +822,8 @@ angle::Result Shader::loadBinary(const Context *context, const void *binary, GLs void Shader::setShaderKey(const Context *context, const ShCompileOptions &compileOptions, - const ShCompilerInstance &compilerInstance) + const ShShaderOutput &outputType, + const ShBuiltInResources &resources) { // Compute shader key. BinaryOutputStream hashStream; @@ -773,17 +832,16 @@ void Shader::setShaderKey(const Context *context, hashStream.writeEnum(mType); hashStream.writeString(mState.getSource()); - // Include the commit hash + // Include the shader program version hash. hashStream.writeString(angle::GetANGLEShaderProgramVersion()); hashStream.writeEnum(Compiler::SelectShaderSpec(context->getState())); - hashStream.writeEnum(compilerInstance.getShaderOutputType()); + hashStream.writeEnum(outputType); hashStream.writeBytes(reinterpret_cast(&compileOptions), sizeof(compileOptions)); // Include the ShBuiltInResources, which represent the extensions and constants used by the // shader. - const ShBuiltInResources resources = compilerInstance.getBuiltInResources(); hashStream.writeBytes(reinterpret_cast(&resources), sizeof(resources)); // Call the secure SHA hashing function. diff --git a/src/libANGLE/Shader.h b/src/libANGLE/Shader.h index b87cce3e2..805a4f12b 100644 --- a/src/libANGLE/Shader.h +++ b/src/libANGLE/Shader.h @@ -268,8 +268,12 @@ class Shader final : angle::NonCopyable, public LabeledObject // Writes a shader's binary to the output memory buffer. angle::Result serialize(const Context *context, angle::MemoryBuffer *binaryOut) const; - angle::Result deserialize(const Context *context, BinaryInputStream &stream); + angle::Result deserialize(BinaryInputStream &stream); + + // Load a binary from shader cache. angle::Result loadBinary(const Context *context, const void *binary, GLsizei length); + // Load a binary from a glShaderBinary call. + angle::Result loadShaderBinary(const Context *context, const void *binary, GLsizei length); void writeShaderKey(BinaryOutputStream *streamOut) const { @@ -286,11 +290,16 @@ class Shader final : angle::NonCopyable, public LabeledObject GLsizei bufSize, GLsizei *length, char *buffer); + angle::Result loadBinaryImpl(const Context *context, + const void *binary, + GLsizei length, + bool generatedWithOfflineCompiler); // Compute a key to uniquely identify the shader object in memory caches. void setShaderKey(const Context *context, const ShCompileOptions &compileOptions, - const ShCompilerInstance &compilerInstance); + const ShShaderOutput &outputType, + const ShBuiltInResources &resources); ShaderState mState; std::unique_ptr mImplementation; diff --git a/src/libANGLE/gles_extensions_autogen.cpp b/src/libANGLE/gles_extensions_autogen.cpp index b4ab0d801..d0fc12078 100644 --- a/src/libANGLE/gles_extensions_autogen.cpp +++ b/src/libANGLE/gles_extensions_autogen.cpp @@ -239,6 +239,7 @@ const ExtensionInfoMap &GetExtensionInfoMap() map["GL_ANGLE_robust_fragment_shader_output"] = enableableExtension(&Extensions::robustFragmentShaderOutputANGLE); map["GL_ANGLE_robust_resource_initialization"] = esOnlyExtension(&Extensions::robustResourceInitializationANGLE); map["GL_ANGLE_semaphore_fuchsia"] = enableableExtension(&Extensions::semaphoreFuchsiaANGLE); + map["GL_ANGLE_shader_binary"] = esOnlyExtension(&Extensions::shaderBinaryANGLE); map["GL_ANGLE_shader_pixel_local_storage"] = esOnlyExtension(&Extensions::shaderPixelLocalStorageANGLE); map["GL_ANGLE_shader_pixel_local_storage_coherent"] = esOnlyExtension(&Extensions::shaderPixelLocalStorageCoherentANGLE); map["GL_CHROMIUM_sync_query"] = enableableExtension(&Extensions::syncQueryCHROMIUM); diff --git a/src/libANGLE/gles_extensions_autogen.h b/src/libANGLE/gles_extensions_autogen.h index 8819b18a3..35f765ddf 100644 --- a/src/libANGLE/gles_extensions_autogen.h +++ b/src/libANGLE/gles_extensions_autogen.h @@ -669,6 +669,9 @@ struct Extensions // GL_ANGLE_semaphore_fuchsia bool semaphoreFuchsiaANGLE = false; + // GL_ANGLE_shader_binary + bool shaderBinaryANGLE = false; + // GL_ANGLE_shader_pixel_local_storage bool shaderPixelLocalStorageANGLE = false; diff --git a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp index b3a91fb42..194d09402 100644 --- a/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp +++ b/src/libANGLE/renderer/vulkan/vk_caps_utils.cpp @@ -950,6 +950,9 @@ void RendererVk::ensureCapsInitialized() const mNativeExtensions.getProgramBinaryOES = true; mNativeCaps.programBinaryFormats.push_back(GL_PROGRAM_BINARY_ANGLE); + // Enable Shader Binary extension. + mNativeCaps.shaderBinaryFormats.push_back(GL_SHADER_BINARY_ANGLE); + // Enable GL_NV_pixel_buffer_object extension. mNativeExtensions.pixelBufferObjectNV = true; diff --git a/src/libANGLE/validationES2.cpp b/src/libANGLE/validationES2.cpp index 3c3539b51..b06b82e9f 100644 --- a/src/libANGLE/validationES2.cpp +++ b/src/libANGLE/validationES2.cpp @@ -10,6 +10,8 @@ #include +#include "common/BinaryStream.h" +#include "common/angle_version_info.h" #include "common/mathutil.h" #include "common/string_utils.h" #include "common/utilities.h" @@ -4904,6 +4906,53 @@ bool ValidateShaderBinary(const Context *context, return false; } + ASSERT(binaryformat == GL_SHADER_BINARY_ANGLE); + + if (n <= 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidShaderCount); + return false; + } + + if (length < 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kNegativeLength); + return false; + } + + // GL_SHADER_BINARY_ANGLE shader binaries contain a single shader. + if (n > 1) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kInvalidShaderCount); + return false; + } + + Shader *shaderObject = GetValidShader(context, entryPoint, shaders[0]); + if (!shaderObject) + { + return false; + } + + // Check ANGLE version used to generate binary matches the current version. + BinaryInputStream stream(binary, length); + std::vector versionString(angle::GetANGLEShaderProgramVersionHashSize(), 0); + stream.readBytes(versionString.data(), versionString.size()); + if (memcmp(versionString.data(), angle::GetANGLEShaderProgramVersion(), versionString.size()) != + 0) + { + context->validationError(entryPoint, GL_INVALID_VALUE, kInvalidShaderBinary); + return false; + } + + // Check that the shader type of the binary matches the type of target shader. + gl::ShaderType shaderType; + stream.readEnum(&shaderType); + if (shaderObject->getType() != shaderType) + { + context->validationError(entryPoint, GL_INVALID_OPERATION, kMismatchedShaderBinaryType); + return false; + } + return true; } diff --git a/src/tests/BUILD.gn b/src/tests/BUILD.gn index 1e5981145..63bb1ba86 100644 --- a/src/tests/BUILD.gn +++ b/src/tests/BUILD.gn @@ -266,6 +266,7 @@ if (is_win || is_linux || is_chromeos || is_android || is_fuchsia || is_apple) { ":angle_common_test_utils_shared", "$angle_root:angle_gl_enum_utils", "$angle_root:angle_image_util", + "$angle_root:translator", ] if (is_ios) { diff --git a/src/tests/angle_end2end_tests.gni b/src/tests/angle_end2end_tests.gni index a7e130224..4ff133ca1 100644 --- a/src/tests/angle_end2end_tests.gni +++ b/src/tests/angle_end2end_tests.gni @@ -134,6 +134,7 @@ angle_end2end_tests_sources = [ "gl_tests/SRGBTextureTest.cpp", "gl_tests/SamplersTest.cpp", "gl_tests/SemaphoreTest.cpp", + "gl_tests/ShaderBinaryTest.cpp", "gl_tests/ShaderInterpTest.cpp", "gl_tests/ShaderNonConstGlobalInitializerTest.cpp", "gl_tests/ShaderStorageBufferTest.cpp", diff --git a/src/tests/gl_tests/ShaderBinaryTest.cpp b/src/tests/gl_tests/ShaderBinaryTest.cpp new file mode 100644 index 000000000..accdc5f1b --- /dev/null +++ b/src/tests/gl_tests/ShaderBinaryTest.cpp @@ -0,0 +1,660 @@ +// +// 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 + +#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); + + mCompileOptions.objectCode = true; + mCompileOptions.variables = 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(); + 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 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.variables = 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 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); +ANGLE_INSTANTIATE_TEST_ES31(ShaderBinaryTestES31);