Преглед изворни кода

Add push constants support in the GL backend

Panagiotis Christopoulos Charitos пре 7 година
родитељ
комит
6a0132a595

+ 6 - 4
src/anki/gr/Common.h

@@ -104,12 +104,14 @@ enum class GpuVendor : U8
 
 extern Array<CString, U(GpuVendor::COUNT)> GPU_VENDOR_STR;
 
-enum class GpuDeviceCapabilitiesBit : U8
+/// Device capabilities.
+class GpuDeviceCapabilities
 {
-	NONE = 0,
-	SHADER_BALLOT = 1 << 0,
+public:
+	U32 m_pushConstantsSize = 128;
+	GpuVendor m_gpuVendor = GpuVendor::UNKNOWN;
+	Bool8 m_shaderSubgroups = false;
 };
-ANKI_ENUM_ALLOW_NUMERIC_OPERATIONS(GpuDeviceCapabilitiesBit, inline)
 
 /// The type of the allocator for heap allocations
 template<typename T>

+ 5 - 12
src/anki/gr/GrManager.h

@@ -42,13 +42,7 @@ public:
 	/// Destroy.
 	static void deleteInstance(GrManager* gr);
 
-	GpuVendor getGpuVendor() const
-	{
-		ANKI_ASSERT(m_gpuVendor != GpuVendor::COUNT);
-		return m_gpuVendor;
-	}
-
-	GpuDeviceCapabilitiesBit getDeviceCapabilities() const
+	const GpuDeviceCapabilities& getDeviceCapabilities() const
 	{
 		return m_capabilities;
 	}
@@ -101,17 +95,16 @@ anki_internal:
 		return m_cacheDir.toCString();
 	}
 
-	U64& getUuidIndex()
+	U64 getNewUuid()
 	{
-		return m_uuidIndex;
+		return m_uuidIndex.fetchAdd(1);
 	}
 
 protected:
 	GrAllocator<U8> m_alloc; ///< Keep it first to get deleted last
 	String m_cacheDir;
-	U64 m_uuidIndex = 1;
-	GpuVendor m_gpuVendor = GpuVendor::COUNT;
-	GpuDeviceCapabilitiesBit m_capabilities = GpuDeviceCapabilitiesBit::NONE;
+	Atomic<U64> m_uuidIndex = {1};
+	GpuDeviceCapabilities m_capabilities;
 
 	GrManager();
 

+ 1 - 1
src/anki/gr/GrObject.cpp

@@ -12,7 +12,7 @@ namespace anki
 GrObject::GrObject(GrManager* manager, GrObjectType type, CString name)
 	: m_refcount(0)
 	, m_manager(manager)
