From 71bacc4469307178c7241212321a731ca6543c1f Mon Sep 17 00:00:00 2001 From: Hans-Kristian Arntzen Date: Sat, 10 Sep 2016 17:48:52 +0200 Subject: [PATCH] Basic combined image sampler emulation hooked up. --- spirv_cross.cpp | 102 ++++++++++++++++++++++++++++++++++++++++++++++++ spirv_cross.hpp | 11 ++++++ spirv_glsl.cpp | 28 ++++++++++++- 3 files changed, 140 insertions(+), 1 deletion(-) diff --git a/spirv_cross.cpp b/spirv_cross.cpp index 8707f9d..f817b7b 100644 --- a/spirv_cross.cpp +++ b/spirv_cross.cpp @@ -2309,6 +2309,108 @@ bool Compiler::interface_variable_exists_in_entry_point(uint32_t id) const end(execution.interface_variables); } +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. + switch (opcode) + { + case OpLoad: + { + if (length < 3) + return false; + + uint32_t result_type = args[0]; + + auto &type = compiler.get(result_type); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + + // If not separate texture or sampler, don't bother. + if (!separate_image && !separate_sampler) + return true; + + uint32_t id = args[1]; + uint32_t ptr = args[2]; + compiler.set(id, "", result_type, true); + compiler.register_read(id, ptr, true); + return true; + } + + case OpInBoundsAccessChain: + case OpAccessChain: + { + if (length < 3) + return false; + + // Technically, it is possible to have arrays of textures and arrays of samplers and combine them, but this becomes essentially + // impossible to implement, since we don't know which concrete sampler we are accessing. + // One potential way is to create a combinatorial explosion where N textures and M samplers are combined into N * M sampler2Ds, + // but this seems ridiculously complicated for a problem which is easy to work around. + // Checking access chains like this assumes we don't have samplers or textures inside uniform structs, but this makes no sense. + + auto &type = compiler.get(args[0]); + bool separate_image = type.basetype == SPIRType::Image && type.image.sampled == 1; + bool separate_sampler = type.basetype == SPIRType::Sampler; + if (separate_image) + throw CompilerError( + "Attempting to use arrays of separate images. This is not possible to statically remap to plain GLSL."); + if (separate_sampler) + throw CompilerError("Attempting to use arrays of separate samplers. This is not possible to statically " + "remap to plain GLSL."); + return true; + } + + case OpSampledImage: + // Do it outside. + break; + + default: + return true; + } + + if (length < 4) + return false; + + auto *image = compiler.maybe_get_backing_variable(args[2]); + auto *sampler = compiler.maybe_get_backing_variable(args[3]); + auto image_id = image ? image->self : args[2]; + auto sampler_id = sampler ? sampler->self : args[3]; + + // FIXME: For function calls, we need to remap IDs which are function parameters into global variables. + // This information is statically known from the current place in the call stack. + // For now, we only look directly at global variables. + // 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. + auto itr = find_if(begin(compiler.combined_image_samplers), end(compiler.combined_image_samplers), + [image_id, sampler_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == sampler_id; + }); + + if (itr == end(compiler.combined_image_samplers)) + { + auto id = compiler.increase_bound_by(2); + auto type_id = id + 0; + 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. + // We will probably have this type lying around, but it doesn't hurt to make duplicates for internal purposes. + auto &type = compiler.set(type_id); + auto &base = compiler.get(sampled_type); + type = base; + type.pointer = true; + type.storage = StorageClassUniformConstant; + + // Build new variable. + compiler.set(combined_id, sampled_type, StorageClassUniformConstant, 0); + compiler.combined_image_samplers.push_back({ combined_id, image_id, sampler_id }); + } + return true; +} + void Compiler::build_combined_image_samplers() { + combined_image_samplers.clear(); + CombinedImageSamplerHandler handler(*this); + traverse_all_reachable_opcodes(get(entry_point), handler); } diff --git a/spirv_cross.hpp b/spirv_cross.hpp index 25c92b4..9960f83 100644 --- a/spirv_cross.hpp +++ b/spirv_cross.hpp @@ -477,6 +477,17 @@ private: std::unordered_set &variables; }; + struct CombinedImageSamplerHandler : OpcodeHandler + { + CombinedImageSamplerHandler(Compiler &compiler_) + : compiler(compiler_) + { + } + bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) override; + + Compiler &compiler; + }; + bool traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const; bool traverse_all_reachable_opcodes(const SPIRFunction &block, OpcodeHandler &handler) const; // This must be an ordered data structure so we always pick the same type aliases. diff --git a/spirv_glsl.cpp b/spirv_glsl.cpp index ac55cff..ff094d3 100644 --- a/spirv_glsl.cpp +++ b/spirv_glsl.cpp @@ -1866,7 +1866,33 @@ void CompilerGLSL::emit_mix_op(uint32_t result_type, uint32_t id, uint32_t left, void CompilerGLSL::emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) { - emit_binary_func_op(result_type, result_id, image_id, samp_id, type_to_glsl(get(result_type)).c_str()); + if (options.vulkan_semantics) + emit_binary_func_op(result_type, result_id, image_id, samp_id, + type_to_glsl(get(result_type)).c_str()); + else + { + // For GLSL and ESSL targets, we must enumerate all possible combinations for sampler2D(texture2D, sampler) and redirect + // all possible combinations into new sampler2D uniforms. + auto *image = maybe_get_backing_variable(image_id); + auto *samp = maybe_get_backing_variable(samp_id); + if (image) + image_id = image->self; + if (samp) + samp_id = samp->self; + + // FIXME: This must be context-dependent. + auto &mapping = combined_image_samplers; + + auto itr = find_if(begin(mapping), end(mapping), [image_id, samp_id](const CombinedImageSampler &combined) { + return combined.image_id == image_id && combined.sampler_id == samp_id; + }); + + if (itr != end(combined_image_samplers)) + emit_op(result_type, result_id, to_expression(itr->combined_id), true, false); + else + throw CompilerError("Cannot find mapping for combined sampler, was build_combined_image_samplers() used " + "before compile() was called?"); + } } void CompilerGLSL::emit_texture_op(const Instruction &i)