mirror of https://github.com/mosra/magnum.git
Browse Source
Currently those will be needed mainly by the Vk library to patch around a SwiftShader bug. I'm not sure yet how the public API should look so it's all hidden in the Implementation namespace for now.pull/495/head
7 changed files with 488 additions and 2 deletions
@ -0,0 +1,183 @@ |
|||||||
|
#ifndef Magnum_ShaderTools_Implementation_spirv_h |
||||||
|
#define Magnum_ShaderTools_Implementation_spirv_h |
||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <Corrade/Containers/ArrayView.h> |
||||||
|
#include <Corrade/Containers/Optional.h> |
||||||
|
#include <Corrade/Containers/Reference.h> |
||||||
|
#include <Corrade/Containers/StringView.h> |
||||||
|
|
||||||
|
#include "Magnum/Magnum.h" |
||||||
|
#include "MagnumExternal/Vulkan/spirv.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace ShaderTools { namespace Implementation { namespace { |
||||||
|
|
||||||
|
/* This is used by both magnum-shaderconverter and the Vk library for
|
||||||
|
SwiftShader workarounds but we don't want the Vk library to depend on |
||||||
|
ShaderTools, so the minimal needed subset is made header-only. |
||||||
|
|
||||||
|
Eventually this should be turned into a public API, but so far it's just a |
||||||
|
bag of random functions with very specific usage patterns and isn't clear |
||||||
|
yet how to expose a usable interface. Moreover, the SwiftShader patching |
||||||
|
needs to mutate the original, which means the outputs are pointers to the |
||||||
|
original data. */ |
||||||
|
|
||||||
|
/* If the code looks like a valid SPIR-V, returns everything after the
|
||||||
|
header. If not, nullptr. */ |
||||||
|
Containers::ArrayView<const UnsignedInt> spirvData(const void* code, UnsignedInt size) { |
||||||
|
const UnsignedInt* const spirv = static_cast<const UnsignedInt*>(code); |
||||||
|
/* Not >= 5*4 because just the header alone is useless also */ |
||||||
|
return size % 4 == 0 && size > 5*4 && spirv[0] == SpvMagicNumber ? |
||||||
|
Containers::ArrayView<const UnsignedInt>{spirv, size/4}.suffix(5) : nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* When an instruction is found, the `data` is advanced after it in order to
|
||||||
|
allow calling this function in a loop. When not found, `data` is left |
||||||
|
untouched. */ |
||||||
|
Containers::ArrayView<const UnsignedInt> spirvFindInstruction(Containers::ArrayView<const UnsignedInt>& data, const SpvOp op) { |
||||||
|
/* Copy the view and iterate that. If we find the instruction, update the
|
||||||
|
passed `data` reference, if not, keep it as it was -- that way, if the |
||||||
|
find fails, `data` won't become empty and can be used further */ |
||||||
|
for(Containers::ArrayView<const UnsignedInt> dataIteration = data; !dataIteration.empty(); ) { |
||||||
|
const UnsignedInt instructionSize = dataIteration[0] >> 16; |
||||||
|
const UnsignedInt instructionOp = dataIteration[0] & 0xffff; |
||||||
|
|
||||||
|
/* Corrupted SPIR-V */ |
||||||
|
/** @todo print a message here? */ |
||||||
|
if(dataIteration.size() < instructionSize) { |
||||||
|
data = dataIteration; |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
/* This is the instruction we're looking for, return it and update the
|
||||||
|
view to point after it. */ |
||||||
|
if(instructionOp == op) { |
||||||
|
data = dataIteration.suffix(instructionSize); |
||||||
|
return dataIteration.prefix(instructionSize); |
||||||
|
} |
||||||
|
|
||||||
|
/* Otherwise advance the view for next round */ |
||||||
|
dataIteration = dataIteration.suffix(instructionSize); |
||||||
|
} |
||||||
|
|
||||||
|
/* Nothing found. Leave the input data as-is. */ |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
struct SpirvEntrypoint { |
||||||
|
Containers::Reference<const SpvExecutionModel> executionModel; |
||||||
|
Containers::StringView name; |
||||||
|
Containers::ArrayView<const UnsignedInt> interfaces; |
||||||
|
}; |
||||||
|
|
||||||
|
/* When an entrypoint is found, the `data` is advanced after the instruction in
|
||||||
|
order to allow calling this function in a loop. When not found, `data` is |
||||||
|
left untouched. Most of other SPIR-V code is meant to appear after the |
||||||
|
entrypoints, so it's fine to feed the resulting `data` to |
||||||
|
spirvEntrypointInterface() and others. */ |
||||||
|
Containers::Optional<SpirvEntrypoint> spirvNextEntrypoint(Containers::ArrayView<const UnsignedInt>& data) { |
||||||
|
while(const Containers::ArrayView<const UnsignedInt> entryPoint = spirvFindInstruction(data, SpvOpEntryPoint)) { |
||||||
|
/* Expecting at least op, execution model, ID, name. If less, it's an
|
||||||
|
invalid SPIR-V. */ |
||||||
|
/** @todo print a message here? */ |
||||||
|
if(entryPoint.size() < 4) return {}; |
||||||
|
|
||||||
|
/* Find where the name ends and interface IDs start. According to the
|
||||||
|
spec, a string literal is null-terminated and all bytes after are |
||||||
|
zeros as well, so it should be enough to check that the last byte is |
||||||
|
zero. */ |
||||||
|
Containers::ArrayView<const UnsignedInt> interfaces; |
||||||
|
for(std::size_t i = 3; i != entryPoint.size(); ++i) { |
||||||
|
if(entryPoint[i] >> 24 == 0) { |
||||||
|
interfaces = entryPoint.suffix(i + 1); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return SpirvEntrypoint{ |
||||||
|
*reinterpret_cast<const SpvExecutionModel*>(entryPoint + 1), |
||||||
|
reinterpret_cast<const char*>(entryPoint + 3), |
||||||
|
interfaces |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
struct SpirvEntrypointInterface { |
||||||
|
/* If null, the interface might be for example builtin */ |
||||||
|
const UnsignedInt* location; |
||||||
|
/* If null, the SPIR-V is probably invalid */ |
||||||
|
const SpvStorageClass* storageClass; |
||||||
|
}; |
||||||
|
|
||||||
|
/* Unlike above, `data` isn't modified by this function -- because the
|
||||||
|
decoration and variable instructions are likely intermixed for different |
||||||
|
entrypoint, it makes sense to restart the search from the beginning for each |
||||||
|
entrypoint. |
||||||
|
|
||||||
|
The `out` array is expected to have the same size as entrypoint.interfaces |
||||||
|
and be zero-initialized (so the not found data stay null). */ |
||||||
|
void spirvEntrypointInterface(Containers::ArrayView<const UnsignedInt> data, const SpirvEntrypoint& entrypoint, Containers::ArrayView<SpirvEntrypointInterface> out) { |
||||||
|
CORRADE_INTERNAL_ASSERT(out.size() == entrypoint.interfaces.size()); |
||||||
|
|
||||||
|
/* Find location decorations */ |
||||||
|
while(const Containers::ArrayView<const UnsignedInt> decoration = spirvFindInstruction(data, SpvOpDecorate)) { |
||||||
|
/* Expecting at least op, ID, SpvDecorationLocation, location. The
|
||||||
|
instruction can be three words, so if we get less than 4 it's not an |
||||||
|
error. */ |
||||||
|
if(decoration.size() < 4 || decoration[2] != SpvDecorationLocation) |
||||||
|
continue; |
||||||
|
|
||||||
|
for(std::size_t i = 0; i != entrypoint.interfaces.size(); ++i) { |
||||||
|
if(decoration[1] == entrypoint.interfaces[i]) { |
||||||
|
out[i].location = decoration + 3; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/* Find storage classes. According to the spec, OpVariable is meant to
|
||||||
|
appear after OpDecorate, so we don't need to restart from the |
||||||
|
beginning. */ |
||||||
|
while(const Containers::ArrayView<const UnsignedInt> variable = spirvFindInstruction(data, SpvOpVariable)) { |
||||||
|
/* Expecting at least op, result, ID, SpvStorageClass. If less, it's an
|
||||||
|
invalid SPIR-V. */ |
||||||
|
/** @todo print a message here? */ |
||||||
|
if(variable.size() < 4) return; |
||||||
|
|
||||||
|
for(std::size_t i = 0; i != entrypoint.interfaces.size(); ++i) { |
||||||
|
if(variable[2] == entrypoint.interfaces[i]) { |
||||||
|
out[i].storageClass = reinterpret_cast<const SpvStorageClass*>(variable + 3); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
#endif |
||||||
@ -0,0 +1,264 @@ |
|||||||
|
/*
|
||||||
|
This file is part of Magnum. |
||||||
|
|
||||||
|
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, |
||||||
|
2020, 2021 Vladimír Vondruš <mosra@centrum.cz> |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a |
||||||
|
copy of this software and associated documentation files (the "Software"), |
||||||
|
to deal in the Software without restriction, including without limitation |
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense, |
||||||
|
and/or sell copies of the Software, and to permit persons to whom the |
||||||
|
Software is furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included |
||||||
|
in all copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
||||||
|
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
||||||
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
||||||
|
DEALINGS IN THE SOFTWARE. |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <string> |
||||||
|
#include <Corrade/Containers/Array.h> |
||||||
|
#include <Corrade/TestSuite/Tester.h> |
||||||
|
#include <Corrade/Utility/Directory.h> |
||||||
|
|
||||||
|
#include "Magnum/ShaderTools/Implementation/spirv.h" |
||||||
|
#include "MagnumExternal/Vulkan/spirv.h" |
||||||
|
|
||||||
|
#include "configure.h" |
||||||
|
|
||||||
|
namespace Magnum { namespace ShaderTools { namespace Test { namespace { |
||||||
|
|
||||||
|
struct SpirvTest: TestSuite::Tester { |
||||||
|
explicit SpirvTest(); |
||||||
|
|
||||||
|
void data(); |
||||||
|
void dataInvalid(); |
||||||
|
|
||||||
|
void findInstruction(); |
||||||
|
void findInstructionNotEnoughData(); |
||||||
|
|
||||||
|
void nextEntrypoint(); |
||||||
|
void nextEntrypointInvalidInstruction(); |
||||||
|
|
||||||
|
void entrypointInterface(); |
||||||
|
void entrypointInterfaceNothing(); |
||||||
|
}; |
||||||
|
|
||||||
|
const UnsignedInt Data[] { |
||||||
|
SpvMagicNumber, SpvVersion, 0, 66, 0, |
||||||
|
0 /* first instruction */ |
||||||
|
}; |
||||||
|
|
||||||
|
const UnsignedInt JustHeader[]{ |
||||||
|
SpvMagicNumber, SpvVersion, 0, 66, 0 |
||||||
|
}; |
||||||
|
|
||||||
|
const UnsignedInt InvalidMagic[]{ |
||||||
|
SpvMagicNumber + 1, SpvVersion, 0, 66, 0, |
||||||
|
0 /* first instruction */ |
||||||
|
}; |
||||||
|
|
||||||
|
const struct { |
||||||
|
const char* name; |
||||||
|
Containers::ArrayView<const void> data; |
||||||
|
} InvalidData[] { |
||||||
|
{"empty", {}}, |
||||||
|
{"just the header", JustHeader}, |
||||||
|
{"invalid magic", InvalidMagic}, |
||||||
|
{"size not divisible by four", Containers::arrayCast<const char>(Data).except(1)} |
||||||
|
}; |
||||||
|
|
||||||
|
SpirvTest::SpirvTest() { |
||||||
|
addTests({&SpirvTest::data}); |
||||||
|
|
||||||
|
addInstancedTests({&SpirvTest::dataInvalid}, |
||||||
|
Containers::arraySize(InvalidData)); |
||||||
|
|
||||||
|
addTests({&SpirvTest::findInstruction, |
||||||
|
&SpirvTest::findInstructionNotEnoughData, |
||||||
|
|
||||||
|
&SpirvTest::nextEntrypoint, |
||||||
|
&SpirvTest::nextEntrypointInvalidInstruction, |
||||||
|
|
||||||
|
&SpirvTest::entrypointInterface, |
||||||
|
&SpirvTest::entrypointInterfaceNothing}); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::data() { |
||||||
|
CORRADE_COMPARE(Implementation::spirvData(Data, sizeof(Data)), Containers::arrayView(Data + 5, 1)); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::dataInvalid() { |
||||||
|
auto&& data = InvalidData[testCaseInstanceId()]; |
||||||
|
setTestCaseDescription(data.name); |
||||||
|
CORRADE_VERIFY(!Implementation::spirvData(data.data, data.data.size())); |
||||||
|
} |
||||||
|
|
||||||
|
UnsignedInt op(UnsignedInt length, SpvOp op) { |
||||||
|
return length << 16 | op; |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::findInstruction() { |
||||||
|
const UnsignedInt data[] { |
||||||
|
op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, |
||||||
|
op(4, SpvOpDecorate), 12, SpvDecorationLocation, 0, |
||||||
|
op(1, SpvOpNop), |
||||||
|
op(1, SpvOpNop), |
||||||
|
op(4, SpvOpDecorate), 13, SpvDecorationLocation, 1, |
||||||
|
}; |
||||||
|
Containers::ArrayView<const UnsignedInt> view = data; |
||||||
|
|
||||||
|
Containers::ArrayView<const UnsignedInt> decorate1 = Implementation::spirvFindInstruction(view, SpvOpDecorate); |
||||||
|
CORRADE_COMPARE(decorate1.size(), 4); |
||||||
|
CORRADE_COMPARE(decorate1.data(), data + 3); |
||||||
|
CORRADE_COMPARE(view.data(), data + 7); |
||||||
|
|
||||||
|
/* Verify a single-word instruction works too */ |
||||||
|
Containers::ArrayView<const UnsignedInt> nop = Implementation::spirvFindInstruction(view, SpvOpNop); |
||||||
|
CORRADE_COMPARE(nop.size(), 1); |
||||||
|
CORRADE_COMPARE(nop.data(), data + 7); |
||||||
|
CORRADE_COMPARE(view.data(), data + 8); |
||||||
|
|
||||||
|
Containers::ArrayView<const UnsignedInt> decorate2 = Implementation::spirvFindInstruction(view, SpvOpDecorate); |
||||||
|
CORRADE_COMPARE(decorate2.size(), 4); |
||||||
|
CORRADE_COMPARE(decorate2.data(), data + 9); |
||||||
|
CORRADE_COMPARE(view.data(), data + 13); |
||||||
|
|
||||||
|
/* We're at the end, there's no more OpDecorate instructions to find */ |
||||||
|
CORRADE_VERIFY(!Implementation::spirvFindInstruction(view, SpvOpDecorate)); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::findInstructionNotEnoughData() { |
||||||
|
const UnsignedInt data[] { |
||||||
|
op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, |
||||||
|
/* Should be 4 */ |
||||||
|
op(5, SpvOpDecorate), 12, SpvDecorationLocation, 0 |
||||||
|
}; |
||||||
|
Containers::ArrayView<const UnsignedInt> view = data; |
||||||
|
|
||||||
|
CORRADE_VERIFY(!Implementation::spirvFindInstruction(view, SpvOpDecorate)); |
||||||
|
/* View gets set to the first invalid instruction */ |
||||||
|
CORRADE_COMPARE(view.data(), data + 3); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::nextEntrypoint() { |
||||||
|
Containers::Array<char> data = Utility::Directory::read(Utility::Directory::join(SHADERTOOLS_TEST_DIR, "SpirvTestFiles/entrypoint-interface.spv")); |
||||||
|
|
||||||
|
/* The file is a full SPIR-V, strip the header first */ |
||||||
|
Containers::ArrayView<const UnsignedInt> view = Implementation::spirvData(data, data.size()); |
||||||
|
CORRADE_VERIFY(view); |
||||||
|
|
||||||
|
Containers::Optional<Implementation::SpirvEntrypoint> vert = Implementation::spirvNextEntrypoint(view); |
||||||
|
CORRADE_VERIFY(vert); |
||||||
|
/* Verify that long names get recognized properly */ |
||||||
|
CORRADE_COMPARE(vert->name, "vertexLongEntrypointName"); |
||||||
|
CORRADE_COMPARE(vert->executionModel, SpvExecutionModelVertex); |
||||||
|
/* We don't care about the contents, those would change with each assembly
|
||||||
|
anyway. Verified fully in entrypointInterface(). */ |
||||||
|
CORRADE_COMPARE(vert->interfaces.size(), 4); |
||||||
|
|
||||||
|
Containers::Optional<Implementation::SpirvEntrypoint> frag = Implementation::spirvNextEntrypoint(view); |
||||||
|
CORRADE_VERIFY(frag); |
||||||
|
CORRADE_COMPARE(frag->name, "fra"); |
||||||
|
CORRADE_COMPARE(frag->executionModel, SpvExecutionModelFragment); |
||||||
|
CORRADE_COMPARE(frag->interfaces.size(), 3); |
||||||
|
|
||||||
|
/* Only two entrypoints in this file */ |
||||||
|
CORRADE_VERIFY(!Implementation::spirvNextEntrypoint(view)); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::nextEntrypointInvalidInstruction() { |
||||||
|
const UnsignedInt data[] { |
||||||
|
op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, |
||||||
|
|
||||||
|
/* Should be 4 (missing name) */ |
||||||
|
op(3, SpvOpEntryPoint), SpvExecutionModelVertex, 1 |
||||||
|
}; |
||||||
|
Containers::ArrayView<const UnsignedInt> view = data; |
||||||
|
|
||||||
|
CORRADE_VERIFY(!Implementation::spirvNextEntrypoint(view)); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::entrypointInterface() { |
||||||
|
Containers::Array<char> data = Utility::Directory::read(Utility::Directory::join(SHADERTOOLS_TEST_DIR, "SpirvTestFiles/entrypoint-interface.spv")); |
||||||
|
|
||||||
|
/* The file is a full SPIR-V, strip the header first */ |
||||||
|
Containers::ArrayView<const UnsignedInt> view = Implementation::spirvData(data, data.size()); |
||||||
|
CORRADE_VERIFY(view); |
||||||
|
|
||||||
|
Containers::Optional<Implementation::SpirvEntrypoint> vert = Implementation::spirvNextEntrypoint(view); |
||||||
|
CORRADE_VERIFY(vert); |
||||||
|
CORRADE_COMPARE(vert->interfaces.size(), 4); |
||||||
|
|
||||||
|
Implementation::SpirvEntrypointInterface vertInterface[4]{}; |
||||||
|
Implementation::spirvEntrypointInterface(view, *vert, vertInterface); |
||||||
|
CORRADE_VERIFY(vertInterface[0].location); /* position */ |
||||||
|
CORRADE_VERIFY(vertInterface[0].storageClass); |
||||||
|
CORRADE_COMPARE(*vertInterface[0].location, 0); |
||||||
|
CORRADE_COMPARE(*vertInterface[0].storageClass, SpvStorageClassInput); |
||||||
|
|
||||||
|
CORRADE_VERIFY(vertInterface[1].location); /* color */ |
||||||
|
CORRADE_VERIFY(vertInterface[1].storageClass); |
||||||
|
CORRADE_COMPARE(*vertInterface[1].location, 1); |
||||||
|
CORRADE_COMPARE(*vertInterface[1].storageClass, SpvStorageClassInput); |
||||||
|
|
||||||
|
/* Verify that absence of location is handled properly */ |
||||||
|
CORRADE_VERIFY(!vertInterface[2].location); /* gl_Position */ |
||||||
|
CORRADE_VERIFY(vertInterface[2].storageClass); |
||||||
|
CORRADE_COMPARE(*vertInterface[2].storageClass, SpvStorageClassOutput); |
||||||
|
|
||||||
|
CORRADE_VERIFY(vertInterface[3].location); /* interpolatedColorOut */ |
||||||
|
CORRADE_VERIFY(vertInterface[2].storageClass); |
||||||
|
CORRADE_COMPARE(*vertInterface[3].location, 0); |
||||||
|
CORRADE_COMPARE(*vertInterface[2].storageClass, SpvStorageClassOutput); |
||||||
|
|
||||||
|
Containers::Optional<Implementation::SpirvEntrypoint> frag = Implementation::spirvNextEntrypoint(view); |
||||||
|
CORRADE_VERIFY(frag); |
||||||
|
CORRADE_COMPARE(frag->interfaces.size(), 3); |
||||||
|
|
||||||
|
Implementation::SpirvEntrypointInterface fragInterface[3]{}; |
||||||
|
Implementation::spirvEntrypointInterface(view, *frag, fragInterface); |
||||||
|
CORRADE_VERIFY(fragInterface[0].location); /* interpolatedColorIn */ |
||||||
|
CORRADE_VERIFY(fragInterface[0].storageClass); |
||||||
|
CORRADE_COMPARE(*fragInterface[0].location, 0); |
||||||
|
CORRADE_COMPARE(*fragInterface[0].storageClass, SpvStorageClassInput); |
||||||
|
|
||||||
|
CORRADE_VERIFY(fragInterface[1].location); /* fragmentColor */ |
||||||
|
CORRADE_VERIFY(fragInterface[1].storageClass); |
||||||
|
CORRADE_COMPARE(*fragInterface[1].location, 0); |
||||||
|
CORRADE_COMPARE(*fragInterface[1].storageClass, SpvStorageClassOutput); |
||||||
|
|
||||||
|
/* Verify that absence of storageClass is handled properly */ |
||||||
|
CORRADE_VERIFY(fragInterface[2].location); /* unknownFragmentInterface */ |
||||||
|
CORRADE_VERIFY(!fragInterface[2].storageClass); |
||||||
|
CORRADE_COMPARE(*fragInterface[2].location, 1); |
||||||
|
} |
||||||
|
|
||||||
|
void SpirvTest::entrypointInterfaceNothing() { |
||||||
|
const UnsignedInt data[] { |
||||||
|
op(3, SpvOpMemoryModel), SpvAddressingModelLogical, SpvMemoryModelGLSL450, |
||||||
|
|
||||||
|
op(4, SpvOpEntryPoint), SpvExecutionModelGLCompute, 1, '\0' |
||||||
|
}; |
||||||
|
Containers::ArrayView<const UnsignedInt> view = data; |
||||||
|
|
||||||
|
Containers::Optional<Implementation::SpirvEntrypoint> comp = Implementation::spirvNextEntrypoint(view); |
||||||
|
CORRADE_VERIFY(comp); |
||||||
|
CORRADE_VERIFY(comp->interfaces.empty()); |
||||||
|
|
||||||
|
Implementation::spirvEntrypointInterface(view, *comp, {}); |
||||||
|
|
||||||
|
/* Well, it shouldn't crash */ |
||||||
|
CORRADE_VERIFY(true); |
||||||
|
} |
||||||
|
|
||||||
|
}}}} |
||||||
|
|
||||||
|
CORRADE_TEST_MAIN(Magnum::ShaderTools::Test::SpirvTest) |
||||||
@ -0,0 +1,5 @@ |
|||||||
|
#!/bin/bash |
||||||
|
|
||||||
|
for i in $(ls *.spvasm); do |
||||||
|
magnum-shaderconverter $i ${i%asm} |
||||||
|
done |
||||||
Binary file not shown.
@ -0,0 +1,23 @@ |
|||||||
|
OpCapability Shader |
||||||
|
OpEntryPoint Vertex %ver "vertexLongEntrypointName" %position %color %gl_Position %interpolatedColorOut |
||||||
|
OpEntryPoint Fragment %fra "fra" %interpolatedColorIn %fragmentColor %unknownFragmentInterface |
||||||
|
OpExecutionMode %fra OriginUpperLeft |
||||||
|
OpDecorate %gl_Position BuiltIn Position |
||||||
|
OpDecorate %position Location 0 |
||||||
|
OpDecorate %color Location 1 |
||||||
|
OpDecorate %fragmentColor Location 0 |
||||||
|
OpDecorate %unknownFragmentInterface Location 1 |
||||||
|
OpDecorate %interpolatedColorIn Location 0 |
||||||
|
OpDecorate %interpolatedColorOut Location 0 |
||||||
|
%void = OpTypeVoid |
||||||
|
%10 = OpTypeFunction %void |
||||||
|
%float = OpTypeFloat 32 |
||||||
|
%v4float = OpTypeVector %float 4 |
||||||
|
%_ptr_Input_v4float = OpTypePointer Input %v4float |
||||||
|
%position = OpVariable %_ptr_Input_v4float Input |
||||||
|
%color = OpVariable %_ptr_Input_v4float Input |
||||||
|
%interpolatedColorIn = OpVariable %_ptr_Input_v4float Input |
||||||
|
%_ptr_Output_v4float = OpTypePointer Output %v4float |
||||||
|
%gl_Position = OpVariable %_ptr_Output_v4float Output |
||||||
|
%interpolatedColorOut = OpVariable %_ptr_Output_v4float Output |
||||||
|
%fragmentColor = OpVariable %_ptr_Output_v4float Output |
||||||
Loading…
Reference in new issue