-	, m_uuid(m_manager->getUuidIndex()++)
+	, m_uuid(m_manager->getNewUuid())
 	, m_type(type)
 {
 	if(name && name.getLength())

+ 5 - 5
src/anki/gr/ShaderCompiler.cpp

@@ -18,7 +18,6 @@ namespace anki
 
 void ShaderCompilerOptions::setFromGrManager(const GrManager& gr)
 {
-	m_gpuVendor = gr.getGpuVendor();
 #if ANKI_GR_BACKEND == ANKI_GR_BACKEND_VULKAN
 	m_outLanguage = ShaderLanguage::SPIRV;
 #else
@@ -41,7 +40,7 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #	define ANKI_TEX_BINDING(set_, binding_) binding = set_ * %u + binding_
 #	define ANKI_IMAGE_BINDING(set_, binding_) binding = set_ * %u + binding_
 #	define ANKI_SPEC_CONST(binding_, type_, name_) const type_ name_ = _anki_spec_const_ ## binding_
-#	define ANKI_PUSH_CONSTANTS(struct_, name_) layout(location = 0) uniform struct_ name_
+#	define ANKI_PUSH_CONSTANTS(struct_, name_) layout(location = 0, row_major) uniform struct_ name_
 #else
 #	define gl_VertexID gl_VertexIndex
 #	define gl_InstanceID gl_InstanceIndex
@@ -50,7 +49,8 @@ static const char* SHADER_HEADER = R"(#version 450 core
 #	define ANKI_SS_BINDING(set_, binding_) set = set_, binding = %u + binding_
 #	define ANKI_IMAGE_BINDING(set_, binding_) set = set_, binding = %u + binding_
 #	define ANKI_SPEC_CONST(binding_, type_, name_) layout(constant_id = binding_) const type_ name_ = type_(0)
-#	define ANKI_PUSH_CONSTANTS(struct_, name_) layout(push_constant) uniform pushConst_ {struct_ name_;}
+#	define ANKI_PUSH_CONSTANTS(struct_, name_) layout(push_constant, row_major, std140) \
+		uniform pushConst_ {struct_ name_;}
 #endif
 
 #if %u
@@ -289,7 +289,7 @@ Error ShaderCompiler::compile(CString source, const ShaderCompilerOptions& optio
 
 	fullSrc.sprintf(SHADER_HEADER,
 		(options.m_outLanguage == ShaderLanguage::GLSL) ? "GL" : "VULKAN",
-		&GPU_VENDOR_STR[options.m_gpuVendor][0],
+		&GPU_VENDOR_STR[options.m_gpuCapabilities.m_gpuVendor][0],
 		SHADER_NAME[options.m_shaderType],
 		// GL bindings
 		MAX_UNIFORM_BUFFER_BINDINGS,
@@ -302,7 +302,7 @@ Error ShaderCompiler::compile(CString source, const ShaderCompilerOptions& optio
 		MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS,
 		MAX_TEXTURE_BINDINGS + MAX_UNIFORM_BUFFER_BINDINGS + MAX_STORAGE_BUFFER_BINDINGS,
 		// Ballot
-		!!(options.m_gpuCapabilities & GpuDeviceCapabilitiesBit::SHADER_BALLOT) ? 1u : 0u,
+		!!(options.m_gpuCapabilities.m_shaderSubgroups) ? 1u : 0u,
 		&source[0]);
 
 	ctx.m_src = fullSrc.toCString();

+ 1 - 2
src/anki/gr/ShaderCompiler.h

@@ -27,8 +27,7 @@ class ShaderCompilerOptions
 public:
 	ShaderLanguage m_outLanguage = ShaderLanguage::COUNT;
 	ShaderType m_shaderType = ShaderType::COUNT;
-	GpuVendor m_gpuVendor = GpuVendor::COUNT;
-	GpuDeviceCapabilitiesBit m_gpuCapabilities = GpuDeviceCapabilitiesBit::NONE;
+	GpuDeviceCapabilities m_gpuCapabilities;
 
 	void setFromGrManager(const GrManager& gr);
 };

+ 55 - 2
src/anki/gr/gl/CommandBuffer.cpp

@@ -822,8 +822,9 @@ void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 		{
 		}
 
-		Error operator()(GlState&)
+		Error operator()(GlState& state)
 		{
+			state.m_crntProg = m_prog;
 			glUseProgram(static_cast<const ShaderProgramImpl&>(*m_prog).getGlName());
 			return Error::NONE;
 		}
@@ -1488,7 +1489,59 @@ void CommandBuffer::writeOcclusionQueryResultToBuffer(OcclusionQueryPtr query, P
 
 void CommandBuffer::setPushConstants(const void* data, U32 dataSize)
 {
-	ANKI_ASSERT(!"TODO");
+	class PushConstants final : public GlCommand
+	{
+	public:
+		DynamicArrayAuto<Vec4> m_data;
+
+		PushConstants(const void* data, U32 dataSize, const CommandBufferAllocator<F32>& alloc)
+			: m_data(alloc)
+		{
+			m_data.create(dataSize / sizeof(Vec4));
+			memcpy(&m_data[0], data, dataSize);
+		}
+
+		Error operator()(GlState& state)
+		{
+			const ShaderProgramImplReflection& refl =
+				static_cast<ShaderProgramImpl&>(*state.m_crntProg).getReflection();
+			ANKI_ASSERT(refl.m_uniformDataSize == m_data.getSizeInBytes());
+
+			for(const ShaderProgramImplReflection::Uniform& uni : refl.m_uniforms)
+			{
+				const U idx = uni.m_location;
+				const U count = uni.m_arrSize;
+				const GLint loc = uni.m_location;
+
+				switch(uni.m_type)
+				{
+				case ShaderVariableDataType::VEC4:
+					glUniform4fv(loc, count, reinterpret_cast<const GLfloat*>(&m_data[idx]));
+					break;
+				case ShaderVariableDataType::IVEC4:
+					glUniform4iv(loc, count, reinterpret_cast<const GLint*>(&m_data[idx]));
+					break;
+				case ShaderVariableDataType::UVEC4:
+					glUniform4uiv(loc, count, reinterpret_cast<const GLuint*>(&m_data[idx]));
+					break;
+				case ShaderVariableDataType::MAT4:
+					glUniformMatrix4fv(loc, count, false, reinterpret_cast<const GLfloat*>(&m_data[idx]));
+					break;
+				default:
+					ANKI_ASSERT(!"TODO");
+				}
+			}
+
+			return Error::NONE;
+		}
+	};
+
+	ANKI_ASSERT(data);
+	ANKI_ASSERT(dataSize);
+	ANKI_ASSERT(dataSize % 16 == 0);
+
+	ANKI_GL_SELF(CommandBufferImpl);
+	self.pushBackNewCommand<PushConstants>(data, dataSize, self.m_alloc);
 }
 
 } // end namespace anki

+ 1 - 0
src/anki/gr/gl/GlState.cpp

@@ -201,6 +201,7 @@ void GlState::initRenderThread()
 void GlState::destroy()
 {
 	glDeleteVertexArrays(1, &m_defaultVao);
+	m_crntProg.reset(nullptr);
 }
 
 } // end namespace anki

+ 3 - 0
src/anki/gr/gl/GlState.h

@@ -7,6 +7,7 @@
 
 #include <anki/gr/gl/Common.h>
 #include <anki/util/DynamicArray.h>
+#include <anki/gr/ShaderProgram.h>
 
 namespace anki
 {
@@ -27,6 +28,8 @@ public:
 	GpuVendor m_gpu = GpuVendor::UNKNOWN;
 	Bool8 m_registerMessages = false;
 
+	ShaderProgramPtr m_crntProg;
+
 	GLuint m_defaultVao = 0;
 
 	U32 m_uboAlignment = 0;

+ 2 - 4
src/anki/gr/gl/GrManagerImpl.cpp

@@ -51,10 +51,8 @@ Error GrManagerImpl::init(GrManagerInitInfo& init, GrAllocator<U8> alloc)
 	m_thread->syncClientServer();
 
 	// Misc
-	m_gpuVendor = m_state->m_gpu;
-	m_capabilities = !!(m_state->m_extensions & GlExtensions::ARB_SHADER_BALLOT)
-						 ? GpuDeviceCapabilitiesBit::SHADER_BALLOT
-						 : GpuDeviceCapabilitiesBit::NONE;
+	m_capabilities.m_gpuVendor = m_state->m_gpu;
+	m_capabilities.m_shaderSubgroups = !!(m_state->m_extensions & GlExtensions::ARB_SHADER_BALLOT);
 
 	return Error::NONE;
 }

+ 64 - 0
src/anki/gr/gl/ShaderProgramImpl.cpp

@@ -20,6 +20,7 @@ static void deletePrograms(GLsizei n, const GLuint* progs)
 ShaderProgramImpl::~ShaderProgramImpl()
 {
 	destroyDeferred(getManager(), deletePrograms);
+	m_refl.m_uniforms.destroy(getAllocator());
 }
 
 Error ShaderProgramImpl::initGraphics(ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag)
@@ -93,4 +94,67 @@ Error ShaderProgramImpl::link(GLuint vert, GLuint frag)
 	return err;
 }
 
+const ShaderProgramImplReflection& ShaderProgramImpl::getReflection()
+{
+	if(m_reflInitialized)
+	{
+		return m_refl;
+	}
+
+	GLint uniformCount = 0;
+	glGetProgramiv(getGlName(), GL_ACTIVE_UNIFORMS, &uniformCount);
+
+	if(uniformCount)
+	{
+		for(U i = 0; i < U(uniformCount); ++i)
+		{
+			// Get uniform info
+			GLsizei len;
+			GLenum type;
+			GLint size;
+			Array<char, 128> name;
+			glGetActiveUniform(getGlName(), i, sizeof(name), &len, &size, &type, &name[0]);
+
+			name[len] = '\0';
+			GLint location = glGetUniformLocation(getGlName(), &name[0]);
+
+			// Store those info
+			ShaderVariableDataType akType = ShaderVariableDataType::NONE;
+			U32 dataSize = 0;
+			switch(type)
+			{
+			case GL_FLOAT_VEC4:
+				akType = ShaderVariableDataType::VEC4;
+				dataSize = 16;
+				break;
+			case GL_INT_VEC4:
+				akType = ShaderVariableDataType::IVEC4;
+				dataSize = 16;
+				break;
+			case GL_UNSIGNED_INT_VEC4:
+				akType = ShaderVariableDataType::UVEC4;
+				dataSize = 16;
+				break;
+			case GL_FLOAT_MAT4:
+				akType = ShaderVariableDataType::MAT4;
+				dataSize = 16 * 4;
+				break;
+			default:
+				ANKI_ASSERT(!"Unsupported type");
+			}
+
+			ShaderProgramImplReflection::Uniform uni;
+			uni.m_location = location;
+			uni.m_type = akType;
+			uni.m_arrSize = size;
+
+			m_refl.m_uniforms.emplaceBack(getAllocator(), uni);
+			m_refl.m_uniformDataSize += dataSize * uni.m_arrSize;
+		}
+	}
+
+	m_reflInitialized = true;
+	return m_refl;
+}
+
 } // end namespace anki

