Explorar o código

[REFACTORING] Some renaming and an additional GR test

Panagiotis Christopoulos Charitos %!s(int64=8) %!d(string=hai) anos
pai
achega
e9f87a411b

+ 1 - 1
src/anki/gr/RenderGraph.inl.h

@@ -47,7 +47,7 @@ inline void RenderPassDescriptionBase::fixSubresource(RenderPassDependency& dep)
 		else
 		{
 			subresource.m_faceCount = textureTypeIsCube(rt.m_initInfo.m_type) ? 6 : 1;
-			subresource.m_mipmapCount = rt.m_initInfo.m_mipmapsCount;
+			subresource.m_mipmapCount = rt.m_initInfo.m_mipmapCount;
 			subresource.m_layerCount = rt.m_initInfo.m_layerCount;
 		}
 	}

+ 8 - 0
src/anki/gr/Shader.h

@@ -97,7 +97,15 @@ class Shader : public GrObject
 public:
 	static const GrObjectType CLASS_TYPE = GrObjectType::SHADER;
 
+	ShaderType getShaderType() const
+	{
+		ANKI_ASSERT(m_shaderType != ShaderType::COUNT);
+		return m_shaderType;
+	}
+
 protected:
+	ShaderType m_shaderType = ShaderType::COUNT;
+
 	/// Construct.
 	Shader(GrManager* manager)
 		: GrObject(manager, CLASS_TYPE)

+ 41 - 0
src/anki/gr/ShaderProgram.h

@@ -19,6 +19,47 @@ class ShaderProgramInitInfo : GrBaseInitInfo
 {
 public:
 	Array<ShaderPtr, U(ShaderType::COUNT)> m_shaders = {};
+
+	ShaderProgramInitInfo(CString name = {})
+		: GrBaseInitInfo(name)
+	{
+	}
+
+	ShaderProgramInitInfo(const ShaderPtr& compute, CString name = {})
+		: GrBaseInitInfo(name)
+	{
+		m_shaders[compute->getShaderType()] = compute;
+	}
+
+	ShaderProgramInitInfo(const ShaderPtr& vert, const ShaderPtr& frag, CString name = {})
+		: GrBaseInitInfo(name)
+	{
+		m_shaders[vert->getShaderType()] = vert;
+		m_shaders[frag->getShaderType()] = frag;
+	}
+
+	Bool isValid() const
+	{
+		I32 invalid = 0;
+
+		if(m_shaders[ShaderType::COMPUTE])
+		{
+			invalid |= m_shaders[ShaderType::VERTEX] || m_shaders[ShaderType::TESSELLATION_CONTROL]
+				|| m_shaders[ShaderType::TESSELLATION_EVALUATION] || m_shaders[ShaderType::GEOMETRY]
+				|| m_shaders[ShaderType::FRAGMENT];
+		}
+		else
+		{
+			invalid |= m_shaders[ShaderType::VERTEX] && m_shaders[ShaderType::FRAGMENT];
+		}
+
+		for(ShaderType type = ShaderType::FIRST; type < ShaderType::COUNT; ++type)
+		{
+			invalid |= m_shaders[type] && m_shaders[type]->getShaderType() != type;
+		}
+
+		return invalid == 0;
+	}
 };
 
 /// GPU program.

+ 2 - 2
src/anki/gr/Texture.h

@@ -26,7 +26,7 @@ public:
 	TextureUsageBit m_initialUsage = TextureUsageBit::NONE; ///< Its initial usage.
 	TextureType m_type = TextureType::_2D;
 
-	U8 m_mipmapsCount = 1;
+	U8 m_mipmapCount = 1;
 
 	PixelFormat m_format;
 	U8 m_samples = 1;
@@ -63,7 +63,7 @@ public:
 	} while(0)
 
 		ANKI_CHECK_VAL_VALIDITY(m_usage != TextureUsageBit::NONE);
-		ANKI_CHECK_VAL_VALIDITY(m_mipmapsCount > 0);
+		ANKI_CHECK_VAL_VALIDITY(m_mipmapCount > 0);
 		ANKI_CHECK_VAL_VALIDITY(m_width > 0);
 		ANKI_CHECK_VAL_VALIDITY(m_height > 0);
 		switch(m_type)

+ 6 - 5
src/anki/gr/gl/Shader.cpp

