Begin implementing texelFetch(texture2D) workaround on GLSL.

This commit is contained in:
Hans-Kristian Arntzen 2018-02-21 13:08:30 +01:00
parent d871a89886
commit 4db7061dd1
3 changed files with 169 additions and 4 deletions

View File

@ -878,6 +878,7 @@ static int main_inner(int argc, char *argv[])
if (combined_image_samplers) if (combined_image_samplers)
{ {
compiler->build_dummy_sampler_for_combined_images();
compiler->build_combined_image_samplers(); compiler->build_combined_image_samplers();
// Give the remapped combined samplers new names. // Give the remapped combined samplers new names.
for (auto &remap : compiler->get_combined_image_samplers()) for (auto &remap : compiler->get_combined_image_samplers())

View File

@ -2823,9 +2823,77 @@ void Compiler::CombinedImageSamplerHandler::register_combined_image_sampler(SPIR
} }
} }
bool Compiler::DummySamplerForCombinedImageHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
{
if (need_dummy_sampler)
{
// No need to traverse further, we know the result.
return false;
}
switch (opcode)
{
case OpLoad:
{
if (length < 3)
return false;
uint32_t result_type = args[0];
auto &type = compiler.get<SPIRType>(result_type);
bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
// If not separate image, don't bother.
if (!separate_image)
return true;
uint32_t id = args[1];
uint32_t ptr = args[2];
compiler.set<SPIRExpression>(id, "", result_type, true);
compiler.register_read(id, ptr, true);
break;
}
case OpImageFetch:
{
// If we are fetching from a plain OpTypeImage, we must pre-combine with our dummy sampler.
auto *var = compiler.maybe_get_backing_variable(args[2]);
if (var)
{
auto &type = compiler.get<SPIRType>(var->basetype);
if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
need_dummy_sampler = true;
}
break;
}
case OpInBoundsAccessChain:
case OpAccessChain:
{
if (length < 3)
return false;
auto &type = compiler.get<SPIRType>(args[0]);
bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer;
if (separate_image)
SPIRV_CROSS_THROW("Attempting to use arrays or structs of separate images. This is not possible to "
"statically remap to plain GLSL.");
break;
}
default:
break;
}
return true;
}
bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length) bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *args, uint32_t length)
{ {
// We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need. // We need to figure out where samplers and images are loaded from, so do only the bare bones compilation we need.
bool is_fetch = false;
switch (opcode) switch (opcode)
{ {
case OpLoad: case OpLoad:
@ -2875,6 +2943,27 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
return true; return true;
} }
case OpImageFetch:
{
// If we are fetching from a plain OpTypeImage, we must pre-combine with our dummy sampler.
auto *var = compiler.maybe_get_backing_variable(args[2]);
if (!var)
return true;
auto &type = compiler.get<SPIRType>(var->basetype);
if (type.basetype == SPIRType::Image && type.image.sampled == 1 && type.image.dim != DimBuffer)
{
if (compiler.dummy_sampler_id == 0)
SPIRV_CROSS_THROW("texelFetch without sampler was found, but no dummy sampler has been created with build_dummy_sampler_for_combined_images().");
// Do it outside.
is_fetch = true;
break;
}
return true;
}
case OpSampledImage: case OpSampledImage:
// Do it outside. // Do it outside.
break; break;
@ -2899,7 +2988,7 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
if (image) if (image)
image_id = image->self; image_id = image->self;
uint32_t sampler_id = args[3]; uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : args[3];
auto *sampler = compiler.maybe_get_backing_variable(sampler_id); auto *sampler = compiler.maybe_get_backing_variable(sampler_id);
if (sampler) if (sampler)
sampler_id = sampler->self; sampler_id = sampler->self;
@ -2914,7 +3003,7 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
// Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know // Function parameters are not necessarily pointers, so if we don't have a backing variable, remapping will know
// which backing variable the image/sample came from. // which backing variable the image/sample came from.
uint32_t image_id = remap_parameter(args[2]); uint32_t image_id = remap_parameter(args[2]);
uint32_t sampler_id = remap_parameter(args[3]); uint32_t sampler_id = is_fetch ? compiler.dummy_sampler_id : remap_parameter(args[3]);
auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers), auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers),
[image_id, sampler_id](const CombinedImageSampler &combined) { [image_id, sampler_id](const CombinedImageSampler &combined) {
@ -2923,10 +3012,24 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
if (itr == end(compiler.combined_image_samplers)) if (itr == end(compiler.combined_image_samplers))
{ {
uint32_t sampled_type;
if (is_fetch)
{
// Have to invent the sampled image type.
sampled_type = compiler.increase_bound_by(1);
auto &type = compiler.set<SPIRType>(sampled_type);
type = compiler.expression_type(args[2]);
type.self = sampled_type;
type.basetype = SPIRType::SampledImage;
}
else
{
sampled_type = args[0];
}
auto id = compiler.increase_bound_by(2); auto id = compiler.increase_bound_by(2);
auto type_id = id + 0; auto type_id = id + 0;
auto combined_id = id + 1; auto combined_id = id + 1;
auto sampled_type = args[0];
// Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type. // Make a new type, pointer to OpTypeSampledImage, so we can make a variable of this type.
// We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes. // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes.
@ -2941,7 +3044,8 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
// Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant). // Inherit RelaxedPrecision (and potentially other useful flags if deemed relevant).
auto &new_flags = compiler.meta[combined_id].decoration.decoration_flags; auto &new_flags = compiler.meta[combined_id].decoration.decoration_flags;
auto old_flags = compiler.meta[sampler_id].decoration.decoration_flags; // Fetch inherits precision from the image, not sampler (there is no sampler).
auto old_flags = compiler.meta[is_fetch ? image_id : sampler_id].decoration.decoration_flags;
new_flags = old_flags & (1ull << DecorationRelaxedPrecision); new_flags = old_flags & (1ull << DecorationRelaxedPrecision);
compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id }); compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id });
@ -2950,6 +3054,35 @@ bool Compiler::CombinedImageSamplerHandler::handle(Op opcode, const uint32_t *ar
return true; return true;
} }
uint32_t Compiler::build_dummy_sampler_for_combined_images()
{
DummySamplerForCombinedImageHandler handler(*this);
traverse_all_reachable_opcodes(get<SPIRFunction>(entry_point), handler);
if (handler.need_dummy_sampler)
{
uint32_t offset = increase_bound_by(3);
auto type_id = offset + 0;
auto ptr_type_id = offset + 1;
auto var_id = offset + 2;
SPIRType sampler_type;
auto &sampler = set<SPIRType>(type_id);
sampler.basetype = SPIRType::Sampler;
auto &ptr_sampler = set<SPIRType>(ptr_type_id);
ptr_sampler = sampler;
ptr_sampler.self = type_id;
ptr_sampler.storage = StorageClassUniformConstant;
set<SPIRVariable>(var_id, ptr_type_id, StorageClassUniformConstant, 0);
set_name(var_id, "SPIRV_Cross_DummySampler");
dummy_sampler_id = var_id;
return var_id;
}
else
return 0;
}
void Compiler::build_combined_image_samplers() void Compiler::build_combined_image_samplers()
{ {
for (auto &id : ids) for (auto &id : ids)

View File

@ -314,6 +314,20 @@ public:
uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y, uint32_t get_work_group_size_specialization_constants(SpecializationConstant &x, SpecializationConstant &y,
SpecializationConstant &z) const; SpecializationConstant &z) const;
// Analyzes all OpImageFetch (texelFetch) opcodes and checks if there are instances where
// said instruction is used without a combined image sampler.
// GLSL targets do not support the use of texelFetch without a sampler.
// To workaround this, we must inject a dummy sampler which can be used to form a sampler2D at the call-site of
// texelFetch as necessary.
//
// This must be called before build_combined_image_samplers().
// build_combined_image_samplers() may refer to the ID returned by this method if the returned ID is non-zero.
// The return value will be the ID of a sampler object if a dummy sampler is necessary, or 0 if no sampler object
// is required.
//
// If the returned ID is non-zero, it can be decorated with set/bindings as desired before calling compile().
uint32_t build_dummy_sampler_for_combined_images();
// Analyzes all separate image and samplers used from the currently selected entry point, // Analyzes all separate image and samplers used from the currently selected entry point,
// and re-routes them all to a combined image sampler instead. // and re-routes them all to a combined image sampler instead.
// This is required to "support" separate image samplers in targets which do not natively support // This is required to "support" separate image samplers in targets which do not natively support
@ -678,6 +692,18 @@ protected:
bool depth); bool depth);
}; };
struct DummySamplerForCombinedImageHandler : OpcodeHandler
{
DummySamplerForCombinedImageHandler(Compiler &compiler_)
: compiler(compiler_)
{
}
bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override;
Compiler &compiler;
bool need_dummy_sampler = false;
};
struct ActiveBuiltinHandler : OpcodeHandler struct ActiveBuiltinHandler : OpcodeHandler
{ {
ActiveBuiltinHandler(Compiler &compiler_) ActiveBuiltinHandler(Compiler &compiler_)
@ -725,6 +751,11 @@ protected:
std::unordered_set<uint32_t> comparison_images; std::unordered_set<uint32_t> comparison_images;
bool need_subpass_input = false; bool need_subpass_input = false;
// In certain backends, we will need to use a dummy sampler to be able to emit code.
// GLSL does not support texelFetch on texture2D objects, but SPIR-V does,
// so we need to workaround by having the application inject a dummy sampler.
uint32_t dummy_sampler_id = 0;
void analyze_image_and_sampler_usage(); void analyze_image_and_sampler_usage();
struct CombinedImageSamplerUsageHandler : OpcodeHandler struct CombinedImageSamplerUsageHandler : OpcodeHandler
{ {