From d9a13e6d080c9ce6fdc53d32a0fd42b6dbc512b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Wed, 10 Feb 2021 01:25:00 +0100 Subject: [PATCH] Vk: patch SPIR-V binaries on load on SwiftShader as a workaround. The things I get to do here. Wow. --- src/Magnum/Vk/Implementation/DeviceState.cpp | 8 ++ src/Magnum/Vk/Implementation/DeviceState.h | 2 + .../Vk/Implementation/DriverWorkaround.cpp | 16 +++ src/Magnum/Vk/Implementation/spirvPatching.h | 116 ++++++++++++++++++ src/Magnum/Vk/Shader.cpp | 39 +++++- src/Magnum/Vk/Shader.h | 7 ++ src/Magnum/Vk/Test/CMakeLists.txt | 3 + src/Magnum/Vk/Test/DeviceVkTest.cpp | 5 +- src/Magnum/Vk/Test/ShaderTest.cpp | 115 ++++++++++++++++- src/Magnum/Vk/Test/ShaderTestFiles/convert.sh | 5 + .../Vk/Test/ShaderTestFiles/vert-frag.spv | Bin 0 -> 676 bytes .../Vk/Test/ShaderTestFiles/vert-frag.spvasm | 53 ++++++++ 12 files changed, 364 insertions(+), 5 deletions(-) create mode 100644 src/Magnum/Vk/Implementation/spirvPatching.h create mode 100755 src/Magnum/Vk/Test/ShaderTestFiles/convert.sh create mode 100644 src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv create mode 100644 src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm diff --git a/src/Magnum/Vk/Implementation/DeviceState.cpp b/src/Magnum/Vk/Implementation/DeviceState.cpp index 93c01d05f..5b848d190 100644 --- a/src/Magnum/Vk/Implementation/DeviceState.cpp +++ b/src/Magnum/Vk/Implementation/DeviceState.cpp @@ -32,6 +32,7 @@ #include "Magnum/Vk/Extensions.h" #include "Magnum/Vk/Image.h" #include "Magnum/Vk/RenderPass.h" +#include "Magnum/Vk/Shader.h" #include "Magnum/Vk/Version.h" #include "Magnum/Vk/Implementation/DriverWorkaround.h" @@ -113,6 +114,13 @@ DeviceState::DeviceState(Device& device, Containers::Array + + 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 + +#include "Magnum/Math/Functions.h" + +/* All code in this file should be self-contained, with no link-time dependency + on ShaderTools */ +#include "Magnum/ShaderTools/Implementation/spirv.h" + +namespace Magnum { namespace Vk { namespace Implementation { namespace { + +/* This kinda throws const-correctness through the window -- the the SPIR-V + utils work on const data, but this function actually does mutate them */ +bool spirvPatchSwiftShaderConflictingMultiEntrypointLocations(Containers::ArrayView data) { + /* Find vertex/fragment entrypoints and count how many is there in total */ + UnsignedInt entrypointCount = 0; + Containers::Optional vertexEntryPoint, fragmentEntryPoint; + while(Containers::Optional entrypoint = ShaderTools::Implementation::spirvNextEntrypoint(data)) { + ++entrypointCount; + if(entrypoint->executionModel == SpvExecutionModelVertex) + vertexEntryPoint = *entrypoint; + else if(entrypoint->executionModel == SpvExecutionModelFragment) + fragmentEntryPoint = *entrypoint; + } + + /* If there aren't both entrypoints, this bug doesn't affect the shader. If + there are more, we won't attempt anything -- right now SwiftShader + doesn't support geom/tess shaders, so the only possibility is that the + module is a library of multiple different vertex / fragment + implementations and that's too frightening as any patching would most + likely break things *really bad*. */ + if(entrypointCount > 2 || !vertexEntryPoint || !fragmentEntryPoint) + return false; + + /* Get locations and storage classes for all entrypoint interfaces */ + Containers::ArrayView vertexInterface, fragmentInterface; + Containers::ArrayTuple interfaceData{ + {Containers::ValueInit, vertexEntryPoint->interfaces.size(), vertexInterface}, + {Containers::ValueInit, fragmentEntryPoint->interfaces.size(), fragmentInterface} + }; + spirvEntrypointInterface(data, *vertexEntryPoint, vertexInterface); + spirvEntrypointInterface(data, *fragmentEntryPoint, fragmentInterface); + + /* Calculate the max location so we know what to change to */ + UnsignedInt maxLocation = 0; + for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: vertexInterface) + if(i.location) maxLocation = Math::max(maxLocation, *i.location); + for(const ShaderTools::Implementation::SpirvEntrypointInterface& i: fragmentInterface) + if(i.location) maxLocation = Math::max(maxLocation, *i.location); + + /* For all vertex outputs check if there are fragment outputs with the same + locations */ + for(const ShaderTools::Implementation::SpirvEntrypointInterface& vertexOutput: vertexInterface) { + /* Ignore what's not an output or what doesn't have a location (for + example a builtin) */ + if(!vertexOutput.storageClass || *vertexOutput.storageClass != SpvStorageClassOutput || !vertexOutput.location) + continue; + + for(const ShaderTools::Implementation::SpirvEntrypointInterface& fragmentOutput: fragmentInterface) { + /* Ignore what's not an output or what doesn't have a location (for + example a builtin) */ + if(!fragmentOutput.storageClass || *fragmentOutput.storageClass != SpvStorageClassOutput || !fragmentOutput.location) continue; + + /* The same location used, we need to remap. Use the next highest + unused location and change also the corresponding fragment + input, if there's any. */ + if(*vertexOutput.location == *fragmentOutput.location) { + const UnsignedInt newLocation = ++maxLocation; + for(const ShaderTools::Implementation::SpirvEntrypointInterface& fragmentInput: fragmentInterface) { + if(*fragmentInput.storageClass != SpvStorageClassInput) + continue; + if(*fragmentInput.location == *vertexOutput.location) { + /** @todo ugly! */ + *const_cast(fragmentInput.location) = newLocation; + break; + } + } + /** @todo ugly! */ + *const_cast(vertexOutput.location) = newLocation; + break; + } + } + } + + return true; +} + +}}}} + +#endif diff --git a/src/Magnum/Vk/Shader.cpp b/src/Magnum/Vk/Shader.cpp index 55126c988..f5b4557ac 100644 --- a/src/Magnum/Vk/Shader.cpp +++ b/src/Magnum/Vk/Shader.cpp @@ -26,11 +26,14 @@ #include "Shader.h" #include "ShaderCreateInfo.h" -#include +#include +#include #include "Magnum/Vk/Assert.h" #include "Magnum/Vk/Device.h" #include "Magnum/Vk/Handle.h" +#include "Magnum/Vk/Implementation/DeviceState.h" +#include "Magnum/Vk/Implementation/spirvPatching.h" namespace Magnum { namespace Vk { @@ -87,7 +90,7 @@ Shader Shader::wrap(Device& device, const VkShaderModule handle, const HandleFla } Shader::Shader(Device& device, const ShaderCreateInfo& info): _device{&device}, _flags{HandleFlag::DestroyOnDestruction} { - MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device->CreateShaderModule(device, info, nullptr, &_handle)); + MAGNUM_VK_INTERNAL_ASSERT_SUCCESS(device.state().createShaderImplementation(device, *info, nullptr, _handle)); } Shader::Shader(NoCreateT): _device{}, _handle{} {} @@ -115,4 +118,36 @@ VkShaderModule Shader::release() { return handle; } +VkResult Shader::createImplementationDefault(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle) { + return device->CreateShaderModule(device, &info, callbacks, &handle); +} + +VkResult Shader::createImplementationSwiftShaderMultiEntryPointPatching(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle) { + /* Can't use {} with GCC 4.8 here because it tries to initialize the first + member instead of doing a copy */ + VkShaderModuleCreateInfo patchedInfo(info); + + /** @todo there's too many casts and it's all slightly weird, figure out a + better API (the problem is that spirvData() skips the header and so we + can't use its output to copy anything anywhere, but even if we could + we'd need to call it again on the copied mutable data and that's even + worse than what's there now) */ + + /* Even though our ShaderCreateInfo *might* have the code owned and we thus + might not need to copy it, the owned code may also be read-only for + whatever reason (memory-mapped location etc). Thus, to prevent issues, + we go the safe route and copy always. */ + Containers::Array mutableCode{Containers::NoInit, info.codeSize}; + Utility::copy(Containers::arrayView(reinterpret_cast(info.pCode), info.codeSize), mutableCode); + + /* If the code looks like SPIR-V, patch it. If not, supply the original and + let SwiftShader deal with it. */ + if(Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(mutableCode, mutableCode.size())) { + Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(spirv); + patchedInfo.pCode = reinterpret_cast(mutableCode.data()); + } + + return createImplementationDefault(device, patchedInfo, callbacks, handle); +} + }} diff --git a/src/Magnum/Vk/Shader.h b/src/Magnum/Vk/Shader.h index ad0a2a83f..a436eba6a 100644 --- a/src/Magnum/Vk/Shader.h +++ b/src/Magnum/Vk/Shader.h @@ -39,6 +39,8 @@ namespace Magnum { namespace Vk { +namespace Implementation { struct DeviceState; } + /** @brief Shader stage @m_since_latest @@ -203,6 +205,11 @@ class MAGNUM_VK_EXPORT Shader { VkShaderModule release(); private: + friend Implementation::DeviceState; + + MAGNUM_VK_LOCAL static VkResult createImplementationDefault(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle); + MAGNUM_VK_LOCAL static VkResult createImplementationSwiftShaderMultiEntryPointPatching(Device& device, const VkShaderModuleCreateInfo& info, const VkAllocationCallbacks* callbacks, VkShaderModule& handle); + /* Can't be a reference because of the NoCreate constructor */ Device* _device; diff --git a/src/Magnum/Vk/Test/CMakeLists.txt b/src/Magnum/Vk/Test/CMakeLists.txt index 3effaf840..7cce44722 100644 --- a/src/Magnum/Vk/Test/CMakeLists.txt +++ b/src/Magnum/Vk/Test/CMakeLists.txt @@ -50,7 +50,10 @@ corrade_add_test(VkPixelFormatTest PixelFormatTest.cpp LIBRARIES MagnumVkTestLib corrade_add_test(VkQueueTest QueueTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkResultTest ResultTest.cpp LIBRARIES MagnumVk) corrade_add_test(VkRenderPassTest RenderPassTest.cpp LIBRARIES MagnumVkTestLib) + corrade_add_test(VkShaderTest ShaderTest.cpp LIBRARIES MagnumVk) +target_include_directories(VkShaderTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$) + corrade_add_test(VkShaderSetTest ShaderSetTest.cpp LIBRARIES MagnumVkTestLib) corrade_add_test(VkVertexFormatTest VertexFormatTest.cpp LIBRARIES MagnumVkTestLib) diff --git a/src/Magnum/Vk/Test/DeviceVkTest.cpp b/src/Magnum/Vk/Test/DeviceVkTest.cpp index ca179edf9..a1a67fbf7 100644 --- a/src/Magnum/Vk/Test/DeviceVkTest.cpp +++ b/src/Magnum/Vk/Test/DeviceVkTest.cpp @@ -163,7 +163,8 @@ struct { "Device: {}\n" "Device version: Vulkan {}.{}{}\n" "Using device driver workarounds:\n" - " swiftshader-image-copy-extent-instead-of-layers\n"}, + " swiftshader-image-copy-extent-instead-of-layers\n" + " swiftshader-spirv-multi-entrypoint-conflicting-locations\n"}, /* Shouldn't print anything if quiet output is enabled */ {"quiet", true, Containers::array({"", @@ -171,7 +172,7 @@ struct { ""}, {"disabled workarounds", true, Containers::array({"", - "--magnum-disable-workarounds", "swiftshader-image-copy-extent-instead-of-layers"}), + "--magnum-disable-workarounds", "swiftshader-image-copy-extent-instead-of-layers swiftshader-spirv-multi-entrypoint-conflicting-locations"}), "Device: {}\n" "Device version: Vulkan {}.{}{}\n"} }; diff --git a/src/Magnum/Vk/Test/ShaderTest.cpp b/src/Magnum/Vk/Test/ShaderTest.cpp index a209d08e0..6b236649e 100644 --- a/src/Magnum/Vk/Test/ShaderTest.cpp +++ b/src/Magnum/Vk/Test/ShaderTest.cpp @@ -23,16 +23,27 @@ DEALINGS IN THE SOFTWARE. */ +#include #include #include +#include +#include "Magnum/ShaderTools/Implementation/spirv.h" #include "Magnum/Vk/ShaderCreateInfo.h" +#include "Magnum/Vk/Implementation/spirvPatching.h" + +#include "configure.h" namespace Magnum { namespace Vk { namespace Test { namespace { struct ShaderTest: TestSuite::Tester { explicit ShaderTest(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocations(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint(); + void spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces(); + void createInfoConstruct(); void createInfoConstructTransferOwnership(); void createInfoConstructNoInit(); @@ -45,7 +56,12 @@ struct ShaderTest: TestSuite::Tester { }; ShaderTest::ShaderTest() { - addTests({&ShaderTest::createInfoConstruct, + addTests({&ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocations, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint, + &ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces, + + &ShaderTest::createInfoConstruct, &ShaderTest::createInfoConstructTransferOwnership, &ShaderTest::createInfoConstructNoInit, &ShaderTest::createInfoConstructFromVk, @@ -56,6 +72,103 @@ ShaderTest::ShaderTest() { &ShaderTest::constructCopy}); } +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocations() { + Containers::Array data = Utility::Directory::read(Utility::Directory::join(VK_TEST_DIR, "ShaderTestFiles/vert-frag.spv")); + + /* The file is a full SPIR-V, strip the header first */ + const Containers::ArrayView spirv = ShaderTools::Implementation::spirvData(data, data.size()); + CORRADE_VERIFY(spirv); + + Containers::ArrayView view = spirv; + Containers::Optional vert = ShaderTools::Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(vert); + CORRADE_COMPARE(vert->name, "ver"); + CORRADE_COMPARE(vert->interfaces.size(), 7); + + Containers::Optional frag = ShaderTools::Implementation::spirvNextEntrypoint(view); + CORRADE_VERIFY(frag); + CORRADE_COMPARE(frag->name, "fra"); + CORRADE_COMPARE(frag->interfaces.size(), 5); + + ShaderTools::Implementation::SpirvEntrypointInterface vertInterfaces[7]{}; + ShaderTools::Implementation::spirvEntrypointInterface(view, *vert, vertInterfaces); + CORRADE_VERIFY(vertInterfaces[0].location); /* position */ + CORRADE_COMPARE(*vertInterfaces[0].location, 0); + CORRADE_VERIFY(vertInterfaces[1].location); /* color */ + CORRADE_COMPARE(*vertInterfaces[1].location, 1); + CORRADE_VERIFY(vertInterfaces[3].location); /* interpolatedColorOut */ + CORRADE_COMPARE(*vertInterfaces[3].location, 0); + CORRADE_VERIFY(vertInterfaces[4].location); /* interpolatedTexCoordsOut */ + CORRADE_COMPARE(*vertInterfaces[4].location, 1); + CORRADE_VERIFY(vertInterfaces[5].location); /* interpolatedNormalOut */ + CORRADE_COMPARE(*vertInterfaces[5].location, 2); + CORRADE_VERIFY(vertInterfaces[6].location); /* unused */ + CORRADE_COMPARE(*vertInterfaces[6].location, 3); + + ShaderTools::Implementation::SpirvEntrypointInterface fragInterfaces[5]{}; + ShaderTools::Implementation::spirvEntrypointInterface(view, *frag, fragInterfaces); + CORRADE_VERIFY(fragInterfaces[0].location); /* interpolatedColorIn */ + CORRADE_COMPARE(*fragInterfaces[0].location, 0); + CORRADE_VERIFY(fragInterfaces[1].location); /* interpolatedTexCoordsIn */ + CORRADE_COMPARE(*fragInterfaces[1].location, 1); + CORRADE_VERIFY(fragInterfaces[2].location); /* interpolatedNormalIn */ + CORRADE_COMPARE(*fragInterfaces[2].location, 2); + CORRADE_VERIFY(fragInterfaces[3].location); /* fragmentColor */ + CORRADE_COMPARE(*fragInterfaces[3].location, 0); + CORRADE_VERIFY(fragInterfaces[4].location); /* weight */ + CORRADE_COMPARE(*fragInterfaces[4].location, 1); + + CORRADE_VERIFY(Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(spirv)); + CORRADE_COMPARE(*vertInterfaces[0].location, 0); + CORRADE_COMPARE(*vertInterfaces[1].location, 1); + CORRADE_COMPARE(*vertInterfaces[3].location, 4); /* changed */ + CORRADE_COMPARE(*vertInterfaces[4].location, 5); /* changed */ + CORRADE_COMPARE(*vertInterfaces[5].location, 2); /* kept, no conflict */ + CORRADE_COMPARE(*vertInterfaces[6].location, 3); /* kept, no conflict */ + + CORRADE_COMPARE(*fragInterfaces[0].location, 4); /* changed */ + CORRADE_COMPARE(*fragInterfaces[1].location, 5); /* changed */ + CORRADE_COMPARE(*fragInterfaces[2].location, 2); /* kept, no conflict */ + CORRADE_COMPARE(*fragInterfaces[3].location, 0); + CORRADE_COMPARE(*fragInterfaces[4].location, 1); +} + +UnsignedInt op(UnsignedInt length, SpvOp op) { + return length << 16 | op; +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsTooManyEntrypoints() { + UnsignedInt data[] { + op(6, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', 4, 5, + op(4, SpvOpEntryPoint), SpvExecutionModelFragment, 2, '\0', + op(6, SpvOpEntryPoint), SpvExecutionModelFragment, 3, '\0', 7, 8, + }; + + /* There's three entrypoints, skip to avoid breaking something we don't + understand */ + CORRADE_VERIFY(!Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsOnlyOneEntrypoint() { + UnsignedInt data[] { + op(6, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', 4, 5, + }; + + /* There's just one entrypoint, the bug doesn't affect this case */ + CORRADE_VERIFY(!Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + +void ShaderTest::spirvPatchSwiftShaderConflictingMultiEntrypointLocationsNoInterfaces() { + UnsignedInt data[] { + op(4, SpvOpEntryPoint), SpvExecutionModelVertex, 1, '\0', + op(4, SpvOpEntryPoint), SpvExecutionModelFragment, 2, '\0' + }; + + /* There's no interfaces and thus nothing to do, but the function should + succeed and not crash */ + CORRADE_VERIFY(Implementation::spirvPatchSwiftShaderConflictingMultiEntrypointLocations(data)); +} + void ShaderTest::createInfoConstruct() { const UnsignedInt data[] { 0xdead, 0xbee5, 0xbaba }; diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh b/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh new file mode 100755 index 000000000..0e65958ac --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderTestFiles/convert.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +for i in $(ls *.spvasm); do + magnum-shaderconverter $i ${i%asm} +done diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spv new file mode 100644 index 0000000000000000000000000000000000000000..4614c82cffefa0d48181e2382e734ca6526aa543 GIT binary patch literal 676 zcmYk(TT%i+429tcD&mC!FQ7hHRJjZnz&d=81@PedJ>M{G^;Bijf1f0sbkAp>i>iyN zTGV^~zD>=>omI_8rVe%YzyAF>C89?4GX5%d9ZR9Qi4Cua0;5WkP@61$Ga za`9$jvG{RfGw;Js@iaMK6Pw2#{W;#;>e8`(?a^Q2#rC@STK+a`vHFmY&C~LCS&Qv; q^SAuhDOTU|vDxG|7TdG?ZuQ@?rnh|TeeB7{?xJV!+t~fMZ~O;f9}so` literal 0 HcmV?d00001 diff --git a/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm new file mode 100644 index 000000000..22baddfa1 --- /dev/null +++ b/src/Magnum/Vk/Test/ShaderTestFiles/vert-frag.spvasm @@ -0,0 +1,53 @@ + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Vertex %ver "ver" %position %color %gl_Position %interpolatedColorOut %interpolatedTexCoordsOut %interpolatedNormalOut %unused + OpEntryPoint Fragment %fra "fra" %interpolatedColorIn %interpolatedTexCoordsIn %interpolatedNormalIn %fragmentColor %weight + + OpDecorate %gl_Position BuiltIn Position + OpDecorate %position Location 0 + OpDecorate %color Location 1 + OpDecorate %fragmentColor Location 0 + OpDecorate %weight Location 1 + + OpDecorate %interpolatedColorOut Location 0 + OpDecorate %interpolatedColorIn Location 0 + + OpDecorate %interpolatedTexCoordsOut Location 1 + OpDecorate %interpolatedTexCoordsIn Location 1 + + OpDecorate %interpolatedNormalOut Location 2 + OpDecorate %interpolatedNormalIn Location 2 + + OpDecorate %unused Location 3 + + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 + %v3float = OpTypeVector %float 3 + %v4float = OpTypeVector %float 4 + +%_ptr_Output_float = OpTypePointer Output %float + %weight = OpVariable %_ptr_Output_float Output + +%_ptr_Input_v2float = OpTypePointer Input %v2float +%interpolatedTexCoordsIn = OpVariable %_ptr_Input_v2float Input + +%_ptr_Output_v2float = OpTypePointer Output %v2float +%interpolatedTexCoordsOut = OpVariable %_ptr_Input_v2float Output + +%_ptr_Input_v3float = OpTypePointer Input %v3float +%interpolatedNormalIn = OpVariable %_ptr_Input_v3float Input + +%_ptr_Output_v3float = OpTypePointer Output %v3float +%interpolatedNormalOut = OpVariable %_ptr_Input_v3float Output + +%_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 +%interpolatedColorOut = OpVariable %_ptr_Output_v4float Output +%gl_Position = OpVariable %_ptr_Output_v4float Output +%fragmentColor = OpVariable %_ptr_Output_v4float Output + + %unused = OpVariable %_ptr_Output_float Output