1616
1717#include " VulkanAsyncHandles.h"
1818
19+ #include " VulkanConstants.h"
20+
21+ #include " vulkan/utils/Spirv.h"
22+
1923#include < backend/DriverEnums.h>
2024
2125#include < utils/debug.h>
@@ -29,6 +33,154 @@ using namespace bluevk;
2933
3034namespace filament ::backend {
3135
36+ namespace {
37+
38+ inline VkShaderStageFlags getVkStage (backend::ShaderStage stage) {
39+ switch (stage) {
40+ case backend::ShaderStage::VERTEX:
41+ return VK_SHADER_STAGE_VERTEX_BIT;
42+ case backend::ShaderStage::FRAGMENT:
43+ return VK_SHADER_STAGE_FRAGMENT_BIT;
44+ case backend::ShaderStage::COMPUTE:
45+ PANIC_POSTCONDITION (" Unsupported stage" );
46+ }
47+ }
48+
49+ } // namespace
50+
51+ PushConstantDescription::PushConstantDescription (backend::Program const & program) {
52+ mRangeCount = 0 ;
53+ uint32_t offset = 0 ;
54+
55+ // The range is laid out so that the vertex constants are defined as the first set of bytes,
56+ // followed by fragment and compute. This means we need to keep track of the offset for each
57+ // stage. We do the bookeeping in mDescriptions.
58+ for (auto stage: { ShaderStage::VERTEX, ShaderStage::FRAGMENT, ShaderStage::COMPUTE }) {
59+ auto const & constants = program.getPushConstants (stage);
60+ if (constants.empty ()) {
61+ continue ;
62+ }
63+
64+ auto & description = mDescriptions [(uint8_t ) stage];
65+ // We store the type of the constant for type-checking when writing.
66+ description.types .reserve (constants.size ());
67+ std::for_each (constants.cbegin (), constants.cend (),
68+ [&description](Program::PushConstant t) { description.types .push_back (t.type ); });
69+
70+ uint32_t const constantsSize = (uint32_t ) constants.size () * ENTRY_SIZE;
71+ mRanges [mRangeCount ++] = {
72+ .stageFlags = getVkStage (stage),
73+ .offset = offset,
74+ .size = constantsSize,
75+ };
76+ description.offset = offset;
77+ offset += constantsSize;
78+ }
79+ }
80+
81+ void PushConstantDescription::write (VkCommandBuffer cmdbuf, VkPipelineLayout layout,
82+ backend::ShaderStage stage, uint8_t index, backend::PushConstantVariant const & value) {
83+
84+ uint32_t binaryValue = 0 ;
85+ auto const & description = mDescriptions [(uint8_t ) stage];
86+ UTILS_UNUSED_IN_RELEASE auto const & types = description.types ;
87+ uint32_t const offset = description.offset ;
88+
89+ if (std::holds_alternative<bool >(value)) {
90+ assert_invariant (types[index] == ConstantType::BOOL);
91+ bool const bval = std::get<bool >(value);
92+ binaryValue = static_cast <uint32_t const >(bval ? VK_TRUE : VK_FALSE);
93+ } else if (std::holds_alternative<float >(value)) {
94+ assert_invariant (types[index] == ConstantType::FLOAT);
95+ float const fval = std::get<float >(value);
96+ binaryValue = *reinterpret_cast <uint32_t const *>(&fval);
97+ } else {
98+ assert_invariant (types[index] == ConstantType::INT);
99+ int const ival = std::get<int >(value);
100+ binaryValue = *reinterpret_cast <uint32_t const *>(&ival);
101+ }
102+
103+ vkCmdPushConstants (cmdbuf, layout, getVkStage (stage), offset + index * ENTRY_SIZE, ENTRY_SIZE,
104+ &binaryValue);
105+ }
106+
107+ VulkanProgram::VulkanProgram (VkDevice device, Program const & builder) noexcept
108+ : HwProgram(builder.getName()),
109+ mInfo (new (std::nothrow) PipelineInfo(builder)),
110+ mDevice(device) {
111+
112+ Program::ShaderSource const & blobs = builder.getShadersSource ();
113+ auto & modules = mInfo ->shaders ;
114+ auto const & specializationConstants = builder.getSpecializationConstants ();
115+ std::vector<uint32_t > shader;
116+
117+ static_assert (static_cast <ShaderStage>(0 ) == ShaderStage::VERTEX &&
118+ static_cast <ShaderStage>(1 ) == ShaderStage::FRAGMENT &&
119+ MAX_SHADER_MODULES == 2 );
120+
121+ for (size_t i = 0 ; i < MAX_SHADER_MODULES; i++) {
122+ Program::ShaderBlob const & blob = blobs[i];
123+
124+ uint32_t * data = (uint32_t *) blob.data ();
125+ size_t dataSize = blob.size ();
126+
127+ if (!specializationConstants.empty ()) {
128+ fvkutils::workaroundSpecConstant (blob, specializationConstants, shader);
129+ data = (uint32_t *) shader.data ();
130+ dataSize = shader.size () * 4 ;
131+ }
132+
133+ VkShaderModule& module = modules[i];
134+ VkShaderModuleCreateInfo moduleInfo = {
135+ .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
136+ .codeSize = dataSize,
137+ .pCode = data,
138+ };
139+ VkResult result = vkCreateShaderModule (mDevice , &moduleInfo, VKALLOC, &module );
140+ FILAMENT_CHECK_POSTCONDITION (result == VK_SUCCESS)
141+ << " Unable to create shader module."
142+ << " error=" << static_cast <int32_t >(result);
143+
144+ #if FVK_ENABLED(FVK_DEBUG_DEBUG_UTILS)
145+ utils::CString name{ builder.getName ().c_str (), builder.getName ().size () };
146+ switch (static_cast <ShaderStage>(i)) {
147+ case ShaderStage::VERTEX:
148+ name += " _vs" ;
149+ break ;
150+ case ShaderStage::FRAGMENT:
151+ name += " _fs" ;
152+ break ;
153+ default :
154+ PANIC_POSTCONDITION (" Unexpected stage" );
155+ break ;
156+ }
157+ VulkanDriver::DebugUtils::setName (VK_OBJECT_TYPE_SHADER_MODULE,
158+ reinterpret_cast <uint64_t >(module ), name.c_str ());
159+ #endif
160+ }
161+
162+ #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE)
163+ FVK_LOGD << " Created VulkanProgram " << builder << " , shaders = (" << modules[0 ]
164+ << " , " << modules[1 ] << " )" ;
165+ #endif
166+ }
167+
168+ VulkanProgram::~VulkanProgram () {
169+ for (auto shader: mInfo ->shaders ) {
170+ vkDestroyShaderModule (mDevice , shader, VKALLOC);
171+ }
172+ delete mInfo ;
173+ }
174+
175+ void VulkanProgram::flushPushConstants (VkPipelineLayout layout) {
176+ // At this point, we really ought to have a VkPipelineLayout.
177+ assert_invariant (layout != VK_NULL_HANDLE);
178+ for (const auto & c : mQueuedPushConstants ) {
179+ mInfo ->pushConstantDescription .write (c.cmdbuf , layout, c.stage , c.index , c.value );
180+ }
181+ mQueuedPushConstants .clear ();
182+ }
183+
32184std::shared_ptr<VulkanCmdFence> VulkanCmdFence::completed () noexcept {
33185 auto cmdFence = std::make_shared<VulkanCmdFence>(VK_NULL_HANDLE);
34186 cmdFence->mStatus = VK_SUCCESS;
0 commit comments