Commit b36e46ab by Shahbaz Youssefi Committed by Commit Bot

Vulkan: Line raster emulation through specialization constant

In preparation for compiling shaders early at link time, this change reworks line raster emulation such that it uses specialization constants instead of a preprocessor condition. This means drawing both triangles and lines with this program will still result in a one-time shader compilation. The compilation is still done at draw time in this change. Bug: angleproject:3394 Change-Id: I0bf91398868d7f7147456533b728906b505192b2 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1992365 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 6d6b91a6
......@@ -26,7 +26,7 @@
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 222
#define ANGLE_SH_VERSION 223
enum ShShaderSpec
{
......@@ -732,6 +732,20 @@ inline bool IsDesktopGLSpec(ShShaderSpec spec)
{
return spec == SH_GL_CORE_SPEC || spec == SH_GL_COMPATIBILITY_SPEC;
}
namespace vk
{
// Specialization constant ids
enum class SpecializationConstantId : uint32_t
{
LineRasterEmulation = 0,
InvalidEnum = 1,
EnumCount = 1,
};
} // namespace vk
} // namespace sh
#endif // GLSLANG_SHADERLANG_H_
......@@ -161,6 +161,10 @@ constexpr ImmutableString kEmulatedDepthRangeParams = ImmutableString("ANGLEDept
constexpr ImmutableString kUniformsBlockName = ImmutableString("ANGLEUniformBlock");
constexpr ImmutableString kUniformsVarName = ImmutableString("ANGLEUniforms");
// Specialization constant names
constexpr ImmutableString kLineRasterEmulationSpecConstVarName =
ImmutableString("ANGLELineRasterEmulation");
constexpr const char kViewport[] = "viewport";
constexpr const char kHalfRenderAreaHeight[] = "halfRenderAreaHeight";
constexpr const char kViewportYScale[] = "viewportYScale";
......@@ -421,25 +425,18 @@ const TVariable *AddComputeDriverUniformsToShader(TIntermBlock *root, TSymbolTab
kUniformsVarName);
}
TIntermPreprocessorDirective *GenerateLineRasterIfDef()
TIntermSymbol *GenerateLineRasterSpecConstRef(TSymbolTable *symbolTable)
{
return new TIntermPreprocessorDirective(
PreprocessorDirective::Ifdef, ImmutableString("ANGLE_ENABLE_LINE_SEGMENT_RASTERIZATION"));
}
TIntermPreprocessorDirective *GenerateEndIf()
{
return new TIntermPreprocessorDirective(PreprocessorDirective::Endif, kEmptyImmutableString);
TVariable *specConstVar =
new TVariable(symbolTable, kLineRasterEmulationSpecConstVarName,
StaticType::GetBasic<EbtBool>(), SymbolType::AngleInternal);
return new TIntermSymbol(specConstVar);
}
TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
TSymbolTable *symbolTable,
TQualifier qualifier)
{
TIntermSequence *insertSequence = new TIntermSequence;
insertSequence->push_back(GenerateLineRasterIfDef());
// Define a driver varying vec2 "ANGLEPosition".
TType *varyingType = new TType(EbtFloat, EbpMedium, qualifier, 2);
TVariable *varyingVar = new TVariable(symbolTable, ImmutableString("ANGLEPosition"),
......@@ -447,9 +444,9 @@ TVariable *AddANGLEPositionVaryingDeclaration(TIntermBlock *root,
TIntermSymbol *varyingDeclarator = new TIntermSymbol(varyingVar);
TIntermDeclaration *varyingDecl = new TIntermDeclaration;
varyingDecl->appendDeclarator(varyingDeclarator);
insertSequence->push_back(varyingDecl);
insertSequence->push_back(GenerateEndIf());
TIntermSequence *insertSequence = new TIntermSequence;
insertSequence->push_back(varyingDecl);
// Insert the declarations before Main.
size_t mainIndex = FindMainIndex(root);
......@@ -514,15 +511,18 @@ ANGLE_NO_DISCARD bool AddBresenhamEmulationVS(TCompiler *compiler,
TIntermSymbol *varyingRef = new TIntermSymbol(anglePosition);
TIntermBinary *varyingAssign = new TIntermBinary(EOpAssign, varyingRef, clampedNDC);
TIntermBlock *emulationBlock = new TIntermBlock;
emulationBlock->appendStatement(ndcDecl);
emulationBlock->appendStatement(windowDecl);
emulationBlock->appendStatement(clampedDecl);
emulationBlock->appendStatement(varyingAssign);
TIntermIfElse *ifEmulation =
new TIntermIfElse(GenerateLineRasterSpecConstRef(symbolTable), emulationBlock, nullptr);
// Ensure the statements run at the end of the main() function.
TIntermFunctionDefinition *main = FindMain(root);
TIntermBlock *mainBody = main->getBody();
mainBody->appendStatement(GenerateLineRasterIfDef());
mainBody->appendStatement(ndcDecl);
mainBody->appendStatement(windowDecl);
mainBody->appendStatement(clampedDecl);
mainBody->appendStatement(varyingAssign);
mainBody->appendStatement(GenerateEndIf());
mainBody->appendStatement(ifEmulation);
return compiler->validateAST(root);
}
......@@ -538,9 +538,9 @@ ANGLE_NO_DISCARD bool InsertFragCoordCorrection(TCompiler *compiler,
BuiltInVariable::gl_FragCoord(), kFlippedFragCoordName, pivot);
}
// This block adds OpenGL line segment rasterization emulation behind #ifdef guards.
// OpenGL's simple rasterization algorithm is a strict subset of the pixels generated by the Vulkan
// algorithm. Thus we can implement a shader patch that rejects pixels if they would not be
// This block adds OpenGL line segment rasterization emulation behind a specialization constant
// guard. OpenGL's simple rasterization algorithm is a strict subset of the pixels generated by the
// Vulkan algorithm. Thus we can implement a shader patch that rejects pixels if they would not be
// generated by the OpenGL algorithm. OpenGL's algorithm is similar to Bresenham's line algorithm.
// It is implemented for each pixel by testing if the line segment crosses a small diamond inside
// the pixel. See the OpenGL ES 2.0 spec section "3.4.1 Basic Line Segment Rasterization". Also
......@@ -652,25 +652,33 @@ ANGLE_NO_DISCARD bool AddBresenhamEmulationFS(TCompiler *compiler,
discardBlock->appendStatement(discard);
TIntermIfElse *ifStatement = new TIntermIfElse(checkXY, discardBlock, nullptr);
TIntermBlock *emulationBlock = new TIntermBlock;
TIntermSequence *emulationSequence = emulationBlock->getSequence();
std::array<TIntermNode *, 8> nodes = {
{pDecl, dDecl, fDecl, p_decl, d_decl, f_decl, iDecl, ifStatement}};
emulationSequence->insert(emulationSequence->begin(), nodes.begin(), nodes.end());
TIntermIfElse *ifEmulation =
new TIntermIfElse(GenerateLineRasterSpecConstRef(symbolTable), emulationBlock, nullptr);
// Ensure the line raster code runs at the beginning of main().
TIntermFunctionDefinition *main = FindMain(root);
TIntermSequence *mainSequence = main->getBody()->getSequence();
ASSERT(mainSequence);
std::array<TIntermNode *, 9> nodes = {
{pDecl, dDecl, fDecl, p_decl, d_decl, f_decl, iDecl, ifStatement, GenerateEndIf()}};
mainSequence->insert(mainSequence->begin(), nodes.begin(), nodes.end());
mainSequence->insert(mainSequence->begin(), ifEmulation);
// If the shader does not use frag coord, we should insert it inside the ifdef.
// If the shader does not use frag coord, we should insert it inside the emulation if.
if (!usesFragCoord)
{
if (!InsertFragCoordCorrection(compiler, root, mainSequence, symbolTable, driverUniforms))
if (!InsertFragCoordCorrection(compiler, root, emulationSequence, symbolTable,
driverUniforms))
{
return false;
}
}
mainSequence->insert(mainSequence->begin(), GenerateLineRasterIfDef());
return compiler->validateAST(root);
}
......@@ -819,6 +827,15 @@ bool TranslatorVulkan::translateImpl(TIntermBlock *root,
{
return false;
}
// Add specialization constant declarations. The default value of the specialization
// constant is irrelevant, as it will be set when creating the pipeline.
if (compileOptions & SH_ADD_BRESENHAM_LINE_RASTER_EMULATION)
{
sink << "layout(constant_id="
<< static_cast<uint32_t>(vk::SpecializationConstantId::LineRasterEmulation)
<< ") const bool " << kLineRasterEmulationSpecConstVarName << " = false;\n\n";
}
}
// Declare gl_FragColor and glFragData as webgl_FragColor and webgl_FragData
......
......@@ -56,11 +56,6 @@ constexpr char kParamsEnd = ')';
constexpr char kUniformQualifier[] = "uniform";
constexpr char kSSBOQualifier[] = "buffer";
constexpr char kUnusedUniformSubstitution[] = "// ";
constexpr char kVersionDefine[] = "#version 450 core\n";
constexpr char kLineRasterDefine[] = R"(#version 450 core
#define ANGLE_ENABLE_LINE_SEGMENT_RASTERIZATION
)";
constexpr uint32_t kANGLEPositionLocationOffset = 1;
constexpr uint32_t kXfbANGLEPositionLocationOffset = 2;
......@@ -1082,7 +1077,7 @@ constexpr gl::ShaderMap<EShLanguage> kShLanguageMap = {
angle::Result GetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
// Enable SPIR-V and Vulkan rules when parsing GLSL
EShMessages messages = static_cast<EShMessages>(EShMsgSpvRules | EShMsgVulkanRules);
......@@ -1144,7 +1139,7 @@ angle::Result GetShaderSpirvCode(GlslangErrorCallback callback,
}
glslang::TIntermediate *intermediate = program.getIntermediate(kShLanguageMap[shaderType]);
glslang::GlslangToSpv(*intermediate, (*shaderCodeOut)[shaderType]);
glslang::GlslangToSpv(*intermediate, (*spirvBlobsOut)[shaderType]);
}
return angle::Result::Continue;
......@@ -1270,33 +1265,9 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
if (enableLineRasterEmulation)
{
ASSERT(shaderSources[gl::ShaderType::Compute].empty());
gl::ShaderMap<std::string> patchedSources = shaderSources;
for (const gl::ShaderType shaderType : gl::kAllGraphicsShaderTypes)
{
if (!shaderSources[shaderType].empty())
{
// #defines must come after the #version directive.
ANGLE_GLSLANG_CHECK(callback,
angle::ReplaceSubstring(&patchedSources[shaderType],
kVersionDefine, kLineRasterDefine),
GlslangError::InvalidShader);
}
}
return GetShaderSpirvCode(callback, glCaps, patchedSources, shaderCodeOut);
}
else
{
return GetShaderSpirvCode(callback, glCaps, shaderSources, shaderCodeOut);
}
return GetShaderSpirvCode(callback, glCaps, shaderSources, spirvBlobsOut);
}
} // namespace rx
......@@ -39,6 +39,8 @@ struct GlslangSourceOptions
bool emulateTransformFeedback = false;
};
using SpirvBlob = std::vector<uint32_t>;
using GlslangErrorCallback = std::function<angle::Result(GlslangError)>;
void GlslangInitialize();
......@@ -56,9 +58,8 @@ void GlslangGetShaderSource(const GlslangSourceOptions &options,
angle::Result GlslangGetShaderSpirvCode(GlslangErrorCallback callback,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
} // namespace rx
......
......@@ -312,8 +312,8 @@ angle::Result ProgramMtl::linkImpl(const gl::Context *glContext, gl::InfoLog &in
// Convert GLSL to spirv code
gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), false,
mShaderSource, &shaderCodes));
ANGLE_TRY(mtl::GlslangGetShaderSpirvCode(contextMtl, contextMtl->getCaps(), mShaderSource,
&shaderCodes));
// Convert spirv code to MSL
ANGLE_TRY(convertToMsl(glContext, gl::ShaderType::Vertex, infoLog,
......
......@@ -24,7 +24,6 @@ void GlslangGetShaderSource(const gl::ProgramState &programState,
angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut);
} // namespace mtl
......
......@@ -50,13 +50,12 @@ void GlslangGetShaderSource(const gl::ProgramState &programState,
angle::Result GlslangGetShaderSpirvCode(ErrorHandler *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
return rx::GlslangGetShaderSpirvCode(
[context](GlslangError error) { return HandleError(context, error); }, glCaps,
enableLineRasterEmulation, shaderSources, shaderCodeOut);
shaderSources, shaderCodeOut);
}
} // namespace mtl
} // namespace rx
......@@ -51,12 +51,11 @@ void GlslangWrapperVk::GetShaderSource(const angle::FeaturesVk &features,
// static
angle::Result GlslangWrapperVk::GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodeOut)
{
return GlslangGetShaderSpirvCode(
[context](GlslangError error) { return ErrorHandler(context, error); }, glCaps,
enableLineRasterEmulation, shaderSources, shaderCodeOut);
shaderSources, shaderCodeOut);
}
} // namespace rx
......@@ -31,7 +31,6 @@ class GlslangWrapperVk
static angle::Result GetShaderCode(vk::Context *context,
const gl::Caps &glCaps,
bool enableLineRasterEmulation,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<std::vector<uint32_t>> *shaderCodesOut);
};
......
......@@ -366,56 +366,99 @@ ProgramVk::ShaderInfo::~ShaderInfo() = default;
angle::Result ProgramVk::ShaderInfo::initShaders(ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
bool enableLineRasterEmulation)
gl::ShaderMap<SpirvBlob> *spirvBlobsOut)
{
ASSERT(!valid());
gl::ShaderMap<std::vector<uint32_t>> shaderCodes;
ANGLE_TRY(GlslangWrapperVk::GetShaderCode(
contextVk, contextVk->getCaps(), enableLineRasterEmulation, shaderSources, &shaderCodes));
ANGLE_TRY(GlslangWrapperVk::GetShaderCode(contextVk, contextVk->getCaps(), shaderSources,
spirvBlobsOut));
mIsInitialized = true;
return angle::Result::Continue;
}
void ProgramVk::ShaderInfo::release(ContextVk *contextVk)
{
for (SpirvBlob &spirvBlob : mSpirvBlobs)
{
spirvBlob.clear();
}
mIsInitialized = false;
}
// ProgramVk::ProgramInfo implementation.
ProgramVk::ProgramInfo::ProgramInfo() {}
ProgramVk::ProgramInfo::~ProgramInfo() = default;
angle::Result ProgramVk::ProgramInfo::initProgram(ContextVk *contextVk,
const ShaderInfo &shaderInfo,
bool enableLineRasterEmulation)
{
const gl::ShaderMap<SpirvBlob> &spirvBlobs = shaderInfo.getSpirvBlobs();
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
if (!shaderSources[shaderType].empty())
const SpirvBlob &spirvBlob = spirvBlobs[shaderType];
if (!spirvBlob.empty())
{
ANGLE_TRY(vk::InitShaderAndSerial(contextVk, &mShaders[shaderType].get(),
shaderCodes[shaderType].data(),
shaderCodes[shaderType].size() * sizeof(uint32_t)));
spirvBlob.data(),
spirvBlob.size() * sizeof(uint32_t)));
mProgramHelper.setShader(shaderType, &mShaders[shaderType]);
}
}
if (enableLineRasterEmulation)
{
mProgramHelper.enableSpecializationConstant(
sh::vk::SpecializationConstantId::LineRasterEmulation);
}
return angle::Result::Continue;
}
angle::Result ProgramVk::loadShaderSource(ContextVk *contextVk, gl::BinaryInputStream *stream)
void ProgramVk::ProgramInfo::release(ContextVk *contextVk)
{
mProgramHelper.release(contextVk);
for (vk::RefCounted<vk::ShaderAndSerial> &shader : mShaders)
{
shader.get().destroy(contextVk->getDevice());
}
}
angle::Result ProgramVk::loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStream *stream)
{
// Read in shader sources for all shader types
// Read in shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
// Read the shader source
mShaderSources[shaderType] = stream->readString();
SpirvBlob *spirvBlob = &mShaderInfo.getSpirvBlobs()[shaderType];
// Read the SPIR-V
stream->readIntVector<uint32_t>(spirvBlob);
}
return angle::Result::Continue;
}
void ProgramVk::saveShaderSource(gl::BinaryOutputStream *stream)
void ProgramVk::saveSpirvBlob(gl::BinaryOutputStream *stream)
{
// Write out shader sources for all shader types
// Write out shader codes for all shader types
for (const gl::ShaderType shaderType : gl::AllShaderTypes())
{
// Write the shader source
stream->writeString(mShaderSources[shaderType]);
}
}
void ProgramVk::ShaderInfo::release(ContextVk *contextVk)
{
mProgramHelper.release(contextVk);
const SpirvBlob &spirvBlob = mShaderInfo.getSpirvBlobs()[shaderType];
for (vk::RefCounted<vk::ShaderAndSerial> &shader : mShaders)
{
shader.get().destroy(contextVk->getDevice());
// Write the SPIR-V
stream->writeIntVector(spirvBlob);
}
}
......@@ -455,8 +498,9 @@ void ProgramVk::reset(ContextVk *contextVk)
uniformBlock.storage.release(renderer);
}
mDefaultShaderInfo.release(contextVk);
mLineRasterShaderInfo.release(contextVk);
mShaderInfo.release(contextVk);
mDefaultProgramInfo.release(contextVk);
mLineRasterProgramInfo.release(contextVk);
mEmptyBuffer.release(renderer);
......@@ -485,7 +529,7 @@ std::unique_ptr<rx::LinkEvent> ProgramVk::load(const gl::Context *context,
gl::ShaderMap<size_t> requiredBufferSize;
requiredBufferSize.fill(0);
angle::Result status = loadShaderSource(contextVk, stream);
angle::Result status = loadSpirvBlob(contextVk, stream);
if (status != angle::Result::Continue)
{
return std::make_unique<LinkEventDone>(status);
......@@ -525,7 +569,7 @@ void ProgramVk::save(const gl::Context *context, gl::BinaryOutputStream *stream)
{
// (geofflang): Look into saving shader modules in ShaderInfo objects (keep in mind that we
// compile shaders lazily)
saveShaderSource(stream);
saveSpirvBlob(stream);
// Serializes the uniformLayout data of mDefaultUniformBlocks
for (gl::ShaderType shaderType : gl::AllShaderTypes())
......
......@@ -14,6 +14,7 @@
#include "common/utilities.h"
#include "libANGLE/renderer/ProgramImpl.h"
#include "libANGLE/renderer/glslang_wrapper_utils.h"
#include "libANGLE/renderer/vulkan/ContextVk.h"
#include "libANGLE/renderer/vulkan/RendererVk.h"
#include "libANGLE/renderer/vulkan/TransformFeedbackVk.h"
......@@ -140,7 +141,7 @@ class ProgramVk : public ProgramImpl
vk::PipelineHelper **pipelineOut)
{
vk::ShaderProgramHelper *shaderProgram;
ANGLE_TRY(initGraphicsShaders(contextVk, mode, &shaderProgram));
ANGLE_TRY(initGraphicsProgram(contextVk, mode, &shaderProgram));
ASSERT(shaderProgram->isGraphicsProgram());
RendererVk *renderer = contextVk->getRenderer();
vk::PipelineCache *pipelineCache = nullptr;
......@@ -154,7 +155,7 @@ class ProgramVk : public ProgramImpl
angle::Result getComputePipeline(ContextVk *contextVk, vk::PipelineAndSerial **pipelineOut)
{
vk::ShaderProgramHelper *shaderProgram;
ANGLE_TRY(initComputeShader(contextVk, &shaderProgram));
ANGLE_TRY(initComputeProgram(contextVk, &shaderProgram));
ASSERT(!shaderProgram->isGraphicsProgram());
return shaderProgram->getComputePipeline(contextVk, mPipelineLayout.get(), pipelineOut);
}
......@@ -212,45 +213,54 @@ class ProgramVk : public ProgramImpl
}
uint32_t getImageBindingsOffset() const { return mImageBindingsOffset; }
class ShaderInfo;
ANGLE_INLINE angle::Result initShaders(ContextVk *contextVk,
class ProgramInfo;
ANGLE_INLINE angle::Result initProgram(ContextVk *contextVk,
bool enableLineRasterEmulation,
ShaderInfo *shaderInfo,
ProgramInfo *programInfo,
vk::ShaderProgramHelper **shaderProgramOut)
{
if (!shaderInfo->valid())
// Compile shaders if not already. This is done only once regardless of specialization
// constants.
if (!mShaderInfo.valid())
{
ANGLE_TRY(
shaderInfo->initShaders(contextVk, mShaderSources, enableLineRasterEmulation));
mShaderInfo.initShaders(contextVk, mShaderSources, &mShaderInfo.getSpirvBlobs()));
}
ASSERT(mShaderInfo.valid());
ASSERT(shaderInfo->valid());
*shaderProgramOut = &shaderInfo->getShaderProgram();
// Create the program pipeline. This is done lazily and once per combination of
// specialization constants.
if (!programInfo->valid())
{
ANGLE_TRY(programInfo->initProgram(contextVk, mShaderInfo, enableLineRasterEmulation));
}
ASSERT(programInfo->valid());
*shaderProgramOut = programInfo->getShaderProgram();
return angle::Result::Continue;
}
ANGLE_INLINE angle::Result initGraphicsShaders(ContextVk *contextVk,
ANGLE_INLINE angle::Result initGraphicsProgram(ContextVk *contextVk,
gl::PrimitiveMode mode,
vk::ShaderProgramHelper **shaderProgramOut)
{
bool enableLineRasterEmulation = UseLineRaster(contextVk, mode);
ShaderInfo &shaderInfo =
enableLineRasterEmulation ? mLineRasterShaderInfo : mDefaultShaderInfo;
ProgramInfo &programInfo =
enableLineRasterEmulation ? mLineRasterProgramInfo : mDefaultProgramInfo;
return initShaders(contextVk, enableLineRasterEmulation, &shaderInfo, shaderProgramOut);
return initProgram(contextVk, enableLineRasterEmulation, &programInfo, shaderProgramOut);
}
ANGLE_INLINE angle::Result initComputeShader(ContextVk *contextVk,
vk::ShaderProgramHelper **shaderProgramOut)
ANGLE_INLINE angle::Result initComputeProgram(ContextVk *contextVk,
vk::ShaderProgramHelper **shaderProgramOut)
{
return initShaders(contextVk, false, &mDefaultShaderInfo, shaderProgramOut);
return initProgram(contextVk, false, &mDefaultProgramInfo, shaderProgramOut);
}
// Save and load implementation for GLES Program Binary support.
angle::Result loadShaderSource(ContextVk *contextVk, gl::BinaryInputStream *stream);
void saveShaderSource(gl::BinaryOutputStream *stream);
angle::Result loadSpirvBlob(ContextVk *contextVk, gl::BinaryInputStream *stream);
void saveSpirvBlob(gl::BinaryOutputStream *stream);
// State for the default uniform blocks.
struct DefaultUniformBlock final : private angle::NonCopyable
......@@ -304,27 +314,48 @@ class ProgramVk : public ProgramImpl
angle::Result initShaders(ContextVk *contextVk,
const gl::ShaderMap<std::string> &shaderSources,
gl::ShaderMap<SpirvBlob> *spirvBlobsOut);
void release(ContextVk *contextVk);
ANGLE_INLINE bool valid() const { return mIsInitialized; }
gl::ShaderMap<SpirvBlob> &getSpirvBlobs() { return mSpirvBlobs; }
const gl::ShaderMap<SpirvBlob> &getSpirvBlobs() const { return mSpirvBlobs; }
private:
gl::ShaderMap<SpirvBlob> mSpirvBlobs;
bool mIsInitialized = false;
};
class ProgramInfo final : angle::NonCopyable
{
public:
ProgramInfo();
~ProgramInfo();
angle::Result initProgram(ContextVk *contextVk,
const ShaderInfo &shaderInfo,
bool enableLineRasterEmulation);
void release(ContextVk *contextVk);
ANGLE_INLINE bool valid() const
{
return mShaders[gl::ShaderType::Vertex].get().valid() ||
mShaders[gl::ShaderType::Compute].get().valid();
}
ANGLE_INLINE bool valid() const { return mProgramHelper.valid(); }
vk::ShaderProgramHelper &getShaderProgram() { return mProgramHelper; }
vk::ShaderProgramHelper *getShaderProgram() { return &mProgramHelper; }
private:
vk::ShaderProgramHelper mProgramHelper;
gl::ShaderMap<vk::RefCounted<vk::ShaderAndSerial>> mShaders;
};
ShaderInfo mDefaultShaderInfo;
ShaderInfo mLineRasterShaderInfo;
ProgramInfo mDefaultProgramInfo;
ProgramInfo mLineRasterProgramInfo;
// We keep the translated linked shader sources to use with shader draw call patching.
// We keep the translated linked shader sources to use with shader draw call compilation.
// TODO(syoussefi): Remove when shader compilation is done at link time.
// http://anglebug.com/3394
gl::ShaderMap<std::string> mShaderSources;
// We keep the SPIR-V code to use for draw call pipeline creation.
ShaderInfo mShaderInfo;
// In their descriptor set, uniform buffers are placed first, then storage buffers, then atomic
// counter buffers and then images. These cached values contain the offsets where storage
......
......@@ -7,23 +7,24 @@ of steps:
shader translator][translator]. The translator compiles application shaders into Vulkan-compatible
GLSL. Vulkan-compatible GLSL matches the [GL_KHR_vulkan_glsl][GL_KHR_vulkan_glsl] extension spec
with some additional workarounds and emulation. We emulate OpenGL's different depth range, viewport
y flipping, default uniforms, and OpenGL [line segment
rasterization](OpenGLLineSegmentRasterization.md). For more info see
y flipping, default uniforms, and OpenGL
[line segment rasterization](OpenGLLineSegmentRasterization.md). For more info see
[TranslatorVulkan.cpp][TranslatorVulkan.cpp]. After initial compilation the shaders are not
complete. They are templated with markers that are filled in later at link time.
1. **Link-Time Translation**: During a call to `glLinkProgram` the Vulkan back-end can know the
necessary locations and properties to write to connect the shader stage interfaces. We get the
completed shader source using ANGLE's [GlslangWrapperVk][GlslangWrapperVk.cpp] helper class. We still
cannot generate `VkShaderModules` since some ANGLE features like [OpenGL line
rasterization](OpenGLLineSegmentRasterization.md) emulation depend on draw-time information.
cannot generate `VkShaderModules` since some ANGLE features like
[OpenGL line rasterization](OpenGLLineSegmentRasterization.md) emulation depend on draw-time
information.
1. **Draw-time SPIR-V Generation**: Once the application records a draw call we use Khronos'
[glslang][glslang] to convert the Vulkan-compatible GLSL into SPIR-V with the correct draw-time
defines. The SPIR-V is then compiled into `VkShaderModules`. For details please see
[GlslangWrapperVk.cpp][GlslangWrapperVk.cpp]. The `VkShaderModules` are then used by `VkPipelines`. Note
that we currently don't use [SPIRV-Tools][SPIRV-Tools] to perform any SPIR-V optimization. This
could be something to improve on in the future.
[glslang][glslang] to convert the Vulkan-compatible GLSL into SPIR-V. The SPIR-V is then compiled
into `VkShaderModules`. For details please see [GlslangWrapperVk.cpp][GlslangWrapperVk.cpp]. The
`VkShaderModules` are then used by `VkPipelines` with the appropriate specialization constant
values. Note that we currently don't use [SPIRV-Tools][SPIRV-Tools] to perform any SPIR-V
optimization. This could be something to improve on in the future.
See the below diagram for a high-level view of the shader translation flow:
......
......@@ -186,6 +186,7 @@ void UnpackBlendAttachmentState(const vk::PackedColorBlendAttachmentState &packe
void SetPipelineShaderStageInfo(const VkStructureType type,
const VkShaderStageFlagBits stage,
const VkShaderModule module,
const VkSpecializationInfo &specializationInfo,
VkPipelineShaderStageCreateInfo *shaderStage)
{
shaderStage->sType = type;
......@@ -193,7 +194,7 @@ void SetPipelineShaderStageInfo(const VkStructureType type,
shaderStage->stage = stage;
shaderStage->module = module;
shaderStage->pName = "main";
shaderStage->pSpecializationInfo = nullptr;
shaderStage->pSpecializationInfo = &specializationInfo;
}
angle::Result InitializeRenderPassFromDesc(vk::Context *context,
......@@ -294,6 +295,29 @@ angle::Result InitializeRenderPassFromDesc(vk::Context *context,
return angle::Result::Continue;
}
void InitializeSpecializationInfo(
vk::SpecializationConstantBitSet specConsts,
vk::SpecializationConstantMap<VkSpecializationMapEntry> *specializationEntriesOut,
vk::SpecializationConstantMap<VkBool32> *specializationValuesOut,
VkSpecializationInfo *specializationInfoOut)
{
// Collect specialization constants.
for (const sh::vk::SpecializationConstantId id :
angle::AllEnums<sh::vk::SpecializationConstantId>())
{
const uint32_t offset = static_cast<uint32_t>(id);
(*specializationValuesOut)[id] = specConsts.test(id);
(*specializationEntriesOut)[id].constantID = offset;
(*specializationEntriesOut)[id].offset = offset;
(*specializationEntriesOut)[id].size = sizeof(VkBool32);
}
specializationInfoOut->mapEntryCount = static_cast<uint32_t>(specializationEntriesOut->size());
specializationInfoOut->pMapEntries = specializationEntriesOut->data();
specializationInfoOut->dataSize = specializationEntriesOut->size() * sizeof(VkBool32);
specializationInfoOut->pData = specializationValuesOut->data();
}
// Utility for setting a value on a packed 4-bit integer array.
template <typename SrcT>
void Int4Array_Set(uint8_t *arrayBytes, uint32_t arrayIndex, SrcT value)
......@@ -617,6 +641,7 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
const ShaderModule *vertexModule,
const ShaderModule *fragmentModule,
const ShaderModule *geometryModule,
vk::SpecializationConstantBitSet specConsts,
Pipeline *pipelineOut) const
{
angle::FixedVector<VkPipelineShaderStageCreateInfo, 3> shaderStages;
......@@ -629,13 +654,20 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
std::array<VkPipelineColorBlendAttachmentState, gl::IMPLEMENTATION_MAX_DRAW_BUFFERS>
blendAttachmentState;
VkPipelineColorBlendStateCreateInfo blendState = {};
VkSpecializationInfo specializationInfo = {};
VkGraphicsPipelineCreateInfo createInfo = {};
vk::SpecializationConstantMap<VkSpecializationMapEntry> specializationEntries;
vk::SpecializationConstantMap<VkBool32> specializationValues;
InitializeSpecializationInfo(specConsts, &specializationEntries, &specializationValues,
&specializationInfo);
// Vertex shader is always expected to be present.
ASSERT(vertexModule != nullptr);
VkPipelineShaderStageCreateInfo vertexStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_VERTEX_BIT, vertexModule->getHandle(), &vertexStage);
VK_SHADER_STAGE_VERTEX_BIT, vertexModule->getHandle(),
specializationInfo, &vertexStage);
shaderStages.push_back(vertexStage);
if (geometryModule)
......@@ -643,7 +675,7 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
VkPipelineShaderStageCreateInfo geometryStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_GEOMETRY_BIT, geometryModule->getHandle(),
&geometryStage);
specializationInfo, &geometryStage);
shaderStages.push_back(geometryStage);
}
......@@ -654,7 +686,7 @@ angle::Result GraphicsPipelineDesc::initializePipeline(
VkPipelineShaderStageCreateInfo fragmentStage = {};
SetPipelineShaderStageInfo(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
VK_SHADER_STAGE_FRAGMENT_BIT, fragmentModule->getHandle(),
&fragmentStage);
specializationInfo, &fragmentStage);
shaderStages.push_back(fragmentStage);
}
......@@ -1754,6 +1786,7 @@ angle::Result GraphicsPipelineCache::insertPipeline(
const vk::ShaderModule *vertexModule,
const vk::ShaderModule *fragmentModule,
const vk::ShaderModule *geometryModule,
vk::SpecializationConstantBitSet specConsts,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
......@@ -1767,7 +1800,7 @@ angle::Result GraphicsPipelineCache::insertPipeline(
ANGLE_TRY(desc.initializePipeline(contextVk, pipelineCacheVk, compatibleRenderPass,
pipelineLayout, activeAttribLocationsMask,
programAttribsTypeMask, vertexModule, fragmentModule,
geometryModule, &newPipeline));
geometryModule, specConsts, &newPipeline));
}
// The Serial will be updated outside of this query.
......
......@@ -363,6 +363,7 @@ class GraphicsPipelineDesc final
const ShaderModule *vertexModule,
const ShaderModule *fragmentModule,
const ShaderModule *geometryModule,
vk::SpecializationConstantBitSet specConsts,
Pipeline *pipelineOut) const;
// Vertex input state. For ES 3.1 this should be separated into binding and attribute.
......@@ -829,6 +830,7 @@ class GraphicsPipelineCache final : angle::NonCopyable
const vk::ShaderModule *vertexModule,
const vk::ShaderModule *fragmentModule,
const vk::ShaderModule *geometryModule,
vk::SpecializationConstantBitSet specConsts,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut)
......@@ -843,7 +845,8 @@ class GraphicsPipelineCache final : angle::NonCopyable
return insertPipeline(contextVk, pipelineCacheVk, compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, programAttribsTypeMask, vertexModule,
fragmentModule, geometryModule, desc, descPtrOut, pipelineOut);
fragmentModule, geometryModule, specConsts, desc, descPtrOut,
pipelineOut);
}
private:
......@@ -856,6 +859,7 @@ class GraphicsPipelineCache final : angle::NonCopyable
const vk::ShaderModule *vertexModule,
const vk::ShaderModule *fragmentModule,
const vk::ShaderModule *geometryModule,
vk::SpecializationConstantBitSet specConsts,
const vk::GraphicsPipelineDesc &desc,
const vk::GraphicsPipelineDesc **descPtrOut,
vk::PipelineHelper **pipelineOut);
......
......@@ -3565,8 +3565,7 @@ ShaderProgramHelper::~ShaderProgramHelper() = default;
bool ShaderProgramHelper::valid() const
{
// This will need to be extended for compute shader support.
return mShaders[gl::ShaderType::Vertex].valid();
return mShaders[gl::ShaderType::Vertex].valid() || mShaders[gl::ShaderType::Compute].valid();
}
void ShaderProgramHelper::destroy(VkDevice device)
......@@ -3594,6 +3593,13 @@ void ShaderProgramHelper::setShader(gl::ShaderType shaderType, RefCounted<Shader
mShaders[shaderType].set(shader);
}
void ShaderProgramHelper::enableSpecializationConstant(sh::vk::SpecializationConstantId id)
{
ASSERT(id < sh::vk::SpecializationConstantId::EnumCount);
mSpecializationConstants.set(id);
}
angle::Result ShaderProgramHelper::getComputePipeline(Context *context,
const PipelineLayout &pipelineLayout,
PipelineAndSerial **pipelineOut)
......
......@@ -1196,6 +1196,7 @@ class ShaderProgramHelper : angle::NonCopyable
ShaderAndSerial &getShader(gl::ShaderType shaderType) { return mShaders[shaderType].get(); }
void setShader(gl::ShaderType shaderType, RefCounted<ShaderAndSerial> *shader);
void enableSpecializationConstant(sh::vk::SpecializationConstantId id);
// For getting a Pipeline and from the pipeline cache.
ANGLE_INLINE angle::Result getGraphicsPipeline(
......@@ -1227,7 +1228,7 @@ class ShaderProgramHelper : angle::NonCopyable
return mGraphicsPipelines.getPipeline(
contextVk, pipelineCache, *compatibleRenderPass, pipelineLayout,
activeAttribLocationsMask, programAttribsTypeMask, vertexShader, fragmentShader,
geometryShader, pipelineDesc, descPtrOut, pipelineOut);
geometryShader, mSpecializationConstants, pipelineDesc, descPtrOut, pipelineOut);
}
angle::Result getComputePipeline(Context *context,
......@@ -1240,6 +1241,9 @@ class ShaderProgramHelper : angle::NonCopyable
// We should probably use PipelineHelper here so we can remove PipelineAndSerial.
PipelineAndSerial mComputePipeline;
// Specialization constants, currently only used by the graphics queue.
vk::SpecializationConstantBitSet mSpecializationConstants;
};
} // namespace vk
} // namespace rx
......
......@@ -13,6 +13,7 @@
#include <atomic>
#include <limits>
#include "GLSLANG/ShaderLang.h"
#include "common/FixedVector.h"
#include "common/Optional.h"
#include "common/PackedEnums.h"
......@@ -605,6 +606,13 @@ class Recycler final : angle::NonCopyable
std::vector<T> mObjectFreeList;
};
using SpecializationConstantBitSet =
angle::PackedEnumBitSet<sh::vk::SpecializationConstantId, uint32_t>;
static_assert(sizeof(SpecializationConstantBitSet) == sizeof(uint32_t), "Unexpected size");
template <typename T>
using SpecializationConstantMap = angle::PackedEnumMap<sh::vk::SpecializationConstantId, T>;
} // namespace vk
namespace gl_vk
......
......@@ -534,6 +534,11 @@
// General Vulkan failures
// ANGLEPosition location assignment overlaps other varyings. This only affects platforms that
// don't support the bresenham line raster extension, which in turn also depends on the driver
// versions.
4251 VULKAN NVIDIA : dEQP-GLES3.functional.shaders.linkage.varying.struct.float_uvec2_vec3 = FAIL
// Tests failing due to the "flat" qualifier in the shader:
3677 VULKAN : dEQP-GLES3.functional.fragment_out.basic.int.* = FAIL
3677 VULKAN : dEQP-GLES3.functional.fragment_out.basic.uint.* = FAIL
......
......@@ -88,12 +88,14 @@ void VulkanPipelineCachePerfTest::step()
gl::AttributesMask am;
gl::ComponentTypeMask ctm;
vk::SpecializationConstantBitSet defaultSpecConsts;
for (unsigned int iteration = 0; iteration < kIterationsPerStep; ++iteration)
{
for (const auto &hit : mCacheHits)
{
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, ctm, &sm, &sm, nullptr, hit,
&desc, &result);
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, ctm, &sm, &sm, nullptr,
defaultSpecConsts, hit, &desc, &result);
}
}
......@@ -101,8 +103,8 @@ void VulkanPipelineCachePerfTest::step()
++missCount, ++mMissIndex)
{
const auto &miss = mCacheMisses[mMissIndex];
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, ctm, &sm, &sm, nullptr, miss,
&desc, &result);
(void)mCache.getPipeline(VK_NULL_HANDLE, pc, rp, pl, am, ctm, &sm, &sm, nullptr,
defaultSpecConsts, miss, &desc, &result);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment