Add support for glShaderBinary

This patch adds the following -
1. ANGLE_shader_binary extension and GL_SHADER_BINARY_ANGLE token.
2. Compiler support to generate shader binaries.
3. Update compiler to use SH_SPIRV_VULKAN_OUTPUT as output type for
Vulkan translator.
4. Support to load GL_SHADER_BINARY_ANGLE binaries.
5. end2end tests for glShaderBinary.

Tests: ShaderBinaryTest*
Bug: angleproject:7833
Change-Id: I191d5ba7c4d5304696f5e743c851dc945fa57858
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4137306
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
Commit-Queue: mohan maiya <m.maiya@samsung.com>
Reviewed-by: Charlie Lao <cclao@google.com>
This commit is contained in:
Mohan Maiya 2023-01-24 16:50:32 -08:00 коммит произвёл Angle LUCI CQ
Родитель 1d9984bd4c
Коммит 228973e731
29 изменённых файлов: 989 добавлений и 34 удалений

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

@ -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) {

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

@ -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) | &#x2714; | &#x2714; | &#x2714; | &#x2714; | &#x2714; | &#x2714; | &#x2714; |
| [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) | | | | | | | |

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

@ -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 <binaryFormat> 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

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

@ -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

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

@ -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<uint32_t>;
using BinaryBlob = std::vector<uint32_t>;
using ShaderBinaryBlob = std::vector<uint8_t>;
//
// 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:

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

@ -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":

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

@ -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":

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

@ -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":

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

@ -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":

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

@ -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":

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

@ -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":

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

@ -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",

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

@ -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);

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

@ -9,7 +9,12 @@
#include <sstream>
#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<gl::ShaderType>(mShaderType);
gl::CompiledShaderState state(shaderType);
state.buildCompiledShaderState(compilerHandle, IsOutputVulkan(mOutputType));
stream.writeBytes(
reinterpret_cast<const unsigned char *>(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<const uint8_t *>(&compileOptions), sizeof(compileOptions));
stream.writeBytes(reinterpret_cast<const uint8_t *>(&mResources), sizeof(mResources));
state.serialize(stream);
ASSERT(binaryOut);
*binaryOut = std::move(stream.getData());
return true;
}
bool TCompiler::validateAST(TIntermNode *root)
{
if (mCompileOptions.validateAST)

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

@ -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

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

@ -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<std::string, std::string> *GetNameHashingMap(const ShHandle handle)
{
TCompiler *compiler = GetCompilerFromHandle(handle);

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

@ -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,

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

@ -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,

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

@ -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, \

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

@ -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.";

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

@ -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<uint32_t>() != 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<uint8_t> 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<ShShaderOutput>(&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<uint8_t *>(&compileOptions), sizeof(ShCompileOptions));
ShBuiltInResources resources;
stream.readBytes(reinterpret_cast<uint8_t *>(&resources), sizeof(ShBuiltInResources));
setShaderKey(context, compileOptions, outputType, resources);
}
else
{
// Load binary from shader cache.
if (stream.readInt<uint32_t>() != 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<const uint8_t *>(&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<const uint8_t *>(&resources), sizeof(resources));
// Call the secure SHA hashing function.

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

@ -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<rx::ShaderImpl> mImplementation;

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

@ -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);

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

@ -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;

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

@ -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;

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

@ -10,6 +10,8 @@
#include <cstdint>
#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<uint8_t> 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;
}

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

@ -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) {

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

@ -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",

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

@ -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 <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);
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<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.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<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);
ANGLE_INSTANTIATE_TEST_ES31(ShaderBinaryTestES31);