зеркало из https://github.com/AvaloniaUI/angle.git
Reland "Add support for glShaderBinary"
This is a reland of commit 228973e731
with the following fixes -
1. Apply patch from Yuly to fix chromium build errors
2. Fix ShaderBinaryTest instantiation call
3. Add ShaderBinaryTest to expectations file for IOS
Original change's description:
> 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>
Bug: angleproject:7833
Change-Id: I21135c52e2bae955342a99aff5631ba0e687eff1
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/4195852
Reviewed-by: Yuly Novikov <ynovikov@chromium.org>
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Shahbaz Youssefi <syoussefi@chromium.org>
This commit is contained in:
Родитель
17ac694311
Коммит
1d9b8d802e
3
BUILD.gn
3
BUILD.gn
|
@ -731,6 +731,7 @@ template("translator_lib") {
|
|||
public_configs += [ ":external_config" ]
|
||||
|
||||
deps = [
|
||||
":angle_common_shader_state",
|
||||
":includes",
|
||||
":preprocessor",
|
||||
"$angle_root/src/common/spirv:angle_spirv_headers",
|
||||
|
@ -749,6 +750,7 @@ template("translator_lib") {
|
|||
public_deps = [
|
||||
":angle_common",
|
||||
":angle_translator_headers",
|
||||
":angle_version_info",
|
||||
]
|
||||
|
||||
if (is_win) {
|
||||
|
@ -975,7 +977,6 @@ angle_source_set("libANGLE_headers") {
|
|||
}
|
||||
public_deps = [
|
||||
":angle_common",
|
||||
":angle_common_shader_state",
|
||||
":angle_translator_headers",
|
||||
]
|
||||
public_configs += [ ":libANGLE_config" ]
|
||||
|
|
|
@ -228,6 +228,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) | | | | | | | |
|
||||
|
|
|
@ -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":
|
||||
"141aed19b332ec30c18435519f6d3677",
|
||||
"f5f52371fbb8c06637279e08226e6b7b",
|
||||
"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":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"src/libANGLE/gen_extensions.py":
|
||||
"6ea1cb1733c4df98b527bbf2752e118b",
|
||||
"src/libANGLE/gles_extensions_autogen.cpp":
|
||||
"b5117a0072ae5b7792bc60955afa2e13",
|
||||
"411ed8f1b3cb12863bbaa23ede010f10",
|
||||
"src/libANGLE/gles_extensions_autogen.h":
|
||||
"4a30be3df8fe821a2860ab3b7694d2eb",
|
||||
"d3bc8f686c8cb53305066d8d8aa5b03e",
|
||||
"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":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"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":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"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":
|
||||
"2a51f60d9a966fe6be5d64a35161979d",
|
||||
"4099e5a9feb583d46ae1eeb3693d78ee",
|
||||
"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":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"src/common/gl_enum_utils_autogen.cpp":
|
||||
"d0d426557a84e5f47943fa53980299fd",
|
||||
"src/common/gl_enum_utils_autogen.h":
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"scripts/gl_angle_ext.xml":
|
||||
"e42890bf014f4d4b7ebc07112bbb9764",
|
||||
"scripts/registry_xml.py":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"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":
|
||||
"4d1a88f9685fff42ac1f96690719e99c",
|
||||
"f1452c7d099b275f007f39c157dd3ec6",
|
||||
"src/libGLESv2/proc_table_cl_autogen.cpp":
|
||||
"ed003b0f041aaaa35b67d3fe07e61f91",
|
||||
"src/libGLESv2/proc_table_egl_autogen.cpp":
|
||||
|
|
|
@ -226,6 +226,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,
|
||||
|
|
|
@ -7580,8 +7580,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,
|
||||
|
|
|
@ -570,6 +570,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.";
|
||||
|
|
|
@ -422,7 +422,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)
|
||||
|
@ -800,24 +801,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;
|
||||
|
@ -825,7 +883,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;
|
||||
|
@ -834,17 +893,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.
|
||||
|
|
|
@ -271,8 +271,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
|
||||
{
|
||||
|
@ -289,11 +293,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;
|
||||
|
|
|
@ -240,6 +240,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);
|
||||
|
|
|
@ -676,6 +676,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;
|
||||
|
||||
|
|
|
@ -951,6 +951,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;
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ libangle_common_headers = [
|
|||
"src/common/CircularBuffer.h",
|
||||
"src/common/Color.h",
|
||||
"src/common/Color.inc",
|
||||
"src/common/CompiledShaderState.h",
|
||||
"src/common/FastVector.h",
|
||||
"src/common/FixedVector.h",
|
||||
"src/common/MemoryBuffer.h",
|
||||
|
@ -85,10 +86,8 @@ libangle_common_sources = libangle_common_headers + [
|
|||
"src/common/utilities.cpp",
|
||||
]
|
||||
|
||||
libangle_common_shader_state_sources = libangle_common_headers + [
|
||||
"src/common/CompiledShaderState.cpp",
|
||||
"src/common/CompiledShaderState.h",
|
||||
]
|
||||
libangle_common_shader_state_sources =
|
||||
libangle_common_headers + [ "src/common/CompiledShaderState.cpp" ]
|
||||
|
||||
libangle_common_cl_sources = [
|
||||
"src/common/PackedCLEnums_autogen.cpp",
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -135,6 +135,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",
|
||||
|
|
|
@ -755,9 +755,8 @@
|
|||
7768 IOS : ClipDistanceAPPLETest.ThreeClipDistances/ES3_OpenGL = SKIP
|
||||
7768 IOS : ClipDistanceAPPLETest.ThreeClipDistancesRedeclared/ES2_OpenGL = SKIP
|
||||
7768 IOS : ClipDistanceAPPLETest.ThreeClipDistancesRedeclared/ES3_OpenGL = SKIP
|
||||
|
||||
7778 IOS : FramebufferTest_ES3.DrawWithDifferentPreRotations/ES3_OpenGL = SKIP
|
||||
|
||||
7833 IOS : ShaderBinaryTest.* = SKIP
|
||||
7853 IOS : FramebufferExtensionsTest.ColorBufferHalfFloat/ES2_OpenGL = SKIP
|
||||
|
||||
// Test is disabled as it only pertains to desktop GLSL
|
||||
|
|
|
@ -0,0 +1,662 @@
|
|||
//
|
||||
// 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);
|
||||
|
||||
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShaderBinaryTestES31);
|
||||
ANGLE_INSTANTIATE_TEST_ES31(ShaderBinaryTestES31);
|
Загрузка…
Ссылка в новой задаче