mirror of
https://github.com/bkaradzic/bgfx.git
synced 2026-02-17 20:52:36 +01:00
Updated glslang.
This commit is contained in:
141
3rdparty/glslang/SPIRV/GlslangToSpv.cpp
vendored
141
3rdparty/glslang/SPIRV/GlslangToSpv.cpp
vendored
@@ -43,6 +43,7 @@
|
||||
#include "spirv.hpp"
|
||||
#include "GlslangToSpv.h"
|
||||
#include "SpvBuilder.h"
|
||||
#include "SpvTools.h"
|
||||
namespace spv {
|
||||
#include "GLSL.std.450.h"
|
||||
#include "GLSL.ext.KHR.h"
|
||||
@@ -66,6 +67,7 @@ namespace spv {
|
||||
#include <iomanip>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
#include <stack>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -164,6 +166,7 @@ protected:
|
||||
spv::Id convertGlslangToSpvType(const glslang::TType& type, bool forwardReferenceOnly = false);
|
||||
spv::Id convertGlslangToSpvType(const glslang::TType& type, glslang::TLayoutPacking, const glslang::TQualifier&,
|
||||
bool lastBufferBlockMember, bool forwardReferenceOnly = false);
|
||||
void applySpirvDecorate(const glslang::TType& type, spv::Id id, std::optional<int> member);
|
||||
bool filterMember(const glslang::TType& member);
|
||||
spv::Id convertGlslangStructToSpvType(const glslang::TType&, const glslang::TTypeList* glslangStruct,
|
||||
glslang::TLayoutPacking, const glslang::TQualifier&);
|
||||
@@ -4705,6 +4708,64 @@ spv::Id TGlslangToSpvTraverser::convertGlslangToSpvType(const glslang::TType& ty
|
||||
return spvType;
|
||||
}
|
||||
|
||||
// Apply SPIR-V decorations to the SPIR-V object (provided by SPIR-V ID). If member index is provided, the
|
||||
// decorations are applied to this member.
|
||||
void TGlslangToSpvTraverser::applySpirvDecorate(const glslang::TType& type, spv::Id id, std::optional<int> member)
|
||||
{
|
||||
assert(type.getQualifier().hasSpirvDecorate());
|
||||
|
||||
const glslang::TSpirvDecorate& spirvDecorate = type.getQualifier().getSpirvDecorate();
|
||||
|
||||
// Add spirv_decorate
|
||||
for (auto& decorate : spirvDecorate.decorates) {
|
||||
if (!decorate.second.empty()) {
|
||||
std::vector<unsigned> literals;
|
||||
TranslateLiterals(decorate.second, literals);
|
||||
if (member.has_value())
|
||||
builder.addMemberDecoration(id, *member, static_cast<spv::Decoration>(decorate.first), literals);
|
||||
else
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorate.first), literals);
|
||||
} else {
|
||||
if (member.has_value())
|
||||
builder.addMemberDecoration(id, *member, static_cast<spv::Decoration>(decorate.first));
|
||||
else
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorate.first));
|
||||
}
|
||||
}
|
||||
|
||||
// Add spirv_decorate_id
|
||||
if (member.has_value()) {
|
||||
// spirv_decorate_id not applied to members
|
||||
assert(spirvDecorate.decorateIds.empty());
|
||||
} else {
|
||||
for (auto& decorateId : spirvDecorate.decorateIds) {
|
||||
std::vector<spv::Id> operandIds;
|
||||
assert(!decorateId.second.empty());
|
||||
for (auto extraOperand : decorateId.second) {
|
||||
if (extraOperand->getQualifier().isFrontEndConstant())
|
||||
operandIds.push_back(createSpvConstant(*extraOperand));
|
||||
else
|
||||
operandIds.push_back(getSymbolId(extraOperand->getAsSymbolNode()));
|
||||
}
|
||||
builder.addDecorationId(id, static_cast<spv::Decoration>(decorateId.first), operandIds);
|
||||
}
|
||||
}
|
||||
|
||||
// Add spirv_decorate_string
|
||||
for (auto& decorateString : spirvDecorate.decorateStrings) {
|
||||
std::vector<const char*> strings;
|
||||
assert(!decorateString.second.empty());
|
||||
for (auto extraOperand : decorateString.second) {
|
||||
const char* string = extraOperand->getConstArray()[0].getSConst()->c_str();
|
||||
strings.push_back(string);
|
||||
}
|
||||
if (member.has_value())
|
||||
builder.addMemberDecoration(id, *member, static_cast<spv::Decoration>(decorateString.first), strings);
|
||||
else
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorateString.first), strings);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this functionality should exist at a higher level, in creating the AST
|
||||
//
|
||||
// Identify interface members that don't have their required extension turned on.
|
||||
@@ -4943,37 +5004,9 @@ void TGlslangToSpvTraverser::decorateStructType(const glslang::TType& type,
|
||||
builder.addExtension(spv::E_SPV_NV_geometry_shader_passthrough);
|
||||
}
|
||||
|
||||
//
|
||||
// Add SPIR-V decorations for members (GL_EXT_spirv_intrinsics)
|
||||
//
|
||||
if (glslangMember.getQualifier().hasSprivDecorate()) {
|
||||
const glslang::TSpirvDecorate& spirvDecorate = glslangMember.getQualifier().getSpirvDecorate();
|
||||
|
||||
// Add spirv_decorate
|
||||
for (auto& decorate : spirvDecorate.decorates) {
|
||||
if (!decorate.second.empty()) {
|
||||
std::vector<unsigned> literals;
|
||||
TranslateLiterals(decorate.second, literals);
|
||||
builder.addMemberDecoration(spvType, member, static_cast<spv::Decoration>(decorate.first), literals);
|
||||
}
|
||||
else
|
||||
builder.addMemberDecoration(spvType, member, static_cast<spv::Decoration>(decorate.first));
|
||||
}
|
||||
|
||||
// spirv_decorate_id not applied to members
|
||||
assert(spirvDecorate.decorateIds.empty());
|
||||
|
||||
// Add spirv_decorate_string
|
||||
for (auto& decorateString : spirvDecorate.decorateStrings) {
|
||||
std::vector<const char*> strings;
|
||||
assert(!decorateString.second.empty());
|
||||
for (auto extraOperand : decorateString.second) {
|
||||
const char* string = extraOperand->getConstArray()[0].getSConst()->c_str();
|
||||
strings.push_back(string);
|
||||
}
|
||||
builder.addDecoration(spvType, static_cast<spv::Decoration>(decorateString.first), strings);
|
||||
}
|
||||
}
|
||||
// Add SPIR-V decorations (GL_EXT_spirv_intrinsics)
|
||||
if (glslangMember.getQualifier().hasSpirvDecorate())
|
||||
applySpirvDecorate(glslangMember, spvType, member);
|
||||
}
|
||||
|
||||
// Decorate the structure
|
||||
@@ -5462,7 +5495,7 @@ void TGlslangToSpvTraverser::makeFunctions(const glslang::TIntermSequence& glslF
|
||||
spv::Function* function = builder.makeFunctionEntry(
|
||||
TranslatePrecisionDecoration(glslFunction->getType()), convertGlslangToSpvType(glslFunction->getType()),
|
||||
glslFunction->getName().c_str(), convertGlslangLinkageToSpv(glslFunction->getLinkType()), paramTypes,
|
||||
paramNames, paramDecorations, &functionBlock);
|
||||
paramDecorations, &functionBlock);
|
||||
builder.setupDebugFunctionEntry(function, glslFunction->getName().c_str(), glslFunction->getLoc().line,
|
||||
paramTypes, paramNames);
|
||||
if (implicitThis)
|
||||
@@ -9598,47 +9631,9 @@ spv::Id TGlslangToSpvTraverser::getSymbolId(const glslang::TIntermSymbol* symbol
|
||||
spv::DecorationRestrictPointerEXT : spv::DecorationAliasedPointerEXT);
|
||||
}
|
||||
|
||||
//
|
||||
// Add SPIR-V decorations for structure (GL_EXT_spirv_intrinsics)
|
||||
//
|
||||
if (symbol->getType().getQualifier().hasSprivDecorate()) {
|
||||
const glslang::TSpirvDecorate& spirvDecorate = symbol->getType().getQualifier().getSpirvDecorate();
|
||||
|
||||
// Add spirv_decorate
|
||||
for (auto& decorate : spirvDecorate.decorates) {
|
||||
if (!decorate.second.empty()) {
|
||||
std::vector<unsigned> literals;
|
||||
TranslateLiterals(decorate.second, literals);
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorate.first), literals);
|
||||
}
|
||||
else
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorate.first));
|
||||
}
|
||||
|
||||
// Add spirv_decorate_id
|
||||
for (auto& decorateId : spirvDecorate.decorateIds) {
|
||||
std::vector<spv::Id> operandIds;
|
||||
assert(!decorateId.second.empty());
|
||||
for (auto extraOperand : decorateId.second) {
|
||||
if (extraOperand->getQualifier().isFrontEndConstant())
|
||||
operandIds.push_back(createSpvConstant(*extraOperand));
|
||||
else
|
||||
operandIds.push_back(getSymbolId(extraOperand->getAsSymbolNode()));
|
||||
}
|
||||
builder.addDecorationId(id, static_cast<spv::Decoration>(decorateId.first), operandIds);
|
||||
}
|
||||
|
||||
// Add spirv_decorate_string
|
||||
for (auto& decorateString : spirvDecorate.decorateStrings) {
|
||||
std::vector<const char*> strings;
|
||||
assert(!decorateString.second.empty());
|
||||
for (auto extraOperand : decorateString.second) {
|
||||
const char* string = extraOperand->getConstArray()[0].getSConst()->c_str();
|
||||
strings.push_back(string);
|
||||
}
|
||||
builder.addDecoration(id, static_cast<spv::Decoration>(decorateString.first), strings);
|
||||
}
|
||||
}
|
||||
// Add SPIR-V decorations (GL_EXT_spirv_intrinsics)
|
||||
if (symbol->getType().getQualifier().hasSpirvDecorate())
|
||||
applySpirvDecorate(symbol->getType(), id, {});
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
20
3rdparty/glslang/SPIRV/GlslangToSpv.h
vendored
20
3rdparty/glslang/SPIRV/GlslangToSpv.h
vendored
@@ -35,19 +35,25 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
||||
#pragma warning(disable : 4464) // relative include path contains '..'
|
||||
#endif
|
||||
|
||||
#include "SpvTools.h"
|
||||
#include "glslang/Include/intermediate.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "Logger.h"
|
||||
|
||||
namespace glslang {
|
||||
class TIntermediate;
|
||||
|
||||
struct SpvOptions {
|
||||
bool generateDebugInfo {false};
|
||||
bool stripDebugInfo {false};
|
||||
bool disableOptimizer {true};
|
||||
bool optimizeSize {false};
|
||||
bool disassemble {false};
|
||||
bool validate {false};
|
||||
bool emitNonSemanticShaderDebugInfo {false};
|
||||
bool emitNonSemanticShaderDebugSource{ false };
|
||||
bool compileOnly{false};
|
||||
};
|
||||
|
||||
void GetSpirvVersion(std::string&);
|
||||
int GetSpirvGeneratorVersion();
|
||||
|
||||
2
3rdparty/glslang/SPIRV/SPVRemapper.h
vendored
2
3rdparty/glslang/SPIRV/SPVRemapper.h
vendored
@@ -77,9 +77,9 @@ public:
|
||||
#include <cassert>
|
||||
|
||||
#include "spirv.hpp"
|
||||
#include "spvIR.h"
|
||||
|
||||
namespace spv {
|
||||
const Id NoResult = 0;
|
||||
|
||||
// class to hold SPIR-V binary data for remapping, DCE, and debug stripping
|
||||
class spirvbin_t : public spirvbin_base_t
|
||||
|
||||
58
3rdparty/glslang/SPIRV/SpvBuilder.cpp
vendored
58
3rdparty/glslang/SPIRV/SpvBuilder.cpp
vendored
@@ -1182,13 +1182,26 @@ Id Builder::makeDebugExpression()
|
||||
return debugExpression;
|
||||
}
|
||||
|
||||
Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const localVariable)
|
||||
Id Builder::makeDebugDeclare(Id const debugLocalVariable, Id const pointer)
|
||||
{
|
||||
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
|
||||
inst->addIdOperand(nonSemanticShaderDebugInfo);
|
||||
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugDeclare);
|
||||
inst->addIdOperand(debugLocalVariable); // debug local variable id
|
||||
inst->addIdOperand(localVariable); // local variable id
|
||||
inst->addIdOperand(pointer); // pointer to local variable id
|
||||
inst->addIdOperand(makeDebugExpression()); // expression id
|
||||
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
|
||||
|
||||
return inst->getResultId();
|
||||
}
|
||||
|
||||
Id Builder::makeDebugValue(Id const debugLocalVariable, Id const value)
|
||||
{
|
||||
Instruction* inst = new Instruction(getUniqueId(), makeVoidType(), OpExtInst);
|
||||
inst->addIdOperand(nonSemanticShaderDebugInfo);
|
||||
inst->addImmediateOperand(NonSemanticShaderDebugInfo100DebugValue);
|
||||
inst->addIdOperand(debugLocalVariable); // debug local variable id
|
||||
inst->addIdOperand(value); // value of local variable id
|
||||
inst->addIdOperand(makeDebugExpression()); // expression id
|
||||
buildPoint->addInstruction(std::unique_ptr<Instruction>(inst));
|
||||
|
||||
@@ -2061,11 +2074,6 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
|
||||
{
|
||||
assert(! entryPointFunction);
|
||||
|
||||
Block* entry;
|
||||
std::vector<Id> paramsTypes;
|
||||
std::vector<char const*> paramNames;
|
||||
std::vector<std::vector<Decoration>> decorations;
|
||||
|
||||
auto const returnType = makeVoidType();
|
||||
|
||||
restoreNonSemanticShaderDebugInfo = emitNonSemanticShaderDebugInfo;
|
||||
@@ -2073,7 +2081,8 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
|
||||
emitNonSemanticShaderDebugInfo = false;
|
||||
}
|
||||
|
||||
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, paramsTypes, paramNames, decorations, &entry);
|
||||
Block* entry = nullptr;
|
||||
entryPointFunction = makeFunctionEntry(NoPrecision, returnType, entryPoint, LinkageTypeMax, {}, {}, &entry);
|
||||
|
||||
emitNonSemanticShaderDebugInfo = restoreNonSemanticShaderDebugInfo;
|
||||
|
||||
@@ -2082,8 +2091,8 @@ Function* Builder::makeEntryPoint(const char* entryPoint)
|
||||
|
||||
// Comments in header
|
||||
Function* Builder::makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
|
||||
const std::vector<Id>& paramTypes, const std::vector<char const*>& paramNames,
|
||||
const std::vector<std::vector<Decoration>>& decorations, Block **entry)
|
||||
const std::vector<Id>& paramTypes,
|
||||
const std::vector<std::vector<Decoration>>& decorations, Block** entry)
|
||||
{
|
||||
// Make the function and initial instructions in it
|
||||
Id typeId = makeFunctionType(returnType, paramTypes);
|
||||
@@ -2148,20 +2157,25 @@ void Builder::setupDebugFunctionEntry(Function* function, const char* name, int
|
||||
Id firstParamId = function->getParamId(0);
|
||||
|
||||
for (size_t p = 0; p < paramTypes.size(); ++p) {
|
||||
auto getParamTypeId = [this](Id const& typeId) {
|
||||
if (isPointerType(typeId) || isArrayType(typeId)) {
|
||||
return getContainedTypeId(typeId);
|
||||
} else {
|
||||
return typeId;
|
||||
}
|
||||
};
|
||||
bool passByRef = false;
|
||||
Id paramTypeId = paramTypes[p];
|
||||
|
||||
// For pointer-typed parameters, they are actually passed by reference and we need unwrap the pointer to get the actual parameter type.
|
||||
if (isPointerType(paramTypeId) || isArrayType(paramTypeId)) {
|
||||
passByRef = true;
|
||||
paramTypeId = getContainedTypeId(paramTypeId);
|
||||
}
|
||||
|
||||
auto const& paramName = paramNames[p];
|
||||
auto const debugLocalVariableId =
|
||||
createDebugLocalVariable(debugId[getParamTypeId(paramTypes[p])], paramName, p + 1);
|
||||
auto const debugLocalVariableId = createDebugLocalVariable(debugId[paramTypeId], paramName, p + 1);
|
||||
auto const paramId = static_cast<Id>(firstParamId + p);
|
||||
debugId[paramId] = debugLocalVariableId;
|
||||
|
||||
debugId[firstParamId + p] = debugLocalVariableId;
|
||||
|
||||
makeDebugDeclare(debugLocalVariableId, firstParamId + p);
|
||||
if (passByRef) {
|
||||
makeDebugDeclare(debugLocalVariableId, paramId);
|
||||
} else {
|
||||
makeDebugValue(debugLocalVariableId, paramId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
9
3rdparty/glslang/SPIRV/SpvBuilder.h
vendored
9
3rdparty/glslang/SPIRV/SpvBuilder.h
vendored
@@ -231,7 +231,7 @@ public:
|
||||
Id createDebugGlobalVariable(Id const type, char const*const name, Id const variable);
|
||||
Id createDebugLocalVariable(Id type, char const*const name, size_t const argNumber = 0);
|
||||
Id makeDebugExpression();
|
||||
Id makeDebugDeclare(Id const debugLocalVariable, Id const localVariable);
|
||||
Id makeDebugDeclare(Id const debugLocalVariable, Id const pointer);
|
||||
Id makeDebugValue(Id const debugLocalVariable, Id const value);
|
||||
Id makeDebugFunctionType(Id returnType, const std::vector<Id>& paramTypes);
|
||||
Id makeDebugFunction(Function* function, Id nameId, Id funcTypeId);
|
||||
@@ -420,10 +420,9 @@ public:
|
||||
// Make a shader-style function, and create its entry block if entry is non-zero.
|
||||
// Return the function, pass back the entry.
|
||||
// The returned pointer is only valid for the lifetime of this builder.
|
||||
Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name,
|
||||
LinkageType linkType, const std::vector<Id>& paramTypes,
|
||||
const std::vector<char const*>& paramNames,
|
||||
const std::vector<std::vector<Decoration>>& precisions, Block **entry = nullptr);
|
||||
Function* makeFunctionEntry(Decoration precision, Id returnType, const char* name, LinkageType linkType,
|
||||
const std::vector<Id>& paramTypes,
|
||||
const std::vector<std::vector<Decoration>>& precisions, Block** entry = nullptr);
|
||||
|
||||
// Create a return. An 'implicit' return is one not appearing in the source
|
||||
// code. In the case of an implicit return, no post-return block is inserted.
|
||||
|
||||
13
3rdparty/glslang/SPIRV/SpvTools.h
vendored
13
3rdparty/glslang/SPIRV/SpvTools.h
vendored
@@ -48,22 +48,11 @@
|
||||
#endif
|
||||
|
||||
#include "glslang/MachineIndependent/localintermediate.h"
|
||||
#include "GlslangToSpv.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace glslang {
|
||||
|
||||
struct SpvOptions {
|
||||
bool generateDebugInfo {false};
|
||||
bool stripDebugInfo {false};
|
||||
bool disableOptimizer {true};
|
||||
bool optimizeSize {false};
|
||||
bool disassemble {false};
|
||||
bool validate {false};
|
||||
bool emitNonSemanticShaderDebugInfo {false};
|
||||
bool emitNonSemanticShaderDebugSource{ false };
|
||||
bool compileOnly{false};
|
||||
};
|
||||
|
||||
#if ENABLE_OPT
|
||||
|
||||
// Translate glslang's view of target versioning to what SPIRV-Tools uses.
|
||||
|
||||
1
3rdparty/glslang/StandAlone/StandAlone.cpp
vendored
1
3rdparty/glslang/StandAlone/StandAlone.cpp
vendored
@@ -46,6 +46,7 @@
|
||||
#include "DirStackFileIncluder.h"
|
||||
#include "./../glslang/Include/ShHandle.h"
|
||||
#include "./../glslang/Public/ShaderLang.h"
|
||||
#include "../glslang/MachineIndependent/localintermediate.h"
|
||||
#include "../SPIRV/GlslangToSpv.h"
|
||||
#include "../SPIRV/GLSL.std.450.h"
|
||||
#include "../SPIRV/doc.h"
|
||||
|
||||
@@ -9551,6 +9551,8 @@ bool HlslParseContext::isInputBuiltIn(const TQualifier& qualifier) const
|
||||
return language == EShLangTessEvaluation;
|
||||
case EbvTessCoord:
|
||||
return language == EShLangTessEvaluation;
|
||||
case EbvViewIndex:
|
||||
return language != EShLangCompute;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -512,6 +512,7 @@ void HlslScanContext::fillInKeywordMap()
|
||||
(*SemanticMap)["SV_PRIMITIVEID"] = EbvPrimitiveId;
|
||||
(*SemanticMap)["SV_OUTPUTCONTROLPOINTID"] = EbvInvocationId;
|
||||
(*SemanticMap)["SV_ISFRONTFACE"] = EbvFace;
|
||||
(*SemanticMap)["SV_VIEWID"] = EbvViewIndex;
|
||||
(*SemanticMap)["SV_INSTANCEID"] = EbvInstanceIndex;
|
||||
(*SemanticMap)["SV_INSIDETESSFACTOR"] = EbvTessLevelInner;
|
||||
(*SemanticMap)["SV_GSINSTANCEID"] = EbvInvocationId;
|
||||
|
||||
28
3rdparty/glslang/glslang/Include/Common.h
vendored
28
3rdparty/glslang/glslang/Include/Common.h
vendored
@@ -294,34 +294,6 @@ template <class T> int IntLog2(T n)
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool IsInfinity(double x) {
|
||||
#ifdef _MSC_VER
|
||||
switch (_fpclass(x)) {
|
||||
case _FPCLASS_NINF:
|
||||
case _FPCLASS_PINF:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return std::isinf(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
inline bool IsNan(double x) {
|
||||
#ifdef _MSC_VER
|
||||
switch (_fpclass(x)) {
|
||||
case _FPCLASS_SNAN:
|
||||
case _FPCLASS_QNAN:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
return std::isnan(x);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // end namespace glslang
|
||||
|
||||
#endif // _COMMON_INCLUDED_
|
||||
|
||||
4
3rdparty/glslang/glslang/Include/Types.h
vendored
4
3rdparty/glslang/glslang/Include/Types.h
vendored
@@ -1075,7 +1075,7 @@ public:
|
||||
}
|
||||
|
||||
// GL_EXT_spirv_intrinsics
|
||||
bool hasSprivDecorate() const { return spirvDecorate != nullptr; }
|
||||
bool hasSpirvDecorate() const { return spirvDecorate != nullptr; }
|
||||
void setSpirvDecorate(int decoration, const TIntermAggregate* args = nullptr);
|
||||
void setSpirvDecorateId(int decoration, const TIntermAggregate* args);
|
||||
void setSpirvDecorateString(int decoration, const TIntermAggregate* args);
|
||||
@@ -2096,7 +2096,7 @@ public:
|
||||
const auto appendInt = [&](int i) { typeString.append(std::to_string(i).c_str()); };
|
||||
|
||||
if (getQualifiers) {
|
||||
if (qualifier.hasSprivDecorate())
|
||||
if (qualifier.hasSpirvDecorate())
|
||||
appendStr(qualifier.getSpirvDecorateQualifierString().c_str());
|
||||
|
||||
if (qualifier.hasLayout()) {
|
||||
|
||||
@@ -628,12 +628,12 @@ TIntermTyped* TIntermConstantUnion::fold(TOperator op, const TType& returnType)
|
||||
|
||||
case EOpIsNan:
|
||||
{
|
||||
newConstArray[i].setBConst(IsNan(unionArray[i].getDConst()));
|
||||
newConstArray[i].setBConst(std::isnan(unionArray[i].getDConst()));
|
||||
break;
|
||||
}
|
||||
case EOpIsInf:
|
||||
{
|
||||
newConstArray[i].setBConst(IsInfinity(unionArray[i].getDConst()));
|
||||
newConstArray[i].setBConst(std::isinf(unionArray[i].getDConst()));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -144,10 +144,10 @@ EProfile EDesktopProfile = static_cast<EProfile>(ENoProfile | ECoreProfile | ECo
|
||||
{ EBadProfile } };
|
||||
const Versioning* Es300Desktop130 = &Es300Desktop130Version[0];
|
||||
|
||||
const Versioning Es310Desktop420Version[] = { { EEsProfile, 0, 310, 0, nullptr },
|
||||
{ EDesktopProfile, 0, 420, 0, nullptr },
|
||||
const Versioning Es310Desktop400Version[] = { { EEsProfile, 0, 310, 0, nullptr },
|
||||
{ EDesktopProfile, 0, 400, 0, nullptr },
|
||||
{ EBadProfile } };
|
||||
const Versioning* Es310Desktop420 = &Es310Desktop420Version[0];
|
||||
const Versioning* Es310Desktop400 = &Es310Desktop400Version[0];
|
||||
|
||||
const Versioning Es310Desktop450Version[] = { { EEsProfile, 0, 310, 0, nullptr },
|
||||
{ EDesktopProfile, 0, 450, 0, nullptr },
|
||||
@@ -246,14 +246,14 @@ const BuiltInFunction BaseFunctions[] = {
|
||||
{ EOpGreaterThanEqual, "greaterThanEqual", 2, TypeU, ClassBNS, Es300Desktop130 },
|
||||
{ EOpVectorEqual, "equal", 2, TypeU, ClassBNS, Es300Desktop130 },
|
||||
{ EOpVectorNotEqual, "notEqual", 2, TypeU, ClassBNS, Es300Desktop130 },
|
||||
{ EOpAtomicAdd, "atomicAdd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicMin, "atomicMin", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicMax, "atomicMax", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicAnd, "atomicAnd", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicOr, "atomicOr", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicXor, "atomicXor", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicExchange, "atomicExchange", 2, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicCompSwap, "atomicCompSwap", 3, TypeIU, ClassV1FIOCV, Es310Desktop420 },
|
||||
{ EOpAtomicAdd, "atomicAdd", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicMin, "atomicMin", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicMax, "atomicMax", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicAnd, "atomicAnd", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicOr, "atomicOr", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicXor, "atomicXor", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicExchange, "atomicExchange", 2, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpAtomicCompSwap, "atomicCompSwap", 3, TypeIU, ClassV1FIOCV, Es310Desktop400 },
|
||||
{ EOpMix, "mix", 3, TypeB, ClassRegular, Es310Desktop450 },
|
||||
{ EOpMix, "mix", 3, TypeIU, ClassLB, Es310Desktop450 },
|
||||
|
||||
|
||||
@@ -2590,6 +2590,18 @@ TIntermConstantUnion* TIntermediate::addConstantUnion(double d, TBasicType baseT
|
||||
{
|
||||
assert(baseType == EbtFloat || baseType == EbtDouble || baseType == EbtFloat16);
|
||||
|
||||
if (isEsProfile() && (baseType == EbtFloat || baseType == EbtFloat16)) {
|
||||
int exponent = 0;
|
||||
frexp(d, &exponent);
|
||||
int minExp = baseType == EbtFloat ? -126 : -14;
|
||||
int maxExp = baseType == EbtFloat ? 127 : 15;
|
||||
if (exponent > maxExp) { //overflow, d = inf
|
||||
d = std::numeric_limits<double>::infinity();
|
||||
} else if (exponent < minExp) { //underflow, d = 0.0;
|
||||
d = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
TConstUnionArray unionArray(1);
|
||||
unionArray[0].setDConst(d);
|
||||
|
||||
@@ -2647,28 +2659,42 @@ TIntermTyped* TIntermediate::addSwizzle(TSwizzleSelectors<selectorType>& selecto
|
||||
// 'swizzleOkay' says whether or not it is okay to consider a swizzle
|
||||
// a valid part of the dereference chain.
|
||||
//
|
||||
// 'BufferReferenceOk' says if type is buffer_reference, the routine stop to find the most left node.
|
||||
// 'bufferReferenceOk' says if type is buffer_reference, the routine will stop to find the most left node.
|
||||
//
|
||||
// 'proc' is an optional function to run on each node that is processed during the traversal. 'proc' must
|
||||
// return true to continue the traversal, or false to end the traversal early.
|
||||
//
|
||||
|
||||
const TIntermTyped* TIntermediate::findLValueBase(const TIntermTyped* node, bool swizzleOkay , bool bufferReferenceOk)
|
||||
const TIntermTyped* TIntermediate::traverseLValueBase(const TIntermTyped* node, bool swizzleOkay,
|
||||
bool bufferReferenceOk,
|
||||
std::function<bool(const TIntermNode&)> proc)
|
||||
{
|
||||
do {
|
||||
const TIntermBinary* binary = node->getAsBinaryNode();
|
||||
if (binary == nullptr)
|
||||
if (binary == nullptr) {
|
||||
if (proc) {
|
||||
proc(*node);
|
||||
}
|
||||
return node;
|
||||
}
|
||||
TOperator op = binary->getOp();
|
||||
if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle && op != EOpMatrixSwizzle)
|
||||
if (op != EOpIndexDirect && op != EOpIndexIndirect && op != EOpIndexDirectStruct && op != EOpVectorSwizzle &&
|
||||
op != EOpMatrixSwizzle)
|
||||
return nullptr;
|
||||
if (! swizzleOkay) {
|
||||
if (!swizzleOkay) {
|
||||
if (op == EOpVectorSwizzle || op == EOpMatrixSwizzle)
|
||||
return nullptr;
|
||||
if ((op == EOpIndexDirect || op == EOpIndexIndirect) &&
|
||||
(binary->getLeft()->getType().isVector() || binary->getLeft()->getType().isScalar()) &&
|
||||
! binary->getLeft()->getType().isArray())
|
||||
!binary->getLeft()->getType().isArray())
|
||||
return nullptr;
|
||||
}
|
||||
node = node->getAsBinaryNode()->getLeft();
|
||||
if (proc) {
|
||||
if (!proc(*node)) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
node = binary->getLeft();
|
||||
if (bufferReferenceOk && node->isReference())
|
||||
return node;
|
||||
} while (true);
|
||||
|
||||
@@ -208,7 +208,7 @@ bool TParseContextBase::lValueErrorCheck(const TSourceLoc& loc, const char* op,
|
||||
//
|
||||
// If we get here, we have an error and a message.
|
||||
//
|
||||
const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true);
|
||||
const TIntermTyped* leftMostTypeNode = TIntermediate::traverseLValueBase(node, true);
|
||||
|
||||
if (symNode)
|
||||
error(loc, " l-value required", op, "\"%s\" (%s)", symbol, message);
|
||||
@@ -234,7 +234,7 @@ void TParseContextBase::rValueErrorCheck(const TSourceLoc& loc, const char* op,
|
||||
const TIntermSymbol* symNode = node->getAsSymbolNode();
|
||||
|
||||
if (node->getQualifier().isWriteOnly()) {
|
||||
const TIntermTyped* leftMostTypeNode = TIntermediate::findLValueBase(node, true);
|
||||
const TIntermTyped* leftMostTypeNode = TIntermediate::traverseLValueBase(node, true);
|
||||
|
||||
if (symNode != nullptr)
|
||||
error(loc, "can't read from writeonly object: ", op, symNode->getName().c_str());
|
||||
|
||||
@@ -2571,7 +2571,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan
|
||||
requireExtensions(loc, 1, &E_GL_EXT_shader_atomic_float2, fnCandidate.getName().c_str());
|
||||
}
|
||||
|
||||
const TIntermTyped* base = TIntermediate::findLValueBase(arg0, true , true);
|
||||
const TIntermTyped* base = TIntermediate::traverseLValueBase(arg0, true, true);
|
||||
const char* errMsg = "Only l-values corresponding to shader block storage or shared variables can be used with "
|
||||
"atomic memory functions.";
|
||||
if (base) {
|
||||
@@ -2591,20 +2591,57 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan
|
||||
case EOpInterpolateAtCentroid:
|
||||
case EOpInterpolateAtSample:
|
||||
case EOpInterpolateAtOffset:
|
||||
case EOpInterpolateAtVertex:
|
||||
// Make sure the first argument is an interpolant, or an array element of an interpolant
|
||||
case EOpInterpolateAtVertex: {
|
||||
if (arg0->getType().getQualifier().storage != EvqVaryingIn) {
|
||||
// It might still be an array element.
|
||||
// Traverse down the left branch of arg0 to ensure this argument is a valid interpolant.
|
||||
//
|
||||
// We could check more, but the semantics of the first argument are already met; the
|
||||
// only way to turn an array into a float/vec* is array dereference and swizzle.
|
||||
// For desktop GL >4.3 we effectively only need to ensure that arg0 represents an l-value from an
|
||||
// input declaration.
|
||||
//
|
||||
// ES and desktop 4.3 and earlier: swizzles may not be used
|
||||
// desktop 4.4 and later: swizzles may be used
|
||||
bool swizzleOkay = (!isEsProfile()) && (version >= 440);
|
||||
const TIntermTyped* base = TIntermediate::findLValueBase(arg0, swizzleOkay);
|
||||
if (base == nullptr || base->getType().getQualifier().storage != EvqVaryingIn)
|
||||
error(loc, "first argument must be an interpolant, or interpolant-array element", fnCandidate.getName().c_str(), "");
|
||||
// For desktop GL <= 4.3 and ES, we must also ensure that swizzling is not used
|
||||
//
|
||||
// For ES, we must also ensure that a field selection operator (i.e., '.') is not used on a named
|
||||
// struct.
|
||||
|
||||
const bool esProfile = isEsProfile();
|
||||
const bool swizzleOkay = !esProfile && (version >= 440);
|
||||
|
||||
std::string interpolantErrorMsg = "first argument must be an interpolant, or interpolant-array element";
|
||||
bool isValid = true; // Assume that the interpolant is valid until we find a condition making it invalid
|
||||
bool isIn = false; // Checks whether or not the interpolant is a shader input
|
||||
bool structAccessOp = false; // Whether or not the previous node in the chain is a struct accessor
|
||||
TIntermediate::traverseLValueBase(
|
||||
arg0, swizzleOkay, false,
|
||||
[&isValid, &isIn, &interpolantErrorMsg, esProfile, &structAccessOp](const TIntermNode& n) -> bool {
|
||||
auto* type = n.getAsTyped();
|
||||
if (type) {
|
||||
if (type->getType().getQualifier().storage == EvqVaryingIn) {
|
||||
isIn = true;
|
||||
}
|
||||
// If a field accessor was used, it can only be used to access a field with an input block, not a struct.
|
||||
if (structAccessOp && (type->getType().getBasicType() != EbtBlock)) {
|
||||
interpolantErrorMsg +=
|
||||
". Using the field of a named struct as an interpolant argument is not "
|
||||
"allowed (ES-only).";
|
||||
isValid = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ES has different requirements for interpolants than GL
|
||||
if (esProfile) {
|
||||
// Swizzling will be taken care of by the 'swizzleOkay' argument passsed to traverseLValueBase,
|
||||
// so we only ned to check whether or not a field accessor has been used with a named struct.
|
||||
auto* binary = n.getAsBinaryNode();
|
||||
if (binary && (binary->getOp() == EOpIndexDirectStruct)) {
|
||||
structAccessOp = true;
|
||||
}
|
||||
}
|
||||
// Don't continue traversing if we know we have an invalid interpolant at this point.
|
||||
return isValid;
|
||||
});
|
||||
if (!isIn || !isValid) {
|
||||
error(loc, interpolantErrorMsg.c_str(), fnCandidate.getName().c_str(), "");
|
||||
}
|
||||
}
|
||||
|
||||
if (callNode.getOp() == EOpInterpolateAtVertex) {
|
||||
@@ -2620,7 +2657,7 @@ void TParseContext::builtInOpCheck(const TSourceLoc& loc, const TFunction& fnCan
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
} break;
|
||||
|
||||
case EOpEmitStreamVertex:
|
||||
case EOpEndStreamPrimitive:
|
||||
@@ -4191,8 +4228,8 @@ void TParseContext::mergeQualifiers(const TSourceLoc& loc, TQualifier& dst, cons
|
||||
dst.spirvStorageClass = src.spirvStorageClass;
|
||||
|
||||
// SPIR-V decorate qualifiers (GL_EXT_spirv_intrinsics)
|
||||
if (src.hasSprivDecorate()) {
|
||||
if (dst.hasSprivDecorate()) {
|
||||
if (src.hasSpirvDecorate()) {
|
||||
if (dst.hasSpirvDecorate()) {
|
||||
const TSpirvDecorate& srcSpirvDecorate = src.getSpirvDecorate();
|
||||
TSpirvDecorate& dstSpirvDecorate = dst.getSpirvDecorate();
|
||||
for (auto& decorate : srcSpirvDecorate.decorates) {
|
||||
@@ -6326,8 +6363,7 @@ void TParseContext::layoutObjectCheck(const TSourceLoc& loc, const TSymbol& symb
|
||||
switch (qualifier.storage) {
|
||||
case EvqVaryingIn:
|
||||
case EvqVaryingOut:
|
||||
if (!type.getQualifier().isTaskMemory() &&
|
||||
!type.getQualifier().hasSprivDecorate() &&
|
||||
if (!type.getQualifier().isTaskMemory() && !type.getQualifier().hasSpirvDecorate() &&
|
||||
(type.getBasicType() != EbtBlock ||
|
||||
(!(*type.getStruct())[0].type->getQualifier().hasLocation() &&
|
||||
(*type.getStruct())[0].type->getQualifier().builtIn == EbvNone)))
|
||||
@@ -8540,7 +8576,7 @@ void TParseContext::declareBlock(const TSourceLoc& loc, TTypeList& typeList, con
|
||||
memberQualifier.storage = EvqtaskPayloadSharedEXT;
|
||||
if (memberQualifier.storage == EvqSpirvStorageClass)
|
||||
error(memberLoc, "member cannot have a spirv_storage_class qualifier", memberType.getFieldName().c_str(), "");
|
||||
if (memberQualifier.hasSprivDecorate() && !memberQualifier.getSpirvDecorate().decorateIds.empty())
|
||||
if (memberQualifier.hasSpirvDecorate() && !memberQualifier.getSpirvDecorate().decorateIds.empty())
|
||||
error(memberLoc, "member cannot have a spirv_decorate_id qualifier", memberType.getFieldName().c_str(), "");
|
||||
if ((currentBlockQualifier.storage == EvqUniform || currentBlockQualifier.storage == EvqBuffer) && (memberQualifier.isInterpolation() || memberQualifier.isAuxiliary()))
|
||||
error(memberLoc, "member of uniform or buffer block cannot have an auxiliary or interpolation qualifier", memberType.getFieldName().c_str(), "");
|
||||
|
||||
@@ -1208,12 +1208,12 @@ bool TOutputTraverser::visitSelection(TVisit /* visit */, TIntermSelection* node
|
||||
// - shows all digits, no premature rounding
|
||||
static void OutputDouble(TInfoSink& out, double value, TOutputTraverser::EExtraOutput extra)
|
||||
{
|
||||
if (IsInfinity(value)) {
|
||||
if (std::isinf(value)) {
|
||||
if (value < 0)
|
||||
out.debug << "-1.#INF";
|
||||
else
|
||||
out.debug << "+1.#INF";
|
||||
} else if (IsNan(value))
|
||||
} else if (std::isnan(value))
|
||||
out.debug << "1.#IND";
|
||||
else {
|
||||
const int maxSize = 340;
|
||||
|
||||
@@ -866,7 +866,7 @@ int TDefaultIoResolverBase::resolveInOutLocation(EShLanguage stage, TVarEntryInf
|
||||
}
|
||||
|
||||
// no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
|
||||
if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
|
||||
if (type.getQualifier().hasLocation() || type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
|
||||
return ent.newLocation = -1;
|
||||
}
|
||||
|
||||
@@ -953,7 +953,7 @@ int TDefaultGlslIoResolver::resolveInOutLocation(EShLanguage stage, TVarEntryInf
|
||||
return ent.newLocation = type.getQualifier().layoutLocation;
|
||||
}
|
||||
// no locations added if already present, a built-in variable, or a variable with SPIR-V decorate
|
||||
if (type.isBuiltIn() || type.getQualifier().hasSprivDecorate()) {
|
||||
if (type.isBuiltIn() || type.getQualifier().hasSpirvDecorate()) {
|
||||
return ent.newLocation = -1;
|
||||
}
|
||||
// no locations on blocks of built-in variables
|
||||
|
||||
@@ -43,11 +43,12 @@
|
||||
#include "../Public/ShaderLang.h"
|
||||
#include "Versions.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <array>
|
||||
|
||||
class TInfoSink;
|
||||
|
||||
@@ -572,7 +573,8 @@ public:
|
||||
TIntermTyped* foldSwizzle(TIntermTyped* node, TSwizzleSelectors<TVectorSelector>& fields, const TSourceLoc&);
|
||||
|
||||
// Tree ops
|
||||
static const TIntermTyped* findLValueBase(const TIntermTyped*, bool swizzleOkay , bool BufferReferenceOk = false);
|
||||
static const TIntermTyped* traverseLValueBase(const TIntermTyped*, bool swizzleOkay, bool bufferReferenceOk = false,
|
||||
std::function<bool(const TIntermNode&)> proc = {});
|
||||
|
||||
// Linkage related
|
||||
void addSymbolLinkageNodes(TIntermAggregate*& linkage, EShLanguage, TSymbolTable&);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "shaderc.h"
|
||||
|
||||
#include <iostream> // std::cout
|
||||
|
||||
BX_PRAGMA_DIAGNOSTIC_PUSH()
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: 'inclusionDepth' : unreferenced formal parameter
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4265) // error C4265: 'spv::spirvbin_t': class has virtual functions, but destructor is not virtual
|
||||
@@ -15,8 +17,9 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u
|
||||
#define ENABLE_OPT 1
|
||||
#include <ShaderLang.h>
|
||||
#include <ResourceLimits.h>
|
||||
#include <SPIRV/SPVRemapper.h>
|
||||
#include <SPIRV/GlslangToSpv.h>
|
||||
#include <SPIRV/SPVRemapper.h>
|
||||
#include <SPIRV/SpvTools.h>
|
||||
#define SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
||||
#include <spirv_msl.hpp>
|
||||
#include <spirv_reflect.hpp>
|
||||
@@ -489,8 +492,6 @@ namespace bgfx { namespace metal
|
||||
program->dumpReflection();
|
||||
}
|
||||
|
||||
BX_UNUSED(spv::MemorySemanticsAllMemory);
|
||||
|
||||
glslang::TIntermediate* intermediate = program->getIntermediate(stage);
|
||||
std::vector<uint32_t> spirv;
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "shaderc.h"
|
||||
|
||||
#include <iostream> // std::cout
|
||||
|
||||
BX_PRAGMA_DIAGNOSTIC_PUSH()
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: 'inclusionDepth' : unreferenced formal parameter
|
||||
BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4265) // error C4265: 'spv::spirvbin_t': class has virtual functions, but destructor is not virtual
|
||||
@@ -17,6 +19,7 @@ BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") // warning: declaration of 'u
|
||||
#include <ResourceLimits.h>
|
||||
#include <SPIRV/SPVRemapper.h>
|
||||
#include <SPIRV/GlslangToSpv.h>
|
||||
#include <SPIRV/SpvTools.h>
|
||||
#define SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS
|
||||
#include <spirv_msl.hpp>
|
||||
#include <spirv_reflect.hpp>
|
||||
@@ -698,8 +701,6 @@ namespace bgfx { namespace spirv
|
||||
program->dumpReflection();
|
||||
}
|
||||
|
||||
BX_UNUSED(spv::MemorySemanticsAllMemory);
|
||||
|
||||
glslang::TIntermediate* intermediate = program->getIntermediate(stage);
|
||||
std::vector<uint32_t> spirv;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user