GPUShaderCreateInfo: Merge changes from eevee-rewrite

This includes multiple commits:
- Fix crash when using std::cerr for error output
- Add auto_resource_location which overrides all resources location (not vert input)
- Improve codestyle of error reporting.
- Add type conversion to string and to `eGPUType`
- Add comparison operator (will be used for hash collision resolution).
- Add members related to generated code (codegen)
This commit is contained in:
2022-02-04 19:55:19 +01:00
parent 03fbb3de24
commit 77d005ea36
2 changed files with 273 additions and 21 deletions

View File

@@ -62,11 +62,6 @@ void ShaderCreateInfo::finalize()
/* Recursive. */
const_cast<ShaderCreateInfo &>(info).finalize();
#if 0 /* Enabled for debugging merging. TODO(fclem) exception handling and error reporting in \
console. */
std::cout << "Merging : " << info_name << " > " << name_ << std::endl;
#endif
interface_names_size_ += info.interface_names_size_;
vertex_inputs_.extend(info.vertex_inputs_);
@@ -83,37 +78,72 @@ void ShaderCreateInfo::finalize()
validate(info);
auto assert_no_overlap = [&](const bool test, const StringRefNull error) {
if (!test) {
std::cout << name_ << ": Validation failed while merging " << info.name_ << " : ";
std::cout << error << std::endl;
BLI_assert(0);
}
};
if (info.compute_layout_.local_size_x != -1) {
compute_layout_.local_size_x = info.compute_layout_.local_size_x;
compute_layout_.local_size_y = info.compute_layout_.local_size_y;
compute_layout_.local_size_z = info.compute_layout_.local_size_z;
assert_no_overlap(compute_layout_.local_size_x == -1, "Compute layout already defined");
compute_layout_ = info.compute_layout_;
}
if (!info.vertex_source_.is_empty()) {
BLI_assert(vertex_source_.is_empty());
assert_no_overlap(vertex_source_.is_empty(), "Vertex source already existing");
vertex_source_ = info.vertex_source_;
}
if (!info.geometry_source_.is_empty()) {
BLI_assert(geometry_source_.is_empty());
assert_no_overlap(geometry_source_.is_empty(), "Geometry source already existing");
geometry_source_ = info.geometry_source_;
geometry_layout_ = info.geometry_layout_;
}
if (!info.fragment_source_.is_empty()) {
BLI_assert(fragment_source_.is_empty());
assert_no_overlap(fragment_source_.is_empty(), "Fragment source already existing");
fragment_source_ = info.fragment_source_;
}
if (!info.compute_source_.is_empty()) {
BLI_assert(compute_source_.is_empty());
assert_no_overlap(compute_source_.is_empty(), "Compute source already existing");
compute_source_ = info.compute_source_;
}
do_static_compilation_ = do_static_compilation_ || info.do_static_compilation_;
}
if (auto_resource_location_) {
int images = 0, samplers = 0, ubos = 0, ssbos = 0;
auto set_resource_slot = [&](Resource &res) {
switch (res.bind_type) {
case Resource::BindType::UNIFORM_BUFFER:
res.slot = ubos++;
break;
case Resource::BindType::STORAGE_BUFFER:
res.slot = ssbos++;
break;
case Resource::BindType::SAMPLER:
res.slot = samplers++;
break;
case Resource::BindType::IMAGE:
res.slot = images++;
break;
}
};
for (auto &res : batch_resources_) {
set_resource_slot(res);
}
for (auto &res : pass_resources_) {
set_resource_slot(res);
}
}
}
void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info)
{
{
if (!auto_resource_location_) {
/* Check same bind-points usage in OGL. */
Set<int> images, samplers, ubos, ssbos;
@@ -133,26 +163,26 @@ void ShaderCreateInfo::validate(const ShaderCreateInfo &other_info)
};
auto print_error_msg = [&](const Resource &res) {
std::cerr << name_ << ": Validation failed : Overlapping ";
std::cout << name_ << ": Validation failed : Overlapping ";
switch (res.bind_type) {
case Resource::BindType::UNIFORM_BUFFER:
std::cerr << "Uniform Buffer " << res.uniformbuf.name;
std::cout << "Uniform Buffer " << res.uniformbuf.name;
break;
case Resource::BindType::STORAGE_BUFFER:
std::cerr << "Storage Buffer " << res.storagebuf.name;
std::cout << "Storage Buffer " << res.storagebuf.name;
break;
case Resource::BindType::SAMPLER:
std::cerr << "Sampler " << res.sampler.name;
std::cout << "Sampler " << res.sampler.name;
break;
case Resource::BindType::IMAGE:
std::cerr << "Image " << res.image.name;
std::cout << "Image " << res.image.name;
break;
default:
std::cerr << "Unknown Type";
std::cout << "Unknown Type";
break;
}
std::cerr << " (" << res.slot << ") while merging " << other_info.name_ << std::endl;
std::cout << " (" << res.slot << ") while merging " << other_info.name_ << std::endl;
};
for (auto &res : batch_resources_) {

View File

@@ -30,8 +30,11 @@
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "GPU_material.h"
#include "GPU_texture.h"
#include <iostream>
namespace blender::gpu::shader {
#ifndef GPU_SHADER_CREATE_INFO
@@ -62,6 +65,59 @@ enum class Type {
BOOL,
};
/* All of these functions is a bit out of place */
static inline Type to_type(const eGPUType type)
{
switch (type) {
case GPU_FLOAT:
return Type::FLOAT;
case GPU_VEC2:
return Type::VEC2;
case GPU_VEC3:
return Type::VEC3;
case GPU_VEC4:
return Type::VEC4;
case GPU_MAT3:
return Type::MAT3;
case GPU_MAT4:
return Type::MAT4;
default:
BLI_assert_msg(0, "Error: Cannot convert eGPUType to shader::Type.");
return Type::FLOAT;
}
}
static inline std::ostream &operator<<(std::ostream &stream, const Type type)
{
switch (type) {
case Type::FLOAT:
return stream << "float";
case Type::VEC2:
return stream << "vec2";
case Type::VEC3:
return stream << "vec3";
case Type::VEC4:
return stream << "vec4";
case Type::MAT3:
return stream << "mat3";
case Type::MAT4:
return stream << "mat4";
default:
BLI_assert(0);
return stream;
}
}
static inline std::ostream &operator<<(std::ostream &stream, const eGPUType type)
{
switch (type) {
case GPU_CLOSURE:
return stream << "Closure";
default:
return stream << to_type(type);
}
}
enum class BuiltinBits {
NONE = 0,
/**
@@ -229,6 +285,8 @@ struct ShaderCreateInfo {
bool do_static_compilation_ = false;
/** If true, all additionally linked create info will be merged into this one. */
bool finalized_ = false;
/** If true, all resources will have an automatic location assigned. */
bool auto_resource_location_ = false;
/**
* Maximum length of all the resource names including each null terminator.
* Only for names used by gpu::ShaderInterface.
@@ -236,11 +294,36 @@ struct ShaderCreateInfo {
size_t interface_names_size_ = 0;
/** Manually set builtins. */
BuiltinBits builtins_ = BuiltinBits::NONE;
/** Manually set generated code. */
std::string vertex_source_generated = "";
std::string fragment_source_generated = "";
std::string typedef_source_generated = "";
/** Manually set generated dependencies. */
Vector<const char *, 0> dependencies_generated;
#define TEST_EQUAL(a, b, _member) \
if (!((a)._member == (b)._member)) { \
return false; \
}
#define TEST_VECTOR_EQUAL(a, b, _vector) \
TEST_EQUAL(a, b, _vector.size()); \
for (auto i : _vector.index_range()) { \
TEST_EQUAL(a, b, _vector[i]); \
}
struct VertIn {
int index;
Type type;
StringRefNull name;
bool operator==(const VertIn &b)
{
TEST_EQUAL(*this, b, index);
TEST_EQUAL(*this, b, type);
TEST_EQUAL(*this, b, name);
return true;
}
};
Vector<VertIn> vertex_inputs_;
@@ -250,6 +333,15 @@ struct ShaderCreateInfo {
PrimitiveOut primitive_out;
/** Set to -1 by default to check if used. */
int max_vertices = -1;
bool operator==(const GeometryStageLayout &b)
{
TEST_EQUAL(*this, b, primitive_in);
TEST_EQUAL(*this, b, invocations);
TEST_EQUAL(*this, b, primitive_out);
TEST_EQUAL(*this, b, max_vertices);
return true;
}
};
GeometryStageLayout geometry_layout_;
@@ -257,8 +349,15 @@ struct ShaderCreateInfo {
int local_size_x = -1;
int local_size_y = -1;
int local_size_z = -1;
};
bool operator==(const ComputeStageLayout &b)
{
TEST_EQUAL(*this, b, local_size_x);
TEST_EQUAL(*this, b, local_size_y);
TEST_EQUAL(*this, b, local_size_z);
return true;
}
};
ComputeStageLayout compute_layout_;
struct FragOut {
@@ -266,6 +365,15 @@ struct ShaderCreateInfo {
Type type;
DualBlend blend;
StringRefNull name;
bool operator==(const FragOut &b)
{
TEST_EQUAL(*this, b, index);
TEST_EQUAL(*this, b, type);
TEST_EQUAL(*this, b, blend);
TEST_EQUAL(*this, b, name);
return true;
}
};
Vector<FragOut> fragment_outputs_;
@@ -311,6 +419,35 @@ struct ShaderCreateInfo {
};
Resource(BindType type, int _slot) : bind_type(type), slot(_slot){};
bool operator==(const Resource &b)
{
TEST_EQUAL(*this, b, bind_type);
TEST_EQUAL(*this, b, slot);
switch (bind_type) {
case UNIFORM_BUFFER:
TEST_EQUAL(*this, b, uniformbuf.type_name);
TEST_EQUAL(*this, b, uniformbuf.name);
break;
case STORAGE_BUFFER:
TEST_EQUAL(*this, b, storagebuf.qualifiers);
TEST_EQUAL(*this, b, storagebuf.type_name);
TEST_EQUAL(*this, b, storagebuf.name);
break;
case SAMPLER:
TEST_EQUAL(*this, b, sampler.type);
TEST_EQUAL(*this, b, sampler.sampler);
TEST_EQUAL(*this, b, sampler.name);
break;
case IMAGE:
TEST_EQUAL(*this, b, image.format);
TEST_EQUAL(*this, b, image.type);
TEST_EQUAL(*this, b, image.qualifiers);
TEST_EQUAL(*this, b, image.name);
break;
}
return true;
}
};
/**
* Resources are grouped by frequency of change.
@@ -327,6 +464,14 @@ struct ShaderCreateInfo {
Type type;
StringRefNull name;
int array_size;
bool operator==(const PushConst &b)
{
TEST_EQUAL(*this, b, type);
TEST_EQUAL(*this, b, name);
TEST_EQUAL(*this, b, array_size);
return true;
}
};
Vector<PushConst> push_constants_;
@@ -554,6 +699,12 @@ struct ShaderCreateInfo {
return *(Self *)this;
}
Self &auto_resource_location(bool value)
{
auto_resource_location_ = value;
return *(Self *)this;
}
/** \} */
/* -------------------------------------------------------------------- */
@@ -624,6 +775,77 @@ struct ShaderCreateInfo {
void validate(const ShaderCreateInfo &other_info);
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operators.
*
* \{ */
/* Comparison operator for GPUPass cache. We only compare if it will create the same shader code.
* So we do not compare name and some other internal stuff. */
bool operator==(const ShaderCreateInfo &b)
{
TEST_EQUAL(*this, b, builtins_);
TEST_EQUAL(*this, b, vertex_source_generated);
TEST_EQUAL(*this, b, fragment_source_generated);
TEST_EQUAL(*this, b, typedef_source_generated);
TEST_VECTOR_EQUAL(*this, b, vertex_inputs_);
TEST_EQUAL(*this, b, geometry_layout_);
TEST_EQUAL(*this, b, compute_layout_);
TEST_VECTOR_EQUAL(*this, b, fragment_outputs_);
TEST_VECTOR_EQUAL(*this, b, pass_resources_);
TEST_VECTOR_EQUAL(*this, b, batch_resources_);
TEST_VECTOR_EQUAL(*this, b, vertex_out_interfaces_);
TEST_VECTOR_EQUAL(*this, b, geometry_out_interfaces_);
TEST_VECTOR_EQUAL(*this, b, push_constants_);
TEST_VECTOR_EQUAL(*this, b, typedef_sources_);
TEST_EQUAL(*this, b, vertex_source_);
TEST_EQUAL(*this, b, geometry_source_);
TEST_EQUAL(*this, b, fragment_source_);
TEST_EQUAL(*this, b, compute_source_);
TEST_VECTOR_EQUAL(*this, b, additional_infos_);
TEST_VECTOR_EQUAL(*this, b, defines_);
return true;
}
/** Debug print */
friend std::ostream &operator<<(std::ostream &stream, const ShaderCreateInfo &info)
{
/* TODO(@fclem): Complete print. */
auto print_resource = [&](const Resource &res) {
switch (res.bind_type) {
case Resource::BindType::UNIFORM_BUFFER:
stream << "UNIFORM_BUFFER(" << res.slot << ", " << res.uniformbuf.name << ")"
<< std::endl;
break;
case Resource::BindType::STORAGE_BUFFER:
stream << "STORAGE_BUFFER(" << res.slot << ", " << res.storagebuf.name << ")"
<< std::endl;
break;
case Resource::BindType::SAMPLER:
stream << "SAMPLER(" << res.slot << ", " << res.sampler.name << ")" << std::endl;
break;
case Resource::BindType::IMAGE:
stream << "IMAGE(" << res.slot << ", " << res.image.name << ")" << std::endl;
break;
}
};
/* TODO(@fclem): Order the resources. */
for (auto &res : info.batch_resources_) {
print_resource(res);
}
for (auto &res : info.pass_resources_) {
print_resource(res);
}
return stream;
}
/** \} */
#undef TEST_EQUAL
#undef TEST_VECTOR_EQUAL
};
} // namespace blender::gpu::shader