@@ -17,12 +17,10 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 	{
 	public:
 		ShaderPtr m_shader;
-		ShaderType m_type;
 		StringAuto m_source;
 
-		ShaderCreateCommand(Shader* shader, ShaderType type, CString source, const CommandBufferAllocator<U8>& alloc)
+		ShaderCreateCommand(Shader* shader, CString source, const CommandBufferAllocator<U8>& alloc)
 			: m_shader(shader)
-			, m_type(type)
 			, m_source(alloc)
 		{
 			m_source.create(source);
@@ -32,7 +30,7 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 		{
 			ShaderImpl& impl = static_cast<ShaderImpl&>(*m_shader);
 
-			Error err = impl.init(m_type, m_source.toCString());
+			Error err = impl.init(m_source.toCString());
 
 			GlObject::State oldState =
 				impl.setStateAtomically((err) ? GlObject::State::ERROR : GlObject::State::CREATED);
@@ -48,12 +46,15 @@ Shader* Shader::newInstance(GrManager* manager, const ShaderInitInfo& init)
 	ShaderImpl* impl = manager->getAllocator().newInstance<ShaderImpl>(manager);
 	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 
+	// Need to pre-init because some funcs ask for members and we don't want to serialize
+	impl->preInit(init);
+
 	// Copy source to the command buffer
 	CommandBufferPtr cmdb = manager->newCommandBuffer(CommandBufferInitInfo());
 	CommandBufferImpl& cmdbimpl = static_cast<CommandBufferImpl&>(*cmdb);
 	CommandBufferAllocator<U8> alloc = cmdbimpl.getInternalAllocator();
 
-	cmdbimpl.pushBackNewCommand<ShaderCreateCommand>(impl, init.m_shaderType, init.m_source, alloc);
+	cmdbimpl.pushBackNewCommand<ShaderCreateCommand>(impl, init.m_source, alloc);
 	cmdbimpl.flush();
 
 	return impl;

+ 3 - 4
src/anki/gr/gl/ShaderImpl.cpp

@@ -54,7 +54,7 @@ ShaderImpl::~ShaderImpl()
 	destroyDeferred(getManager(), deleteShaders);
 }
 
-Error ShaderImpl::init(ShaderType type, const CString& source)
+Error ShaderImpl::init(const CString& source)
 {
 	ANKI_ASSERT(source);
 	ANKI_ASSERT(!isCreated());
@@ -73,8 +73,7 @@ Error ShaderImpl::init(ShaderType type, const CString& source)
 		"ANKI_FRAGMENT_SHADER",
 		"ANKI_COMPUTE_SHADER"}};
 
-	m_type = type;
-	m_glType = gltype[U(type)];
+	m_glType = gltype[U(m_shaderType)];
 
 	// 1) Append some things in the source string
 	//
@@ -98,7 +97,7 @@ Error ShaderImpl::init(ShaderType type, const CString& source)
 		version,
 		versionType,
 		&GPU_VENDOR_STR[static_cast<const GrManagerImpl&>(getManager()).getState().m_gpu][0],
-		shaderName[U(type)],
+		shaderName[U(m_shaderType)],
 		MAX_UNIFORM_BUFFER_BINDINGS,
 		MAX_STORAGE_BUFFER_BINDINGS,
 		MAX_TEXTURE_BINDINGS,

+ 7 - 7
src/anki/gr/gl/ShaderImpl.h

@@ -13,17 +13,15 @@ namespace anki
 
 // Forward
 class CString;
-class String;
 
 /// @addtogroup opengl
 /// @{
 
-/// Shader program. It only contains a single shader and it can be combined with other programs in a program pipiline.
+/// Shader program implementation.
 class ShaderImpl final : public Shader, public GlObject
 {
 public:
 	GLenum m_glType = 0;
-	ShaderType m_type;
 
 	ShaderImpl(GrManager* manager)
 		: Shader(manager)
@@ -32,10 +30,12 @@ public:
 
 	~ShaderImpl();
 
-	/// Create the shader.
-	/// @param shaderType The type of the shader in the program
-	/// @param source The shader's source
-	ANKI_USE_RESULT Error init(ShaderType shaderType, const CString& source);
+	void preInit(const ShaderInitInfo& init)
+	{
+		m_shaderType = init.m_shaderType;
+	}
+
+	ANKI_USE_RESULT Error init(const CString& source);
 };
 /// @}
 

+ 3 - 1
src/anki/gr/gl/ShaderProgram.cpp

@@ -34,7 +34,7 @@ ShaderProgram* ShaderProgram::newInstance(GrManager* manager, const ShaderProgra
 			ShaderPtr comp)
 			: m_prog(prog)
 			, m_vert(vert)
-			, m_tessc(tesse)
+			, m_tessc(tessc)
 			, m_tesse(tesse)
 			, m_geom(geom)
 			, m_frag(frag)
@@ -55,6 +55,8 @@ ShaderProgram* ShaderProgram::newInstance(GrManager* manager, const ShaderProgra
 		}
 	};
 