+ 19 - 0
src/anki/gr/gl/ShaderProgramImpl.h

@@ -14,6 +14,20 @@ namespace anki
 /// @addtogroup opengl
 /// @{
 
+class ShaderProgramImplReflection
+{
+public:
+	struct Uniform
+	{
+		I32 m_location;
+		ShaderVariableDataType m_type;
+		U8 m_arrSize;
+	};
+
+	DynamicArray<Uniform> m_uniforms;
+	U32 m_uniformDataSize = 0;
+};
+
 /// Shader program implementation.
 class ShaderProgramImpl final : public ShaderProgram, public GlObject
 {
@@ -29,8 +43,13 @@ public:
 		ShaderPtr vert, ShaderPtr tessc, ShaderPtr tesse, ShaderPtr geom, ShaderPtr frag);
 	ANKI_USE_RESULT Error initCompute(ShaderPtr comp);
 
+	// Do that only when is needed to avoid serializing the thread the driver is using for compilation.
+	const ShaderProgramImplReflection& getReflection();
+
 private:
 	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders;
+	ShaderProgramImplReflection m_refl;
+	Bool8 m_reflInitialized = false;
 
 	ANKI_USE_RESULT Error link(GLuint vert, GLuint frag);
 };

+ 1 - 0
src/anki/gr/vulkan/CommandBufferImpl.inl.h

