| 1 | //************************************ bs::framework - Copyright 2018 Marko Pintera **************************************// |
| 2 | //*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********// |
| 3 | #include "RenderAPI/BsGpuPipelineParamInfo.h" |
| 4 | #include "RenderAPI/BsGpuParamDesc.h" |
| 5 | #include "Managers/BsRenderStateManager.h" |
| 6 | |
| 7 | namespace bs |
| 8 | { |
| 9 | GpuPipelineParamInfoBase::GpuPipelineParamInfoBase(const GPU_PIPELINE_PARAMS_DESC& desc) |
| 10 | :mNumSets(0), mNumElements(0), mSetInfos(nullptr), mResourceInfos() |
| 11 | { |
| 12 | bs_zero_out(mNumElementsPerType); |
| 13 | |
| 14 | mParamDescs[GPT_FRAGMENT_PROGRAM] = desc.fragmentParams; |
| 15 | mParamDescs[GPT_VERTEX_PROGRAM] = desc.vertexParams; |
| 16 | mParamDescs[GPT_GEOMETRY_PROGRAM] = desc.geometryParams; |
| 17 | mParamDescs[GPT_HULL_PROGRAM] = desc.hullParams; |
| 18 | mParamDescs[GPT_DOMAIN_PROGRAM] = desc.domainParams; |
| 19 | mParamDescs[GPT_COMPUTE_PROGRAM] = desc.computeParams; |
| 20 | |
| 21 | auto countElements = [&](auto& entry, ParamType type) |
| 22 | { |
| 23 | int typeIdx = (int)type; |
| 24 | |
| 25 | if ((entry.set + 1) > mNumSets) |
| 26 | mNumSets = entry.set + 1; |
| 27 | |
| 28 | mNumElementsPerType[typeIdx]++; |
| 29 | mNumElements++; |
| 30 | }; |
| 31 | |
| 32 | UINT32 numParamDescs = sizeof(mParamDescs) / sizeof(mParamDescs[0]); |
| 33 | for (UINT32 i = 0; i < numParamDescs; i++) |
| 34 | { |
| 35 | const SPtr<GpuParamDesc>& paramDesc = mParamDescs[i]; |
| 36 | if (paramDesc == nullptr) |
| 37 | continue; |
| 38 | |
| 39 | for (auto& paramBlock : paramDesc->paramBlocks) |
| 40 | countElements(paramBlock.second, ParamType::ParamBlock); |
| 41 | |
| 42 | for (auto& texture : paramDesc->textures) |
| 43 | countElements(texture.second, ParamType::Texture); |
| 44 | |
| 45 | for (auto& texture : paramDesc->loadStoreTextures) |
| 46 | countElements(texture.second, ParamType::LoadStoreTexture); |
| 47 | |
| 48 | for (auto& buffer : paramDesc->buffers) |
| 49 | countElements(buffer.second, ParamType::Buffer); |
| 50 | |
| 51 | for (auto& sampler : paramDesc->samplers) |
| 52 | countElements(sampler.second, ParamType::SamplerState); |
| 53 | } |
| 54 | |
| 55 | UINT32* numSlotsPerSet = (UINT32*)bs_stack_alloc(mNumSets * sizeof(UINT32)); |
| 56 | bs_zero_out(numSlotsPerSet, mNumSets); |
| 57 | |
| 58 | for (UINT32 i = 0; i < numParamDescs; i++) |
| 59 | { |
| 60 | const SPtr<GpuParamDesc>& paramDesc = mParamDescs[i]; |
| 61 | if (paramDesc == nullptr) |
| 62 | continue; |
| 63 | |
| 64 | for (auto& paramBlock : paramDesc->paramBlocks) |
| 65 | numSlotsPerSet[paramBlock.second.set] = |
| 66 | std::max(numSlotsPerSet[paramBlock.second.set], paramBlock.second.slot + 1); |
| 67 | |
| 68 | for (auto& texture : paramDesc->textures) |
| 69 | numSlotsPerSet[texture.second.set] = |
| 70 | std::max(numSlotsPerSet[texture.second.set], texture.second.slot + 1); |
| 71 | |
| 72 | for (auto& texture : paramDesc->loadStoreTextures) |
| 73 | numSlotsPerSet[texture.second.set] = |
| 74 | std::max(numSlotsPerSet[texture.second.set], texture.second.slot + 1); |
| 75 | |
| 76 | for (auto& buffer : paramDesc->buffers) |
| 77 | numSlotsPerSet[buffer.second.set] = |
| 78 | std::max(numSlotsPerSet[buffer.second.set], buffer.second.slot + 1); |
| 79 | |
| 80 | for (auto& sampler : paramDesc->samplers) |
| 81 | numSlotsPerSet[sampler.second.set] = |
| 82 | std::max(numSlotsPerSet[sampler.second.set], sampler.second.slot + 1); |
| 83 | } |
| 84 | |
| 85 | UINT32 totalNumSlots = 0; |
| 86 | for (UINT32 i = 0; i < mNumSets; i++) |
| 87 | totalNumSlots += numSlotsPerSet[i]; |
| 88 | |
| 89 | mAlloc.reserve<SetInfo>(mNumSets) |
| 90 | .reserve<UINT32>(totalNumSlots) |
| 91 | .reserve<ParamType>(totalNumSlots) |
| 92 | .reserve<UINT32>(totalNumSlots); |
| 93 | |
| 94 | for (UINT32 i = 0; i < (UINT32)ParamType::Count; i++) |
| 95 | mAlloc.reserve<ResourceInfo>(mNumElementsPerType[i]); |
| 96 | |
| 97 | mAlloc.init(); |
| 98 | |
| 99 | mSetInfos = mAlloc.alloc<SetInfo>(mNumSets); |
| 100 | |
| 101 | if(mSetInfos != nullptr) |
| 102 | bs_zero_out(mSetInfos, mNumSets); |
| 103 | |
| 104 | for (UINT32 i = 0; i < mNumSets; i++) |
| 105 | mSetInfos[i].numSlots = numSlotsPerSet[i]; |
| 106 | |
| 107 | bs_stack_free(numSlotsPerSet); |
| 108 | |
| 109 | for(UINT32 i = 0; i < mNumSets; i++) |
| 110 | { |
| 111 | mSetInfos[i].slotIndices = mAlloc.alloc<UINT32>(mSetInfos[i].numSlots); |
| 112 | memset(mSetInfos[i].slotIndices, -1, sizeof(UINT32) * mSetInfos[i].numSlots); |
| 113 | |
| 114 | mSetInfos[i].slotTypes = mAlloc.alloc<ParamType>(mSetInfos[i].numSlots); |
| 115 | |
| 116 | mSetInfos[i].slotSamplers = mAlloc.alloc<UINT32>(mSetInfos[i].numSlots); |
| 117 | memset(mSetInfos[i].slotSamplers, -1, sizeof(UINT32) * mSetInfos[i].numSlots); |
| 118 | } |
| 119 | |
| 120 | for (UINT32 i = 0; i < (UINT32)ParamType::Count; i++) |
| 121 | { |
| 122 | mResourceInfos[i] = mAlloc.alloc<ResourceInfo>(mNumElementsPerType[i]); |
| 123 | mNumElementsPerType[i] = 0; |
| 124 | } |
| 125 | |
| 126 | auto populateSetInfo = [&](auto& entry, ParamType type) |
| 127 | { |
| 128 | int typeIdx = (int)type; |
| 129 | |
| 130 | UINT32 sequentialIdx = mNumElementsPerType[typeIdx]; |
| 131 | |
| 132 | SetInfo& setInfo = mSetInfos[entry.set]; |
| 133 | setInfo.slotIndices[entry.slot] = sequentialIdx; |
| 134 | setInfo.slotTypes[entry.slot] = type; |
| 135 | |
| 136 | mResourceInfos[typeIdx][sequentialIdx].set = entry.set; |
| 137 | mResourceInfos[typeIdx][sequentialIdx].slot = entry.slot; |
| 138 | |
| 139 | mNumElementsPerType[typeIdx]++; |
| 140 | }; |
| 141 | |
| 142 | for (UINT32 i = 0; i < numParamDescs; i++) |
| 143 | { |
| 144 | const SPtr<GpuParamDesc>& paramDesc = mParamDescs[i]; |
| 145 | if (paramDesc == nullptr) |
| 146 | continue; |
| 147 | |
| 148 | for (auto& paramBlock : paramDesc->paramBlocks) |
| 149 | populateSetInfo(paramBlock.second, ParamType::ParamBlock); |
| 150 | |
| 151 | for (auto& texture : paramDesc->textures) |
| 152 | populateSetInfo(texture.second, ParamType::Texture); |
| 153 | |
| 154 | for (auto& texture : paramDesc->loadStoreTextures) |
| 155 | populateSetInfo(texture.second, ParamType::LoadStoreTexture); |
| 156 | |
| 157 | for (auto& buffer : paramDesc->buffers) |
| 158 | populateSetInfo(buffer.second, ParamType::Buffer); |
| 159 | |
| 160 | // Samplers need to be handled specially because certain slots could be texture/buffer + sampler combinations |
| 161 | { |
| 162 | int typeIdx = (int)ParamType::SamplerState; |
| 163 | for (auto& entry : paramDesc->samplers) |
| 164 | { |
| 165 | const GpuParamObjectDesc& samplerDesc = entry.second; |
| 166 | UINT32 sequentialIdx = mNumElementsPerType[typeIdx]; |
| 167 | |
| 168 | SetInfo& setInfo = mSetInfos[samplerDesc.set]; |
| 169 | if (setInfo.slotIndices[samplerDesc.slot] == (UINT32)-1) // Slot is sampler only |
| 170 | { |
| 171 | setInfo.slotIndices[samplerDesc.slot] = sequentialIdx; |
| 172 | setInfo.slotTypes[samplerDesc.slot] = ParamType::SamplerState; |
| 173 | } |
| 174 | else // Slot is a combination |
| 175 | { |
| 176 | setInfo.slotSamplers[samplerDesc.slot] = sequentialIdx; |
| 177 | } |
| 178 | |
| 179 | mResourceInfos[typeIdx][sequentialIdx].set = samplerDesc.set; |
| 180 | mResourceInfos[typeIdx][sequentialIdx].slot = samplerDesc.slot; |
| 181 | |
| 182 | mNumElementsPerType[typeIdx]++; |
| 183 | } |
| 184 | } |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | UINT32 GpuPipelineParamInfoBase::getSequentialSlot(ParamType type, UINT32 set, UINT32 slot) const |
| 189 | { |
| 190 | #if BS_DEBUG_MODE |
| 191 | if (set >= mNumSets) |
| 192 | { |
| 193 | LOGERR("Set index out of range: Valid range: [0, " + |
| 194 | toString(mNumSets) + "). Requested: " + toString(set) + "." ); |
| 195 | return -1; |
| 196 | } |
| 197 | |
| 198 | if (slot >= mSetInfos[set].numSlots) |
| 199 | { |
| 200 | LOGERR("Slot index out of range: Valid range: [0, " + |
| 201 | toString(mSetInfos[set].numSlots) + "). Requested: " + toString(slot) + "." ); |
| 202 | return -1; |
| 203 | } |
| 204 | |
| 205 | ParamType slotType = mSetInfos[set].slotTypes[slot]; |
| 206 | if(slotType != type) |
| 207 | { |
| 208 | // Allow sampler states & textures/buffers to share the same slot, as some APIs combine them |
| 209 | if(type == ParamType::SamplerState) |
| 210 | { |
| 211 | if (mSetInfos[set].slotSamplers[slot] != (UINT32)-1) |
| 212 | return mSetInfos[set].slotSamplers[slot]; |
| 213 | } |
| 214 | |
| 215 | LOGERR("Requested parameter is not of the valid type. Requested: " + toString((UINT32)type) + ". Actual: " + |
| 216 | toString((UINT32)mSetInfos[set].slotTypes[slot]) + "." ); |
| 217 | return -1; |
| 218 | } |
| 219 | |
| 220 | #endif |
| 221 | |
| 222 | return mSetInfos[set].slotIndices[slot]; |
| 223 | } |
| 224 | |
| 225 | void GpuPipelineParamInfoBase::getBinding(ParamType type, UINT32 sequentialSlot, UINT32& set, UINT32& slot) const |
| 226 | { |
| 227 | #if BS_DEBUG_MODE |
| 228 | if(sequentialSlot >= mNumElementsPerType[(int)type]) |
| 229 | { |
| 230 | LOGERR("Sequential slot index out of range: Valid range: [0, " + toString(mNumElementsPerType[(int)type]) + |
| 231 | "). Requested: " + toString(sequentialSlot) + "." ); |
| 232 | |
| 233 | set = 0; |
| 234 | slot = 0; |
| 235 | return; |
| 236 | } |
| 237 | #endif |
| 238 | |
| 239 | set = mResourceInfos[(int)type][sequentialSlot].set; |
| 240 | slot = mResourceInfos[(int)type][sequentialSlot].slot; |
| 241 | } |
| 242 | |
| 243 | void GpuPipelineParamInfoBase::getBindings(ParamType type, const String& name, GpuParamBinding (& bindings)[GPT_COUNT]) |
| 244 | { |
| 245 | constexpr UINT32 numParamDescs = sizeof(mParamDescs) / sizeof(mParamDescs[0]); |
| 246 | static_assert( |
| 247 | numParamDescs == GPT_COUNT, |
| 248 | "Number of param descriptor structures must match the number of GPU program stages." |
| 249 | ); |
| 250 | |
| 251 | for (UINT32 i = 0; i < numParamDescs; i++) |
| 252 | getBinding((GpuProgramType)i, type, name, bindings[i]); |
| 253 | } |
| 254 | |
| 255 | void GpuPipelineParamInfoBase::getBinding(GpuProgramType progType, ParamType type, const String& name, |
| 256 | GpuParamBinding &binding) |
| 257 | { |
| 258 | auto findBinding = [](auto& paramMap, const String& name, GpuParamBinding& binding) |
| 259 | { |
| 260 | auto iterFind = paramMap.find(name); |
| 261 | if (iterFind != paramMap.end()) |
| 262 | { |
| 263 | binding.set = iterFind->second.set; |
| 264 | binding.slot = iterFind->second.slot; |
| 265 | } |
| 266 | else |
| 267 | binding.set = binding.slot = (UINT32)-1; |
| 268 | }; |
| 269 | |
| 270 | const SPtr<GpuParamDesc>& paramDesc = mParamDescs[(UINT32)progType]; |
| 271 | if (paramDesc == nullptr) |
| 272 | { |
| 273 | binding.set = binding.slot = (UINT32)-1; |
| 274 | return; |
| 275 | } |
| 276 | |
| 277 | switch(type) |
| 278 | { |
| 279 | case ParamType::ParamBlock: |
| 280 | findBinding(paramDesc->paramBlocks, name, binding); |
| 281 | break; |
| 282 | case ParamType::Texture: |
| 283 | findBinding(paramDesc->textures, name, binding); |
| 284 | break; |
| 285 | case ParamType::LoadStoreTexture: |
| 286 | findBinding(paramDesc->loadStoreTextures, name, binding); |
| 287 | break; |
| 288 | case ParamType::Buffer: |
| 289 | findBinding(paramDesc->buffers, name, binding); |
| 290 | break; |
| 291 | case ParamType::SamplerState: |
| 292 | findBinding(paramDesc->samplers, name, binding); |
| 293 | break; |
| 294 | default: |
| 295 | break; |
| 296 | } |
| 297 | } |
| 298 | |
| 299 | GpuPipelineParamInfo::GpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc) |
| 300 | :GpuPipelineParamInfoBase(desc) |
| 301 | { } |
| 302 | |
| 303 | SPtr<GpuPipelineParamInfo> GpuPipelineParamInfo::create(const GPU_PIPELINE_PARAMS_DESC& desc) |
| 304 | { |
| 305 | SPtr<GpuPipelineParamInfo> paramInfo = |
| 306 | bs_core_ptr<GpuPipelineParamInfo>(new (bs_alloc<GpuPipelineParamInfo>()) GpuPipelineParamInfo(desc)); |
| 307 | paramInfo->_setThisPtr(paramInfo); |
| 308 | paramInfo->initialize(); |
| 309 | |
| 310 | return paramInfo; |
| 311 | } |
| 312 | |
| 313 | SPtr<ct::GpuPipelineParamInfo> GpuPipelineParamInfo::getCore() const |
| 314 | { |
| 315 | return std::static_pointer_cast<ct::GpuPipelineParamInfo>(mCoreSpecific); |
| 316 | } |
| 317 | |
| 318 | SPtr<ct::CoreObject> GpuPipelineParamInfo::createCore() const |
| 319 | { |
| 320 | GPU_PIPELINE_PARAMS_DESC desc; |
| 321 | desc.fragmentParams = mParamDescs[GPT_FRAGMENT_PROGRAM]; |
| 322 | desc.vertexParams = mParamDescs[GPT_VERTEX_PROGRAM]; |
| 323 | desc.geometryParams = mParamDescs[GPT_GEOMETRY_PROGRAM]; |
| 324 | desc.hullParams = mParamDescs[GPT_HULL_PROGRAM]; |
| 325 | desc.domainParams = mParamDescs[GPT_DOMAIN_PROGRAM]; |
| 326 | desc.computeParams = mParamDescs[GPT_COMPUTE_PROGRAM]; |
| 327 | |
| 328 | return ct::RenderStateManager::instance()._createPipelineParamInfo(desc); |
| 329 | } |
| 330 | |
| 331 | namespace ct |
| 332 | { |
| 333 | GpuPipelineParamInfo::GpuPipelineParamInfo(const GPU_PIPELINE_PARAMS_DESC& desc, GpuDeviceFlags deviceMask) |
| 334 | :GpuPipelineParamInfoBase(desc) |
| 335 | { } |
| 336 | |
| 337 | SPtr<GpuPipelineParamInfo> GpuPipelineParamInfo::create(const GPU_PIPELINE_PARAMS_DESC& desc, |
| 338 | GpuDeviceFlags deviceMask) |
| 339 | { |
| 340 | return RenderStateManager::instance().createPipelineParamInfo(desc, deviceMask); |
| 341 | } |
| 342 | } |
| 343 | } |