Begin implementing texelFetch(texture2D) workaround on GLSL.
This commit is contained in:
parent
d871a89886
commit
4db7061dd1
1
main.cpp
1
main.cpp
@ -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())
|
||||||
|
141
spirv_cross.cpp
141
spirv_cross.cpp
@ -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)
|
||||||
|
@ -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
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user