188 lines
6.1 KiB
C++
188 lines
6.1 KiB
C++
/*
|
|
* Copyright 2017, The Android Open Source Project
|
|
*
|
|
* 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 "GlobalAllocSPIRITPass.h"
|
|
|
|
#include "Context.h"
|
|
#include "spirit.h"
|
|
#include "transformer.h"
|
|
|
|
#include <sstream>
|
|
|
|
namespace android {
|
|
namespace spirit {
|
|
|
|
namespace {
|
|
|
|
// Metadata buffer for global allocations
|
|
// struct metadata {
|
|
// uint32_t element_size;
|
|
// uint32_t x_size;
|
|
// uint32_t y_size;
|
|
// uint32_t unused
|
|
// };
|
|
|
|
VariableInst *AddGAMetadata(Builder &b, Module *m) {
|
|
TypeIntInst *UInt32Ty = m->getUnsignedIntType(32);
|
|
std::vector<Instruction *> metadata{UInt32Ty, UInt32Ty, UInt32Ty, UInt32Ty};
|
|
auto MetadataStructTy = m->getStructType(metadata.data(), metadata.size());
|
|
// FIXME: workaround on a weird OpAccessChain member offset problem. Somehow
|
|
// when given constant indices, OpAccessChain returns pointers that are 4
|
|
// bytes less than what are supposed to be (at runtime). For now workaround
|
|
// this with +4 the member offsets.
|
|
MetadataStructTy->memberDecorate(0, Decoration::Offset)->addExtraOperand(4);
|
|
MetadataStructTy->memberDecorate(1, Decoration::Offset)->addExtraOperand(8);
|
|
MetadataStructTy->memberDecorate(2, Decoration::Offset)->addExtraOperand(12);
|
|
MetadataStructTy->memberDecorate(3, Decoration::Offset)->addExtraOperand(16);
|
|
// TBD: Implement getArrayType. RuntimeArray requires buffers and hence we
|
|
// cannot use PushConstant underneath
|
|
auto MetadataBufSTy = m->getRuntimeArrayType(MetadataStructTy);
|
|
// Stride of metadata.
|
|
MetadataBufSTy->decorate(Decoration::ArrayStride)
|
|
->addExtraOperand(metadata.size() * sizeof(uint32_t));
|
|
auto MetadataSSBO = m->getStructType(MetadataBufSTy);
|
|
MetadataSSBO->decorate(Decoration::BufferBlock);
|
|
auto MetadataPtrTy = m->getPointerType(StorageClass::Uniform, MetadataSSBO);
|
|
|
|
VariableInst *MetadataVar =
|
|
b.MakeVariable(MetadataPtrTy, StorageClass::Uniform);
|
|
MetadataVar->decorate(Decoration::DescriptorSet)->addExtraOperand(0);
|
|
MetadataVar->decorate(Decoration::Binding)->addExtraOperand(1);
|
|
m->addVariable(MetadataVar);
|
|
|
|
return MetadataVar;
|
|
}
|
|
|
|
std::string CreateGAIDMetadata(
|
|
const llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &Allocs) {
|
|
|
|
std::stringstream mapping;
|
|
bool printed = false;
|
|
|
|
mapping << "{\"__RSoV_GA\": {";
|
|
for (auto &A : Allocs) {
|
|
// Skip unused GAs
|
|
if (!A.hasID()) {
|
|
continue;
|
|
}
|
|
if (printed)
|
|
mapping << ", ";
|
|
// "GA name" to the ID of the GA
|
|
mapping << "\"" << A.VarName.substr(1) << "\":" << A.ID;
|
|
printed = true;
|
|
}
|
|
mapping << "}}";
|
|
|
|
if (printed)
|
|
return mapping.str().c_str();
|
|
else
|
|
return "";
|
|
}
|
|
} // anonymous namespace
|
|
|
|
// Replacing calls to lowered accessors, e.g., __rsov_rsAllocationGetDimX
|
|
// which was created from rsAllocationGetDimX by replacing the allocation
|
|
// with an ID in an earlier LLVM pass (see GlobalAllocationPass.cpp),
|
|
// to access the global allocation metadata.
|
|
//
|
|
// For example, the source code may look like:
|
|
//
|
|
// rs_allocation g;
|
|
// ...
|
|
// uint32_t foo = rsAllocationGetDimX(g);
|
|
//
|
|
// After the GlobalAllocPass, it would look like the LLVM IR
|
|
// equivalent of:
|
|
//
|
|
// uint32_t foo = __rsov_rsAllocationGetDimX(0);
|
|
//
|
|
// After that pass, g is removed, and references in intrinsics
|
|
// to g would be replaced with an assigned unique id (0 here), and
|
|
// rsAllocationGetDimX() would be replaced by __rsov_rsAllocationGetDimX()
|
|
// where the only difference is the argument being replaced by the unique
|
|
// ID. __rsov_rsAllocationGetDimX() does not really exist - it is used
|
|
// as a marker for this pass to work on.
|
|
//
|
|
// After this GAAccessTransformer pass, it would look like (in SPIRIT):
|
|
//
|
|
// uint32_t foo = Metadata[0].size_x;
|
|
//
|
|
// where the OpFunctionCall to __rsov_rsAllocationGetDim() is replaced by
|
|
// an OpAccessChain and OpLoad from the metadata buffer.
|
|
|
|
class GAAccessorTransformer : public Transformer {
|
|
public:
|
|
GAAccessorTransformer()
|
|
: mBuilder(), mMetadata(nullptr),
|
|
mAllocs(rs2spirv::Context::getInstance().getGlobalAllocs()) {}
|
|
|
|
std::vector<uint32_t> runAndSerialize(Module *module, int *error) override {
|
|
std::string GAMD = CreateGAIDMetadata(mAllocs);
|
|
if (GAMD.size() > 0) {
|
|
module->addString(GAMD.c_str());
|
|
}
|
|
mMetadata = AddGAMetadata(mBuilder, module);
|
|
return Transformer::runAndSerialize(module, error);
|
|
}
|
|
|
|
Instruction *transform(FunctionCallInst *call) {
|
|
FunctionInst *func =
|
|
static_cast<FunctionInst *>(call->mOperand1.mInstruction);
|
|
const char *name = getModule()->lookupNameByInstruction(func);
|
|
if (!name) {
|
|
return call;
|
|
}
|
|
|
|
Instruction *inst = nullptr;
|
|
// Maps name into a SPIR-V instruction
|
|
// TODO: generalize it to support more accessors
|
|
if (!strcmp(name, "__rsov_rsAllocationGetDimX")) {
|
|
TypeIntInst *UInt32Ty = getModule()->getUnsignedIntType(32);
|
|
// TODO: hardcoded layout
|
|
auto ConstZero = getModule()->getConstant(UInt32Ty, 0U);
|
|
auto ConstOne = getModule()->getConstant(UInt32Ty, 1U);
|
|
|
|
// TODO: Use constant memory later
|
|
auto resultPtrType =
|
|
getModule()->getPointerType(StorageClass::Uniform, UInt32Ty);
|
|
AccessChainInst *LoadPtr = mBuilder.MakeAccessChain(
|
|
resultPtrType, mMetadata, {ConstZero, ConstZero, ConstOne});
|
|
insert(LoadPtr);
|
|
|
|
inst = mBuilder.MakeLoad(UInt32Ty, LoadPtr);
|
|
inst->setId(call->getId());
|
|
} else {
|
|
inst = call;
|
|
}
|
|
return inst;
|
|
}
|
|
|
|
private:
|
|
Builder mBuilder;
|
|
VariableInst *mMetadata;
|
|
llvm::SmallVectorImpl<rs2spirv::RSAllocationInfo> &mAllocs;
|
|
};
|
|
|
|
} // namespace spirit
|
|
} // namespace android
|
|
|
|
namespace rs2spirv {
|
|
|
|
android::spirit::Pass *CreateGAPass(void) {
|
|
return new android::spirit::GAAccessorTransformer();
|
|
}
|
|
|
|
} // namespace rs2spirv
|