#pragma once #include #include "hwrenderer/data/uniformbuffer.h" #include "v_video.h" enum class UniformType { Int, UInt, Float, Vec2, Vec3, Vec4, IVec2, IVec3, IVec4, UVec2, UVec3, UVec4, Mat4 }; class UniformFieldDesc { public: UniformFieldDesc() { } UniformFieldDesc(const char *name, UniformType type, std::size_t offset) : Name(name), Type(type), Offset(offset) { } const char *Name; UniformType Type; std::size_t Offset; }; template class ShaderUniforms { public: ShaderUniforms() { memset(&Values, 0, sizeof(Values)); } ~ShaderUniforms() { if (mBuffer != nullptr) delete mBuffer; } int BindingPoint() const { return bindingpoint; } FString CreateDeclaration(const char *name, const std::vector &fields) { mFields = fields; FString decl; FString layout; if (screen->glslversion < 4.20) { layout = "std140"; } else { layout.Format("std140, binding = %d", bindingpoint); } decl.Format("layout(%s) uniform %s\n{\n", layout.GetChars(), name); for (const auto &field : fields) { decl.AppendFormat("\t%s %s;\n", GetTypeStr(field.Type), field.Name); } decl += "};\n"; return decl; } void Init() { if (mBuffer == nullptr) mBuffer = screen->CreateUniformBuffer(sizeof(T)); } void Set(bool bind = true) { if (mBuffer != nullptr) mBuffer->SetData(&Values); // Let's hope this can be done better when things have moved further ahead. // This really is not the best place to add something that depends on API behavior. if (bind) mBuffer->Bind(bindingpoint); } T *operator->() { return &Values; } const T *operator->() const { return &Values; } T Values; private: ShaderUniforms(const ShaderUniforms &) = delete; ShaderUniforms &operator=(const ShaderUniforms &) = delete; const char *GetTypeStr(UniformType type) { switch (type) { default: case UniformType::Int: return "int"; case UniformType::UInt: return "uint"; case UniformType::Float: return "float"; case UniformType::Vec2: return "vec2"; case UniformType::Vec3: return "vec3"; case UniformType::Vec4: return "vec4"; case UniformType::IVec2: return "ivec2"; case UniformType::IVec3: return "ivec3"; case UniformType::IVec4: return "ivec4"; case UniformType::UVec2: return "uvec2"; case UniformType::UVec3: return "uvec3"; case UniformType::UVec4: return "uvec4"; case UniformType::Mat4: return "mat4"; } } IUniformBuffer *mBuffer = nullptr; std::vector mFields; }; enum class SamplerType : int { Sampler1D, Sampler2D, Sampler3D, SamplerCube, iSampler1D, iSampler2D, iSampler3D, iSamplerCube, uSampler1D, uSampler2D, uSampler3D, uSamplerCube, }; struct SamplerUniform { const char *GetTypeStr() const { switch (mType) { default: case SamplerType::Sampler1D: return "sampler1D"; case SamplerType::Sampler2D: return "sampler2D"; case SamplerType::Sampler3D: return "sampler3D"; case SamplerType::SamplerCube: return "samplerCube"; case SamplerType::iSampler1D: return "isampler1D"; case SamplerType::iSampler2D: return "isampler2D"; case SamplerType::iSampler3D: return "isampler3D"; case SamplerType::iSamplerCube: return "isamplerCube"; case SamplerType::uSampler1D: return "usampler1D"; case SamplerType::uSampler2D: return "usampler2D"; case SamplerType::uSampler3D: return "usampler3D"; case SamplerType::uSamplerCube: return "usamplerCube"; } } static FString CreateDeclaration(std::vector &samplers) { FString build; for (auto &sampler : samplers) { if (screen->glslversion >= 4.2f) build.AppendFormat("layout(binding = %d) uniform", sampler.mBinding); build.AppendFormat("%s %s;\n", sampler.GetTypeStr(), sampler.mName); } return build; } int mBinding; SamplerType mType; const char *mName; };