+	ANKI_ASSERT(init.isValid());
+
 	ShaderProgramImpl* impl = manager->getAllocator().newInstance<ShaderProgramImpl>(manager);
 	impl->getRefcount().fetchAdd(1); // Hold a reference in case the command finishes and deletes quickly
 

+ 2 - 2
src/anki/gr/gl/TextureImpl.cpp

@@ -129,11 +129,11 @@ void TextureImpl::preInit(const TextureInitInfo& init)
 
 	if(m_target != GL_TEXTURE_3D)
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount2d(m_width, m_height));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount2d(m_width, m_height));
 	}
 	else
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
 	}
 
 	// Surface count

+ 1 - 1
src/anki/gr/vulkan/GrManager.cpp

@@ -76,7 +76,7 @@ void GrManager::swapBuffers()
 
 void GrManager::finish()
 {
-	// TODO
+	ANKI_ASSERT(!"TODO");
 }
 
 BufferPtr GrManager::newBuffer(const BufferInitInfo& init)

+ 0 - 1
src/anki/gr/vulkan/ShaderImpl.h

@@ -22,7 +22,6 @@ class ShaderImpl final : public Shader, public VulkanObject<Shader, ShaderImpl>
 {
 public:
 	VkShaderModule m_handle = VK_NULL_HANDLE;
-	ShaderType m_shaderType = ShaderType::COUNT;
 
 	Array<DynamicArray<DescriptorBinding>, MAX_DESCRIPTOR_SETS> m_bindings;
 	BitSet<MAX_COLOR_ATTACHMENTS, U8> m_colorAttachmentWritemask = {false};

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

@@ -28,6 +28,7 @@ ShaderProgramImpl::~ShaderProgramImpl()
 
 Error ShaderProgramImpl::init(const ShaderProgramInitInfo& inf)
 {
+	ANKI_ASSERT(inf.isValid());
 	ShaderTypeBit shaderMask = ShaderTypeBit::NONE;
 	m_shaders = inf.m_shaders;
 

+ 4 - 4
src/anki/gr/vulkan/TextureImpl.cpp

@@ -63,13 +63,13 @@ Error TextureImpl::init(const TextureInitInfo& init_)
 
 	if(m_texType == TextureType::_3D)
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount3d(m_width, m_height, m_depth));
 	}
 	else
 	{
-		m_mipCount = min<U>(init.m_mipmapsCount, computeMaxMipmapCount2d(m_width, m_height));
+		m_mipCount = min<U>(init.m_mipmapCount, computeMaxMipmapCount2d(m_width, m_height));
 	}
-	init.m_mipmapsCount = m_mipCount;
+	init.m_mipmapCount = m_mipCount;
 
 	m_layerCount = init.m_layerCount;
 
