Browse Source

ShaderTools: initial internal tools for SPIR-V reflection.

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
Vladimír Vondruš 5 years ago
parent
commit
cbdc5c9e21
  1. 10
      src/Magnum/ShaderTools/CMakeLists.txt
  2. 183
      src/Magnum/ShaderTools/Implementation/spirv.h
  3. 5
      src/Magnum/ShaderTools/Test/CMakeLists.txt
  4. 264
      src/Magnum/ShaderTools/Test/SpirvTest.cpp
  5. 5
      src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh
  6. BIN
      src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv
  7. 23
      src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm

10
src/Magnum/ShaderTools/CMakeLists.txt

@ -40,6 +40,9 @@ set(MagnumShaderTools_HEADERS
visibility.h) visibility.h)
set(MagnumShaderTools_PRIVATE_HEADERS
Implementation/spirv.h)
if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) if(NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/configure.h) ${CMAKE_CURRENT_BINARY_DIR}/configure.h)
@ -48,7 +51,8 @@ endif()
# # Objects shared between main and test library # # Objects shared between main and test library
# add_library(MagnumShaderToolsObjects OBJECT # add_library(MagnumShaderToolsObjects OBJECT
# ${MagnumShaderTools_SRCS} # ${MagnumShaderTools_SRCS}
# ${MagnumShaderTools_HEADERS}) # # ${MagnumShaderTools_HEADERS}
# # ${MagnumShaderTools_PRIVATE_HEADERS})
# target_include_directories(MagnumShaderToolsObjects PUBLIC $<TARGET_PROPERTY:Magnum,INTERFACE_INCLUDE_DIRECTORIES>) # target_include_directories(MagnumShaderToolsObjects PUBLIC $<TARGET_PROPERTY:Magnum,INTERFACE_INCLUDE_DIRECTORIES>)
# if(NOT BUILD_STATIC) # if(NOT BUILD_STATIC)
# target_compile_definitions(MagnumShaderToolsObjects PRIVATE "MagnumShaderToolsObjects_EXPORTS") # target_compile_definitions(MagnumShaderToolsObjects PRIVATE "MagnumShaderToolsObjects_EXPORTS")
@ -61,7 +65,9 @@ endif()
# Main ShaderTools library # Main ShaderTools library
add_library(MagnumShaderTools ${SHARED_OR_STATIC} add_library(MagnumShaderTools ${SHARED_OR_STATIC}
# $<TARGET_OBJECTS:MagnumShaderToolsObjects> # $<TARGET_OBJECTS:MagnumShaderToolsObjects>
${MagnumShaderTools_GracefulAssert_SRCS}) ${MagnumShaderTools_GracefulAssert_SRCS}
${MagnumShaderTools_HEADERS}
${MagnumShaderTools_PRIVATE_HEADERS})
set_target_properties(MagnumShaderTools PROPERTIES set_target_properties(MagnumShaderTools PROPERTIES
DEBUG_POSTFIX "-d" DEBUG_POSTFIX "-d"
FOLDER "Magnum/ShaderTools") FOLDER "Magnum/ShaderTools")

183
src/Magnum/ShaderTools/Implementation/spirv.h

@ -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

5
src/Magnum/ShaderTools/Test/CMakeLists.txt

@ -38,9 +38,14 @@ corrade_add_test(ShaderToolsAbstractConverterTest AbstractConverterTest.cpp
LIBRARIES MagnumShaderToolsTestLib LIBRARIES MagnumShaderToolsTestLib
FILES file.dat another.dat) FILES file.dat another.dat)
target_include_directories(ShaderToolsAbstractConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(ShaderToolsAbstractConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
corrade_add_test(ShaderToolsSpirvTest SpirvTest.cpp
LIBRARIES MagnumShaderTools
FILES SpirvTestFiles/entrypoint-interface.spv)
target_include_directories(ShaderToolsSpirvTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
corrade_add_test(ShaderToolsStageTest StageTest.cpp LIBRARIES MagnumShaderTools) corrade_add_test(ShaderToolsStageTest StageTest.cpp LIBRARIES MagnumShaderTools)
set_target_properties( set_target_properties(
ShaderToolsAbstractConverterTest ShaderToolsAbstractConverterTest
ShaderToolsSpirvTest
ShaderToolsStageTest ShaderToolsStageTest
PROPERTIES FOLDER "Magnum/ShaderTools/Test") PROPERTIES FOLDER "Magnum/ShaderTools/Test")

264
src/Magnum/ShaderTools/Test/SpirvTest.cpp

@ -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)

5
src/Magnum/ShaderTools/Test/SpirvTestFiles/convert.sh

@ -0,0 +1,5 @@
#!/bin/bash
for i in $(ls *.spvasm); do
magnum-shaderconverter $i ${i%asm}
done

BIN
src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spv

Binary file not shown.

23
src/Magnum/ShaderTools/Test/SpirvTestFiles/entrypoint-interface.spvasm

@ -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…
Cancel
Save