@@ -746,6 +746,7 @@ inline Bool CommandBufferImpl::flipViewport() const
 
 inline void CommandBufferImpl::setPushConstants(const void* data, U32 dataSize)
 {
+	ANKI_ASSERT(data && dataSize && dataSize % 16 == 0);
 	const ShaderProgramImpl* prog = m_state.tryGetBoundShaderProgram();
 	ANKI_ASSERT(prog && "Need have bound the ShaderProgram first");
 	ANKI_ASSERT(prog->getReflectionInfo().m_pushConstantsSize == dataSize

+ 7 - 9
src/anki/gr/vulkan/GrManagerImpl.cpp

@@ -204,9 +204,7 @@ Error GrManagerImpl::initInternal(const GrManagerInitInfo& init)
 	m_descrFactory.init(getAllocator(), m_device);
 	m_pplineLayoutFactory.init(getAllocator(), m_device);
 
-	m_capabilities |= !!(m_extensions & VulkanExtensions::EXT_SHADER_SUBGROUP_BALLOT)
-						  ? GpuDeviceCapabilitiesBit::SHADER_BALLOT
-						  : GpuDeviceCapabilitiesBit::NONE;
+	m_capabilities.m_shaderSubgroups = !!(m_extensions & VulkanExtensions::EXT_SHADER_SUBGROUP_BALLOT);
 
 	return Error::NONE;
 }
@@ -389,22 +387,22 @@ Error GrManagerImpl::initInstance(const GrManagerInitInfo& init)
 	switch(m_devProps.vendorID)
 	{
 	case 0x13B5:
-		m_gpuVendor = GpuVendor::ARM;
+		m_capabilities.m_gpuVendor = GpuVendor::ARM;
 		break;
 	case 0x10DE:
-		m_gpuVendor = GpuVendor::NVIDIA;
+		m_capabilities.m_gpuVendor = GpuVendor::NVIDIA;
 		break;
 	case 0x1002:
 	case 0x1022:
-		m_gpuVendor = GpuVendor::AMD;
+		m_capabilities.m_gpuVendor = GpuVendor::AMD;
 		break;
 	case 0x8086:
-		m_gpuVendor = GpuVendor::INTEL;
+		m_capabilities.m_gpuVendor = GpuVendor::INTEL;
 		break;
 	default:
-		m_gpuVendor = GpuVendor::UNKNOWN;
+		m_capabilities.m_gpuVendor = GpuVendor::UNKNOWN;
 	}
-	ANKI_VK_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_gpuVendor][0]);
+	ANKI_VK_LOGI("GPU vendor is %s", &GPU_VENDOR_STR[m_capabilities.m_gpuVendor][0]);
 
 	vkGetPhysicalDeviceFeatures(m_physicalDevice, &m_devFeatures);
 

