angle/util/shader_utils.cpp

592 строки
12 KiB
C++
Исходник Обычный вид История

//
// Copyright 2014 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.
//
2019-10-09 17:26:25 +03:00
#include "util/shader_utils.h"
#include <cstring>
#include <fstream>
#include <iostream>
#include <vector>
2019-10-09 17:26:25 +03:00
#include "util/test_utils.h"
namespace
{
2019-10-09 17:26:25 +03:00
bool ReadEntireFile(const std::string &filePath, std::string *contentsOut)
{
2019-10-09 17:26:25 +03:00
constexpr uint32_t kMaxBufferSize = 2000;
char buffer[kMaxBufferSize] = {};
if (!angle::ReadEntireFileToString(filePath.c_str(), buffer, kMaxBufferSize) ||
strlen(buffer) == 0)
return false;
*contentsOut = buffer;
return true;
}
GLuint CompileProgramInternal(const char *vsSource,
const char *gsSource,
const char *fsSource,
const std::function<void(GLuint)> &preLinkCallback)
{
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
if (vs == 0 || fs == 0)
{
glDeleteShader(fs);
glDeleteShader(vs);
return 0;
}
GLuint program = glCreateProgram();
glAttachShader(program, vs);
glDeleteShader(vs);
glAttachShader(program, fs);
glDeleteShader(fs);
GLuint gs = 0;
if (strlen(gsSource) > 0)
{
gs = CompileShader(GL_GEOMETRY_SHADER_EXT, gsSource);
if (gs == 0)
{
glDeleteShader(vs);
glDeleteShader(fs);
glDeleteProgram(program);
return 0;
}
glAttachShader(program, gs);
glDeleteShader(gs);
}
if (preLinkCallback)
{
preLinkCallback(program);
}
glLinkProgram(program);
return CheckLinkStatusAndReturnProgram(program, true);
}
} // namespace
GLuint CompileShader(GLenum type, const char *source)
{
GLuint shader = glCreateShader(type);
const char *sourceArray[1] = {source};
glShaderSource(shader, 1, sourceArray, nullptr);
glCompileShader(shader);
GLint compileResult;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
if (compileResult == 0)
{
GLint infoLogLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLogLength);
// Info log length includes the null terminator, so 1 means that the info log is an empty
// string.
if (infoLogLength > 1)
{
std::vector<GLchar> infoLog(infoLogLength);
glGetShaderInfoLog(shader, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
std::cerr << "shader compilation failed: " << &infoLog[0];
}
else
{
std::cerr << "shader compilation failed. <Empty log message>";
}
std::cerr << std::endl;
glDeleteShader(shader);
shader = 0;
}
return shader;
}
GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
{
2019-10-09 17:26:25 +03:00
std::string source;
if (!ReadEntireFile(sourcePath, &source))
{
2019-10-09 17:26:25 +03:00
std::cerr << "Error reading shader file: " << sourcePath << "\n";
return 0;
}
return CompileShader(type, source.c_str());
}
GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
{
if (glGetError() != GL_NO_ERROR)
return 0;
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == 0)
{
if (outputErrorMessages)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
// Info log length includes the null terminator, so 1 means that the info log is an
// empty string.
if (infoLogLength > 1)
{
std::vector<GLchar> infoLog(infoLogLength);
glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
&infoLog[0]);
std::cerr << "program link failed: " << &infoLog[0];
}
else
{
std::cerr << "program link failed. <Empty log message>";
}
}
glDeleteProgram(program);
return 0;
}
return program;
}
GLuint CompileProgramWithTransformFeedback(
const char *vsSource,
const char *fsSource,
const std::vector<std::string> &transformFeedbackVaryings,
GLenum bufferMode)
{
auto preLink = [&](GLuint program) {
if (transformFeedbackVaryings.size() > 0)
{
std::vector<const char *> constCharTFVaryings;
for (const std::string &transformFeedbackVarying : transformFeedbackVaryings)
{
constCharTFVaryings.push_back(transformFeedbackVarying.c_str());
}
glTransformFeedbackVaryings(program,
static_cast<GLsizei>(transformFeedbackVaryings.size()),
&constCharTFVaryings[0], bufferMode);
}
};
return CompileProgramInternal(vsSource, "", fsSource, preLink);
}
GLuint CompileProgram(const char *vsSource, const char *fsSource)
{
return CompileProgramInternal(vsSource, "", fsSource, nullptr);
}
GLuint CompileProgram(const char *vsSource,
const char *fsSource,
const std::function<void(GLuint)> &preLinkCallback)
{
return CompileProgramInternal(vsSource, "", fsSource, preLinkCallback);
}
GLuint CompileProgramWithGS(const char *vsSource, const char *gsSource, const char *fsSource)
{
return CompileProgramInternal(vsSource, gsSource, fsSource, nullptr);
}
GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath)
{
2019-10-09 17:26:25 +03:00
std::string vsSource;
if (!ReadEntireFile(vsPath, &vsSource))
{
std::cerr << "Error reading shader: " << vsPath << "\n";
return 0;
}
std::string fsSource;
if (!ReadEntireFile(fsPath, &fsSource))
{
2019-10-09 17:26:25 +03:00
std::cerr << "Error reading shader: " << fsPath << "\n";
return 0;
}
return CompileProgram(vsSource.c_str(), fsSource.c_str());
}
GLuint CompileComputeProgram(const char *csSource, bool outputErrorMessages)
{
GLuint program = glCreateProgram();
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
if (cs == 0)
{
glDeleteProgram(program);
return 0;
}
glAttachShader(program, cs);
glLinkProgram(program);
return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
}
GLuint LoadBinaryProgramOES(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
GLuint program = glCreateProgram();
glProgramBinaryOES(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
return CheckLinkStatusAndReturnProgram(program, true);
}
GLuint LoadBinaryProgramES3(const std::vector<uint8_t> &binary, GLenum binaryFormat)
{
GLuint program = glCreateProgram();
glProgramBinary(program, binaryFormat, binary.data(), static_cast<GLint>(binary.size()));
return CheckLinkStatusAndReturnProgram(program, true);
}
bool LinkAttachedProgram(GLuint program)
{
glLinkProgram(program);
return (CheckLinkStatusAndReturnProgram(program, true) != 0);
}
namespace angle
{
namespace essl1_shaders
{
const char *PositionAttrib()
{
return "a_position";
}
const char *ColorUniform()
{
return "u_color";
}
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
const char *Texture2DUniform()
{
return "u_tex2D";
}
namespace vs
{
// A shader that sets gl_Position to zero.
const char *Zero()
{
return R"(void main()
{
gl_Position = vec4(0);
})";
}
// A shader that sets gl_Position to attribute a_position.
const char *Simple()
{
return R"(precision highp float;
attribute vec4 a_position;
void main()
{
gl_Position = a_position;
})";
}
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// v_position.
const char *Passthrough()
{
return R"(precision highp float;
attribute vec4 a_position;
varying vec4 v_position;
void main()
{
gl_Position = a_position;
v_position = a_position;
})";
}
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// texcoord.
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
const char *Texture2D()
{
return R"(precision highp float;
attribute vec4 a_position;
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
varying vec2 v_texCoord;
void main()
{
gl_Position = vec4(a_position.xy, 0.0, 1.0);
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
v_texCoord = a_position.xy * 0.5 + vec2(0.5);
})";
}
} // namespace vs
namespace fs
{
// A shader that renders a simple checker pattern of red and green. X axis and y axis separate the
// different colors. Needs varying v_position.
const char *Checkered()
{
return R"(precision highp float;
varying vec4 v_position;
void main()
{
if (v_position.x * v_position.y > 0.0)
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
else
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
}
})";
}
// A shader that fills with color taken from uniform named "color".
const char *UniformColor()
{
return R"(uniform mediump vec4 u_color;
void main(void)
{
gl_FragColor = u_color;
})";
}
// A shader that fills with 100% opaque red.
const char *Red()
{
return R"(precision mediump float;
void main()
{
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
})";
}
// A shader that fills with 100% opaque green.
const char *Green()
{
return R"(precision mediump float;
void main()
{
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
}
// A shader that fills with 100% opaque blue.
const char *Blue()
{
return R"(precision mediump float;
void main()
{
gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
})";
}
// A shader that samples the texture.
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
const char *Texture2D()
{
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
return R"(precision mediump float;
uniform sampler2D u_tex2D;
Vulkan: Implement a texture descriptor cache. We noticed a significant hotspot in vkAllocateDesctiptorSets. The app was repeatedly cycling through a few combinations of active textures. For each state change in ANGLE we were allocating a new desctiptor set. This in turn would trigger internal driver memory allocation and cause jank. Using a cache avoids allocations entirely since the application is rotating through a stable set of textures. The descriptor cache is stored in each program. It is indexed by a set of 32-bit serials. Each texture generates a unique serial for every combination of VkImage and VkSampler that the texture owns. The texture descriptor is refreshed every time a texture changes or is rebound. The descriptor cache is accessed via an unoredered map with the texture serial sets as the hash key. We also store the maximum active texture index in the cache key so we don't need to hash and memcmp on all 64 active textures. This will currently fail if more than MAX_UINT serials are generated. But that number is high enough that it shouldn't be possible to hit in practice in a practical amount of time. Requires shifting the texture sync to ContextVk so we can get the new serial after the textures are updated. And to make sure to update the image layouts even if the descriptors are not dirty. Improves performance of the T-Rex demo. Also improves the score of the texture state change microbenchmark by about 40%. Bug: angleproject:3117 Change-Id: Ieb9bec1e8c1a7619814afab767a1980b959a8241 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1642226 Reviewed-by: Jamie Madill <jmadill@chromium.org> Commit-Queue: Jamie Madill <jmadill@chromium.org>
2019-06-13 21:17:48 +03:00
varying vec2 v_texCoord;
void main()
{
gl_FragColor = texture2D(u_tex2D, v_texCoord);
})";
}
} // namespace fs
} // namespace essl1_shaders
namespace essl3_shaders
{
const char *PositionAttrib()
{
return "a_position";
}
namespace vs
{
// A shader that sets gl_Position to zero.
const char *Zero()
{
return R"(#version 300 es
void main()
{
gl_Position = vec4(0);
})";
}
// A shader that sets gl_Position to attribute a_position.
const char *Simple()
{
return R"(#version 300 es
in vec4 a_position;
void main()
{
gl_Position = a_position;
})";
}
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// v_position.
const char *Passthrough()
{
return R"(#version 300 es
in vec4 a_position;
out vec4 v_position;
void main()
{
gl_Position = a_position;
v_position = a_position;
})";
}
} // namespace vs
namespace fs
{
// A shader that fills with 100% opaque red.
const char *Red()
{
return R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
})";
}
// A shader that fills with 100% opaque green.
const char *Green()
{
return R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
}
// A shader that fills with 100% opaque blue.
const char *Blue()
{
return R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
})";
}
} // namespace fs
} // namespace essl3_shaders
namespace essl31_shaders
{
const char *PositionAttrib()
{
return "a_position";
}
namespace vs
{
// A shader that sets gl_Position to zero.
const char *Zero()
{
return R"(#version 310 es
void main()
{
gl_Position = vec4(0);
})";
}
// A shader that sets gl_Position to attribute a_position.
const char *Simple()
{
return R"(#version 310 es
in vec4 a_position;
void main()
{
gl_Position = a_position;
})";
}
// A shader that simply passes through attribute a_position, setting it to gl_Position and varying
// v_position.
const char *Passthrough()
{
return R"(#version 310 es
in vec4 a_position;
out vec4 v_position;
void main()
{
gl_Position = a_position;
v_position = a_position;
})";
}
} // namespace vs
namespace fs
{
// A shader that fills with 100% opaque red.
const char *Red()
{
return R"(#version 310 es
precision highp float;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
})";
}
// A shader that fills with 100% opaque green.
const char *Green()
{
return R"(#version 310 es
precision highp float;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
})";
}
} // namespace fs
} // namespace essl31_shaders
} // namespace angle