mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Update spirv-cross
This commit is contained in:
@@ -1,27 +1,10 @@
|
||||
/*
|
||||
** Copyright (c) 2014-2016 The Khronos Group Inc.
|
||||
**
|
||||
** Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
** of this software and/or associated documentation files (the "Materials"),
|
||||
** to deal in the Materials without restriction, including without limitation
|
||||
** the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
** and/or sell copies of the Materials, and to permit persons to whom the
|
||||
** Materials are furnished to do so, subject to the following conditions:
|
||||
**
|
||||
** The above copyright notice and this permission notice shall be included in
|
||||
** all copies or substantial portions of the Materials.
|
||||
**
|
||||
** MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
||||
** STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
||||
** HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
||||
**
|
||||
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||
** OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
** FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
** THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
** LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
** FROM,OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE USE OR OTHER DEALINGS
|
||||
** IN THE MATERIALS.
|
||||
* Copyright 2014-2016,2021 The Khronos Group, Inc.
|
||||
* SPDX-License-Identifier: MIT
|
||||
*
|
||||
* MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS KHRONOS
|
||||
* STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS SPECIFICATIONS AND
|
||||
* HEADER INFORMATION ARE LOCATED AT https://www.khronos.org/registry/
|
||||
*/
|
||||
|
||||
#ifndef GLSLstd450_H
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2017 ARM Limited
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cfg.hpp"
|
||||
@@ -135,7 +135,9 @@ bool CFG::post_order_visit(uint32_t block_id)
|
||||
break;
|
||||
|
||||
case SPIRBlock::MultiSelect:
|
||||
for (auto &target : block.cases)
|
||||
{
|
||||
const auto &cases = compiler.get_case_list(block);
|
||||
for (const auto &target : cases)
|
||||
{
|
||||
if (post_order_visit(target.block))
|
||||
add_branch(block_id, target.block);
|
||||
@@ -143,7 +145,7 @@ bool CFG::post_order_visit(uint32_t block_id)
|
||||
if (block.default_block && post_order_visit(block.default_block))
|
||||
add_branch(block_id, block.default_block);
|
||||
break;
|
||||
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -385,7 +387,9 @@ void DominatorBuilder::lift_continue_block_dominator()
|
||||
break;
|
||||
|
||||
case SPIRBlock::MultiSelect:
|
||||
for (auto &target : block.cases)
|
||||
{
|
||||
auto &cases = cfg.get_compiler().get_case_list(block);
|
||||
for (auto &target : cases)
|
||||
{
|
||||
if (cfg.get_visit_order(target.block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
@@ -393,6 +397,7 @@ void DominatorBuilder::lift_continue_block_dominator()
|
||||
if (block.default_block && cfg.get_visit_order(block.default_block) > post_order)
|
||||
back_edge_dominator = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_CFG_HPP
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_COMMON_HPP
|
||||
@@ -211,6 +211,28 @@ inline std::string convert_to_string(const T &t)
|
||||
return std::to_string(t);
|
||||
}
|
||||
|
||||
static inline std::string convert_to_string(int32_t value)
|
||||
{
|
||||
// INT_MIN is ... special on some backends. If we use a decimal literal, and negate it, we
|
||||
// could accidentally promote the literal to long first, then negate.
|
||||
// To workaround it, emit int(0x80000000) instead.
|
||||
if (value == std::numeric_limits<int32_t>::min())
|
||||
return "int(0x80000000)";
|
||||
else
|
||||
return std::to_string(value);
|
||||
}
|
||||
|
||||
static inline std::string convert_to_string(int64_t value, const std::string &int64_type, bool long_long_literal_suffix)
|
||||
{
|
||||
// INT64_MIN is ... special on some backends.
|
||||
// If we use a decimal literal, and negate it, we might overflow the representable numbers.
|
||||
// To workaround it, emit int(0x80000000) instead.
|
||||
if (value == std::numeric_limits<int64_t>::min())
|
||||
return join(int64_type, "(0x8000000000000000u", (long_long_literal_suffix ? "ll" : "l"), ")");
|
||||
else
|
||||
return std::to_string(value) + (long_long_literal_suffix ? "ll" : "l");
|
||||
}
|
||||
|
||||
// Allow implementations to set a convenient standard precision
|
||||
#ifndef SPIRV_CROSS_FLT_FMT
|
||||
#define SPIRV_CROSS_FLT_FMT "%.32g"
|
||||
@@ -417,6 +439,11 @@ struct IVariant
|
||||
virtual ~IVariant() = default;
|
||||
virtual IVariant *clone(ObjectPoolBase *pool) = 0;
|
||||
ID self = 0;
|
||||
|
||||
protected:
|
||||
IVariant() = default;
|
||||
IVariant(const IVariant&) = default;
|
||||
IVariant &operator=(const IVariant&) = default;
|
||||
};
|
||||
|
||||
#define SPIRV_CROSS_DECLARE_CLONE(T) \
|
||||
@@ -827,10 +854,11 @@ struct SPIRBlock : IVariant
|
||||
|
||||
struct Case
|
||||
{
|
||||
uint32_t value;
|
||||
uint64_t value;
|
||||
BlockID block;
|
||||
};
|
||||
SmallVector<Case> cases;
|
||||
SmallVector<Case> cases_32bit;
|
||||
SmallVector<Case> cases_64bit;
|
||||
|
||||
// If we have tried to optimize code for this block but failed,
|
||||
// keep track of this.
|
||||
@@ -1372,7 +1400,7 @@ public:
|
||||
~Variant()
|
||||
{
|
||||
if (holder)
|
||||
group->pools[type]->free_opaque(holder);
|
||||
group->pools[type]->deallocate_opaque(holder);
|
||||
}
|
||||
|
||||
// Marking custom move constructor as noexcept is important.
|
||||
@@ -1391,7 +1419,7 @@ public:
|
||||
if (this != &other)
|
||||
{
|
||||
if (holder)
|
||||
group->pools[type]->free_opaque(holder);
|
||||
group->pools[type]->deallocate_opaque(holder);
|
||||
holder = other.holder;
|
||||
group = other.group;
|
||||
type = other.type;
|
||||
@@ -1415,7 +1443,7 @@ public:
|
||||
if (this != &other)
|
||||
{
|
||||
if (holder)
|
||||
group->pools[type]->free_opaque(holder);
|
||||
group->pools[type]->deallocate_opaque(holder);
|
||||
|
||||
if (other.holder)
|
||||
holder = other.holder->clone(group->pools[other.type].get());
|
||||
@@ -1431,13 +1459,13 @@ public:
|
||||
void set(IVariant *val, Types new_type)
|
||||
{
|
||||
if (holder)
|
||||
group->pools[type]->free_opaque(holder);
|
||||
group->pools[type]->deallocate_opaque(holder);
|
||||
holder = nullptr;
|
||||
|
||||
if (!allow_type_rewrite && type != TypeNone && type != new_type)
|
||||
{
|
||||
if (val)
|
||||
group->pools[new_type]->free_opaque(val);
|
||||
group->pools[new_type]->deallocate_opaque(val);
|
||||
SPIRV_CROSS_THROW("Overwriting a variant with new type.");
|
||||
}
|
||||
|
||||
@@ -1492,7 +1520,7 @@ public:
|
||||
void reset()
|
||||
{
|
||||
if (holder)
|
||||
group->pools[type]->free_opaque(holder);
|
||||
group->pools[type]->deallocate_opaque(holder);
|
||||
holder = nullptr;
|
||||
type = TypeNone;
|
||||
}
|
||||
@@ -1763,6 +1791,22 @@ struct SetBindingPair
|
||||
}
|
||||
};
|
||||
|
||||
struct LocationComponentPair
|
||||
{
|
||||
uint32_t location;
|
||||
uint32_t component;
|
||||
|
||||
inline bool operator==(const LocationComponentPair &other) const
|
||||
{
|
||||
return location == other.location && component == other.component;
|
||||
}
|
||||
|
||||
inline bool operator<(const LocationComponentPair &other) const
|
||||
{
|
||||
return location < other.location || (location == other.location && component < other.component);
|
||||
}
|
||||
};
|
||||
|
||||
struct StageSetBinding
|
||||
{
|
||||
spv::ExecutionModel model;
|
||||
@@ -1785,6 +1829,14 @@ struct InternalHasher
|
||||
return (hash_set * 0x10001b31) ^ hash_binding;
|
||||
}
|
||||
|
||||
inline size_t operator()(const LocationComponentPair &value) const
|
||||
{
|
||||
// Quality of hash doesn't really matter here.
|
||||
auto hash_set = std::hash<uint32_t>()(value.location);
|
||||
auto hash_binding = std::hash<uint32_t>()(value.component);
|
||||
return (hash_set * 0x10001b31) ^ hash_binding;
|
||||
}
|
||||
|
||||
inline size_t operator()(const StageSetBinding &value) const
|
||||
{
|
||||
// Quality of hash doesn't really matter here.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cpp.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_CPP_HPP
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cross.hpp"
|
||||
@@ -167,6 +167,12 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
|
||||
case OpTraceRayKHR:
|
||||
case OpExecuteCallableNV:
|
||||
case OpExecuteCallableKHR:
|
||||
case OpRayQueryInitializeKHR:
|
||||
case OpRayQueryTerminateKHR:
|
||||
case OpRayQueryGenerateIntersectionKHR:
|
||||
case OpRayQueryConfirmIntersectionKHR:
|
||||
case OpRayQueryProceedKHR:
|
||||
// There are various getters in ray query, but they are considered pure.
|
||||
return false;
|
||||
|
||||
// OpExtInst is potentially impure depending on extension, but GLSL builtins are at least pure.
|
||||
@@ -175,6 +181,30 @@ bool Compiler::block_is_pure(const SPIRBlock &block)
|
||||
// This is a global side effect of the function.
|
||||
return false;
|
||||
|
||||
case OpExtInst:
|
||||
{
|
||||
uint32_t extension_set = ops[2];
|
||||
if (get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
|
||||
{
|
||||
auto op_450 = static_cast<GLSLstd450>(ops[3]);
|
||||
switch (op_450)
|
||||
{
|
||||
case GLSLstd450Modf:
|
||||
case GLSLstd450Frexp:
|
||||
{
|
||||
auto &type = expression_type(ops[5]);
|
||||
if (type.storage != StorageClassFunction)
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -284,31 +314,6 @@ SPIRVariable *Compiler::maybe_get_backing_variable(uint32_t chain)
|
||||
return var;
|
||||
}
|
||||
|
||||
StorageClass Compiler::get_expression_effective_storage_class(uint32_t ptr)
|
||||
{
|
||||
auto *var = maybe_get_backing_variable(ptr);
|
||||
|
||||
// If the expression has been lowered to a temporary, we need to use the Generic storage class.
|
||||
// We're looking for the effective storage class of a given expression.
|
||||
// An access chain or forwarded OpLoads from such access chains
|
||||
// will generally have the storage class of the underlying variable, but if the load was not forwarded
|
||||
// we have lost any address space qualifiers.
|
||||
bool forced_temporary = ir.ids[ptr].get_type() == TypeExpression && !get<SPIRExpression>(ptr).access_chain &&
|
||||
(forced_temporaries.count(ptr) != 0 || forwarded_temporaries.count(ptr) == 0);
|
||||
|
||||
if (var && !forced_temporary)
|
||||
{
|
||||
// Normalize SSBOs to StorageBuffer here.
|
||||
if (var->storage == StorageClassUniform &&
|
||||
has_decoration(get<SPIRType>(var->basetype).self, DecorationBufferBlock))
|
||||
return StorageClassStorageBuffer;
|
||||
else
|
||||
return var->storage;
|
||||
}
|
||||
else
|
||||
return expression_type(ptr).storage;
|
||||
}
|
||||
|
||||
void Compiler::register_read(uint32_t expr, uint32_t chain, bool forwarded)
|
||||
{
|
||||
auto &e = get<SPIRExpression>(expr);
|
||||
@@ -735,6 +740,15 @@ bool Compiler::InterfaceVariableAccessHandler::handle(Op opcode, const uint32_t
|
||||
break;
|
||||
}
|
||||
|
||||
case GLSLstd450Modf:
|
||||
case GLSLstd450Fract:
|
||||
{
|
||||
auto *var = compiler.maybe_get<SPIRVariable>(args[5]);
|
||||
if (var && storage_class_is_interface(var->storage))
|
||||
variables.insert(args[5]);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -853,19 +867,79 @@ ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> *
|
||||
|
||||
// It is possible for uniform storage classes to be passed as function parameters, so detect
|
||||
// that. To detect function parameters, check of StorageClass of variable is function scope.
|
||||
if (var.storage == StorageClassFunction || !type.pointer || is_builtin_variable(var))
|
||||
if (var.storage == StorageClassFunction || !type.pointer)
|
||||
return;
|
||||
|
||||
if (active_variables && active_variables->find(var.self) == end(*active_variables))
|
||||
return;
|
||||
|
||||
// In SPIR-V 1.4 and up, every global must be present in the entry point interface list,
|
||||
// not just IO variables.
|
||||
bool active_in_entry_point = true;
|
||||
if (ir.get_spirv_version() < 0x10400)
|
||||
{
|
||||
if (var.storage == StorageClassInput || var.storage == StorageClassOutput)
|
||||
active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
|
||||
}
|
||||
else
|
||||
active_in_entry_point = interface_variable_exists_in_entry_point(var.self);
|
||||
|
||||
if (!active_in_entry_point)
|
||||
return;
|
||||
|
||||
bool is_builtin = is_builtin_variable(var);
|
||||
|
||||
if (is_builtin)
|
||||
{
|
||||
if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
auto &list = var.storage == StorageClassInput ? res.builtin_inputs : res.builtin_outputs;
|
||||
BuiltInResource resource;
|
||||
|
||||
if (has_decoration(type.self, DecorationBlock))
|
||||
{
|
||||
resource.resource = { var.self, var.basetype, type.self,
|
||||
get_remapped_declared_block_name(var.self, false) };
|
||||
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
{
|
||||
resource.value_type_id = type.member_types[i];
|
||||
resource.builtin = BuiltIn(get_member_decoration(type.self, i, DecorationBuiltIn));
|
||||
list.push_back(resource);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bool strip_array =
|
||||
!has_decoration(var.self, DecorationPatch) && (
|
||||
get_execution_model() == ExecutionModelTessellationControl ||
|
||||
(get_execution_model() == ExecutionModelTessellationEvaluation &&
|
||||
var.storage == StorageClassInput));
|
||||
|
||||
resource.resource = { var.self, var.basetype, type.self, get_name(var.self) };
|
||||
|
||||
if (strip_array && !type.array.empty())
|
||||
resource.value_type_id = get_variable_data_type(var).parent_type;
|
||||
else
|
||||
resource.value_type_id = get_variable_data_type_id(var);
|
||||
|
||||
assert(resource.value_type_id);
|
||||
|
||||
resource.builtin = BuiltIn(get_decoration(var.self, DecorationBuiltIn));
|
||||
list.push_back(std::move(resource));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Input
|
||||
if (var.storage == StorageClassInput && interface_variable_exists_in_entry_point(var.self))
|
||||
if (var.storage == StorageClassInput)
|
||||
{
|
||||
if (has_decoration(type.self, DecorationBlock))
|
||||
{
|
||||
res.stage_inputs.push_back(
|
||||
{ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
|
||||
{ var.self, var.basetype, type.self,
|
||||
get_remapped_declared_block_name(var.self, false) });
|
||||
}
|
||||
else
|
||||
res.stage_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
|
||||
@@ -876,12 +950,12 @@ ShaderResources Compiler::get_shader_resources(const unordered_set<VariableID> *
|
||||
res.subpass_inputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
|
||||
}
|
||||
// Outputs
|
||||
else if (var.storage == StorageClassOutput && interface_variable_exists_in_entry_point(var.self))
|
||||
else if (var.storage == StorageClassOutput)
|
||||
{
|
||||
if (has_decoration(type.self, DecorationBlock))
|
||||
{
|
||||
res.stage_outputs.push_back(
|
||||
{ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
|
||||
{ var.self, var.basetype, type.self, get_remapped_declared_block_name(var.self, false) });
|
||||
}
|
||||
else
|
||||
res.stage_outputs.push_back({ var.self, var.basetype, type.self, get_name(var.self) });
|
||||
@@ -1585,6 +1659,39 @@ SPIRBlock::ContinueBlockType Compiler::continue_block_type(const SPIRBlock &bloc
|
||||
}
|
||||
}
|
||||
|
||||
const SmallVector<SPIRBlock::Case> &Compiler::get_case_list(const SPIRBlock &block) const
|
||||
{
|
||||
uint32_t width = 0;
|
||||
|
||||
// First we check if we can get the type directly from the block.condition
|
||||
// since it can be a SPIRConstant or a SPIRVariable.
|
||||
if (const auto *constant = maybe_get<SPIRConstant>(block.condition))
|
||||
{
|
||||
const auto &type = get<SPIRType>(constant->constant_type);
|
||||
width = type.width;
|
||||
}
|
||||
else if (const auto *var = maybe_get<SPIRVariable>(block.condition))
|
||||
{
|
||||
const auto &type = get<SPIRType>(var->basetype);
|
||||
width = type.width;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto search = ir.load_type_width.find(block.condition);
|
||||
if (search == ir.load_type_width.end())
|
||||
{
|
||||
SPIRV_CROSS_THROW("Use of undeclared variable on a switch statement.");
|
||||
}
|
||||
|
||||
width = search->second;
|
||||
}
|
||||
|
||||
if (width > 32)
|
||||
return block.cases_64bit;
|
||||
|
||||
return block.cases_32bit;
|
||||
}
|
||||
|
||||
bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHandler &handler) const
|
||||
{
|
||||
handler.set_current_block(block);
|
||||
@@ -1618,6 +1725,9 @@ bool Compiler::traverse_all_reachable_opcodes(const SPIRBlock &block, OpcodeHand
|
||||
}
|
||||
}
|
||||
|
||||
if (!handler.handle_terminator(block))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1685,10 +1795,22 @@ size_t Compiler::get_declared_struct_size(const SPIRType &type) const
|
||||
if (type.member_types.empty())
|
||||
SPIRV_CROSS_THROW("Declared struct in block cannot be empty.");
|
||||
|
||||
uint32_t last = uint32_t(type.member_types.size() - 1);
|
||||
size_t offset = type_struct_member_offset(type, last);
|
||||
size_t size = get_declared_struct_member_size(type, last);
|
||||
return offset + size;
|
||||
// Offsets can be declared out of order, so we need to deduce the actual size
|
||||
// based on last member instead.
|
||||
uint32_t member_index = 0;
|
||||
size_t highest_offset = 0;
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
{
|
||||
size_t offset = type_struct_member_offset(type, i);
|
||||
if (offset > highest_offset)
|
||||
{
|
||||
highest_offset = offset;
|
||||
member_index = i;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size = get_declared_struct_member_size(type, member_index);
|
||||
return highest_offset + size;
|
||||
}
|
||||
|
||||
size_t Compiler::get_declared_struct_size_runtime_array(const SPIRType &type, size_t array_size) const
|
||||
@@ -2968,12 +3090,15 @@ void Compiler::AnalyzeVariableScopeAccessHandler::set_current_block(const SPIRBl
|
||||
break;
|
||||
|
||||
case SPIRBlock::MultiSelect:
|
||||
{
|
||||
notify_variable_access(block.condition, block.self);
|
||||
for (auto &target : block.cases)
|
||||
auto &cases = compiler.get_case_list(block);
|
||||
for (auto &target : cases)
|
||||
test_phi(target.block);
|
||||
if (block.default_block)
|
||||
test_phi(block.default_block);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
@@ -3014,6 +3139,27 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::id_is_potential_temporary(uint
|
||||
return compiler.ir.ids[id].empty() || (compiler.ir.ids[id].get_type() == TypeExpression);
|
||||
}
|
||||
|
||||
bool Compiler::AnalyzeVariableScopeAccessHandler::handle_terminator(const SPIRBlock &block)
|
||||
{
|
||||
switch (block.terminator)
|
||||
{
|
||||
case SPIRBlock::Return:
|
||||
if (block.return_value)
|
||||
notify_variable_access(block.return_value, block.self);
|
||||
break;
|
||||
|
||||
case SPIRBlock::Select:
|
||||
case SPIRBlock::MultiSelect:
|
||||
notify_variable_access(block.condition, block.self);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint32_t *args, uint32_t length)
|
||||
{
|
||||
// Keep track of the types of temporaries, so we can hoist them out as necessary.
|
||||
@@ -3185,15 +3331,69 @@ bool Compiler::AnalyzeVariableScopeAccessHandler::handle(spv::Op op, const uint3
|
||||
break;
|
||||
}
|
||||
|
||||
case OpSelect:
|
||||
{
|
||||
// In case of variable pointers, we might access a variable here.
|
||||
// We cannot prove anything about these accesses however.
|
||||
for (uint32_t i = 1; i < length; i++)
|
||||
{
|
||||
if (i >= 3)
|
||||
{
|
||||
auto *var = compiler.maybe_get_backing_variable(args[i]);
|
||||
if (var)
|
||||
{
|
||||
accessed_variables_to_block[var->self].insert(current_block->self);
|
||||
// Assume we can get partial writes to this variable.
|
||||
partial_write_variables_to_block[var->self].insert(current_block->self);
|
||||
}
|
||||
}
|
||||
|
||||
// Might try to copy a Phi variable here.
|
||||
notify_variable_access(args[i], current_block->self);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpExtInst:
|
||||
{
|
||||
for (uint32_t i = 4; i < length; i++)
|
||||
notify_variable_access(args[i], current_block->self);
|
||||
notify_variable_access(args[1], current_block->self);
|
||||
|
||||
uint32_t extension_set = args[2];
|
||||
if (compiler.get<SPIRExtension>(extension_set).ext == SPIRExtension::GLSL)
|
||||
{
|
||||
auto op_450 = static_cast<GLSLstd450>(args[3]);
|
||||
switch (op_450)
|
||||
{
|
||||
case GLSLstd450Modf:
|
||||
case GLSLstd450Frexp:
|
||||
{
|
||||
uint32_t ptr = args[5];
|
||||
auto *var = compiler.maybe_get_backing_variable(ptr);
|
||||
if (var)
|
||||
{
|
||||
accessed_variables_to_block[var->self].insert(current_block->self);
|
||||
if (var->self == ptr)
|
||||
complete_write_variables_to_block[var->self].insert(current_block->self);
|
||||
else
|
||||
partial_write_variables_to_block[var->self].insert(current_block->self);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OpArrayLength:
|
||||
// Only result is a temporary.
|
||||
notify_variable_access(args[1], current_block->self);
|
||||
break;
|
||||
|
||||
case OpLine:
|
||||
case OpNoLine:
|
||||
// Uses literals, but cannot be a phi variable or temporary, so ignore.
|
||||
@@ -4071,7 +4271,7 @@ void Compiler::update_active_builtins()
|
||||
}
|
||||
|
||||
// Returns whether this shader uses a builtin of the storage class
|
||||
bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage)
|
||||
bool Compiler::has_active_builtin(BuiltIn builtin, StorageClass storage) const
|
||||
{
|
||||
const Bitset *flags;
|
||||
switch (storage)
|
||||
@@ -4284,16 +4484,13 @@ bool Compiler::CombinedImageSamplerUsageHandler::handle(Op opcode, const uint32_
|
||||
if (length < 4)
|
||||
return false;
|
||||
|
||||
uint32_t result_type = args[0];
|
||||
uint32_t result_id = args[1];
|
||||
auto &type = compiler.get<SPIRType>(result_type);
|
||||
|
||||
// If the underlying resource has been used for comparison then duplicate loads of that resource must be too.
|
||||
// This image must be a depth image.
|
||||
uint32_t result_id = args[1];
|
||||
uint32_t image = args[2];
|
||||
uint32_t sampler = args[3];
|
||||
|
||||
if (type.image.depth || dref_combined_samplers.count(result_id) != 0)
|
||||
if (dref_combined_samplers.count(result_id) != 0)
|
||||
{
|
||||
add_hierarchy_to_comparison_ids(image);
|
||||
|
||||
@@ -4553,9 +4750,11 @@ bool Compiler::is_desktop_only_format(spv::ImageFormat format)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Compiler::image_is_comparison(const SPIRType &type, uint32_t id) const
|
||||
// An image is determined to be a depth image if it is marked as a depth image and is not also
|
||||
// explicitly marked with a color format, or if there are any sample/gather compare operations on it.
|
||||
bool Compiler::is_depth_image(const SPIRType &type, uint32_t id) const
|
||||
{
|
||||
return type.image.depth || (comparison_ids.count(id) != 0);
|
||||
return (type.image.depth && type.image.format == ImageFormatUnknown) || comparison_ids.count(id);
|
||||
}
|
||||
|
||||
bool Compiler::type_is_opaque_value(const SPIRType &type) const
|
||||
@@ -4585,31 +4784,181 @@ Compiler::PhysicalStorageBufferPointerHandler::PhysicalStorageBufferPointerHandl
|
||||
{
|
||||
}
|
||||
|
||||
bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t)
|
||||
Compiler::PhysicalBlockMeta *Compiler::PhysicalStorageBufferPointerHandler::find_block_meta(uint32_t id) const
|
||||
{
|
||||
if (op == OpConvertUToPtr || op == OpBitcast)
|
||||
auto chain_itr = access_chain_to_physical_block.find(id);
|
||||
if (chain_itr != access_chain_to_physical_block.end())
|
||||
return chain_itr->second;
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Compiler::PhysicalStorageBufferPointerHandler::mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length)
|
||||
{
|
||||
uint32_t mask = *args;
|
||||
args++;
|
||||
length--;
|
||||
if (length && (mask & MemoryAccessVolatileMask) != 0)
|
||||
{
|
||||
auto &type = compiler.get<SPIRType>(args[0]);
|
||||
if (type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer && type.pointer_depth == 1)
|
||||
args++;
|
||||
length--;
|
||||
}
|
||||
|
||||
if (length && (mask & MemoryAccessAlignedMask) != 0)
|
||||
{
|
||||
uint32_t alignment = *args;
|
||||
auto *meta = find_block_meta(id);
|
||||
|
||||
// This makes the assumption that the application does not rely on insane edge cases like:
|
||||
// Bind buffer with ADDR = 8, use block offset of 8 bytes, load/store with 16 byte alignment.
|
||||
// If we emit the buffer with alignment = 16 here, the first element at offset = 0 should
|
||||
// actually have alignment of 8 bytes, but this is too theoretical and awkward to support.
|
||||
// We could potentially keep track of any offset in the access chain, but it's
|
||||
// practically impossible for high level compilers to emit code like that,
|
||||
// so deducing overall alignment requirement based on maximum observed Alignment value is probably fine.
|
||||
if (meta && alignment > meta->alignment)
|
||||
meta->alignment = alignment;
|
||||
}
|
||||
}
|
||||
|
||||
bool Compiler::PhysicalStorageBufferPointerHandler::type_is_bda_block_entry(uint32_t type_id) const
|
||||
{
|
||||
auto &type = compiler.get<SPIRType>(type_id);
|
||||
return type.storage == StorageClassPhysicalStorageBufferEXT && type.pointer &&
|
||||
type.pointer_depth == 1 && !compiler.type_is_array_of_pointers(type);
|
||||
}
|
||||
|
||||
uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_minimum_scalar_alignment(const SPIRType &type) const
|
||||
{
|
||||
if (type.storage == spv::StorageClassPhysicalStorageBufferEXT)
|
||||
return 8;
|
||||
else if (type.basetype == SPIRType::Struct)
|
||||
{
|
||||
uint32_t alignment = 0;
|
||||
for (auto &member_type : type.member_types)
|
||||
{
|
||||
// If we need to cast to a pointer type which is not a block, we might need to synthesize ourselves
|
||||
// a block type which wraps this POD type.
|
||||
if (type.basetype != SPIRType::Struct)
|
||||
types.insert(args[0]);
|
||||
uint32_t member_align = get_minimum_scalar_alignment(compiler.get<SPIRType>(member_type));
|
||||
if (member_align > alignment)
|
||||
alignment = member_align;
|
||||
}
|
||||
return alignment;
|
||||
}
|
||||
else
|
||||
return type.width / 8;
|
||||
}
|
||||
|
||||
void Compiler::PhysicalStorageBufferPointerHandler::setup_meta_chain(uint32_t type_id, uint32_t var_id)
|
||||
{
|
||||
if (type_is_bda_block_entry(type_id))
|
||||
{
|
||||
auto &meta = physical_block_type_meta[type_id];
|
||||
access_chain_to_physical_block[var_id] = &meta;
|
||||
|
||||
auto &type = compiler.get<SPIRType>(type_id);
|
||||
if (type.basetype != SPIRType::Struct)
|
||||
non_block_types.insert(type_id);
|
||||
|
||||
if (meta.alignment == 0)
|
||||
meta.alignment = get_minimum_scalar_alignment(compiler.get_pointee_type(type));
|
||||
}
|
||||
}
|
||||
|
||||
bool Compiler::PhysicalStorageBufferPointerHandler::handle(Op op, const uint32_t *args, uint32_t length)
|
||||
{
|
||||
// When a BDA pointer comes to life, we need to keep a mapping of SSA ID -> type ID for the pointer type.
|
||||
// For every load and store, we'll need to be able to look up the type ID being accessed and mark any alignment
|
||||
// requirements.
|
||||
switch (op)
|
||||
{
|
||||
case OpConvertUToPtr:
|
||||
case OpBitcast:
|
||||
case OpCompositeExtract:
|
||||
// Extract can begin a new chain if we had a struct or array of pointers as input.
|
||||
// We don't begin chains before we have a pure scalar pointer.
|
||||
setup_meta_chain(args[0], args[1]);
|
||||
break;
|
||||
|
||||
case OpAccessChain:
|
||||
case OpInBoundsAccessChain:
|
||||
case OpPtrAccessChain:
|
||||
case OpCopyObject:
|
||||
{
|
||||
auto itr = access_chain_to_physical_block.find(args[2]);
|
||||
if (itr != access_chain_to_physical_block.end())
|
||||
access_chain_to_physical_block[args[1]] = itr->second;
|
||||
break;
|
||||
}
|
||||
|
||||
case OpLoad:
|
||||
{
|
||||
setup_meta_chain(args[0], args[1]);
|
||||
if (length >= 4)
|
||||
mark_aligned_access(args[2], args + 3, length - 3);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
{
|
||||
if (length >= 3)
|
||||
mark_aligned_access(args[0], args + 2, length - 2);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Compiler::PhysicalStorageBufferPointerHandler::get_base_non_block_type_id(uint32_t type_id) const
|
||||
{
|
||||
auto *type = &compiler.get<SPIRType>(type_id);
|
||||
while (type->pointer &&
|
||||
type->storage == StorageClassPhysicalStorageBufferEXT &&
|
||||
!type_is_bda_block_entry(type_id))
|
||||
{
|
||||
type_id = type->parent_type;
|
||||
type = &compiler.get<SPIRType>(type_id);
|
||||
}
|
||||
|
||||
assert(type_is_bda_block_entry(type_id));
|
||||
return type_id;
|
||||
}
|
||||
|
||||
void Compiler::PhysicalStorageBufferPointerHandler::analyze_non_block_types_from_block(const SPIRType &type)
|
||||
{
|
||||
for (auto &member : type.member_types)
|
||||
{
|
||||
auto &subtype = compiler.get<SPIRType>(member);
|
||||
if (subtype.basetype != SPIRType::Struct && subtype.pointer &&
|
||||
subtype.storage == spv::StorageClassPhysicalStorageBufferEXT)
|
||||
{
|
||||
non_block_types.insert(get_base_non_block_type_id(member));
|
||||
}
|
||||
else if (subtype.basetype == SPIRType::Struct && !subtype.pointer)
|
||||
analyze_non_block_types_from_block(subtype);
|
||||
}
|
||||
}
|
||||
|
||||
void Compiler::analyze_non_block_pointer_types()
|
||||
{
|
||||
PhysicalStorageBufferPointerHandler handler(*this);
|
||||
traverse_all_reachable_opcodes(get<SPIRFunction>(ir.default_entry_point), handler);
|
||||
physical_storage_non_block_pointer_types.reserve(handler.types.size());
|
||||
for (auto type : handler.types)
|
||||
|
||||
// Analyze any block declaration we have to make. It might contain
|
||||
// physical pointers to POD types which we never used, and thus never added to the list.
|
||||
// We'll need to add those pointer types to the set of types we declare.
|
||||
ir.for_each_typed_id<SPIRType>([&](uint32_t, SPIRType &type) {
|
||||
if (has_decoration(type.self, DecorationBlock) || has_decoration(type.self, DecorationBufferBlock))
|
||||
handler.analyze_non_block_types_from_block(type);
|
||||
});
|
||||
|
||||
physical_storage_non_block_pointer_types.reserve(handler.non_block_types.size());
|
||||
for (auto type : handler.non_block_types)
|
||||
physical_storage_non_block_pointer_types.push_back(type);
|
||||
sort(begin(physical_storage_non_block_pointer_types), end(physical_storage_non_block_pointer_types));
|
||||
physical_storage_type_to_alignment = move(handler.physical_block_type_meta);
|
||||
}
|
||||
|
||||
bool Compiler::InterlockedResourceAccessPrepassHandler::handle(Op op, const uint32_t *, uint32_t)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_HPP
|
||||
@@ -59,6 +59,27 @@ struct Resource
|
||||
std::string name;
|
||||
};
|
||||
|
||||
struct BuiltInResource
|
||||
{
|
||||
// This is mostly here to support reflection of builtins such as Position/PointSize/CullDistance/ClipDistance.
|
||||
// This needs to be different from Resource since we can collect builtins from blocks.
|
||||
// A builtin present here does not necessarily mean it's considered an active builtin,
|
||||
// since variable ID "activeness" is only tracked on OpVariable level, not Block members.
|
||||
// For that, update_active_builtins() -> has_active_builtin() can be used to further refine the reflection.
|
||||
spv::BuiltIn builtin;
|
||||
|
||||
// This is the actual value type of the builtin.
|
||||
// Typically float4, float, array<float, N> for the gl_PerVertex builtins.
|
||||
// If the builtin is a control point, the control point array type will be stripped away here as appropriate.
|
||||
TypeID value_type_id;
|
||||
|
||||
// This refers to the base resource which contains the builtin.
|
||||
// If resource is a Block, it can hold multiple builtins, or it might not be a block.
|
||||
// For advanced reflection scenarios, all information in builtin/value_type_id can be deduced,
|
||||
// it's just more convenient this way.
|
||||
Resource resource;
|
||||
};
|
||||
|
||||
struct ShaderResources
|
||||
{
|
||||
SmallVector<Resource> uniform_buffers;
|
||||
@@ -79,6 +100,9 @@ struct ShaderResources
|
||||
// these correspond to separate texture2D and samplers respectively.
|
||||
SmallVector<Resource> separate_images;
|
||||
SmallVector<Resource> separate_samplers;
|
||||
|
||||
SmallVector<BuiltInResource> builtin_inputs;
|
||||
SmallVector<BuiltInResource> builtin_outputs;
|
||||
};
|
||||
|
||||
struct CombinedImageSampler
|
||||
@@ -324,7 +348,7 @@ public:
|
||||
|
||||
// Traverses all reachable opcodes and sets active_builtins to a bitmask of all builtin variables which are accessed in the shader.
|
||||
void update_active_builtins();
|
||||
bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage);
|
||||
bool has_active_builtin(spv::BuiltIn builtin, spv::StorageClass storage) const;
|
||||
|
||||
// Query and modify OpExecutionMode.
|
||||
const Bitset &get_execution_mode_bitset() const;
|
||||
@@ -647,7 +671,6 @@ protected:
|
||||
bool expression_is_lvalue(uint32_t id) const;
|
||||
bool variable_storage_is_aliased(const SPIRVariable &var);
|
||||
SPIRVariable *maybe_get_backing_variable(uint32_t chain);
|
||||
spv::StorageClass get_expression_effective_storage_class(uint32_t ptr);
|
||||
|
||||
void register_read(uint32_t expr, uint32_t chain, bool forwarded);
|
||||
void register_write(uint32_t chain);
|
||||
@@ -742,6 +765,10 @@ protected:
|
||||
// Return true if traversal should continue.
|
||||
// If false, traversal will end immediately.
|
||||
virtual bool handle(spv::Op opcode, const uint32_t *args, uint32_t length) = 0;
|
||||
virtual bool handle_terminator(const SPIRBlock &)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool follow_function_call(const SPIRFunction &)
|
||||
{
|
||||
@@ -956,6 +983,7 @@ protected:
|
||||
bool id_is_phi_variable(uint32_t id) const;
|
||||
bool id_is_potential_temporary(uint32_t id) const;
|
||||
bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
|
||||
bool handle_terminator(const SPIRBlock &block) override;
|
||||
|
||||
Compiler &compiler;
|
||||
SPIRFunction &entry;
|
||||
@@ -982,15 +1010,32 @@ protected:
|
||||
uint32_t write_count = 0;
|
||||
};
|
||||
|
||||
struct PhysicalBlockMeta
|
||||
{
|
||||
uint32_t alignment = 0;
|
||||
};
|
||||
|
||||
struct PhysicalStorageBufferPointerHandler : OpcodeHandler
|
||||
{
|
||||
explicit PhysicalStorageBufferPointerHandler(Compiler &compiler_);
|
||||
bool handle(spv::Op op, const uint32_t *args, uint32_t length) override;
|
||||
Compiler &compiler;
|
||||
std::unordered_set<uint32_t> types;
|
||||
|
||||
std::unordered_set<uint32_t> non_block_types;
|
||||
std::unordered_map<uint32_t, PhysicalBlockMeta> physical_block_type_meta;
|
||||
std::unordered_map<uint32_t, PhysicalBlockMeta *> access_chain_to_physical_block;
|
||||
|
||||
void mark_aligned_access(uint32_t id, const uint32_t *args, uint32_t length);
|
||||
PhysicalBlockMeta *find_block_meta(uint32_t id) const;
|
||||
bool type_is_bda_block_entry(uint32_t type_id) const;
|
||||
void setup_meta_chain(uint32_t type_id, uint32_t var_id);
|
||||
uint32_t get_minimum_scalar_alignment(const SPIRType &type) const;
|
||||
void analyze_non_block_types_from_block(const SPIRType &type);
|
||||
uint32_t get_base_non_block_type_id(uint32_t type_id) const;
|
||||
};
|
||||
void analyze_non_block_pointer_types();
|
||||
SmallVector<uint32_t> physical_storage_non_block_pointer_types;
|
||||
std::unordered_map<uint32_t, PhysicalBlockMeta> physical_storage_type_to_alignment;
|
||||
|
||||
void analyze_variable_scope(SPIRFunction &function, AnalyzeVariableScopeAccessHandler &handler);
|
||||
void find_function_local_luts(SPIRFunction &function, const AnalyzeVariableScopeAccessHandler &handler,
|
||||
@@ -1062,7 +1107,7 @@ protected:
|
||||
Bitset combined_decoration_for_member(const SPIRType &type, uint32_t index) const;
|
||||
static bool is_desktop_only_format(spv::ImageFormat format);
|
||||
|
||||
bool image_is_comparison(const SPIRType &type, uint32_t id) const;
|
||||
bool is_depth_image(const SPIRType &type, uint32_t id) const;
|
||||
|
||||
void set_extended_decoration(uint32_t id, ExtendedDecorations decoration, uint32_t value = 0);
|
||||
uint32_t get_extended_decoration(uint32_t id, ExtendedDecorations decoration) const;
|
||||
@@ -1090,6 +1135,11 @@ protected:
|
||||
|
||||
bool is_vertex_like_shader() const;
|
||||
|
||||
// Get the correct case list for the OpSwitch, since it can be either a
|
||||
// 32 bit wide condition or a 64 bit, but the type is not embedded in the
|
||||
// instruction itself.
|
||||
const SmallVector<SPIRBlock::Case> &get_case_list(const SPIRBlock &block) const;
|
||||
|
||||
private:
|
||||
// Used only to implement the old deprecated get_entry_point() interface.
|
||||
const SPIREntryPoint &get_first_entry_point(const std::string &name) const;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Hans-Kristian Arntzen
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cross_c.h"
|
||||
@@ -197,8 +197,11 @@ struct spvc_resources_s : ScratchMemoryAllocation
|
||||
SmallVector<spvc_reflected_resource> separate_images;
|
||||
SmallVector<spvc_reflected_resource> separate_samplers;
|
||||
SmallVector<spvc_reflected_resource> acceleration_structures;
|
||||
SmallVector<spvc_reflected_builtin_resource> builtin_inputs;
|
||||
SmallVector<spvc_reflected_builtin_resource> builtin_outputs;
|
||||
|
||||
bool copy_resources(SmallVector<spvc_reflected_resource> &outputs, const SmallVector<Resource> &inputs);
|
||||
bool copy_resources(SmallVector<spvc_reflected_builtin_resource> &outputs, const SmallVector<BuiltInResource> &inputs);
|
||||
bool copy_resources(const ShaderResources &resources);
|
||||
};
|
||||
|
||||
@@ -469,6 +472,9 @@ spvc_result spvc_compiler_options_set_uint(spvc_compiler_options options, spvc_c
|
||||
case SPVC_COMPILER_OPTION_GLSL_FORCE_FLATTENED_IO_BLOCKS:
|
||||
options->glsl.force_flattened_io_blocks = value != 0;
|
||||
break;
|
||||
case SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT:
|
||||
options->glsl.ovr_multiview_view_count = value;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#if SPIRV_CROSS_C_API_HLSL
|
||||
@@ -818,6 +824,44 @@ spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spv
|
||||
#endif
|
||||
}
|
||||
|
||||
spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler,
|
||||
unsigned location, unsigned component)
|
||||
{
|
||||
#if SPIRV_CROSS_C_API_GLSL
|
||||
if (compiler->backend == SPVC_BACKEND_NONE)
|
||||
{
|
||||
compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
|
||||
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
static_cast<CompilerGLSL *>(compiler->compiler.get())->mask_stage_output_by_location(location, component);
|
||||
return SPVC_SUCCESS;
|
||||
#else
|
||||
(void)location;
|
||||
(void)component;
|
||||
compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
|
||||
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||
#endif
|
||||
}
|
||||
|
||||
spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin)
|
||||
{
|
||||
#if SPIRV_CROSS_C_API_GLSL
|
||||
if (compiler->backend == SPVC_BACKEND_NONE)
|
||||
{
|
||||
compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
|
||||
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
static_cast<CompilerGLSL *>(compiler->compiler.get())->mask_stage_output_by_builtin(spv::BuiltIn(builtin));
|
||||
return SPVC_SUCCESS;
|
||||
#else
|
||||
(void)builtin;
|
||||
compiler->context->report_error("Cross-compilation related option used on NONE backend which only supports reflection.");
|
||||
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||
#endif
|
||||
}
|
||||
|
||||
spvc_result spvc_compiler_hlsl_set_root_constants_layout(spvc_compiler compiler,
|
||||
const spvc_hlsl_root_constants *constant_info,
|
||||
size_t count)
|
||||
@@ -1551,6 +1595,30 @@ bool spvc_resources_s::copy_resources(SmallVector<spvc_reflected_resource> &outp
|
||||
return true;
|
||||
}
|
||||
|
||||
bool spvc_resources_s::copy_resources(SmallVector<spvc_reflected_builtin_resource> &outputs,
|
||||
const SmallVector<BuiltInResource> &inputs)
|
||||
{
|
||||
for (auto &i : inputs)
|
||||
{
|
||||
spvc_reflected_builtin_resource br;
|
||||
|
||||
br.value_type_id = i.value_type_id;
|
||||
br.builtin = SpvBuiltIn(i.builtin);
|
||||
|
||||
auto &r = br.resource;
|
||||
r.base_type_id = i.resource.base_type_id;
|
||||
r.type_id = i.resource.type_id;
|
||||
r.id = i.resource.id;
|
||||
r.name = context->allocate_name(i.resource.name);
|
||||
if (!r.name)
|
||||
return false;
|
||||
|
||||
outputs.push_back(br);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool spvc_resources_s::copy_resources(const ShaderResources &resources)
|
||||
{
|
||||
if (!copy_resources(uniform_buffers, resources.uniform_buffers))
|
||||
@@ -1577,6 +1645,10 @@ bool spvc_resources_s::copy_resources(const ShaderResources &resources)
|
||||
return false;
|
||||
if (!copy_resources(acceleration_structures, resources.acceleration_structures))
|
||||
return false;
|
||||
if (!copy_resources(builtin_inputs, resources.builtin_inputs))
|
||||
return false;
|
||||
if (!copy_resources(builtin_outputs, resources.builtin_outputs))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -1735,6 +1807,37 @@ spvc_result spvc_resources_get_resource_list_for_type(spvc_resources resources,
|
||||
return SPVC_SUCCESS;
|
||||
}
|
||||
|
||||
spvc_result spvc_resources_get_builtin_resource_list_for_type(
|
||||
spvc_resources resources, spvc_builtin_resource_type type,
|
||||
const spvc_reflected_builtin_resource **resource_list,
|
||||
size_t *resource_size)
|
||||
{
|
||||
const SmallVector<spvc_reflected_builtin_resource> *list = nullptr;
|
||||
switch (type)
|
||||
{
|
||||
case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT:
|
||||
list = &resources->builtin_inputs;
|
||||
break;
|
||||
|
||||
case SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT:
|
||||
list = &resources->builtin_outputs;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!list)
|
||||
{
|
||||
resources->context->report_error("Invalid argument.");
|
||||
return SPVC_ERROR_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
*resource_size = list->size();
|
||||
*resource_list = list->data();
|
||||
return SPVC_SUCCESS;
|
||||
}
|
||||
|
||||
void spvc_compiler_set_decoration(spvc_compiler compiler, SpvId id, SpvDecoration decoration, unsigned argument)
|
||||
{
|
||||
compiler->compiler->set_decoration(id, static_cast<spv::Decoration>(decoration), argument);
|
||||
@@ -1937,6 +2040,18 @@ SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler)
|
||||
return static_cast<SpvExecutionModel>(compiler->compiler->get_execution_model());
|
||||
}
|
||||
|
||||
void spvc_compiler_update_active_builtins(spvc_compiler compiler)
|
||||
{
|
||||
compiler->compiler->update_active_builtins();
|
||||
}
|
||||
|
||||
spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage)
|
||||
{
|
||||
return compiler->compiler->has_active_builtin(static_cast<spv::BuiltIn>(builtin), static_cast<spv::StorageClass>(storage)) ?
|
||||
SPVC_TRUE :
|
||||
SPVC_FALSE;
|
||||
}
|
||||
|
||||
spvc_type spvc_compiler_get_type_handle(spvc_compiler compiler, spvc_type_id id)
|
||||
{
|
||||
// Should only throw if an intentionally garbage ID is passed, but the IDs are not type-safe.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Hans-Kristian Arntzen
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_C_API_H
|
||||
@@ -40,7 +40,7 @@ extern "C" {
|
||||
/* Bumped if ABI or API breaks backwards compatibility. */
|
||||
#define SPVC_C_API_VERSION_MAJOR 0
|
||||
/* Bumped if APIs or enumerations are added in a backwards compatible way. */
|
||||
#define SPVC_C_API_VERSION_MINOR 45
|
||||
#define SPVC_C_API_VERSION_MINOR 48
|
||||
/* Bumped if internal implementation details change. */
|
||||
#define SPVC_C_API_VERSION_PATCH 0
|
||||
|
||||
@@ -99,6 +99,13 @@ typedef struct spvc_reflected_resource
|
||||
const char *name;
|
||||
} spvc_reflected_resource;
|
||||
|
||||
typedef struct spvc_reflected_builtin_resource
|
||||
{
|
||||
SpvBuiltIn builtin;
|
||||
spvc_type_id value_type_id;
|
||||
spvc_reflected_resource resource;
|
||||
} spvc_reflected_builtin_resource;
|
||||
|
||||
/* See C++ API. */
|
||||
typedef struct spvc_entry_point
|
||||
{
|
||||
@@ -221,6 +228,14 @@ typedef enum spvc_resource_type
|
||||
SPVC_RESOURCE_TYPE_INT_MAX = 0x7fffffff
|
||||
} spvc_resource_type;
|
||||
|
||||
typedef enum spvc_builtin_resource_type
|
||||
{
|
||||
SPVC_BUILTIN_RESOURCE_TYPE_UNKNOWN = 0,
|
||||
SPVC_BUILTIN_RESOURCE_TYPE_STAGE_INPUT = 1,
|
||||
SPVC_BUILTIN_RESOURCE_TYPE_STAGE_OUTPUT = 2,
|
||||
SPVC_BUILTIN_RESOURCE_TYPE_INT_MAX = 0x7fffffff
|
||||
} spvc_builtin_resource_type;
|
||||
|
||||
/* Maps to spirv_cross::SPIRType::BaseType. */
|
||||
typedef enum spvc_basetype
|
||||
{
|
||||
@@ -660,6 +675,8 @@ typedef enum spvc_compiler_option
|
||||
SPVC_COMPILER_OPTION_MSL_FORCE_SAMPLE_RATE_SHADING = 75 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||
SPVC_COMPILER_OPTION_MSL_IOS_SUPPORT_BASE_VERTEX_INSTANCE = 76 | SPVC_COMPILER_OPTION_MSL_BIT,
|
||||
|
||||
SPVC_COMPILER_OPTION_GLSL_OVR_MULTIVIEW_VIEW_COUNT = 77 | SPVC_COMPILER_OPTION_GLSL_BIT,
|
||||
|
||||
SPVC_COMPILER_OPTION_INT_MAX = 0x7fffffff
|
||||
} spvc_compiler_option;
|
||||
|
||||
@@ -722,6 +739,10 @@ SPVC_PUBLIC_API spvc_result spvc_compiler_flatten_buffer_block(spvc_compiler com
|
||||
|
||||
SPVC_PUBLIC_API spvc_bool spvc_compiler_variable_is_depth_or_compare(spvc_compiler compiler, spvc_variable_id id);
|
||||
|
||||
SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_location(spvc_compiler compiler,
|
||||
unsigned location, unsigned component);
|
||||
SPVC_PUBLIC_API spvc_result spvc_compiler_mask_stage_output_by_builtin(spvc_compiler compiler, SpvBuiltIn builtin);
|
||||
|
||||
/*
|
||||
* HLSL specifics.
|
||||
* Maps to C++ API.
|
||||
@@ -805,6 +826,11 @@ SPVC_PUBLIC_API spvc_result spvc_resources_get_resource_list_for_type(spvc_resou
|
||||
const spvc_reflected_resource **resource_list,
|
||||
size_t *resource_size);
|
||||
|
||||
SPVC_PUBLIC_API spvc_result spvc_resources_get_builtin_resource_list_for_type(
|
||||
spvc_resources resources, spvc_builtin_resource_type type,
|
||||
const spvc_reflected_builtin_resource **resource_list,
|
||||
size_t *resource_size);
|
||||
|
||||
/*
|
||||
* Decorations.
|
||||
* Maps to C++ API.
|
||||
@@ -861,6 +887,8 @@ SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument(spvc_compiler
|
||||
SPVC_PUBLIC_API unsigned spvc_compiler_get_execution_mode_argument_by_index(spvc_compiler compiler,
|
||||
SpvExecutionMode mode, unsigned index);
|
||||
SPVC_PUBLIC_API SpvExecutionModel spvc_compiler_get_execution_model(spvc_compiler compiler);
|
||||
SPVC_PUBLIC_API void spvc_compiler_update_active_builtins(spvc_compiler compiler);
|
||||
SPVC_PUBLIC_API spvc_bool spvc_compiler_has_active_builtin(spvc_compiler compiler, SpvBuiltIn builtin, SpvStorageClass storage);
|
||||
|
||||
/*
|
||||
* Type query interface.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2019-2021 Hans-Kristian Arntzen
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_CONTAINERS_HPP
|
||||
@@ -327,8 +327,8 @@ public:
|
||||
|
||||
void reserve(size_t count) SPIRV_CROSS_NOEXCEPT
|
||||
{
|
||||
if ((count > std::numeric_limits<size_t>::max() / sizeof(T)) ||
|
||||
(count > std::numeric_limits<size_t>::max() / 2))
|
||||
if ((count > (std::numeric_limits<size_t>::max)() / sizeof(T)) ||
|
||||
(count > (std::numeric_limits<size_t>::max)() / 2))
|
||||
{
|
||||
// Only way this should ever happen is with garbage input, terminate.
|
||||
std::terminate();
|
||||
@@ -546,7 +546,7 @@ class ObjectPoolBase
|
||||
{
|
||||
public:
|
||||
virtual ~ObjectPoolBase() = default;
|
||||
virtual void free_opaque(void *ptr) = 0;
|
||||
virtual void deallocate_opaque(void *ptr) = 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
@@ -580,15 +580,15 @@ public:
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void free(T *ptr)
|
||||
void deallocate(T *ptr)
|
||||
{
|
||||
ptr->~T();
|
||||
vacants.push_back(ptr);
|
||||
}
|
||||
|
||||
void free_opaque(void *ptr) override
|
||||
void deallocate_opaque(void *ptr) override
|
||||
{
|
||||
free(static_cast<T *>(ptr));
|
||||
deallocate(static_cast<T *>(ptr));
|
||||
}
|
||||
|
||||
void clear()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_ERROR_HANDLING
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cross_parsed_ir.hpp"
|
||||
@@ -83,6 +83,7 @@ ParsedIR &ParsedIR::operator=(ParsedIR &&other) SPIRV_CROSS_NOEXCEPT
|
||||
loop_iteration_depth_soft = other.loop_iteration_depth_soft;
|
||||
|
||||
meta_needing_name_fixup = std::move(other.meta_needing_name_fixup);
|
||||
load_type_width = std::move(other.load_type_width);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
@@ -115,7 +116,9 @@ ParsedIR &ParsedIR::operator=(const ParsedIR &other)
|
||||
addressing_model = other.addressing_model;
|
||||
memory_model = other.memory_model;
|
||||
|
||||
|
||||
meta_needing_name_fixup = other.meta_needing_name_fixup;
|
||||
load_type_width = other.load_type_width;
|
||||
|
||||
// Very deliberate copying of IDs. There is no default copy constructor, nor a simple default constructor.
|
||||
// Construct object first so we have the correct allocator set-up, then we can copy object into our new pool group.
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_PARSED_IR_HPP
|
||||
@@ -78,6 +78,13 @@ public:
|
||||
SmallVector<ID> ids_for_constant_or_type;
|
||||
SmallVector<ID> ids_for_constant_or_variable;
|
||||
|
||||
// We need to keep track of the width the Ops that contains a type for the
|
||||
// OpSwitch instruction, since this one doesn't contains the type in the
|
||||
// instruction itself. And in some case we need to cast the condition to
|
||||
// wider types. We only need the width to do the branch fixup since the
|
||||
// type check itself can be done at runtime
|
||||
std::unordered_map<ID, uint32_t> load_type_width;
|
||||
|
||||
// Declared capabilities and extensions in the SPIR-V module.
|
||||
// Not really used except for reflection at the moment.
|
||||
SmallVector<spv::Capability> declared_capabilities;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_cross_util.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_UTIL_HPP
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2015-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_GLSL_HPP
|
||||
@@ -65,7 +65,8 @@ enum AccessChainFlagBits
|
||||
ACCESS_CHAIN_PTR_CHAIN_BIT = 1 << 2,
|
||||
ACCESS_CHAIN_SKIP_REGISTER_EXPRESSION_READ_BIT = 1 << 3,
|
||||
ACCESS_CHAIN_LITERAL_MSB_FORCE_ID = 1 << 4,
|
||||
ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5
|
||||
ACCESS_CHAIN_FLATTEN_ALL_MEMBERS_BIT = 1 << 5,
|
||||
ACCESS_CHAIN_FORCE_COMPOSITE_BIT = 1 << 6
|
||||
};
|
||||
typedef uint32_t AccessChainFlags;
|
||||
|
||||
@@ -132,6 +133,9 @@ public:
|
||||
// what happens on legacy GLSL targets for blocks and structs.
|
||||
bool force_flattened_io_blocks = false;
|
||||
|
||||
// If non-zero, controls layout(num_views = N) in; in GL_OVR_multiview2.
|
||||
uint32_t ovr_multiview_view_count = 0;
|
||||
|
||||
enum Precision
|
||||
{
|
||||
DontCare,
|
||||
@@ -177,7 +181,8 @@ public:
|
||||
|
||||
// Redirect a subpassInput reading from input_attachment_index to instead load its value from
|
||||
// the color attachment at location = color_location. Requires ESSL.
|
||||
void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location);
|
||||
// If coherent, uses GL_EXT_shader_framebuffer_fetch, if not, uses noncoherent variant.
|
||||
void remap_ext_framebuffer_fetch(uint32_t input_attachment_index, uint32_t color_location, bool coherent);
|
||||
|
||||
explicit CompilerGLSL(std::vector<uint32_t> spirv_)
|
||||
: Compiler(std::move(spirv_))
|
||||
@@ -250,6 +255,16 @@ public:
|
||||
// - Images which are statically used at least once with Dref opcodes.
|
||||
bool variable_is_depth_or_compare(VariableID id) const;
|
||||
|
||||
// If a shader output is active in this stage, but inactive in a subsequent stage,
|
||||
// this can be signalled here. This can be used to work around certain cross-stage matching problems
|
||||
// which plagues MSL and HLSL in certain scenarios.
|
||||
// An output which matches one of these will not be emitted in stage output interfaces, but rather treated as a private
|
||||
// variable.
|
||||
// This option is only meaningful for MSL and HLSL, since GLSL matches by location directly.
|
||||
// Masking builtins only takes effect if the builtin in question is part of the stage output interface.
|
||||
void mask_stage_output_by_location(uint32_t location, uint32_t component);
|
||||
void mask_stage_output_by_builtin(spv::BuiltIn builtin);
|
||||
|
||||
protected:
|
||||
struct ShaderSubgroupSupportHelper
|
||||
{
|
||||
@@ -371,10 +386,11 @@ protected:
|
||||
virtual void emit_struct_padding_target(const SPIRType &type);
|
||||
virtual std::string image_type_glsl(const SPIRType &type, uint32_t id = 0);
|
||||
std::string constant_expression(const SPIRConstant &c);
|
||||
std::string constant_op_expression(const SPIRConstantOp &cop);
|
||||
virtual std::string constant_op_expression(const SPIRConstantOp &cop);
|
||||
virtual std::string constant_expression_vector(const SPIRConstant &c, uint32_t vector);
|
||||
virtual void emit_fixup();
|
||||
virtual std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0);
|
||||
virtual bool variable_decl_is_remapped_storage(const SPIRVariable &var, spv::StorageClass storage) const;
|
||||
virtual std::string to_func_call_arg(const SPIRFunction::Parameter &arg, uint32_t id);
|
||||
|
||||
struct TextureFunctionBaseArguments
|
||||
@@ -569,13 +585,15 @@ protected:
|
||||
bool use_array_constructor = false;
|
||||
bool needs_row_major_load_workaround = false;
|
||||
bool support_pointer_to_pointer = false;
|
||||
bool support_precise_qualifier = false;
|
||||
bool support_64bit_switch = false;
|
||||
} backend;
|
||||
|
||||
void emit_struct(SPIRType &type);
|
||||
void emit_resources();
|
||||
void emit_extension_workarounds(spv::ExecutionModel model);
|
||||
void emit_buffer_block_native(const SPIRVariable &var);
|
||||
void emit_buffer_reference_block(SPIRType &type, bool forward_declaration);
|
||||
void emit_buffer_reference_block(uint32_t type_id, bool forward_declaration);
|
||||
void emit_buffer_block_legacy(const SPIRVariable &var);
|
||||
void emit_buffer_block_flattened(const SPIRVariable &type);
|
||||
void fixup_implicit_builtin_block_names();
|
||||
@@ -616,6 +634,8 @@ protected:
|
||||
void emit_trinary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2,
|
||||
const char *op);
|
||||
void emit_binary_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
|
||||
void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, const char *op);
|
||||
void emit_atomic_func_op(uint32_t result_type, uint32_t result_id, uint32_t op0, uint32_t op1, uint32_t op2, const char *op);
|
||||
|
||||
void emit_unary_func_op_cast(uint32_t result_type, uint32_t result_id, uint32_t op0, const char *op,
|
||||
SPIRType::BaseType input_type, SPIRType::BaseType expected_result_type);
|
||||
@@ -661,6 +681,9 @@ protected:
|
||||
std::string access_chain_internal(uint32_t base, const uint32_t *indices, uint32_t count, AccessChainFlags flags,
|
||||
AccessChainMeta *meta);
|
||||
|
||||
spv::StorageClass get_expression_effective_storage_class(uint32_t ptr);
|
||||
virtual bool access_chain_needs_stage_io_builtin_translation(uint32_t base);
|
||||
|
||||
virtual void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type,
|
||||
spv::StorageClass storage, bool &is_packed);
|
||||
|
||||
@@ -691,6 +714,7 @@ protected:
|
||||
void emit_uninitialized_temporary(uint32_t type, uint32_t id);
|
||||
SPIRExpression &emit_uninitialized_temporary_expression(uint32_t type, uint32_t id);
|
||||
void append_global_func_args(const SPIRFunction &func, uint32_t index, SmallVector<std::string> &arglist);
|
||||
std::string to_non_uniform_aware_expression(uint32_t id);
|
||||
std::string to_expression(uint32_t id, bool register_expression_read = true);
|
||||
std::string to_composite_constructor_expression(uint32_t id, bool uses_buffer_offset);
|
||||
std::string to_rerolled_array_expression(const std::string &expr, const SPIRType &type);
|
||||
@@ -716,17 +740,17 @@ protected:
|
||||
virtual std::string to_qualifiers_glsl(uint32_t id);
|
||||
void fixup_io_block_patch_qualifiers(const SPIRVariable &var);
|
||||
void emit_output_variable_initializer(const SPIRVariable &var);
|
||||
const char *to_precision_qualifiers_glsl(uint32_t id);
|
||||
std::string to_precision_qualifiers_glsl(uint32_t id);
|
||||
virtual const char *to_storage_qualifiers_glsl(const SPIRVariable &var);
|
||||
const char *flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags);
|
||||
std::string flags_to_qualifiers_glsl(const SPIRType &type, const Bitset &flags);
|
||||
const char *format_to_glsl(spv::ImageFormat format);
|
||||
virtual std::string layout_for_member(const SPIRType &type, uint32_t index);
|
||||
virtual std::string to_interpolation_qualifiers(const Bitset &flags);
|
||||
std::string layout_for_variable(const SPIRVariable &variable);
|
||||
std::string to_combined_image_sampler(VariableID image_id, VariableID samp_id);
|
||||
virtual bool skip_argument(uint32_t id) const;
|
||||
virtual void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage,
|
||||
spv::StorageClass rhs_storage);
|
||||
virtual void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
|
||||
spv::StorageClass lhs_storage, spv::StorageClass rhs_storage);
|
||||
virtual void emit_block_hints(const SPIRBlock &block);
|
||||
virtual std::string to_initializer_expression(const SPIRVariable &var);
|
||||
virtual std::string to_zero_initialized_expression(uint32_t type_id);
|
||||
@@ -741,6 +765,7 @@ protected:
|
||||
uint32_t type_to_packed_alignment(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
|
||||
uint32_t type_to_packed_array_stride(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
|
||||
uint32_t type_to_packed_size(const SPIRType &type, const Bitset &flags, BufferPackingStandard packing);
|
||||
uint32_t type_to_location_count(const SPIRType &type) const;
|
||||
|
||||
std::string bitcast_glsl(const SPIRType &result_type, uint32_t arg);
|
||||
virtual std::string bitcast_glsl_op(const SPIRType &result_type, const SPIRType &argument_type);
|
||||
@@ -838,7 +863,9 @@ protected:
|
||||
|
||||
// GL_EXT_shader_framebuffer_fetch support.
|
||||
std::vector<std::pair<uint32_t, uint32_t>> subpass_to_framebuffer_fetch_attachment;
|
||||
std::unordered_set<uint32_t> inout_color_attachments;
|
||||
std::vector<std::pair<uint32_t, bool>> inout_color_attachments;
|
||||
bool location_is_framebuffer_fetch(uint32_t location) const;
|
||||
bool location_is_non_coherent_framebuffer_fetch(uint32_t location) const;
|
||||
bool subpass_input_is_framebuffer_fetch(uint32_t id) const;
|
||||
void emit_inout_fragment_outputs_copy_to_subpass_inputs();
|
||||
const SPIRVariable *find_subpass_input_by_attachment_index(uint32_t index) const;
|
||||
@@ -877,11 +904,11 @@ protected:
|
||||
// Builtins in GLSL are always specific signedness, but the SPIR-V can declare them
|
||||
// as either unsigned or signed.
|
||||
// Sometimes we will need to automatically perform casts on load and store to make this work.
|
||||
virtual void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
|
||||
virtual void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
|
||||
virtual void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type);
|
||||
virtual void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type);
|
||||
void unroll_array_from_complex_load(uint32_t target_id, uint32_t source_id, std::string &expr);
|
||||
bool unroll_array_to_complex_store(uint32_t target_id, uint32_t source_id);
|
||||
void convert_non_uniform_expression(const SPIRType &type, std::string &expr);
|
||||
void convert_non_uniform_expression(std::string &expr, uint32_t ptr_id);
|
||||
|
||||
void handle_store_to_invariant_variable(uint32_t store_id, uint32_t value_id);
|
||||
void disallow_forwarding_in_expression_chain(const SPIRExpression &expr);
|
||||
@@ -900,10 +927,17 @@ protected:
|
||||
void fixup_type_alias();
|
||||
void reorder_type_alias();
|
||||
|
||||
void propagate_nonuniform_qualifier(uint32_t id);
|
||||
|
||||
static const char *vector_swizzle(int vecsize, int index);
|
||||
|
||||
bool is_stage_output_location_masked(uint32_t location, uint32_t component) const;
|
||||
bool is_stage_output_builtin_masked(spv::BuiltIn builtin) const;
|
||||
bool is_stage_output_variable_masked(const SPIRVariable &var) const;
|
||||
bool is_stage_output_block_member_masked(const SPIRVariable &var, uint32_t index, bool strip_array) const;
|
||||
uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const;
|
||||
uint32_t get_declared_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array) const;
|
||||
std::unordered_set<LocationComponentPair, InternalHasher> masked_output_locations;
|
||||
std::unordered_set<uint32_t> masked_output_builtins;
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Robert Konrad
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,7 +20,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_hlsl.hpp"
|
||||
@@ -570,7 +570,7 @@ void CompilerHLSL::emit_builtin_outputs_in_struct()
|
||||
switch (builtin)
|
||||
{
|
||||
case BuiltInPosition:
|
||||
type = "float4";
|
||||
type = is_position_invariant() && backend.support_precise_qualifier ? "precise float4" : "float4";
|
||||
semantic = legacy ? "POSITION" : "SV_Position";
|
||||
break;
|
||||
|
||||
@@ -641,7 +641,6 @@ void CompilerHLSL::emit_builtin_outputs_in_struct()
|
||||
|
||||
default:
|
||||
SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (type && semantic)
|
||||
@@ -770,7 +769,6 @@ void CompilerHLSL::emit_builtin_inputs_in_struct()
|
||||
|
||||
default:
|
||||
SPIRV_CROSS_THROW("Unsupported builtin in HLSL.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (type && semantic)
|
||||
@@ -818,8 +816,8 @@ string CompilerHLSL::to_interpolation_qualifiers(const Bitset &flags)
|
||||
res += "patch "; // Seems to be different in actual HLSL.
|
||||
if (flags.get(DecorationSample))
|
||||
res += "sample ";
|
||||
if (flags.get(DecorationInvariant))
|
||||
res += "invariant "; // Not supported?
|
||||
if (flags.get(DecorationInvariant) && backend.support_precise_qualifier)
|
||||
res += "precise "; // Not supported?
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -854,48 +852,25 @@ std::string CompilerHLSL::to_initializer_expression(const SPIRVariable &var)
|
||||
return CompilerGLSL::to_initializer_expression(var);
|
||||
}
|
||||
|
||||
void CompilerHLSL::emit_io_block(const SPIRVariable &var)
|
||||
void CompilerHLSL::emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index,
|
||||
uint32_t location,
|
||||
std::unordered_set<uint32_t> &active_locations)
|
||||
{
|
||||
auto &execution = get_entry_point();
|
||||
auto type = get<SPIRType>(var.basetype);
|
||||
auto semantic = to_semantic(location, execution.model, var.storage);
|
||||
auto mbr_name = join(to_name(type.self), "_", to_member_name(type, member_index));
|
||||
auto &mbr_type = get<SPIRType>(type.member_types[member_index]);
|
||||
|
||||
auto &type = get<SPIRType>(var.basetype);
|
||||
add_resource_name(type.self);
|
||||
statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, member_index)),
|
||||
type_to_glsl(mbr_type),
|
||||
" ", mbr_name, type_to_array_glsl(mbr_type),
|
||||
" : ", semantic, ";");
|
||||
|
||||
statement("struct ", to_name(type.self));
|
||||
begin_scope();
|
||||
type.member_name_cache.clear();
|
||||
|
||||
uint32_t base_location = get_decoration(var.self, DecorationLocation);
|
||||
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
{
|
||||
string semantic;
|
||||
if (has_member_decoration(type.self, i, DecorationLocation))
|
||||
{
|
||||
uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
|
||||
semantic = join(" : ", to_semantic(location, execution.model, var.storage));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the block itself has a location, but not its members, use the implicit location.
|
||||
// There could be a conflict if the block members partially specialize the locations.
|
||||
// It is unclear how SPIR-V deals with this. Assume this does not happen for now.
|
||||
uint32_t location = base_location + i;
|
||||
semantic = join(" : ", to_semantic(location, execution.model, var.storage));
|
||||
}
|
||||
|
||||
add_member_name(type, i);
|
||||
|
||||
auto &membertype = get<SPIRType>(type.member_types[i]);
|
||||
statement(to_interpolation_qualifiers(get_member_decoration_bitset(type.self, i)),
|
||||
variable_decl(membertype, to_member_name(type, i)), semantic, ";");
|
||||
}
|
||||
|
||||
end_scope_decl();
|
||||
statement("");
|
||||
|
||||
statement("static ", variable_decl(var), ";");
|
||||
statement("");
|
||||
// Structs and arrays should consume more locations.
|
||||
uint32_t consumed_locations = type_to_consumed_locations(mbr_type);
|
||||
for (uint32_t i = 0; i < consumed_locations; i++)
|
||||
active_locations.insert(location + i);
|
||||
}
|
||||
|
||||
void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unordered_set<uint32_t> &active_locations)
|
||||
@@ -930,7 +905,6 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
|
||||
|
||||
bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
|
||||
|
||||
auto &m = ir.meta[var.self].decoration;
|
||||
auto name = to_name(var.self);
|
||||
if (use_location_number)
|
||||
{
|
||||
@@ -938,8 +912,8 @@ void CompilerHLSL::emit_interface_block_in_struct(const SPIRVariable &var, unord
|
||||
|
||||
// If an explicit location exists, use it with TEXCOORD[N] semantic.
|
||||
// Otherwise, pick a vacant location.
|
||||
if (m.decoration_flags.get(DecorationLocation))
|
||||
location_number = m.location;
|
||||
if (has_decoration(var.self, DecorationLocation))
|
||||
location_number = get_decoration(var.self, DecorationLocation);
|
||||
else
|
||||
location_number = get_vacant_location();
|
||||
|
||||
@@ -1188,10 +1162,10 @@ void CompilerHLSL::emit_composite_constants()
|
||||
|
||||
auto &type = this->get<SPIRType>(c.constant_type);
|
||||
|
||||
// Cannot declare block type constants here.
|
||||
// We do not have the struct type yet.
|
||||
bool is_block = has_decoration(type.self, DecorationBlock);
|
||||
if (!is_block && (type.basetype == SPIRType::Struct || !type.array.empty()))
|
||||
if (type.basetype == SPIRType::Struct && is_builtin_type(type))
|
||||
return;
|
||||
|
||||
if (type.basetype == SPIRType::Struct || !type.array.empty())
|
||||
{
|
||||
auto name = to_name(c.self);
|
||||
statement("static const ", variable_decl(type, name), " = ", constant_expression(c), ";");
|
||||
@@ -1209,6 +1183,18 @@ void CompilerHLSL::emit_specialization_constants_and_structs()
|
||||
SpecializationConstant wg_x, wg_y, wg_z;
|
||||
ID workgroup_size_id = get_work_group_size_specialization_constants(wg_x, wg_y, wg_z);
|
||||
|
||||
std::unordered_set<TypeID> io_block_types;
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, const SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
if ((var.storage == StorageClassInput || var.storage == StorageClassOutput) &&
|
||||
!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
|
||||
interface_variable_exists_in_entry_point(var.self) &&
|
||||
has_decoration(type.self, DecorationBlock))
|
||||
{
|
||||
io_block_types.insert(type.self);
|
||||
}
|
||||
});
|
||||
|
||||
auto loop_lock = ir.create_loop_hard_lock();
|
||||
for (auto &id_ : ir.ids_for_constant_or_type)
|
||||
{
|
||||
@@ -1251,9 +1237,11 @@ void CompilerHLSL::emit_specialization_constants_and_structs()
|
||||
else if (id.get_type() == TypeType)
|
||||
{
|
||||
auto &type = id.get<SPIRType>();
|
||||
if (type.basetype == SPIRType::Struct && type.array.empty() && !type.pointer &&
|
||||
(!ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) &&
|
||||
!ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock)))
|
||||
bool is_non_io_block = has_decoration(type.self, DecorationBlock) &&
|
||||
io_block_types.count(type.self) == 0;
|
||||
bool is_buffer_block = has_decoration(type.self, DecorationBufferBlock);
|
||||
if (type.basetype == SPIRType::Struct && type.array.empty() &&
|
||||
!type.pointer && !is_non_io_block && !is_buffer_block)
|
||||
{
|
||||
if (emitted)
|
||||
statement("");
|
||||
@@ -1379,16 +1367,12 @@ void CompilerHLSL::emit_resources()
|
||||
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
|
||||
// Do not emit I/O blocks here.
|
||||
// I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
|
||||
// and tessellation down the line.
|
||||
if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
||||
if (var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
||||
(var.storage == StorageClassInput || var.storage == StorageClassOutput) && !is_builtin_variable(var) &&
|
||||
interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
// Only emit non-builtins which are not blocks here. Builtin variables are handled separately.
|
||||
// Builtin variables are handled separately.
|
||||
emit_interface_block_globally(var);
|
||||
emitted = true;
|
||||
}
|
||||
@@ -1402,69 +1386,72 @@ void CompilerHLSL::emit_resources()
|
||||
require_output = false;
|
||||
unordered_set<uint32_t> active_inputs;
|
||||
unordered_set<uint32_t> active_outputs;
|
||||
SmallVector<SPIRVariable *> input_variables;
|
||||
SmallVector<SPIRVariable *> output_variables;
|
||||
|
||||
struct IOVariable
|
||||
{
|
||||
const SPIRVariable *var;
|
||||
uint32_t location;
|
||||
uint32_t block_member_index;
|
||||
bool block;
|
||||
};
|
||||
|
||||
SmallVector<IOVariable> input_variables;
|
||||
SmallVector<IOVariable> output_variables;
|
||||
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
bool block = has_decoration(type.self, DecorationBlock);
|
||||
|
||||
if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
// Do not emit I/O blocks here.
|
||||
// I/O blocks can be arrayed, so we must deal with them separately to support geometry shaders
|
||||
// and tessellation down the line.
|
||||
if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
|
||||
if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
|
||||
interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
if (var.storage == StorageClassInput)
|
||||
input_variables.push_back(&var);
|
||||
else
|
||||
output_variables.push_back(&var);
|
||||
}
|
||||
|
||||
// Reserve input and output locations for block variables as necessary.
|
||||
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
auto &active = var.storage == StorageClassInput ? active_inputs : active_outputs;
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
if (block)
|
||||
{
|
||||
if (has_member_decoration(type.self, i, DecorationLocation))
|
||||
for (uint32_t i = 0; i < uint32_t(type.member_types.size()); i++)
|
||||
{
|
||||
uint32_t location = get_member_decoration(type.self, i, DecorationLocation);
|
||||
active.insert(location);
|
||||
uint32_t location = get_declared_member_location(var, i, false);
|
||||
if (var.storage == StorageClassInput)
|
||||
input_variables.push_back({ &var, location, i, true });
|
||||
else
|
||||
output_variables.push_back({ &var, location, i, true });
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the block struct and a global variable here.
|
||||
emit_io_block(var);
|
||||
else
|
||||
{
|
||||
uint32_t location = get_decoration(var.self, DecorationLocation);
|
||||
if (var.storage == StorageClassInput)
|
||||
input_variables.push_back({ &var, location, 0, false });
|
||||
else
|
||||
output_variables.push_back({ &var, location, 0, false });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const auto variable_compare = [&](const SPIRVariable *a, const SPIRVariable *b) -> bool {
|
||||
const auto variable_compare = [&](const IOVariable &a, const IOVariable &b) -> bool {
|
||||
// Sort input and output variables based on, from more robust to less robust:
|
||||
// - Location
|
||||
// - Variable has a location
|
||||
// - Name comparison
|
||||
// - Variable has a name
|
||||
// - Fallback: ID
|
||||
bool has_location_a = has_decoration(a->self, DecorationLocation);
|
||||
bool has_location_b = has_decoration(b->self, DecorationLocation);
|
||||
bool has_location_a = a.block || has_decoration(a.var->self, DecorationLocation);
|
||||
bool has_location_b = b.block || has_decoration(b.var->self, DecorationLocation);
|
||||
|
||||
if (has_location_a && has_location_b)
|
||||
{
|
||||
return get_decoration(a->self, DecorationLocation) < get_decoration(b->self, DecorationLocation);
|
||||
}
|
||||
return a.location < b.location;
|
||||
else if (has_location_a && !has_location_b)
|
||||
return true;
|
||||
else if (!has_location_a && has_location_b)
|
||||
return false;
|
||||
|
||||
const auto &name1 = to_name(a->self);
|
||||
const auto &name2 = to_name(b->self);
|
||||
const auto &name1 = to_name(a.var->self);
|
||||
const auto &name2 = to_name(b.var->self);
|
||||
|
||||
if (name1.empty() && name2.empty())
|
||||
return a->self < b->self;
|
||||
return a.var->self < b.var->self;
|
||||
else if (name1.empty())
|
||||
return true;
|
||||
else if (name2.empty())
|
||||
@@ -1491,8 +1478,13 @@ void CompilerHLSL::emit_resources()
|
||||
|
||||
begin_scope();
|
||||
sort(input_variables.begin(), input_variables.end(), variable_compare);
|
||||
for (auto var : input_variables)
|
||||
emit_interface_block_in_struct(*var, active_inputs);
|
||||
for (auto &var : input_variables)
|
||||
{
|
||||
if (var.block)
|
||||
emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_inputs);
|
||||
else
|
||||
emit_interface_block_in_struct(*var.var, active_inputs);
|
||||
}
|
||||
emit_builtin_inputs_in_struct();
|
||||
end_scope_decl();
|
||||
statement("");
|
||||
@@ -1504,10 +1496,14 @@ void CompilerHLSL::emit_resources()
|
||||
statement("struct SPIRV_Cross_Output");
|
||||
|
||||
begin_scope();
|
||||
// FIXME: Use locations properly if they exist.
|
||||
sort(output_variables.begin(), output_variables.end(), variable_compare);
|
||||
for (auto var : output_variables)
|
||||
emit_interface_block_in_struct(*var, active_outputs);
|
||||
for (auto &var : output_variables)
|
||||
{
|
||||
if (var.block)
|
||||
emit_interface_block_member_in_struct(*var.var, var.block_member_index, var.location, active_outputs);
|
||||
else
|
||||
emit_interface_block_in_struct(*var.var, active_outputs);
|
||||
}
|
||||
emit_builtin_outputs_in_struct();
|
||||
end_scope_decl();
|
||||
statement("");
|
||||
@@ -1944,6 +1940,28 @@ void CompilerHLSL::emit_resources()
|
||||
end_scope();
|
||||
statement("");
|
||||
}
|
||||
|
||||
for (TypeID type_id : composite_selection_workaround_types)
|
||||
{
|
||||
// Need out variable since HLSL does not support returning arrays.
|
||||
auto &type = get<SPIRType>(type_id);
|
||||
auto type_str = type_to_glsl(type);
|
||||
auto type_arr_str = type_to_array_glsl(type);
|
||||
statement("void spvSelectComposite(out ", type_str, " out_value", type_arr_str, ", bool cond, ",
|
||||
type_str, " true_val", type_arr_str, ", ",
|
||||
type_str, " false_val", type_arr_str, ")");
|
||||
begin_scope();
|
||||
statement("if (cond)");
|
||||
begin_scope();
|
||||
statement("out_value = true_val;");
|
||||
end_scope();
|
||||
statement("else");
|
||||
begin_scope();
|
||||
statement("out_value = false_val;");
|
||||
end_scope();
|
||||
end_scope();
|
||||
statement("");
|
||||
}
|
||||
}
|
||||
|
||||
void CompilerHLSL::emit_texture_size_variants(uint64_t variant_mask, const char *vecsize_qualifier, bool uav,
|
||||
@@ -2051,13 +2069,6 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type
|
||||
if (index < memb.size())
|
||||
memberflags = memb[index].decoration_flags;
|
||||
|
||||
string qualifiers;
|
||||
bool is_block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock) ||
|
||||
ir.meta[type.self].decoration.decoration_flags.get(DecorationBufferBlock);
|
||||
|
||||
if (is_block)
|
||||
qualifiers = to_interpolation_qualifiers(memberflags);
|
||||
|
||||
string packing_offset;
|
||||
bool is_push_constant = type.storage == StorageClassPushConstant;
|
||||
|
||||
@@ -2072,7 +2083,7 @@ void CompilerHLSL::emit_struct_member(const SPIRType &type, uint32_t member_type
|
||||
packing_offset = join(" : packoffset(c", offset / 16, packing_swizzle[(offset & 15) >> 2], ")");
|
||||
}
|
||||
|
||||
statement(layout_for_member(type, index), qualifiers, qualifier,
|
||||
statement(layout_for_member(type, index), qualifier,
|
||||
variable_decl(membertype, to_member_name(type, index)), packing_offset, ";");
|
||||
}
|
||||
|
||||
@@ -2249,7 +2260,7 @@ void CompilerHLSL::emit_push_constant_block(const SPIRVariable &var)
|
||||
|
||||
string CompilerHLSL::to_sampler_expression(uint32_t id)
|
||||
{
|
||||
auto expr = join("_", to_expression(id));
|
||||
auto expr = join("_", to_non_uniform_aware_expression(id));
|
||||
auto index = expr.find_first_of('[');
|
||||
if (index == string::npos)
|
||||
{
|
||||
@@ -2369,7 +2380,7 @@ void CompilerHLSL::emit_function_prototype(SPIRFunction &func, const Bitset &ret
|
||||
arg_type.image.dim != DimBuffer)
|
||||
{
|
||||
// Manufacture automatic sampler arg for SampledImage texture
|
||||
arglist.push_back(join(image_is_comparison(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
|
||||
arglist.push_back(join(is_depth_image(arg_type, arg.id) ? "SamplerComparisonState " : "SamplerState ",
|
||||
to_sampler_expression(arg.id), type_to_array_glsl(arg_type)));
|
||||
}
|
||||
|
||||
@@ -2407,27 +2418,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
|
||||
if (require_input)
|
||||
arguments.push_back("SPIRV_Cross_Input stage_input");
|
||||
|
||||
// Add I/O blocks as separate arguments with appropriate storage qualifier.
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
|
||||
if (var.storage != StorageClassInput && var.storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
if (var.storage == StorageClassInput)
|
||||
{
|
||||
arguments.push_back(join("in ", variable_decl(type, join("stage_input", to_name(var.self)))));
|
||||
}
|
||||
else if (var.storage == StorageClassOutput)
|
||||
{
|
||||
arguments.push_back(join("out ", variable_decl(type, join("stage_output", to_name(var.self)))));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
auto &execution = get_entry_point();
|
||||
|
||||
switch (execution.model)
|
||||
@@ -2588,36 +2578,43 @@ void CompilerHLSL::emit_hlsl_entry_point()
|
||||
// Copy from stage input struct to globals.
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
bool block = has_decoration(type.self, DecorationBlock);
|
||||
|
||||
if (var.storage != StorageClassInput)
|
||||
return;
|
||||
|
||||
bool need_matrix_unroll = var.storage == StorageClassInput && execution.model == ExecutionModelVertex;
|
||||
|
||||
if (!block && !var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
|
||||
if (!var.remapped_variable && type.pointer && !is_builtin_variable(var) &&
|
||||
interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
auto name = to_name(var.self);
|
||||
auto &mtype = this->get<SPIRType>(var.basetype);
|
||||
if (need_matrix_unroll && mtype.columns > 1)
|
||||
if (block)
|
||||
{
|
||||
// Unroll matrices.
|
||||
for (uint32_t col = 0; col < mtype.columns; col++)
|
||||
statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
|
||||
auto type_name = to_name(type.self);
|
||||
auto var_name = to_name(var.self);
|
||||
for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
|
||||
{
|
||||
auto mbr_name = to_member_name(type, mbr_idx);
|
||||
auto flat_name = join(type_name, "_", mbr_name);
|
||||
statement(var_name, ".", mbr_name, " = stage_input.", flat_name, ";");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
statement(name, " = stage_input.", name, ";");
|
||||
auto name = to_name(var.self);
|
||||
auto &mtype = this->get<SPIRType>(var.basetype);
|
||||
if (need_matrix_unroll && mtype.columns > 1)
|
||||
{
|
||||
// Unroll matrices.
|
||||
for (uint32_t col = 0; col < mtype.columns; col++)
|
||||
statement(name, "[", col, "] = stage_input.", name, "_", col, ";");
|
||||
}
|
||||
else
|
||||
{
|
||||
statement(name, " = stage_input.", name, ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// I/O blocks don't use the common stage input/output struct, but separate outputs.
|
||||
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
auto name = to_name(var.self);
|
||||
statement(name, " = stage_input", name, ";");
|
||||
}
|
||||
});
|
||||
|
||||
// Run the shader.
|
||||
@@ -2630,22 +2627,6 @@ void CompilerHLSL::emit_hlsl_entry_point()
|
||||
else
|
||||
SPIRV_CROSS_THROW("Unsupported shader stage.");
|
||||
|
||||
// Copy block outputs.
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
|
||||
if (var.storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
// I/O blocks don't use the common stage input/output struct, but separate outputs.
|
||||
if (block && !is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
auto name = to_name(var.self);
|
||||
statement("stage_output", name, " = ", name, ";");
|
||||
}
|
||||
});
|
||||
|
||||
// Copy stage outputs.
|
||||
if (require_output)
|
||||
{
|
||||
@@ -2682,27 +2663,43 @@ void CompilerHLSL::emit_hlsl_entry_point()
|
||||
|
||||
ir.for_each_typed_id<SPIRVariable>([&](uint32_t, SPIRVariable &var) {
|
||||
auto &type = this->get<SPIRType>(var.basetype);
|
||||
bool block = ir.meta[type.self].decoration.decoration_flags.get(DecorationBlock);
|
||||
bool block = has_decoration(type.self, DecorationBlock);
|
||||
|
||||
if (var.storage != StorageClassOutput)
|
||||
return;
|
||||
|
||||
if (!block && var.storage != StorageClassFunction && !var.remapped_variable && type.pointer &&
|
||||
!is_builtin_variable(var) && interface_variable_exists_in_entry_point(var.self))
|
||||
if (!var.remapped_variable && type.pointer &&
|
||||
!is_builtin_variable(var) &&
|
||||
interface_variable_exists_in_entry_point(var.self))
|
||||
{
|
||||
auto name = to_name(var.self);
|
||||
|
||||
if (legacy && execution.model == ExecutionModelFragment)
|
||||
if (block)
|
||||
{
|
||||
string output_filler;
|
||||
for (uint32_t size = type.vecsize; size < 4; ++size)
|
||||
output_filler += ", 0.0";
|
||||
|
||||
statement("stage_output.", name, " = float4(", name, output_filler, ");");
|
||||
// I/O blocks need to flatten output.
|
||||
auto type_name = to_name(type.self);
|
||||
auto var_name = to_name(var.self);
|
||||
for (uint32_t mbr_idx = 0; mbr_idx < uint32_t(type.member_types.size()); mbr_idx++)
|
||||
{
|
||||
auto mbr_name = to_member_name(type, mbr_idx);
|
||||
auto flat_name = join(type_name, "_", mbr_name);
|
||||
statement("stage_output.", flat_name, " = ", var_name, ".", mbr_name, ";");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
statement("stage_output.", name, " = ", name, ";");
|
||||
auto name = to_name(var.self);
|
||||
|
||||
if (legacy && execution.model == ExecutionModelFragment)
|
||||
{
|
||||
string output_filler;
|
||||
for (uint32_t size = type.vecsize; size < 4; ++size)
|
||||
output_filler += ", 0.0";
|
||||
|
||||
statement("stage_output.", name, " = float4(", name, output_filler, ");");
|
||||
}
|
||||
else
|
||||
{
|
||||
statement("stage_output.", name, " = ", name, ";");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -2754,14 +2751,17 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
|
||||
bool proj = false;
|
||||
const uint32_t *opt = nullptr;
|
||||
auto *combined_image = maybe_get<SPIRCombinedImageSampler>(img);
|
||||
auto img_expr = to_expression(combined_image ? combined_image->image : img);
|
||||
|
||||
if (combined_image && has_decoration(img, DecorationNonUniform))
|
||||
{
|
||||
set_decoration(combined_image->image, DecorationNonUniform);
|
||||
set_decoration(combined_image->sampler, DecorationNonUniform);
|
||||
}
|
||||
|
||||
auto img_expr = to_non_uniform_aware_expression(combined_image ? combined_image->image : img);
|
||||
|
||||
inherited_expressions.push_back(coord);
|
||||
|
||||
// Make sure non-uniform decoration is back-propagated to where it needs to be.
|
||||
if (has_decoration(img, DecorationNonUniformEXT))
|
||||
propagate_nonuniform_qualifier(img);
|
||||
|
||||
switch (op)
|
||||
{
|
||||
case OpImageSampleDrefImplicitLod:
|
||||
@@ -2910,7 +2910,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
|
||||
{
|
||||
texop += img_expr;
|
||||
|
||||
if (image_is_comparison(imgtype, img))
|
||||
if (is_depth_image(imgtype, img))
|
||||
{
|
||||
if (gather)
|
||||
{
|
||||
@@ -3016,7 +3016,7 @@ void CompilerHLSL::emit_texture_op(const Instruction &i, bool sparse)
|
||||
{
|
||||
string sampler_expr;
|
||||
if (combined_image)
|
||||
sampler_expr = to_expression(combined_image->sampler);
|
||||
sampler_expr = to_non_uniform_aware_expression(combined_image->sampler);
|
||||
else
|
||||
sampler_expr = to_sampler_expression(img);
|
||||
expr += sampler_expr;
|
||||
@@ -3386,7 +3386,7 @@ void CompilerHLSL::emit_modern_uniform(const SPIRVariable &var)
|
||||
if (type.basetype == SPIRType::SampledImage && type.image.dim != DimBuffer)
|
||||
{
|
||||
// For combined image samplers, also emit a combined image sampler.
|
||||
if (image_is_comparison(type, var.self))
|
||||
if (is_depth_image(type, var.self))
|
||||
statement("SamplerComparisonState ", to_sampler_expression(var.self), type_to_array_glsl(type),
|
||||
to_resource_binding_sampler(var), ";");
|
||||
else
|
||||
@@ -3812,6 +3812,10 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
||||
SPIRV_CROSS_THROW("Reading types other than 32-bit from ByteAddressBuffer not yet supported, unless SM 6.2 and "
|
||||
"native 16-bit types are enabled.");
|
||||
|
||||
string base = chain.base;
|
||||
if (has_decoration(chain.self, DecorationNonUniform))
|
||||
convert_non_uniform_expression(base, chain.self);
|
||||
|
||||
bool templated_load = hlsl_options.shader_model >= 62;
|
||||
string load_expr;
|
||||
|
||||
@@ -3844,7 +3848,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
||||
if (templated_load)
|
||||
load_op = "Load";
|
||||
|
||||
load_expr = join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
|
||||
load_expr = join(base, ".", load_op, template_expr, "(", chain.dynamic_index, chain.static_index, ")");
|
||||
}
|
||||
else if (type.columns == 1)
|
||||
{
|
||||
@@ -3866,7 +3870,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
||||
|
||||
for (uint32_t r = 0; r < type.vecsize; r++)
|
||||
{
|
||||
load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index,
|
||||
load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + r * chain.matrix_stride, ")");
|
||||
if (r + 1 < type.vecsize)
|
||||
load_expr += ", ";
|
||||
@@ -3915,7 +3919,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
||||
|
||||
for (uint32_t c = 0; c < type.columns; c++)
|
||||
{
|
||||
load_expr += join(chain.base, ".", load_op, template_expr, "(", chain.dynamic_index,
|
||||
load_expr += join(base, ".", load_op, template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + c * chain.matrix_stride, ")");
|
||||
if (c + 1 < type.columns)
|
||||
load_expr += ", ";
|
||||
@@ -3944,7 +3948,7 @@ void CompilerHLSL::read_access_chain(string *expr, const string &lhs, const SPIR
|
||||
{
|
||||
for (uint32_t r = 0; r < type.vecsize; r++)
|
||||
{
|
||||
load_expr += join(chain.base, ".Load", template_expr, "(", chain.dynamic_index,
|
||||
load_expr += join(base, ".Load", template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ")");
|
||||
|
||||
if ((r + 1 < type.vecsize) || (c + 1 < type.columns))
|
||||
@@ -3981,9 +3985,6 @@ void CompilerHLSL::emit_load(const Instruction &instruction)
|
||||
uint32_t id = ops[1];
|
||||
uint32_t ptr = ops[2];
|
||||
|
||||
if (has_decoration(ptr, DecorationNonUniformEXT))
|
||||
propagate_nonuniform_qualifier(ptr);
|
||||
|
||||
auto &type = get<SPIRType>(result_type);
|
||||
bool composite_load = !type.array.empty() || type.basetype == SPIRType::Struct;
|
||||
|
||||
@@ -4122,9 +4123,6 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
// Make sure we trigger a read of the constituents in the access chain.
|
||||
track_expression_read(chain.self);
|
||||
|
||||
if (has_decoration(chain.self, DecorationNonUniformEXT))
|
||||
propagate_nonuniform_qualifier(chain.self);
|
||||
|
||||
SPIRType target_type;
|
||||
target_type.basetype = SPIRType::UInt;
|
||||
target_type.vecsize = type.vecsize;
|
||||
@@ -4148,6 +4146,10 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
|
||||
bool templated_store = hlsl_options.shader_model >= 62;
|
||||
|
||||
auto base = chain.base;
|
||||
if (has_decoration(chain.self, DecorationNonUniform))
|
||||
convert_non_uniform_expression(base, chain.self);
|
||||
|
||||
string template_expr;
|
||||
if (templated_store)
|
||||
template_expr = join("<", type_to_glsl(type), ">");
|
||||
@@ -4183,7 +4185,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
}
|
||||
else
|
||||
store_op = "Store";
|
||||
statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ",
|
||||
statement(base, ".", store_op, template_expr, "(", chain.dynamic_index, chain.static_index, ", ",
|
||||
store_expr, ");");
|
||||
}
|
||||
else if (type.columns == 1)
|
||||
@@ -4214,7 +4216,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
store_expr = join(bitcast_op, "(", store_expr, ")");
|
||||
}
|
||||
|
||||
statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index,
|
||||
statement(base, ".Store", template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + chain.matrix_stride * r, ", ", store_expr, ");");
|
||||
}
|
||||
}
|
||||
@@ -4258,7 +4260,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
store_expr = join(bitcast_op, "(", store_expr, ")");
|
||||
}
|
||||
|
||||
statement(chain.base, ".", store_op, template_expr, "(", chain.dynamic_index,
|
||||
statement(base, ".", store_op, template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + c * chain.matrix_stride, ", ", store_expr, ");");
|
||||
}
|
||||
}
|
||||
@@ -4282,7 +4284,7 @@ void CompilerHLSL::write_access_chain(const SPIRAccessChain &chain, uint32_t val
|
||||
auto bitcast_op = bitcast_glsl_op(target_type, type);
|
||||
if (!bitcast_op.empty())
|
||||
store_expr = join(bitcast_op, "(", store_expr, ")");
|
||||
statement(chain.base, ".Store", template_expr, "(", chain.dynamic_index,
|
||||
statement(base, ".Store", template_expr, "(", chain.dynamic_index,
|
||||
chain.static_index + c * (type.width / 8) + r * chain.matrix_stride, ", ", store_expr, ");");
|
||||
}
|
||||
}
|
||||
@@ -4384,9 +4386,6 @@ void CompilerHLSL::emit_access_chain(const Instruction &instruction)
|
||||
inherit_expression_dependencies(ops[1], ops[i]);
|
||||
add_implied_read_expression(e, ops[i]);
|
||||
}
|
||||
|
||||
if (has_decoration(ops[1], DecorationNonUniformEXT))
|
||||
propagate_nonuniform_qualifier(ops[1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -4486,13 +4485,16 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
||||
|
||||
if (data_type.storage == StorageClassImage || !chain)
|
||||
{
|
||||
statement(atomic_op, "(", to_expression(ops[0]), ", ", to_expression(ops[3]), ", ", to_expression(tmp_id),
|
||||
");");
|
||||
statement(atomic_op, "(", to_non_uniform_aware_expression(ops[0]), ", ",
|
||||
to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
|
||||
}
|
||||
else
|
||||
{
|
||||
string base = chain->base;
|
||||
if (has_decoration(chain->self, DecorationNonUniform))
|
||||
convert_non_uniform_expression(base, chain->self);
|
||||
// RWByteAddress buffer is always uint in its underlying type.
|
||||
statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ",
|
||||
statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ",
|
||||
to_expression(ops[3]), ", ", to_expression(tmp_id), ");");
|
||||
}
|
||||
}
|
||||
@@ -4510,14 +4512,17 @@ void CompilerHLSL::emit_atomic(const uint32_t *ops, uint32_t length, spv::Op op)
|
||||
SPIRType::BaseType expr_type;
|
||||
if (data_type.storage == StorageClassImage || !chain)
|
||||
{
|
||||
statement(atomic_op, "(", to_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
|
||||
statement(atomic_op, "(", to_non_uniform_aware_expression(ops[2]), ", ", value_expr, ", ", to_name(id), ");");
|
||||
expr_type = data_type.basetype;
|
||||
}
|
||||
else
|
||||
{
|
||||
// RWByteAddress buffer is always uint in its underlying type.
|
||||
string base = chain->base;
|
||||
if (has_decoration(chain->self, DecorationNonUniform))
|
||||
convert_non_uniform_expression(base, chain->self);
|
||||
expr_type = SPIRType::UInt;
|
||||
statement(chain->base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
|
||||
statement(base, ".", atomic_op, "(", chain->dynamic_index, chain->static_index, ", ", value_expr,
|
||||
", ", to_name(id), ");");
|
||||
}
|
||||
|
||||
@@ -4581,19 +4586,15 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
|
||||
|
||||
case OpGroupNonUniformInverseBallot:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement InverseBallot in HLSL.");
|
||||
break;
|
||||
|
||||
case OpGroupNonUniformBallotBitExtract:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement BallotBitExtract in HLSL.");
|
||||
break;
|
||||
|
||||
case OpGroupNonUniformBallotFindLSB:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement BallotFindLSB in HLSL.");
|
||||
break;
|
||||
|
||||
case OpGroupNonUniformBallotFindMSB:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement BallotFindMSB in HLSL.");
|
||||
break;
|
||||
|
||||
case OpGroupNonUniformBallotBitCount:
|
||||
{
|
||||
@@ -4618,13 +4619,35 @@ void CompilerHLSL::emit_subgroup_op(const Instruction &i)
|
||||
}
|
||||
|
||||
case OpGroupNonUniformShuffle:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement Shuffle in HLSL.");
|
||||
emit_binary_func_op(result_type, id, ops[3], ops[4], "WaveReadLaneAt");
|
||||
break;
|
||||
case OpGroupNonUniformShuffleXor:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement ShuffleXor in HLSL.");
|
||||
{
|
||||
bool forward = should_forward(ops[3]);
|
||||
emit_op(ops[0], ops[1],
|
||||
join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
|
||||
"WaveGetLaneIndex() ^ ", to_enclosed_expression(ops[4]), ")"), forward);
|
||||
inherit_expression_dependencies(ops[1], ops[3]);
|
||||
break;
|
||||
}
|
||||
case OpGroupNonUniformShuffleUp:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement ShuffleUp in HLSL.");
|
||||
{
|
||||
bool forward = should_forward(ops[3]);
|
||||
emit_op(ops[0], ops[1],
|
||||
join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
|
||||
"WaveGetLaneIndex() - ", to_enclosed_expression(ops[4]), ")"), forward);
|
||||
inherit_expression_dependencies(ops[1], ops[3]);
|
||||
break;
|
||||
}
|
||||
case OpGroupNonUniformShuffleDown:
|
||||
SPIRV_CROSS_THROW("Cannot trivially implement ShuffleDown in HLSL.");
|
||||
{
|
||||
bool forward = should_forward(ops[3]);
|
||||
emit_op(ops[0], ops[1],
|
||||
join("WaveReadLaneAt(", to_unpacked_expression(ops[3]), ", ",
|
||||
"WaveGetLaneIndex() + ", to_enclosed_expression(ops[4]), ")"), forward);
|
||||
inherit_expression_dependencies(ops[1], ops[3]);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpGroupNonUniformAll:
|
||||
emit_unary_func_op(result_type, id, ops[3], "WaveActiveAllTrue");
|
||||
@@ -4771,6 +4794,34 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
break;
|
||||
}
|
||||
|
||||
case OpSelect:
|
||||
{
|
||||
auto &value_type = expression_type(ops[3]);
|
||||
if (value_type.basetype == SPIRType::Struct || is_array(value_type))
|
||||
{
|
||||
// HLSL does not support ternary expressions on composites.
|
||||
// Cannot use branches, since we might be in a continue block
|
||||
// where explicit control flow is prohibited.
|
||||
// Emit a helper function where we can use control flow.
|
||||
TypeID value_type_id = expression_type_id(ops[3]);
|
||||
auto itr = std::find(composite_selection_workaround_types.begin(),
|
||||
composite_selection_workaround_types.end(),
|
||||
value_type_id);
|
||||
if (itr == composite_selection_workaround_types.end())
|
||||
{
|
||||
composite_selection_workaround_types.push_back(value_type_id);
|
||||
force_recompile();
|
||||
}
|
||||
emit_uninitialized_temporary_expression(ops[0], ops[1]);
|
||||
statement("spvSelectComposite(",
|
||||
to_expression(ops[1]), ", ", to_expression(ops[2]), ", ",
|
||||
to_expression(ops[3]), ", ", to_expression(ops[4]), ");");
|
||||
}
|
||||
else
|
||||
CompilerGLSL::emit_instruction(instruction);
|
||||
break;
|
||||
}
|
||||
|
||||
case OpStore:
|
||||
{
|
||||
emit_store(instruction);
|
||||
@@ -5150,7 +5201,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
auto dummy_samples_levels = join(get_fallback_name(id), "_dummy_parameter");
|
||||
statement("uint ", dummy_samples_levels, ";");
|
||||
|
||||
auto expr = join("spvTextureSize(", to_expression(ops[2]), ", ",
|
||||
auto expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", ",
|
||||
bitcast_expression(SPIRType::UInt, ops[3]), ", ", dummy_samples_levels, ")");
|
||||
|
||||
auto &restype = get<SPIRType>(ops[0]);
|
||||
@@ -5176,9 +5227,9 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
|
||||
string expr;
|
||||
if (uav)
|
||||
expr = join("spvImageSize(", to_expression(ops[2]), ", ", dummy_samples_levels, ")");
|
||||
expr = join("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", dummy_samples_levels, ")");
|
||||
else
|
||||
expr = join("spvTextureSize(", to_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
|
||||
expr = join("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", dummy_samples_levels, ")");
|
||||
|
||||
auto &restype = get<SPIRType>(ops[0]);
|
||||
expr = bitcast_expression(restype, SPIRType::UInt, expr);
|
||||
@@ -5208,9 +5259,9 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
statement(variable_decl(type, to_name(id)), ";");
|
||||
|
||||
if (uav)
|
||||
statement("spvImageSize(", to_expression(ops[2]), ", ", to_name(id), ");");
|
||||
statement("spvImageSize(", to_non_uniform_aware_expression(ops[2]), ", ", to_name(id), ");");
|
||||
else
|
||||
statement("spvTextureSize(", to_expression(ops[2]), ", 0u, ", to_name(id), ");");
|
||||
statement("spvTextureSize(", to_non_uniform_aware_expression(ops[2]), ", 0u, ", to_name(id), ");");
|
||||
|
||||
auto &restype = get<SPIRType>(ops[0]);
|
||||
auto expr = bitcast_expression(restype, SPIRType::UInt, to_name(id));
|
||||
@@ -5241,16 +5292,16 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
if (operands != ImageOperandsSampleMask || instruction.length != 6)
|
||||
SPIRV_CROSS_THROW("Multisampled image used in OpImageRead, but unexpected operand mask was used.");
|
||||
uint32_t sample = ops[5];
|
||||
imgexpr = join(to_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
|
||||
imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int2(gl_FragCoord.xy), ", to_expression(sample), ")");
|
||||
}
|
||||
else
|
||||
imgexpr = join(to_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
|
||||
imgexpr = join(to_non_uniform_aware_expression(ops[2]), ".Load(int3(int2(gl_FragCoord.xy), 0))");
|
||||
|
||||
pure = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
imgexpr = join(to_expression(ops[2]), "[", to_expression(ops[3]), "]");
|
||||
imgexpr = join(to_non_uniform_aware_expression(ops[2]), "[", to_expression(ops[3]), "]");
|
||||
// The underlying image type in HLSL depends on the image format, unlike GLSL, where all images are "vec4",
|
||||
// except that the underlying type changes how the data is interpreted.
|
||||
|
||||
@@ -5299,7 +5350,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
value_expr = remap_swizzle(narrowed_type, expression_type(ops[2]).vecsize, value_expr);
|
||||
}
|
||||
|
||||
statement(to_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
|
||||
statement(to_non_uniform_aware_expression(ops[0]), "[", to_expression(ops[1]), "] = ", value_expr, ";");
|
||||
if (var && variable_storage_is_aliased(*var))
|
||||
flush_all_aliased_variables();
|
||||
break;
|
||||
@@ -5311,10 +5362,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
uint32_t id = ops[1];
|
||||
|
||||
auto expr = to_expression(ops[2]);
|
||||
if (has_decoration(id, DecorationNonUniformEXT) || has_decoration(ops[2], DecorationNonUniformEXT))
|
||||
convert_non_uniform_expression(expression_type(ops[2]), expr);
|
||||
expr += join("[", to_expression(ops[3]), "]");
|
||||
|
||||
auto &e = set<SPIRExpression>(id, expr, result_type, true);
|
||||
|
||||
// When using the pointer, we need to know which variable it is actually loaded from.
|
||||
@@ -5492,7 +5540,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
|
||||
case OpArrayLength:
|
||||
{
|
||||
auto *var = maybe_get<SPIRVariable>(ops[2]);
|
||||
auto *var = maybe_get_backing_variable(ops[2]);
|
||||
if (!var)
|
||||
SPIRV_CROSS_THROW("Array length must point directly to an SSBO block.");
|
||||
|
||||
@@ -5502,7 +5550,7 @@ void CompilerHLSL::emit_instruction(const Instruction &instruction)
|
||||
|
||||
// This must be 32-bit uint, so we're good to go.
|
||||
emit_uninitialized_temporary_expression(ops[0], ops[1]);
|
||||
statement(to_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");");
|
||||
statement(to_non_uniform_aware_expression(ops[2]), ".GetDimensions(", to_expression(ops[1]), ");");
|
||||
uint32_t offset = type_struct_member_offset(type, ops[3]);
|
||||
uint32_t stride = type_struct_member_array_stride(type, ops[3]);
|
||||
statement(to_expression(ops[1]), " = (", to_expression(ops[1]), " - ", offset, ") / ", stride, ";");
|
||||
@@ -5648,6 +5696,7 @@ VariableID CompilerHLSL::remap_num_workgroups_builtin()
|
||||
ir.meta[variable_id].decoration.alias = "SPIRV_Cross_NumWorkgroups";
|
||||
|
||||
num_workgroups_builtin = variable_id;
|
||||
get_entry_point().interface_variables.push_back(num_workgroups_builtin);
|
||||
return variable_id;
|
||||
}
|
||||
|
||||
@@ -5718,6 +5767,9 @@ string CompilerHLSL::compile()
|
||||
backend.nonuniform_qualifier = "NonUniformResourceIndex";
|
||||
backend.support_case_fallthrough = false;
|
||||
|
||||
// SM 4.1 does not support precise for some reason.
|
||||
backend.support_precise_qualifier = hlsl_options.shader_model >= 50 || hlsl_options.shader_model == 40;
|
||||
|
||||
fixup_type_alias();
|
||||
reorder_type_alias();
|
||||
build_function_control_flow_graphs_and_analyze();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016-2021 Robert Konrad
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_HLSL_HPP
|
||||
@@ -219,7 +219,10 @@ private:
|
||||
void emit_resources();
|
||||
void declare_undefined_values() override;
|
||||
void emit_interface_block_globally(const SPIRVariable &type);
|
||||
void emit_interface_block_in_struct(const SPIRVariable &type, std::unordered_set<uint32_t> &active_locations);
|
||||
void emit_interface_block_in_struct(const SPIRVariable &var, std::unordered_set<uint32_t> &active_locations);
|
||||
void emit_interface_block_member_in_struct(const SPIRVariable &var, uint32_t member_index,
|
||||
uint32_t location,
|
||||
std::unordered_set<uint32_t> &active_locations);
|
||||
void emit_builtin_inputs_in_struct();
|
||||
void emit_builtin_outputs_in_struct();
|
||||
void emit_texture_op(const Instruction &i, bool sparse) override;
|
||||
@@ -347,7 +350,6 @@ private:
|
||||
|
||||
uint32_t type_to_consumed_locations(const SPIRType &type) const;
|
||||
|
||||
void emit_io_block(const SPIRVariable &var);
|
||||
std::string to_semantic(uint32_t location, spv::ExecutionModel em, spv::StorageClass sc);
|
||||
|
||||
uint32_t num_workgroups_builtin = 0;
|
||||
@@ -369,6 +371,8 @@ private:
|
||||
|
||||
// Returns true for BuiltInSampleMask because gl_SampleMask[] is an array in SPIR-V, but SV_Coverage is a scalar in HLSL.
|
||||
bool builtin_translates_to_nonarray(spv::BuiltIn builtin) const override;
|
||||
|
||||
std::vector<TypeID> composite_selection_workaround_types;
|
||||
};
|
||||
} // namespace SPIRV_CROSS_NAMESPACE
|
||||
|
||||
|
||||
+1629
-677
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2016-2021 The Brenwill Workshop Ltd.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_MSL_HPP
|
||||
@@ -60,6 +60,7 @@ enum MSLShaderInputFormat
|
||||
struct MSLShaderInput
|
||||
{
|
||||
uint32_t location = 0;
|
||||
uint32_t component = 0;
|
||||
MSLShaderInputFormat format = MSL_SHADER_INPUT_FORMAT_OTHER;
|
||||
spv::BuiltIn builtin = spv::BuiltInMax;
|
||||
uint32_t vecsize = 0;
|
||||
@@ -71,15 +72,23 @@ struct MSLShaderInput
|
||||
// resources consumed by this binding, if the binding represents an array of resources.
|
||||
// If the resource array is a run-time-sized array, which are legal in GLSL or SPIR-V, this value
|
||||
// will be used to declare the array size in MSL, which does not support run-time-sized arrays.
|
||||
// For resources that are not held in a run-time-sized array, the count field does not need to be populated.
|
||||
// If pad_argument_buffer_resources is enabled, the base_type and count values are used to
|
||||
// specify the base type and array size of the resource in the argument buffer, if that resource
|
||||
// is not defined and used by the shader. With pad_argument_buffer_resources enabled, this
|
||||
// information will be used to pad the argument buffer structure, in order to align that
|
||||
// structure consistently for all uses, across all shaders, of the descriptor set represented
|
||||
// by the arugment buffer. If pad_argument_buffer_resources is disabled, base_type does not
|
||||
// need to be populated, and if the resource is also not a run-time sized array, the count
|
||||
// field does not need to be populated.
|
||||
// If using MSL 2.0 argument buffers, the descriptor set is not marked as a discrete descriptor set,
|
||||
// and (for iOS only) the resource is not a storage image (sampled != 2), the binding reference we
|
||||
// remap to will become an [[id(N)]] attribute within the "descriptor set" argument buffer structure.
|
||||
// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will become a
|
||||
// [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
|
||||
// For resources which are bound in the "classic" MSL 1.0 way or discrete descriptors, the remap will
|
||||
// become a [[buffer(N)]], [[texture(N)]] or [[sampler(N)]] depending on the resource types used.
|
||||
struct MSLResourceBinding
|
||||
{
|
||||
spv::ExecutionModel stage = spv::ExecutionModelMax;
|
||||
SPIRType::BaseType basetype = SPIRType::Unknown;
|
||||
uint32_t desc_set = 0;
|
||||
uint32_t binding = 0;
|
||||
uint32_t count = 0;
|
||||
@@ -346,6 +355,19 @@ public:
|
||||
// and would otherwise declare a different IAB.
|
||||
bool force_active_argument_buffer_resources = false;
|
||||
|
||||
// Aligns each resource in an argument buffer to its assigned index value, id(N),
|
||||
// by adding synthetic padding members in the argument buffer struct for any resources
|
||||
// in the argument buffer that are not defined and used by the shader. This allows
|
||||
// the shader to index into the correct argument in a descriptor set argument buffer
|
||||
// that is shared across shaders, where not all resources in the argument buffer are
|
||||
// defined in each shader. For this to work, an MSLResourceBinding must be provided for
|
||||
// all descriptors in any descriptor set held in an argument buffer in the shader, and
|
||||
// that MSLResourceBinding must have the basetype and count members populated correctly.
|
||||
// The implementation here assumes any inline blocks in the argument buffer is provided
|
||||
// in a Metal buffer, and doesn't take into consideration inline blocks that are
|
||||
// optionally embedded directly into the argument buffer via add_inline_uniform_block().
|
||||
bool pad_argument_buffer_resources = false;
|
||||
|
||||
// Forces the use of plain arrays, which works around certain driver bugs on certain versions
|
||||
// of Intel Macbooks. See https://github.com/KhronosGroup/SPIRV-Cross/issues/1210.
|
||||
// May reduce performance in scenarios where arrays are copied around as value-types.
|
||||
@@ -634,6 +656,8 @@ protected:
|
||||
SPVFuncImplImage2DAtomicCoords, // Emulate texture2D atomic operations
|
||||
SPVFuncImplFMul,
|
||||
SPVFuncImplFAdd,
|
||||
SPVFuncImplFSub,
|
||||
SPVFuncImplQuantizeToF16,
|
||||
SPVFuncImplCubemapTo2DArrayFace,
|
||||
SPVFuncImplUnsafeArray, // Allow Metal to use the array<T> template to make arrays a value type
|
||||
SPVFuncImplInverse4x4,
|
||||
@@ -708,13 +732,17 @@ protected:
|
||||
const std::string &qualifier = "", uint32_t base_offset = 0) override;
|
||||
void emit_struct_padding_target(const SPIRType &type) override;
|
||||
std::string type_to_glsl(const SPIRType &type, uint32_t id = 0) override;
|
||||
void emit_block_hints(const SPIRBlock &block) override;
|
||||
|
||||
// Allow Metal to use the array<T> template to make arrays a value type
|
||||
std::string type_to_array_glsl(const SPIRType &type) override;
|
||||
std::string constant_op_expression(const SPIRConstantOp &cop) override;
|
||||
|
||||
// Threadgroup arrays can't have a wrapper type
|
||||
std::string variable_decl(const SPIRVariable &variable) override;
|
||||
|
||||
bool variable_decl_is_remapped_storage(const SPIRVariable &variable, spv::StorageClass storage) const override;
|
||||
|
||||
// GCC workaround of lambdas calling protected functions (for older GCC versions)
|
||||
std::string variable_decl(const SPIRType &type, const std::string &name, uint32_t id = 0) override;
|
||||
|
||||
@@ -775,33 +803,44 @@ protected:
|
||||
{
|
||||
struct LocationMeta
|
||||
{
|
||||
uint32_t base_type_id = 0;
|
||||
uint32_t num_components = 0;
|
||||
uint32_t ib_index = ~0u;
|
||||
bool flat = false;
|
||||
bool noperspective = false;
|
||||
bool centroid = false;
|
||||
bool sample = false;
|
||||
};
|
||||
std::unordered_map<uint32_t, LocationMeta> location_meta;
|
||||
bool strip_array = false;
|
||||
bool allow_local_declaration = false;
|
||||
};
|
||||
|
||||
std::string to_tesc_invocation_id();
|
||||
void emit_local_masked_variable(const SPIRVariable &masked_var, bool strip_array);
|
||||
void add_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref, SPIRType &ib_type,
|
||||
SPIRVariable &var, InterfaceBlockMeta &meta);
|
||||
void add_composite_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
|
||||
SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
|
||||
void add_plain_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
|
||||
SPIRType &ib_type, SPIRVariable &var, InterfaceBlockMeta &meta);
|
||||
bool add_component_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
|
||||
SPIRVariable &var, const SPIRType &type,
|
||||
InterfaceBlockMeta &meta);
|
||||
void add_plain_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
|
||||
SPIRType &ib_type, SPIRVariable &var, uint32_t index,
|
||||
InterfaceBlockMeta &meta);
|
||||
void add_composite_member_variable_to_interface_block(spv::StorageClass storage, const std::string &ib_var_ref,
|
||||
SPIRType &ib_type, SPIRVariable &var, uint32_t index,
|
||||
InterfaceBlockMeta &meta);
|
||||
uint32_t get_accumulated_member_location(const SPIRVariable &var, uint32_t mbr_idx, bool strip_array);
|
||||
void add_tess_level_input_to_interface_block(const std::string &ib_var_ref, SPIRType &ib_type, SPIRVariable &var);
|
||||
|
||||
void fix_up_interface_member_indices(spv::StorageClass storage, uint32_t ib_type_id);
|
||||
|
||||
void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type, spv::StorageClass storage);
|
||||
void mark_location_as_used_by_shader(uint32_t location, const SPIRType &type,
|
||||
spv::StorageClass storage, bool fallback = false);
|
||||
uint32_t ensure_correct_builtin_type(uint32_t type_id, spv::BuiltIn builtin);
|
||||
uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t num_components = 0);
|
||||
uint32_t ensure_correct_input_type(uint32_t type_id, uint32_t location, uint32_t component,
|
||||
uint32_t num_components, bool strip_array);
|
||||
|
||||
void emit_custom_templates();
|
||||
void emit_custom_functions();
|
||||
@@ -825,11 +864,13 @@ protected:
|
||||
std::string to_swizzle_expression(uint32_t id);
|
||||
std::string to_buffer_size_expression(uint32_t id);
|
||||
bool is_sample_rate() const;
|
||||
bool is_intersection_query() const;
|
||||
bool is_direct_input_builtin(spv::BuiltIn builtin);
|
||||
std::string builtin_qualifier(spv::BuiltIn builtin);
|
||||
std::string builtin_type_decl(spv::BuiltIn builtin, uint32_t id = 0);
|
||||
std::string built_in_func_arg(spv::BuiltIn builtin, bool prefix_comma);
|
||||
std::string member_attribute_qualifier(const SPIRType &type, uint32_t index);
|
||||
std::string member_location_attribute_qualifier(const SPIRType &type, uint32_t index);
|
||||
std::string argument_decl(const SPIRFunction::Parameter &arg);
|
||||
std::string round_fp_tex_coords(std::string tex_coords, bool coord_is_fp);
|
||||
uint32_t get_metal_resource_index(SPIRVariable &var, SPIRType::BaseType basetype, uint32_t plane = 0);
|
||||
@@ -886,11 +927,13 @@ protected:
|
||||
void add_pragma_line(const std::string &line);
|
||||
void add_typedef_line(const std::string &line);
|
||||
void emit_barrier(uint32_t id_exe_scope, uint32_t id_mem_scope, uint32_t id_mem_sem);
|
||||
void emit_array_copy(const std::string &lhs, uint32_t rhs_id, spv::StorageClass lhs_storage,
|
||||
spv::StorageClass rhs_storage) override;
|
||||
void emit_array_copy(const std::string &lhs, uint32_t lhs_id, uint32_t rhs_id,
|
||||
spv::StorageClass lhs_storage, spv::StorageClass rhs_storage) override;
|
||||
void build_implicit_builtins();
|
||||
uint32_t build_constant_uint_array_pointer();
|
||||
void emit_entry_point_declarations() override;
|
||||
bool uses_explicit_early_fragment_test();
|
||||
|
||||
uint32_t builtin_frag_coord_id = 0;
|
||||
uint32_t builtin_sample_id_id = 0;
|
||||
uint32_t builtin_sample_mask_id = 0;
|
||||
@@ -913,15 +956,19 @@ protected:
|
||||
uint32_t view_mask_buffer_id = 0;
|
||||
uint32_t dynamic_offsets_buffer_id = 0;
|
||||
uint32_t uint_type_id = 0;
|
||||
uint32_t argument_buffer_padding_buffer_type_id = 0;
|
||||
uint32_t argument_buffer_padding_image_type_id = 0;
|
||||
uint32_t argument_buffer_padding_sampler_type_id = 0;
|
||||
|
||||
bool does_shader_write_sample_mask = false;
|
||||
|
||||
void cast_to_builtin_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
|
||||
void cast_from_builtin_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
|
||||
void cast_to_variable_store(uint32_t target_id, std::string &expr, const SPIRType &expr_type) override;
|
||||
void cast_from_variable_load(uint32_t source_id, std::string &expr, const SPIRType &expr_type) override;
|
||||
void emit_store_statement(uint32_t lhs_expression, uint32_t rhs_expression) override;
|
||||
|
||||
void analyze_sampled_image_usage();
|
||||
|
||||
bool access_chain_needs_stage_io_builtin_translation(uint32_t base) override;
|
||||
void prepare_access_chain_for_scalar_access(std::string &expr, const SPIRType &type, spv::StorageClass storage,
|
||||
bool &is_packed) override;
|
||||
void fix_up_interpolant_access_chain(const uint32_t *ops, uint32_t length);
|
||||
@@ -938,9 +985,10 @@ protected:
|
||||
Options msl_options;
|
||||
std::set<SPVFuncImpl> spv_function_implementations;
|
||||
// Must be ordered to ensure declarations are in a specific order.
|
||||
std::map<uint32_t, MSLShaderInput> inputs_by_location;
|
||||
std::map<LocationComponentPair, MSLShaderInput> inputs_by_location;
|
||||
std::unordered_map<uint32_t, MSLShaderInput> inputs_by_builtin;
|
||||
std::unordered_set<uint32_t> location_inputs_in_use;
|
||||
std::unordered_set<uint32_t> location_inputs_in_use_fallback;
|
||||
std::unordered_map<uint32_t, uint32_t> fragment_output_components;
|
||||
std::unordered_map<uint32_t, uint32_t> builtin_to_automatic_input_location;
|
||||
std::set<std::string> pragma_lines;
|
||||
@@ -948,7 +996,7 @@ protected:
|
||||
SmallVector<uint32_t> vars_needing_early_declaration;
|
||||
|
||||
std::unordered_map<StageSetBinding, std::pair<MSLResourceBinding, bool>, InternalHasher> resource_bindings;
|
||||
uint32_t type_to_location_count(const SPIRType &type) const;
|
||||
std::unordered_map<StageSetBinding, uint32_t, InternalHasher> resource_arg_buff_idx_to_binding_number;
|
||||
|
||||
uint32_t next_metal_resource_index_buffer = 0;
|
||||
uint32_t next_metal_resource_index_texture = 0;
|
||||
@@ -962,6 +1010,7 @@ protected:
|
||||
VariableID patch_stage_out_var_id = 0;
|
||||
VariableID stage_in_ptr_var_id = 0;
|
||||
VariableID stage_out_ptr_var_id = 0;
|
||||
VariableID stage_out_masked_builtin_type_id = 0;
|
||||
|
||||
// Handle HLSL-style 0-based vertex/instance index.
|
||||
enum class TriState
|
||||
@@ -1027,6 +1076,11 @@ protected:
|
||||
|
||||
void analyze_argument_buffers();
|
||||
bool descriptor_set_is_argument_buffer(uint32_t desc_set) const;
|
||||
MSLResourceBinding &get_argument_buffer_resource(uint32_t desc_set, uint32_t arg_idx);
|
||||
void add_argument_buffer_padding_buffer_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_image_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_sampler_type(SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, MSLResourceBinding &rez_bind);
|
||||
void add_argument_buffer_padding_type(uint32_t mbr_type_id, SPIRType &struct_type, uint32_t &mbr_idx, uint32_t &arg_buff_index, uint32_t count);
|
||||
|
||||
uint32_t get_target_components_for_fragment_location(uint32_t location) const;
|
||||
uint32_t build_extended_vector_type(uint32_t type_id, uint32_t components,
|
||||
@@ -1044,6 +1098,11 @@ protected:
|
||||
bool type_is_pointer_to_pointer(const SPIRType &type) const;
|
||||
bool is_supported_argument_buffer_type(const SPIRType &type) const;
|
||||
|
||||
bool variable_storage_requires_stage_io(spv::StorageClass storage) const;
|
||||
|
||||
bool has_additional_fixed_sample_mask() const { return msl_options.additional_fixed_sample_mask != 0xffffffff; }
|
||||
std::string additional_fixed_sample_mask_str() const;
|
||||
|
||||
// OpcodeHandler that handles several MSL preprocessing operations.
|
||||
struct OpCodePreprocessor : OpcodeHandler
|
||||
{
|
||||
@@ -1087,11 +1146,8 @@ protected:
|
||||
{
|
||||
enum SortAspect
|
||||
{
|
||||
Location,
|
||||
LocationReverse,
|
||||
Offset,
|
||||
OffsetThenLocationReverse,
|
||||
Alphabetical
|
||||
LocationThenBuiltInType,
|
||||
Offset
|
||||
};
|
||||
|
||||
void sort();
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_parser.hpp"
|
||||
@@ -961,6 +961,49 @@ void Parser::parse(const Instruction &instruction)
|
||||
current_block->false_block = ops[2];
|
||||
|
||||
current_block->terminator = SPIRBlock::Select;
|
||||
|
||||
if (current_block->true_block == current_block->false_block)
|
||||
{
|
||||
// Bogus conditional, translate to a direct branch.
|
||||
// Avoids some ugly edge cases later when analyzing CFGs.
|
||||
|
||||
// There are some super jank cases where the merge block is different from the true/false,
|
||||
// and later branches can "break" out of the selection construct this way.
|
||||
// This is complete nonsense, but CTS hits this case.
|
||||
// In this scenario, we should see the selection construct as more of a Switch with one default case.
|
||||
// The problem here is that this breaks any attempt to break out of outer switch statements,
|
||||
// but it's theoretically solvable if this ever comes up using the ladder breaking system ...
|
||||
|
||||
if (current_block->true_block != current_block->next_block &&
|
||||
current_block->merge == SPIRBlock::MergeSelection)
|
||||
{
|
||||
uint32_t ids = ir.increase_bound_by(2);
|
||||
|
||||
SPIRType type;
|
||||
type.basetype = SPIRType::Int;
|
||||
type.width = 32;
|
||||
set<SPIRType>(ids, type);
|
||||
auto &c = set<SPIRConstant>(ids + 1, ids);
|
||||
|
||||
current_block->condition = c.self;
|
||||
current_block->default_block = current_block->true_block;
|
||||
current_block->terminator = SPIRBlock::MultiSelect;
|
||||
ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
|
||||
ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
ir.block_meta[current_block->next_block] &= ~ParsedIR::BLOCK_META_SELECTION_MERGE_BIT;
|
||||
current_block->next_block = current_block->true_block;
|
||||
current_block->condition = 0;
|
||||
current_block->true_block = 0;
|
||||
current_block->false_block = 0;
|
||||
current_block->merge_block = 0;
|
||||
current_block->merge = SPIRBlock::MergeNone;
|
||||
current_block->terminator = SPIRBlock::Direct;
|
||||
}
|
||||
}
|
||||
|
||||
current_block = nullptr;
|
||||
break;
|
||||
}
|
||||
@@ -975,8 +1018,21 @@ void Parser::parse(const Instruction &instruction)
|
||||
current_block->condition = ops[0];
|
||||
current_block->default_block = ops[1];
|
||||
|
||||
for (uint32_t i = 2; i + 2 <= length; i += 2)
|
||||
current_block->cases.push_back({ ops[i], ops[i + 1] });
|
||||
uint32_t remaining_ops = length - 2;
|
||||
if ((remaining_ops % 2) == 0)
|
||||
{
|
||||
for (uint32_t i = 2; i + 2 <= length; i += 2)
|
||||
current_block->cases_32bit.push_back({ ops[i], ops[i + 1] });
|
||||
}
|
||||
|
||||
if ((remaining_ops % 3) == 0)
|
||||
{
|
||||
for (uint32_t i = 2; i + 3 <= length; i += 3)
|
||||
{
|
||||
uint64_t value = (static_cast<uint64_t>(ops[i + 1]) << 32) | ops[i];
|
||||
current_block->cases_64bit.push_back({ value, ops[i + 2] });
|
||||
}
|
||||
}
|
||||
|
||||
// If we jump to next block, make it break instead since we're inside a switch case block at that point.
|
||||
ir.block_meta[current_block->next_block] |= ParsedIR::BLOCK_META_MULTISELECT_MERGE_BIT;
|
||||
@@ -1134,6 +1190,14 @@ void Parser::parse(const Instruction &instruction)
|
||||
// Actual opcodes.
|
||||
default:
|
||||
{
|
||||
if (length >= 2)
|
||||
{
|
||||
const auto *type = maybe_get<SPIRType>(ops[0]);
|
||||
if (type)
|
||||
{
|
||||
ir.load_type_width.insert({ ops[1], type->width });
|
||||
}
|
||||
}
|
||||
if (!current_block)
|
||||
SPIRV_CROSS_THROW("Currently no block to insert opcode.");
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Arm Limited
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_PARSER_HPP
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Bradley Austin Davis
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#include "spirv_reflect.hpp"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright 2018-2021 Bradley Austin Davis
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -18,7 +19,6 @@
|
||||
* At your option, you may choose to accept this material under either:
|
||||
* 1. The Apache License, Version 2.0, found at <http://www.apache.org/licenses/LICENSE-2.0>, or
|
||||
* 2. The MIT License, found at <http://opensource.org/licenses/MIT>.
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT.
|
||||
*/
|
||||
|
||||
#ifndef SPIRV_CROSS_REFLECT_HPP
|
||||
|
||||
Reference in New Issue
Block a user