Emit flattened loads and stores.

This commit is contained in:
Hans-Kristian Arntzen 2017-08-10 17:12:48 +02:00
parent 3cbdbec712
commit 7d7f4b3b50
5 changed files with 199 additions and 53 deletions

View File

@ -637,6 +637,7 @@ struct SPIRAccessChain : IVariant
uint32_t loaded_from = 0;
bool need_transpose = false;
bool immutable = false;
};
struct SPIRVariable : IVariant

View File

@ -249,6 +249,10 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
auto *cexpr = maybe_get<SPIRExpression>(chain);
if (cexpr)
var = maybe_get<SPIRVariable>(cexpr->loaded_from);
auto *access_chain = maybe_get<SPIRAccessChain>(chain);
if (access_chain)
var = maybe_get<SPIRVariable>(access_chain->loaded_from);
}
return var;
@ -283,6 +287,10 @@ void Compiler::register_write(uint32_t chain)
auto *expr = maybe_get<SPIRExpression>(chain);
if (expr && expr->loaded_from)
var = maybe_get<SPIRVariable>(expr->loaded_from);
auto *access_chain = maybe_get<SPIRAccessChain>(chain);
if (access_chain && access_chain->loaded_from)
var = maybe_get<SPIRVariable>(access_chain->loaded_from);
}
if (var)
@ -397,6 +405,8 @@ bool Compiler::is_immutable(uint32_t id) const
bool pointer_to_const = var.storage == StorageClassUniformConstant;
return pointer_to_const || var.phi_variable || !expression_is_lvalue(id);
}
else if (ids[id].get_type() == TypeAccessChain)
return get<SPIRAccessChain>(id).immutable;
else if (ids[id].get_type() == TypeExpression)
return get<SPIRExpression>(id).immutable;
else if (ids[id].get_type() == TypeConstant || ids[id].get_type() == TypeConstantOp ||

View File

@ -1979,6 +1979,10 @@ string CompilerGLSL::to_expression(uint32_t id)
// expression ala sampler2D(texture, sampler).
SPIRV_CROSS_THROW("Combined image samplers have no default expression representation.");
case TypeAccessChain:
// We cannot express this type. They only have meaning in other OpAccessChains, OpStore or OpLoad.
SPIRV_CROSS_THROW("Access chains have no default expression representation.");
default:
return to_name(id);
}

View File

