Initial stub implementation of SpirvShader
This will eventually replace Shader and related subclasses. The
interesting bit here is the instruction iterator, which allows fairly
safe access to the instruction stream without needing the rest of the
code to care too much about the physical layout.
Bug: b/120799499
Change-Id: Id0d94c4b807ddb1e4325de147ca1f651171779b7
Reviewed-on: https://swiftshader-review.googlesource.com/c/23049
Reviewed-by: Corentin Wallez <cwallez@google.com>
Reviewed-by: Alexis Hétu <sugoi@google.com>
Tested-by: Chris Forbes <chrisforbes@google.com>
diff --git a/src/Pipeline/SpirvShader.cpp b/src/Pipeline/SpirvShader.cpp
new file mode 100644
index 0000000..79dad22
--- /dev/null
+++ b/src/Pipeline/SpirvShader.cpp
@@ -0,0 +1,75 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <spirv/unified1/spirv.hpp>
+#include "SpirvShader.hpp"
+#include "System/Math.hpp"
+#include "System/Debug.hpp"
+#include "Device/Config.hpp"
+
+namespace sw
+{
+ volatile int SpirvShader::serialCounter = 1; // Start at 1, 0 is invalid shader.
+
+ SpirvShader::SpirvShader(InsnStore const &insns) : insns{insns}, serialID{serialCounter++}, modes{}
+ {
+ // Simplifying assumptions (to be satisfied by earlier transformations)
+ // - There is exactly one extrypoint in the module, and it's the one we want
+ // - Input / Output interface blocks, builtin or otherwise, have been split.
+ // - The only input/output OpVariables present are those used by the entrypoint
+
+ for (auto insn : *this) {
+ switch (insn.opcode()) {
+ case spv::OpExecutionMode:
+ ProcessExecutionMode(insn);
+ break;
+
+ default:
+ break; // This is OK, these passes are intentionally partial
+ }
+ }
+ }
+
+ void SpirvShader::ProcessExecutionMode(InsnIterator insn)
+ {
+ auto mode = static_cast<spv::ExecutionMode>(insn.word(2));
+ switch (mode) {
+ case spv::ExecutionModeEarlyFragmentTests:
+ modes.EarlyFragmentTests = true;
+ break;
+ case spv::ExecutionModeDepthReplacing:
+ modes.DepthReplacing = true;
+ break;
+ case spv::ExecutionModeDepthGreater:
+ modes.DepthGreater = true;
+ break;
+ case spv::ExecutionModeDepthLess:
+ modes.DepthLess = true;
+ break;
+ case spv::ExecutionModeDepthUnchanged:
+ modes.DepthUnchanged = true;
+ break;
+ case spv::ExecutionModeLocalSize:
+ modes.LocalSizeX = insn.word(3);
+ modes.LocalSizeZ = insn.word(5);
+ modes.LocalSizeY = insn.word(4);
+ break;
+ case spv::ExecutionModeOriginUpperLeft:
+ // This is always the case for a Vulkan shader. Do nothing.
+ break;
+ default:
+ UNIMPLEMENTED("No other execution modes are permitted");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Pipeline/SpirvShader.hpp b/src/Pipeline/SpirvShader.hpp
new file mode 100644
index 0000000..b19cc7b
--- /dev/null
+++ b/src/Pipeline/SpirvShader.hpp
@@ -0,0 +1,120 @@
+// Copyright 2018 The SwiftShader Authors. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef sw_SpirvShader_hpp
+#define sw_SpirvShader_hpp
+
+#include "System/Types.hpp"
+#include "Vulkan/VkDebug.hpp"
+
+#include <string>
+#include <vector>
+#include <unordered_map>
+#include <cstdint>
+#include <spirv/unified1/spirv.hpp>
+
+namespace sw
+{
+ class SpirvShader
+ {
+ public:
+ using InsnStore = std::vector<uint32_t>;
+ InsnStore insns;
+
+ /* Pseudo-iterator over SPIRV instructions, designed to support range-based-for. */
+ class InsnIterator
+ {
+ InsnStore::const_iterator iter;
+
+ public:
+ spv::Op opcode() const
+ {
+ return static_cast<spv::Op>(*iter & spv::OpCodeMask);
+ }
+
+ uint32_t wordCount() const
+ { return *iter >> spv::WordCountShift; }
+
+ uint32_t word(uint32_t n) const
+ {
+ ASSERT(n < wordCount());
+ return iter[n];
+ }
+
+ bool operator!=(InsnIterator const &other) const
+ {
+ return iter != other.iter;
+ }
+
+ InsnIterator operator*() const
+ { return *this; }
+
+ InsnIterator &operator++()
+ {
+ iter += wordCount();
+ return *this;
+ }
+
+ InsnIterator const operator++(int)
+ {
+ InsnIterator ret{*this};
+ iter += wordCount();
+ return ret;
+ }
+
+ InsnIterator(InsnIterator const &other) = default;
+
+ InsnIterator() = default;
+
+ explicit InsnIterator(InsnStore::const_iterator iter) : iter{iter}
+ {}
+ };
+
+ /* range-based-for interface */
+ InsnIterator begin() const
+ { return InsnIterator{insns.cbegin() + 5}; }
+
+ InsnIterator end() const
+ { return InsnIterator{insns.cend()}; }
+
+ int getSerialID() const
+ { return serialID; }
+
+ explicit SpirvShader(InsnStore const &insns);
+
+ struct Modes
+ {
+ bool EarlyFragmentTests : 1;
+ bool DepthReplacing : 1;
+ bool DepthGreater : 1;
+ bool DepthLess : 1;
+ bool DepthUnchanged : 1;
+
+ // Compute workgroup dimensions
+ int LocalSizeX, LocalSizeY, LocalSizeZ;
+ };
+
+ Modes const &getModes() const
+ { return modes; }
+
+ private:
+ const int serialID;
+ static volatile int serialCounter;
+ Modes modes;
+
+ void ProcessExecutionMode(InsnIterator it);
+ };
+}
+
+#endif // sw_SpirvShader_hpp
\ No newline at end of file
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index ec472f7..917f5a9 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+#include <Pipeline/SpirvShader.hpp>
#include "VkPipeline.hpp"
#include "VkShaderModule.hpp"
@@ -370,6 +371,8 @@
void GraphicsPipeline::destroyPipeline(const VkAllocationCallbacks* pAllocator)
{
+ delete vertexShader;
+ delete fragmentShader;
}
size_t GraphicsPipeline::ComputeRequiredAllocationSize(const VkGraphicsPipelineCreateInfo* pCreateInfo)
@@ -379,8 +382,30 @@
void GraphicsPipeline::compileShaders(const VkAllocationCallbacks* pAllocator, const VkGraphicsPipelineCreateInfo* pCreateInfo)
{
- vertexRoutine = Cast(pCreateInfo->pStages[0].module)->compile(pAllocator);
- fragmentRoutine = Cast(pCreateInfo->pStages[1].module)->compile(pAllocator);
+ for (auto pStage = pCreateInfo->pStages; pStage != pCreateInfo->pStages + pCreateInfo->stageCount; pStage++) {
+ auto module = Cast(pStage->module);
+
+ // TODO: apply prep passes using SPIRV-Opt here.
+ // - Apply and freeze specializations, etc.
+ auto code = module->getCode();
+
+ // TODO: pass in additional information here:
+ // - any NOS from pCreateInfo which we'll actually need
+ auto spirvShader = new sw::SpirvShader{code};
+
+ switch (pStage->stage) {
+ case VK_SHADER_STAGE_VERTEX_BIT:
+ vertexShader = spirvShader;
+ break;
+
+ case VK_SHADER_STAGE_FRAGMENT_BIT:
+ fragmentShader = spirvShader;
+ break;
+
+ default:
+ UNIMPLEMENTED("Unsupported stage");
+ }
+ }
}
uint32_t GraphicsPipeline::computePrimitiveCount(uint32_t vertexCount) const
diff --git a/src/Vulkan/VkPipeline.hpp b/src/Vulkan/VkPipeline.hpp
index a38a105..0c7fef2 100644
--- a/src/Vulkan/VkPipeline.hpp
+++ b/src/Vulkan/VkPipeline.hpp
@@ -18,6 +18,8 @@
#include "VkObject.hpp"
#include "Device/Renderer.hpp"
+namespace sw { class SpirvShader; }
+
namespace vk
{
@@ -65,8 +67,12 @@
const sw::Color<float>& getBlendConstants() const;
private:
+ sw::SpirvShader *vertexShader = nullptr;
+ sw::SpirvShader *fragmentShader = nullptr;
+
rr::Routine* vertexRoutine;
rr::Routine* fragmentRoutine;
+
sw::Context context;
sw::Rect scissor;
VkViewport viewport;
diff --git a/src/Vulkan/VkShaderModule.cpp b/src/Vulkan/VkShaderModule.cpp
index 4c824a3..b48c909 100644
--- a/src/Vulkan/VkShaderModule.cpp
+++ b/src/Vulkan/VkShaderModule.cpp
@@ -22,6 +22,7 @@
ShaderModule::ShaderModule(const VkShaderModuleCreateInfo* pCreateInfo, void* mem) : code(reinterpret_cast<uint32_t*>(mem))
{
memcpy(code, pCreateInfo->pCode, pCreateInfo->codeSize);
+ wordCount = static_cast<uint32_t>(pCreateInfo->codeSize / sizeof(uint32_t));
}
void ShaderModule::destroy(const VkAllocationCallbacks* pAllocator)
@@ -34,10 +35,4 @@
return pCreateInfo->codeSize;
}
-rr::Routine* ShaderModule::compile(const VkAllocationCallbacks* pAllocator)
-{
- // FIXME: Compile the code here
- return nullptr;
-}
-
} // namespace vk
diff --git a/src/Vulkan/VkShaderModule.hpp b/src/Vulkan/VkShaderModule.hpp
index f7a4de7..7287d69 100644
--- a/src/Vulkan/VkShaderModule.hpp
+++ b/src/Vulkan/VkShaderModule.hpp
@@ -16,6 +16,7 @@
#define VK_SHADER_MODULE_HPP_
#include "VkObject.hpp"
+#include <vector>
namespace rr
{
@@ -32,12 +33,14 @@
~ShaderModule() = delete;
void destroy(const VkAllocationCallbacks* pAllocator);
- rr::Routine* compile(const VkAllocationCallbacks* pAllocator);
-
static size_t ComputeRequiredAllocationSize(const VkShaderModuleCreateInfo* pCreateInfo);
+ // TODO: reconsider boundary of ShaderModule class; try to avoid 'expose the
+ // guts' operations, and this copy.
+ std::vector<uint32_t> getCode() const { return std::vector<uint32_t>{ code, code + wordCount };}
private:
uint32_t* code = nullptr;
+ uint32_t wordCount = 0;
};
static inline ShaderModule* Cast(VkShaderModule object)
diff --git a/src/Vulkan/vulkan.vcxproj b/src/Vulkan/vulkan.vcxproj
index 410fadf..32abd27 100644
--- a/src/Vulkan/vulkan.vcxproj
+++ b/src/Vulkan/vulkan.vcxproj
@@ -145,6 +145,7 @@
<ClCompile Include="..\Pipeline\SetupRoutine.cpp" />
<ClCompile Include="..\Pipeline\Shader.cpp" />
<ClCompile Include="..\Pipeline\ShaderCore.cpp" />
+ <ClCompile Include="..\Pipeline\SpirvShader.cpp" />
<ClCompile Include="..\Pipeline\VertexProgram.cpp" />
<ClCompile Include="..\Pipeline\VertexRoutine.cpp" />
<ClCompile Include="..\Pipeline\VertexShader.cpp" />
@@ -251,6 +252,7 @@
<ClInclude Include="..\Pipeline\SetupRoutine.hpp" />
<ClInclude Include="..\Pipeline\Shader.hpp" />
<ClInclude Include="..\Pipeline\ShaderCore.hpp" />
+ <ClInclude Include="..\Pipeline\SpirvShader.hpp" />
<ClInclude Include="..\Pipeline\VertexPipeline.hpp" />
<ClInclude Include="..\Pipeline\VertexProgram.hpp" />
<ClInclude Include="..\Pipeline\VertexRoutine.hpp" />
@@ -319,4 +321,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file
+</Project>