+ 5 - 1
src/anki/gr/vulkan/ShaderImpl.cpp

@@ -196,7 +196,11 @@ void ShaderImpl::doReflection(ConstWeakArray<U8> spirv, SpecConstsVector& specCo
 	// Push consts
 	if(rsrc.push_constant_buffers.size() == 1)
 	{
-		U32 blockSize = spvc.get_declared_struct_size(spvc.get_type(rsrcActive.push_constant_buffers[0].base_type_id));
+		const U32 blockSize =
+			spvc.get_declared_struct_size(spvc.get_type(rsrcActive.push_constant_buffers[0].base_type_id));
+		ANKI_ASSERT(blockSize > 0);
+		ANKI_ASSERT(blockSize % 16 == 0 && "Should be aligned");
+		ANKI_ASSERT(blockSize <= getGrManagerImpl().getDeviceCapabilities().m_pushConstantsSize);
 		m_pushConstantsSize = blockSize;
 	}
 }

+ 9 - 1
src/anki/gr/vulkan/ShaderProgramImpl.cpp

@@ -79,7 +79,15 @@ Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 				}
 			}
 
-			m_refl.m_pushConstantsSize = max(m_refl.m_pushConstantsSize, simpl.m_pushConstantsSize);
+			if(simpl.m_pushConstantsSize > 0)
+			{
+				if(m_refl.m_pushConstantsSize > 0)
+				{
+					ANKI_ASSERT(m_refl.m_pushConstantsSize == simpl.m_pushConstantsSize);
+				}
+
+				m_refl.m_pushConstantsSize = max(m_refl.m_pushConstantsSize, simpl.m_pushConstantsSize);
+			}
 		}
 
 		if(counts[set])

+ 13 - 4
tests/gr/Gr.cpp

@@ -2060,7 +2060,9 @@ ANKI_TEST(Gr, PushConsts)
 struct PC
 {
 	vec4 color;
-	vec4 color2;
+	ivec4 icolor;
+	vec4 arr[2];
+	mat4 mat;
 };
 ANKI_PUSH_CONSTANTS(PC, regs);
 	
@@ -2085,7 +2087,9 @@ void main()
 struct PC
 {
 	vec4 color;
-	vec4 color2;
+	ivec4 icolor;
+	vec4 arr[2];
+	mat4 mat;
 };
 ANKI_PUSH_CONSTANTS(PC, regs);
 	
@@ -2103,7 +2107,9 @@ void main()
 
 	if(gl_FragCoord.x == 0.5 && gl_FragCoord.y == 0.5)
 	{
-		if(in_color != vec4(1.0, 0.0, 1.0, 0.0) || regs.color2 != vec4(0.0, 1.0, 0.25, 0.5))
+		if(in_color != vec4(1.0, 0.0, 1.0, 0.0) || regs.icolor != ivec4(-1, 1, 2147483647, -2147483647)
+			|| regs.arr[0] != vec4(1, 2, 3, 4) || regs.arr[1] != vec4(10, 20, 30, 40)
+			|| regs.mat[1][0] != 0.5)
 		{
 			u_result = uvec4(1u);
 		}
@@ -2138,8 +2144,11 @@ void main()
 	struct PushConstants
 	{
 		Vec4 m_color = Vec4(1.0, 0.0, 1.0, 0.0);
-		Vec4 m_color1 = Vec4(0.0, 1.0, 0.25, 0.5);
+		IVec4 m_icolor = IVec4(-1, 1, 2147483647, -2147483647);
+		Vec4 m_arr[2] = {Vec4(1, 2, 3, 4), Vec4(10, 20, 30, 40)};
+		Mat4 m_mat = Mat4(0.0f);
 	} pc;
+	pc.m_mat(0, 1) = 0.5f;
 	cmdb->setPushConstants(&pc, sizeof(pc));
 
 	cmdb->bindStorageBuffer(0, 0, resultBuff, 0, resultBuff->getSize());