@@ -127,7 +127,7 @@ VkFormatFeatureFlags TextureImpl::calcFeatures(const TextureInitInfo& init)
 {
 	VkFormatFeatureFlags flags = 0;
 
-	if(init.m_mipmapsCount > 1 && !!(init.m_usage & TextureUsageBit::GENERATE_MIPMAPS))
+	if(init.m_mipmapCount > 1 && !!(init.m_usage & TextureUsageBit::GENERATE_MIPMAPS))
 	{
 		// May be used for mip gen.
 		flags |= VK_FORMAT_FEATURE_BLIT_DST_BIT | VK_FORMAT_FEATURE_BLIT_SRC_BIT;

+ 1 - 1
src/anki/renderer/Indirect.cpp

@@ -200,7 +200,7 @@ Error Indirect::initLightShading(const ConfigSet& config)
 			TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE
 				| TextureUsageBit::GENERATE_MIPMAPS,
 			"GI refl");
-		texinit.m_mipmapsCount = m_lightShading.m_mipCount;
+		texinit.m_mipmapCount = m_lightShading.m_mipCount;
 		texinit.m_type = TextureType::CUBE_ARRAY;
 		texinit.m_layerCount = m_cacheEntries.getSize();
 		texinit.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;

+ 4 - 4
src/anki/renderer/Renderer.cpp

@@ -311,7 +311,7 @@ TextureInitInfo Renderer::create2DRenderTargetInitInfo(
 	init.m_layerCount = 1;
 	init.m_type = TextureType::_2D;
 	init.m_format = format;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_samples = 1;
 	init.m_usage = usage;
 
@@ -330,7 +330,7 @@ RenderTargetDescription Renderer::create2DRenderTargetDescription(
 	init.m_layerCount = 1;
 	init.m_type = TextureType::_2D;
 	init.m_format = format;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_samples = 1;
 	init.m_usage = usage;
 
@@ -349,13 +349,13 @@ TexturePtr Renderer::createAndClearRenderTarget(const TextureInitInfo& inf, cons
 	// Clear all surfaces
 	CommandBufferInitInfo cmdbinit;
 	cmdbinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
-	if((inf.m_mipmapsCount * faceCount * inf.m_layerCount * 4) < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
+	if((inf.m_mipmapCount * faceCount * inf.m_layerCount * 4) < COMMAND_BUFFER_SMALL_BATCH_MAX_COMMANDS)
 	{
 		cmdbinit.m_flags |= CommandBufferFlag::SMALL_BATCH;
 	}
 	CommandBufferPtr cmdb = m_gr->newCommandBuffer(cmdbinit);
 
-	for(U mip = 0; mip < inf.m_mipmapsCount; ++mip)
+	for(U mip = 0; mip < inf.m_mipmapCount; ++mip)
 	{
 		for(U face = 0; face < faceCount; ++face)
 		{

+ 1 - 1
src/anki/resource/ShaderProgramResource.cpp

@@ -1073,7 +1073,7 @@ void ShaderProgramResource::initVariant(WeakArray<const ShaderProgramResourceMut
 	shaderHeaderSrc.join("", shaderHeader);
 
 	// Create the shaders and the program
-	ShaderProgramInitInfo progInf;
+	ShaderProgramInitInfo progInf("RsrcProg");
 	for(ShaderType i = ShaderType::FIRST; i < ShaderType::COUNT; ++i)
 	{
 		if(!m_sources[i])

+ 1 - 1
src/anki/resource/TextureResource.cpp

@@ -161,7 +161,7 @@ Error TextureResource::load(const ResourceFilename& filename, Bool async)
 	}
 
 	// mipmapsCount
-	init.m_mipmapsCount = loader.getMipLevelsCount();
+	init.m_mipmapCount = loader.getMipLevelsCount();
 
 	// Create the texture
 	m_tex = getManager().getGrManager().newTexture(init);

+ 1 - 1
src/anki/ui/Font.cpp

@@ -83,7 +83,7 @@ void Font::createTexture(const void* data, U32 width, U32 height)
 	texInit.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
 	texInit.m_usage =
 		TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::GENERATE_MIPMAPS;
-	texInit.m_mipmapsCount = 4;
+	texInit.m_mipmapCount = 4;
 
 	m_tex = m_manager->getGrManager().newTexture(texInit);
 

+ 149 - 11
tests/gr/Gr.cpp

@@ -356,11 +356,7 @@ static ShaderProgramPtr createProgram(CString vertSrc, CString fragSrc, GrManage
 {
 	ShaderPtr vert = gr.newShader({ShaderType::VERTEX, vertSrc});
 	ShaderPtr frag = gr.newShader({ShaderType::FRAGMENT, fragSrc});
-
-	ShaderProgramInitInfo inf;
-	inf.m_shaders[ShaderType::VERTEX] = vert;
-	inf.m_shaders[ShaderType::FRAGMENT] = frag;
-	return gr.newShaderProgram(inf);
+	return gr.newShaderProgram(ShaderProgramInitInfo(vert, frag));
 }
 
 static FramebufferPtr createDefaultFb(GrManager& gr)
@@ -576,7 +572,7 @@ ANKI_TEST(Gr, ViewportAndScissorOffscreen)
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::FRAMEBUFFER_ATTACHMENT_READ_WRITE;
 	init.m_height = RT_HEIGHT;
 	init.m_width = RT_WIDTH;
-	init.m_mipmapsCount = 1;
+	init.m_mipmapCount = 1;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
 	init.m_samples = 1;
@@ -883,7 +879,7 @@ ANKI_TEST(Gr, Texture)
 	init.m_usage = TextureUsageBit::SAMPLED_FRAGMENT;
 	init.m_height = 4;
 	init.m_width = 4;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
 	init.m_samples = 1;
@@ -920,7 +916,7 @@ ANKI_TEST(Gr, DrawWithTexture)
 	init.m_initialUsage = TextureUsageBit::SAMPLED_FRAGMENT;
 	init.m_height = 2;
 	init.m_width = 2;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_samples = 1;
 	init.m_depth = 1;
 	init.m_layerCount = 1;
@@ -935,7 +931,7 @@ ANKI_TEST(Gr, DrawWithTexture)
 	//
 	init.m_width = 4;
 	init.m_height = 4;
-	init.m_mipmapsCount = 3;
+	init.m_mipmapCount = 3;
 	init.m_usage =
 		TextureUsageBit::SAMPLED_FRAGMENT | TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::GENERATE_MIPMAPS;
 	init.m_initialUsage = TextureUsageBit::NONE;
@@ -1315,7 +1311,7 @@ ANKI_TEST(Gr, ImageLoadStore)
 
 	TextureInitInfo init;
 	init.m_width = init.m_height = 4;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_usage = TextureUsageBit::CLEAR | TextureUsageBit::SAMPLED_ALL | TextureUsageBit::IMAGE_COMPUTE_WRITE;
 	init.m_type = TextureType::_2D;
 	init.m_format = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
@@ -1437,7 +1433,7 @@ ANKI_TEST(Gr, 3DTextures)
 	init.m_initialUsage = TextureUsageBit::TRANSFER_DESTINATION;
 	init.m_height = 2;
 	init.m_width = 2;
-	init.m_mipmapsCount = 2;
+	init.m_mipmapCount = 2;
 	init.m_samples = 1;
 	init.m_depth = 2;
 	init.m_layerCount = 1;
@@ -1782,4 +1778,146 @@ ANKI_TEST(Gr, RenderGraph)
 	COMMON_END()
 }
 
+/// Test workarounds for some unsupported formats
+ANKI_TEST(Gr, VkWorkarounds)
+{
+	COMMON_BEGIN()
+
+	// Create program
+	static const char* COMP_SRC = R"(
+layout(local_size_x = 8, local_size_y = 8, local_size_z = 2) in;
+
+layout(ANKI_TEX_BINDING(0, 0)) uniform usampler2D u_tex;
+	
+layout(ANKI_SS_BINDING(0, 0)) buffer s_
+{
+	uvec4 u_result;
+};
+	
+shared uint g_wrong;
+	
+void main()
+{
+	g_wrong = 0;
+	memoryBarrierShared();
+	barrier();
+	
+	int lod;
+	uint idx;
+	
+	if(gl_LocalInvocationID.z == 0)
+	{
+		// First mip
+	
+		lod = 0;
+		uint idx = gl_LocalInvocationID.y * 8 + gl_LocalInvocationID.x;
+	}
+	else if(gl_LocalInvocationID.x < 4u && gl_LocalInvocationID.y < 4u)
+	{
+		lod = 1;
+		idx = gl_LocalInvocationID.y * 4 + gl_LocalInvocationID.x;
+	}
+	
+	uvec3 col = texelFetch(u_tex, ivec2(gl_LocalInvocationID.x, gl_LocalInvocationID.x), lod).rgb;
+	if(col.x != idx || col.y != idx + 1 || col.z != idx + 2)
+	{
+		atomicAdd(g_wrong, 1);
+	}
+	
+	memoryBarrierShared();
+	barrier();
+	
+	if(g_wrong != 0)
+	{
+		u_result = uvec4(1);
+	}
+	else
+	{
+		u_result = uvec4(2);
+	}
+})";
+
+	ShaderPtr comp = gr->newShader(ShaderInitInfo(ShaderType::COMPUTE, COMP_SRC));
+	ShaderProgramPtr prog = gr->newShaderProgram(ShaderProgramInitInfo(comp));
+
+	// Create the texture
+	TextureInitInfo texInit;
+	texInit.m_width = texInit.m_height = 8;
+	texInit.m_format = PixelFormat(ComponentFormat::R8G8B8, TransformFormat::UINT);
+	texInit.m_type = TextureType::_2D;
+	texInit.m_usage = TextureUsageBit::TRANSFER_DESTINATION | TextureUsageBit::SAMPLED_ALL;
+	texInit.m_mipmapCount = 2;
+	TexturePtr tex = gr->newTexture(texInit);
+	TextureViewPtr texView = gr->newTextureView(TextureViewInitInfo(tex));
+
+	SamplerInitInfo samplerInit;
+	SamplerPtr sampler = gr->newSampler(samplerInit);
+
+	// Create the buffer to copy to the texture
+	BufferPtr uploadBuff = gr->newBuffer(BufferInitInfo(
+		texInit.m_width * texInit.m_height * 3, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::WRITE));
+	U8* data = static_cast<U8*>(uploadBuff->map(0, uploadBuff->getSize(), BufferMapAccessBit::WRITE));
+	for(U i = 0; i < texInit.m_width * texInit.m_height; ++i)
+	{
+		data[0] = i;
+		data[1] = i + 1;
+		data[2] = i + 2;
+		data += 3;
+	}
+
+	BufferPtr uploadBuff2 = gr->newBuffer(BufferInitInfo(
+		(texInit.m_width >> 1) * (texInit.m_height >> 1) * 3, BufferUsageBit::TRANSFER_ALL, BufferMapAccessBit::WRITE));
+	data = static_cast<U8*>(uploadBuff2->map(0, uploadBuff2->getSize(), BufferMapAccessBit::WRITE));
+	for(U i = 0; i < (texInit.m_width >> 1) * (texInit.m_height >> 1); ++i)
+	{
+		data[0] = i;
+		data[1] = i + 1;
+		data[2] = i + 2;
+		data += 3;
+	}
+
+	// Create the result buffer
+	BufferPtr resultBuff =
+		gr->newBuffer(BufferInitInfo(sizeof(UVec4), BufferUsageBit::STORAGE_COMPUTE_WRITE, BufferMapAccessBit::READ));
+
+	// Upload data and test them
+	CommandBufferInitInfo cmdbInit;
+	cmdbInit.m_flags = CommandBufferFlag::TRANSFER_WORK | CommandBufferFlag::SMALL_BATCH;
+	CommandBufferPtr cmdb = gr->newCommandBuffer(cmdbInit);
+
+	TextureSubresourceInfo subresource;
+	subresource.m_mipmapCount = texInit.m_mipmapCount;
+	cmdb->setTextureBarrier(tex, TextureUsageBit::NONE, TextureUsageBit::TRANSFER_DESTINATION, subresource);
+	cmdb->copyBufferToTextureView(uploadBuff,
+		0,
+		uploadBuff->getSize(),
+		gr->newTextureView(TextureViewInitInfo(tex, TextureSurfaceInfo(0, 0, 0, 0))));
+	cmdb->copyBufferToTextureView(uploadBuff2,
+		0,
+		uploadBuff2->getSize(),
+		gr->newTextureView(TextureViewInitInfo(tex, TextureSurfaceInfo(1, 0, 0, 0))));
+
+	cmdb->setTextureBarrier(tex, TextureUsageBit::TRANSFER_DESTINATION, TextureUsageBit::SAMPLED_COMPUTE, subresource);
+	cmdb->bindShaderProgram(prog);
+	cmdb->bindTextureAndSampler(0, 0, texView, sampler, TextureUsageBit::SAMPLED_COMPUTE);
+	cmdb->bindStorageBuffer(0, 0, resultBuff, 0, resultBuff->getSize());
+
+	cmdb->setBufferBarrier(resultBuff,
+		BufferUsageBit::STORAGE_COMPUTE_WRITE,
+		BufferUsageBit::STORAGE_COMPUTE_WRITE,
+		0,
+		resultBuff->getSize());
+
+	cmdb->finish();
+
+	// Get the result
+	UVec4* result = static_cast<UVec4*>(resultBuff->map(0, resultBuff->getSize(), BufferMapAccessBit::READ));
+	ANKI_TEST_EXPECT_EQ(result->x(), 2);
+	ANKI_TEST_EXPECT_EQ(result->y(), 2);
+	ANKI_TEST_EXPECT_EQ(result->z(), 2);
+	ANKI_TEST_EXPECT_EQ(result->w(), 2);
+
+	COMMON_END()
+}
+
 } // end namespace anki