@ -1798,11 +1798,180 @@ void CompilerHLSL::emit_glsl_op(uint32_t result_type, uint32_t id, uint32_t eop,
}
}
void CompilerHLSL::emit_load(const Instruction &instruction)
{
auto ops = stream(instruction);
auto *chain = maybe_get<SPIRAccessChain>(ops[2]);
if (chain)
{
auto &type = get<SPIRType>(chain->basetype);
uint32_t result_type = ops[0];
uint32_t id = ops[1];
uint32_t ptr = ops[2];
SPIRType target_type;
target_type.basetype = SPIRType::UInt;
target_type.vecsize = type.vecsize;
target_type.columns = type.columns;
if (type.columns != 1)
SPIRV_CROSS_THROW("Reading matrices from ByteAddressBuffer not yet supported.");
if (type.basetype == SPIRType::Struct)
SPIRV_CROSS_THROW("Reading structs from ByteAddressBuffer not yet supported.");
if (type.width != 32)
SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported.");
const char *load_op = nullptr;
switch (type.vecsize)
{
case 1:
load_op = "Load";
break;
case 2:
load_op = "Load2";
break;
case 3:
load_op = "Load3";
break;
case 4:
load_op = "Load4";
break;
default:
SPIRV_CROSS_THROW("Unknown vector size.");
}
auto load_expr = join(chain->base, ".", load_op, "(", chain->dynamic_index, chain->static_index, ")");
auto bitcast_op = bitcast_glsl_op(type, target_type);
if (!bitcast_op.empty())
load_expr = join(bitcast_op, "(", load_expr, ")");
bool forward = should_forward(ptr) && forced_temporaries.find(id) == end(forced_temporaries);
auto &e = emit_op(result_type, id, load_expr, forward, true);
e.need_transpose = false; // TODO: Forward this somehow.
register_read(id, ptr, forward);
}
else
CompilerGLSL::emit_instruction(instruction);
}
void CompilerHLSL::emit_store(const Instruction &instruction)
{
auto ops = stream(instruction);
auto *chain = maybe_get<SPIRAccessChain>(ops[0]);
if (chain)
{
auto &type = expression_type(ops[0]);
SPIRType target_type;
target_type.basetype = SPIRType::UInt;
target_type.vecsize = type.vecsize;
target_type.columns = type.columns;
const char *store_op = nullptr;
switch (type.vecsize)
{
case 1:
store_op = "Store";
break;
case 2:
store_op = "Store2";
break;
case 3:
store_op = "Store3";
break;
case 4:
store_op = "Store4";
break;
default:
SPIRV_CROSS_THROW("Unknown vector size.");
}
if (type.columns != 1)
SPIRV_CROSS_THROW("Writing matrices to RWByteAddressBuffer not yet supported.");
if (type.basetype == SPIRType::Struct)
SPIRV_CROSS_THROW("Writing structs to RWByteAddressBuffer not yet supported.");
if (type.width != 32)
SPIRV_CROSS_THROW("Writing types other than 32-bit to RWByteAddressBuffer not yet supported.");
auto store_expr = to_expression(ops[1]);
auto bitcast_op = bitcast_glsl_op(target_type, type);
if (!bitcast_op.empty())
store_expr = join(bitcast_op, "(", store_expr, ")");
statement(chain->base, ".", store_op, "(", chain->dynamic_index, chain->static_index, ", ", store_expr, ");");
register_write(ops[0]);
}
else
CompilerGLSL::emit_instruction(instruction);
}
void CompilerHLSL::emit_access_chain(const Instruction &instruction)
{
auto ops = stream(instruction);
uint32_t length = instruction.length;
bool need_byte_access_chain = false;
auto &type = expression_type(ops[2]);
const SPIRAccessChain *chain = nullptr;
if (has_decoration(type.self, DecorationBufferBlock))
{
// If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need
// to emit SPIRAccessChain rather than a plain SPIRExpression.
uint32_t chain_arguments = length - 3;
if (chain_arguments > type.array.size())
need_byte_access_chain = true;
}
else
{
// Keep tacking on an existing access chain.
chain = maybe_get<SPIRAccessChain>(ops[2]);
if (chain)
need_byte_access_chain = true;
}
if (need_byte_access_chain)
{
uint32_t to_plain_buffer_length = type.array.size();
string base;
if (to_plain_buffer_length != 0)
{
bool need_transpose;
base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get<SPIRType>(ops[0]), &need_transpose);
}
else
base = to_expression(ops[2]);
auto *basetype = &type;
for (uint32_t i = 0; i < to_plain_buffer_length; i++)
basetype = &get<SPIRType>(type.parent_type);
uint32_t matrix_stride = 0;
bool need_transpose = false;
auto offsets = flattened_access_chain_offset(*basetype,
&ops[3 + to_plain_buffer_length], length - 3 - to_plain_buffer_length,
0, 1, &need_transpose, &matrix_stride);
auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
if (chain)
{
e.dynamic_index += chain->dynamic_index;
e.static_index += chain->static_index;
}
e.immutable = should_forward(ops[2]);
}
else
{
CompilerGLSL::emit_instruction(instruction);
}
}
void CompilerHLSL::emit_instruction(const Instruction &instruction)
{
auto ops = stream(instruction);
auto opcode = static_cast<Op>(instruction.op);
uint32_t length = instruction.length;
#define BOP(op) emit_binary_op(ops[0], ops[1], ops[2], ops[3], #op)
#define BOP_CAST(op, type) \
@ -1821,60 +1990,19 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
case OpAccessChain:
case OpInBoundsAccessChain:
{
bool need_byte_access_chain = false;
auto &type = expression_type(ops[2]);
const SPIRAccessChain *chain = nullptr;
if (has_decoration(type.self, DecorationBufferBlock))
{
// If we are starting to poke into an SSBO, we are dealing with ByteAddressBuffers, and we need
// to emit SPIRAccessChain rather than a plain SPIRExpression.
uint32_t chain_arguments = length - 3;
if (chain_arguments > type.array.size())
need_byte_access_chain = true;
}
else
{
// Keep tacking on an existing access chain.
chain = maybe_get<SPIRAccessChain>(ops[2]);
if (chain)
need_byte_access_chain = true;
}
emit_access_chain(instruction);
break;
}
if (need_byte_access_chain)
{
uint32_t to_plain_buffer_length = type.array.size();
case OpStore:
{
emit_store(instruction);
break;
}
string base;
if (to_plain_buffer_length != 0)
{
bool need_transpose;
base = access_chain(ops[2], &ops[3], to_plain_buffer_length, get<SPIRType>(ops[0]), &need_transpose);
}
else
base = to_expression(ops[2]);
auto *basetype = &type;
for (uint32_t i = 0; i < to_plain_buffer_length; i++)
basetype = &get<SPIRType>(type.parent_type);
uint32_t matrix_stride = 0;
bool need_transpose = false;
auto offsets = flattened_access_chain_offset(*basetype,
&ops[3 + to_plain_buffer_length], length - 3 - to_plain_buffer_length,
0, 1, &need_transpose, &matrix_stride);
auto &e = set<SPIRAccessChain>(ops[1], ops[0], type.storage, base, offsets.first, offsets.second);
if (chain)
{
e.dynamic_index += chain->dynamic_index;
e.static_index += chain->static_index;
}
}
else
{
CompilerGLSL::emit_instruction(instruction);
}
case OpLoad:
{
emit_load(instruction);
break;
}

View File

@ -88,6 +88,9 @@ private:
std::string to_resource_binding(const SPIRVariable &var);
std::string to_resource_binding_sampler(const SPIRVariable &var);
void emit_sampled_image_op(uint32_t result_type, uint32_t result_id, uint32_t image_id, uint32_t samp_id) override;
void emit_access_chain(const Instruction &instruction);
void emit_load(const Instruction &instruction);
void emit_store(const Instruction &instruction);
const char *to_storage_qualifiers_glsl(const SPIRVariable &var) override;