Browse Source

Add some more code in the texture viewer

Panagiotis Christopoulos Charitos 4 years ago
parent
commit
537c469eef
6 changed files with 269 additions and 28 deletions
  1. 64 0
      AnKi/Shaders/UiVisualizeImage.ankiprog
  2. 103 15
      AnKi/Ui/Canvas.cpp
  3. 15 2
      AnKi/Ui/Canvas.h
  4. 40 0
      AnKi/Ui/Common.h
  5. 1 1
      AnKi/Ui/Font.cpp
  6. 46 10
      Tools/Texture/TextureViewerMain.cpp

+ 64 - 0
AnKi/Shaders/UiVisualizeImage.ankiprog

@@ -0,0 +1,64 @@
+// Copyright (C) 2009-2021, Panagiotis Christopoulos Charitos and contributors.
+// All rights reserved.
+// Code licensed under the BSD License.
+// http://www.anki3d.org/LICENSE
+
+layout(push_constant) uniform b_pc
+{
+	Vec4 u_transform; // x: x scale, y: y scale, z: x transl, w: y transl
+	Vec4 u_colorScale;
+};
+
+#pragma anki start vert
+#include <AnKi/Shaders/Common.glsl>
+
+layout(location = 0) in Vec2 in_pos;
+layout(location = 1) in Vec4 in_col;
+layout(location = 2) in Vec2 in_uv;
+
+layout(location = 0) out Vec2 out_uv;
+layout(location = 1) out Vec4 out_col;
+
+out gl_PerVertex
+{
+	Vec4 gl_Position;
+};
+
+void main()
+{
+	out_uv = in_uv;
+	out_col = in_col;
+
+	const Vec2 pos = u_transform.xy * in_pos + u_transform.zw;
+	gl_Position = Vec4(pos, 0.0, 1.0);
+}
+#pragma anki end
+
+#pragma anki start frag
+#include <AnKi/Shaders/Common.glsl>
+
+layout(location = 0) in Vec2 in_uv;
+layout(location = 1) in Vec4 in_col;
+
+layout(location = 0) out Vec4 out_col;
+
+layout(set = 0, binding = 0) uniform sampler u_trilinearRepeatSampler;
+layout(set = 0, binding = 1) uniform texture2D u_tex;
+
+void main()
+{
+	out_col = in_col * texture(u_tex, u_trilinearRepeatSampler, in_uv) * u_colorScale;
+
+	if(u_colorScale.a == 0.0)
+	{
+		out_col.a = 1.0;
+	}
+
+	F32 alphaPattern = ((U32(gl_FragCoord.x) / 16u) & 1u) == 1u ? 1.0 : 0.75;
+	alphaPattern *= ((U32(gl_FragCoord.y) / 16u) & 1u) == 1u ? 1.0 : 0.75;
+
+	out_col.rgb = mix(Vec3(alphaPattern), out_col.rgb, out_col.a);
+	out_col.a = 1.0f;
+}
+
+#pragma anki end

+ 103 - 15
AnKi/Ui/Canvas.cpp

@@ -15,6 +15,25 @@
 namespace anki
 namespace anki
 {
 {
 
 
+class Canvas::DrawingState
+{
+public:
+	ShaderProgramPtr m_program;
+	Array<U8, 128 - sizeof(Vec4)> m_extraPushConstants;
+	U32 m_extraPushConstantSize = 0;
+};
+
+/// Custom command that can manipulate the drawing state.
+class Canvas::CustomCommand
+{
+public:
+	virtual ~CustomCommand()
+	{
+	}
+
+	virtual void operator()(DrawingState& state) = 0;
+};
+
 Canvas::Canvas(UiManager* manager)
 Canvas::Canvas(UiManager* manager)
 	: UiObject(manager)
 	: UiObject(manager)
 {
 {
@@ -53,7 +72,11 @@ Error Canvas::init(FontPtr font, U32 fontHeight, U32 width, U32 height)
 	samplerInit.m_minMagFilter = SamplingFilter::LINEAR;
 	samplerInit.m_minMagFilter = SamplingFilter::LINEAR;
 	samplerInit.m_mipmapFilter = SamplingFilter::LINEAR;
 	samplerInit.m_mipmapFilter = SamplingFilter::LINEAR;
 	samplerInit.m_addressing = SamplingAddressing::REPEAT;
 	samplerInit.m_addressing = SamplingAddressing::REPEAT;
-	m_sampler = m_manager->getGrManager().newSampler(samplerInit);
+	m_linearLinearRepeatSampler = m_manager->getGrManager().newSampler(samplerInit);
+
+	samplerInit.m_minMagFilter = SamplingFilter::NEAREST;
+	samplerInit.m_mipmapFilter = SamplingFilter::NEAREST;
+	m_nearestNearestRepeatSampler = m_manager->getGrManager().newSampler(samplerInit);
 
 
 	// Allocator
 	// Allocator
 	m_stackAlloc = StackAllocator<U8>(getAllocator().getMemoryPool().getAllocationCallback(),
 	m_stackAlloc = StackAllocator<U8>(getAllocator().getMemoryPool().getAllocationCallback(),
@@ -241,8 +264,6 @@ void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
 
 
 	cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
 	cmdb->bindIndexBuffer(indicesToken.m_buffer, indicesToken.m_offset, IndexType::U16);
 
 
-	cmdb->bindSampler(0, 0, m_sampler);
-
 	// Will project scissor/clipping rectangles into framebuffer space
 	// Will project scissor/clipping rectangles into framebuffer space
 	const Vec2 clipOff = drawData.DisplayPos; // (0,0) unless using multi-viewports
 	const Vec2 clipOff = drawData.DisplayPos; // (0,0) unless using multi-viewports
 	const Vec2 clipScale = drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
 	const Vec2 clipScale = drawData.FramebufferScale; // (1,1) unless using retina display which are often (2,2)
@@ -250,6 +271,7 @@ void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
 	// Render
 	// Render
 	U32 vertOffset = 0;
 	U32 vertOffset = 0;
 	U32 idxOffset = 0;
 	U32 idxOffset = 0;
+	DrawingState state;
 	for(I32 n = 0; n < drawData.CmdListsCount; n++)
 	for(I32 n = 0; n < drawData.CmdListsCount; n++)
 	{
 	{
 		const ImDrawList& cmdList = *drawData.CmdLists[n];
 		const ImDrawList& cmdList = *drawData.CmdLists[n];
@@ -259,7 +281,9 @@ void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
 			if(pcmd.UserCallback)
 			if(pcmd.UserCallback)
 			{
 			{
 				// User callback (registered via ImDrawList::AddCallback)
 				// User callback (registered via ImDrawList::AddCallback)
-				pcmd.UserCallback(&cmdList, &pcmd);
+				CustomCommand* userCmd = static_cast<CustomCommand*>(pcmd.UserCallbackData);
+				(*userCmd)(state);
+				m_stackAlloc.deleteInstance(userCmd);
 			}
 			}
 			else
 			else
 			{
 			{
@@ -286,25 +310,48 @@ void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
 					cmdb->setScissor(U32(clipRect.x()), U32(clipRect.y()), U32(clipRect.z() - clipRect.x()),
 					cmdb->setScissor(U32(clipRect.x()), U32(clipRect.y()), U32(clipRect.z() - clipRect.x()),
 									 U32(clipRect.w() - clipRect.y()));
 									 U32(clipRect.w() - clipRect.y()));
 
 
-					if(pcmd.TextureId)
+					// Program
+					if(state.m_program.isCreated())
+					{
+						cmdb->bindShaderProgram(state.m_program);
+					}
+					else if(pcmd.TextureId)
 					{
 					{
 						cmdb->bindShaderProgram(m_grProgs[RGBA_TEX]);
 						cmdb->bindShaderProgram(m_grProgs[RGBA_TEX]);
-
-						TextureView* view = static_cast<TextureView*>(pcmd.TextureId);
-						cmdb->bindTexture(0, 1, TextureViewPtr(view), TextureUsageBit::SAMPLED_FRAGMENT);
 					}
 					}
 					else
 					else
 					{
 					{
 						cmdb->bindShaderProgram(m_grProgs[NO_TEX]);
 						cmdb->bindShaderProgram(m_grProgs[NO_TEX]);
 					}
 					}
 
 
-					// Push constants. TODO: Set them once
-					Vec4 transform;
-					transform.x() = 2.0f / drawData.DisplaySize.x;
-					transform.y() = -2.0f / drawData.DisplaySize.y;
-					transform.z() = (drawData.DisplayPos.x / drawData.DisplaySize.x) * 2.0f - 1.0f;
-					transform.w() = -((drawData.DisplayPos.y / drawData.DisplaySize.y) * 2.0f - 1.0f);
-					cmdb->setPushConstants(&transform, sizeof(transform));
+					// Bindings
+					if(pcmd.TextureId)
+					{
+						UiImageId id(pcmd.TextureId);
+						TextureViewPtr view(numberToPtr<TextureView*>(id.m_bits.m_textureViewPtr));
+
+						cmdb->bindSampler(0, 0,
+										  (id.m_bits.m_pointSampling) ? m_nearestNearestRepeatSampler
+																	  : m_linearLinearRepeatSampler);
+						cmdb->bindTexture(0, 1, view, TextureUsageBit::SAMPLED_FRAGMENT);
+					}
+
+					// Push constants
+					class PC
+					{
+					public:
+						Vec4 m_transform;
+						Array<U8, sizeof(DrawingState::m_extraPushConstants)> m_extra;
+					} pc;
+					pc.m_transform.x() = 2.0f / drawData.DisplaySize.x;
+					pc.m_transform.y() = -2.0f / drawData.DisplaySize.y;
+					pc.m_transform.z() = (drawData.DisplayPos.x / drawData.DisplaySize.x) * 2.0f - 1.0f;
+					pc.m_transform.w() = -((drawData.DisplayPos.y / drawData.DisplaySize.y) * 2.0f - 1.0f);
+					if(state.m_extraPushConstantSize)
+					{
+						memcpy(&pc.m_extra[0], &state.m_extraPushConstants[0], state.m_extraPushConstantSize);
+					}
+					cmdb->setPushConstants(&pc, sizeof(Vec4) + state.m_extraPushConstantSize);
 
 
 					// Draw
 					// Draw
 					cmdb->drawElements(PrimitiveTopology::TRIANGLES, pcmd.ElemCount, 1, idxOffset, vertOffset);
 					cmdb->drawElements(PrimitiveTopology::TRIANGLES, pcmd.ElemCount, 1, idxOffset, vertOffset);
@@ -320,4 +367,45 @@ void Canvas::appendToCommandBufferInternal(CommandBufferPtr& cmdb)
 	cmdb->setCullMode(FaceSelectionBit::BACK);
 	cmdb->setCullMode(FaceSelectionBit::BACK);
 }
 }
 
 
+void Canvas::setShaderProgram(ShaderProgramPtr program, const void* extraPushConstants, U32 extraPushConstantSize)
+{
+	class Command : public CustomCommand
+	{
+	public:
+		ShaderProgramPtr m_prog;
+		Array<U8, sizeof(DrawingState::m_extraPushConstants)> m_extraPushConstants;
+		U32 m_extraPushConstantSize;
+
+		void operator()(DrawingState& state) override
+		{
+			state.m_program = m_prog;
+			if(m_extraPushConstantSize)
+			{
+				ANKI_ASSERT(m_extraPushConstantSize <= sizeof(m_extraPushConstants));
+				memcpy(&state.m_extraPushConstants[0], &m_extraPushConstants[0], m_extraPushConstantSize);
+			}
+			state.m_extraPushConstantSize = m_extraPushConstantSize;
+		}
+	};
+
+	Command* newcmd = m_stackAlloc.newInstance<Command>();
+	newcmd->m_prog = program;
+	if(extraPushConstantSize)
+	{
+		ANKI_ASSERT(extraPushConstants);
+		memcpy(&newcmd->m_extraPushConstants[0], extraPushConstants, extraPushConstantSize);
+		newcmd->m_extraPushConstantSize = extraPushConstantSize;
+	}
+	else
+	{
+		newcmd->m_extraPushConstantSize = 0;
+	}
+
+	ImGui::GetWindowDrawList()->AddCallback(
+		[](const ImDrawList* list, const ImDrawCmd* cmd) {
+			// Do nothing, only care about the user data
+		},
+		newcmd);
+}
+
 } // end namespace anki
 } // end namespace anki

+ 15 - 2
AnKi/Ui/Canvas.h

@@ -64,17 +64,29 @@ public:
 		ImGui::PopFont();
 		ImGui::PopFont();
 	}
 	}
 
 
+	/// Override the default program with a user defined one.
+	void setShaderProgram(ShaderProgramPtr program, const void* extraPushConstants, U32 extraPushConstantSize);
+
+	/// Undo what setShaderProgram() did.
+	void clearShaderProgram()
+	{
+		setShaderProgram(ShaderProgramPtr(), nullptr, 0);
+	}
+
 	void appendToCommandBuffer(CommandBufferPtr cmdb);
 	void appendToCommandBuffer(CommandBufferPtr cmdb);
 	/// @}
 	/// @}
 
 
 private:
 private:
+	class CustomCommand;
+	class DrawingState;
+
 	FontPtr m_font;
 	FontPtr m_font;
 	U32 m_dfltFontHeight = 0;
 	U32 m_dfltFontHeight = 0;
 	ImGuiContext* m_imCtx = nullptr;
 	ImGuiContext* m_imCtx = nullptr;
 	U32 m_width;
 	U32 m_width;
 	U32 m_height;
 	U32 m_height;
 
 
-	enum SHADER_TYPE
+	enum ShaderType
 	{
 	{
 		NO_TEX,
 		NO_TEX,
 		RGBA_TEX,
 		RGBA_TEX,
@@ -83,7 +95,8 @@ private:
 
 
 	ShaderProgramResourcePtr m_prog;
 	ShaderProgramResourcePtr m_prog;
 	Array<ShaderProgramPtr, SHADER_COUNT> m_grProgs;
 	Array<ShaderProgramPtr, SHADER_COUNT> m_grProgs;
-	SamplerPtr m_sampler;
+	SamplerPtr m_linearLinearRepeatSampler;
+	SamplerPtr m_nearestNearestRepeatSampler;
 
 
 	StackAllocator<U8> m_stackAlloc;
 	StackAllocator<U8> m_stackAlloc;
 
 

+ 40 - 0
AnKi/Ui/Common.h

@@ -11,6 +11,7 @@
 
 
 #include <AnKi/Util/Allocator.h>
 #include <AnKi/Util/Allocator.h>
 #include <AnKi/Util/Ptr.h>
 #include <AnKi/Util/Ptr.h>
+#include <AnKi/Gr/TextureView.h>
 
 
 namespace anki
 namespace anki
 {
 {
@@ -36,6 +37,45 @@ ANKI_UI_OBJECT_FW(Font)
 ANKI_UI_OBJECT_FW(Canvas)
 ANKI_UI_OBJECT_FW(Canvas)
 ANKI_UI_OBJECT_FW(UiImmediateModeBuilder)
 ANKI_UI_OBJECT_FW(UiImmediateModeBuilder)
 #undef ANKI_UI_OBJECT
 #undef ANKI_UI_OBJECT
+
+/// This is what someone should push to ImGui::Image() function.
+class UiImageId
+{
+	friend class Canvas;
+
+public:
+	UiImageId(TextureViewPtr textureView, Bool pointSampling = false)
+	{
+		m_bits.m_textureViewPtr = ptrToNumber(textureView.get()) & 0x7FFFFFFFFFFFFFFFllu;
+		m_bits.m_pointSampling = pointSampling;
+	}
+
+	operator void*() const
+	{
+		return numberToPtr<void*>(m_allBits);
+	}
+
+private:
+	class Bits
+	{
+	public:
+		U64 m_textureViewPtr : 63;
+		U64 m_pointSampling : 1;
+	};
+
+	union
+	{
+		Bits m_bits;
+		U64 m_allBits;
+	};
+
+	UiImageId(void* ptr)
+		: m_allBits(ptrToNumber(ptr))
+	{
+		ANKI_ASSERT(ptr);
+	}
+};
+static_assert(sizeof(UiImageId) == sizeof(void*), "See file");
 /// @}
 /// @}
 
 
 } // end namespace anki
 } // end namespace anki

+ 1 - 1
AnKi/Ui/Font.cpp

@@ -91,7 +91,7 @@ void Font::createTexture(const void* data, U32 width, U32 height)
 
 
 	// Create the whole texture view
 	// Create the whole texture view
 	m_texView = m_manager->getGrManager().newTextureView(TextureViewInitInfo(m_tex, "Font"));
 	m_texView = m_manager->getGrManager().newTextureView(TextureViewInitInfo(m_tex, "Font"));
-	m_imFontAtlas->SetTexID(m_texView.get());
+	m_imFontAtlas->SetTexID(UiImageId(m_texView));
 
 
 	// Do the copy
 	// Do the copy
 	static const TextureSurfaceInfo surf(0, 0, 0, 0);
 	static const TextureSurfaceInfo surf(0, 0, 0, 0);

+ 46 - 10
Tools/Texture/TextureViewerMain.cpp

@@ -23,6 +23,12 @@ public:
 
 
 		ANKI_CHECK_AND_IGNORE(getSceneGraph().getUiManager().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf",
 		ANKI_CHECK_AND_IGNORE(getSceneGraph().getUiManager().newInstance(m_font, "EngineAssets/UbuntuMonoRegular.ttf",
 																		 Array<U32, 1>{16}));
 																		 Array<U32, 1>{16}));
+
+		ANKI_CHECK_AND_IGNORE(getSceneGraph().getResourceManager().loadResource(
+			"AnKi/Shaders/UiVisualizeImage.ankiprog", m_imageProgram));
+		const ShaderProgramResourceVariant* variant;
+		m_imageProgram->getOrCreateVariant(variant);
+		m_imageGrProgram = variant->getProgram();
 	}
 	}
 
 
 	Error frameUpdate(Second prevUpdateTime, Second crntTime)
 	Error frameUpdate(Second prevUpdateTime, Second crntTime)
@@ -37,9 +43,13 @@ public:
 
 
 private:
 private:
 	FontPtr m_font;
 	FontPtr m_font;
+	ShaderProgramResourcePtr m_imageProgram;
+	ShaderProgramPtr m_imageGrProgram;
 	TextureViewPtr m_textureView;
 	TextureViewPtr m_textureView;
 	U32 m_crntMip = 0;
 	U32 m_crntMip = 0;
 	F32 m_zoom = 1.0f;
 	F32 m_zoom = 1.0f;
+	Bool m_pointSampling = true;
+	Array<Bool, 4> m_colorChannel = {true, true, true, true};
 
 
 	void draw(CanvasPtr& canvas)
 	void draw(CanvasPtr& canvas)
 	{
 	{
@@ -52,20 +62,32 @@ private:
 		ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
 		ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
 		ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight())));
 		ImGui::SetWindowSize(Vec2(F32(canvas->getWidth()), F32(canvas->getHeight())));
 
 
-		ImGui::BeginChild("Tools", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.25f, -1.0f), false, 0);
+		ImGui::BeginChild("Tools", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.25f, -1.0f), true, 0);
 
 
 		// Info
 		// Info
 		ImGui::TextWrapped("Size %ux%u Mips %u", grTex.getWidth(), grTex.getHeight(), grTex.getMipmapCount());
 		ImGui::TextWrapped("Size %ux%u Mips %u", grTex.getWidth(), grTex.getHeight(), grTex.getMipmapCount());
 
 
 		// Zoom
 		// Zoom
-		ImGui::DragFloat("Zoom", &m_zoom, 0.01f, 0.1f, 10.0f, "%.3f");
+		ImGui::DragFloat("Zoom", &m_zoom, 0.01f, 0.1f, 20.0f, "%.3f");
+
+		// Sampling
+		ImGui::Checkbox("Point sampling", &m_pointSampling);
+
+		// Colors
+		ImGui::Checkbox("Red", &m_colorChannel[0]);
+		ImGui::SameLine();
+		ImGui::Checkbox("Green", &m_colorChannel[1]);
+		ImGui::SameLine();
+		ImGui::Checkbox("Blue", &m_colorChannel[2]);
+		ImGui::SameLine();
+		ImGui::Checkbox("Alpha", &m_colorChannel[3]);
 
 
 		// Mips combo
 		// Mips combo
 		{
 		{
-			Array<CString, 8> mipTexts = {"0", "1", "2", "3", "4", "5", "6", "7"};
+			Array<CString, 11> mipTexts = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
 			CString comboLevel = mipTexts[m_crntMip];
 			CString comboLevel = mipTexts[m_crntMip];
 			const U32 lastCrntMip = m_crntMip;
 			const U32 lastCrntMip = m_crntMip;
-			if(ImGui::BeginCombo("Mipmap", comboLevel.cstr(), 0))
+			if(ImGui::BeginCombo("Mipmap", comboLevel.cstr(), ImGuiComboFlags_HeightLarge))
 			{
 			{
 				for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
 				for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
 				{
 				{
@@ -88,21 +110,35 @@ private:
 				// Re-create the image view
 				// Re-create the image view
 				TextureViewInitInfo viewInitInf(m_textureResource->getGrTexture());
 				TextureViewInitInfo viewInitInf(m_textureResource->getGrTexture());
 				viewInitInf.m_firstMipmap = m_crntMip;
 				viewInitInf.m_firstMipmap = m_crntMip;
+				viewInitInf.m_mipmapCount = 1;
 				m_textureView = getSceneGraph().getGrManager().newTextureView(viewInitInf);
 				m_textureView = getSceneGraph().getGrManager().newTextureView(viewInitInf);
 			}
 			}
 		}
 		}
 
 
 		ImGui::EndChild();
 		ImGui::EndChild();
-
 		ImGui::SameLine();
 		ImGui::SameLine();
 
 
-		ImGui::BeginChild("Image", ImVec2(ImGui::GetWindowContentRegionWidth() * 0.75f, -1.0f), false,
-						  ImGuiWindowFlags_HorizontalScrollbar);
+		// Image
+		ImGui::BeginChild("Image", ImVec2(-1.0f, -1.0f), true, ImGuiWindowFlags_HorizontalScrollbar);
+		{
+			class ExtraPushConstants
+			{
+			public:
+				Vec4 m_colorScale;
+			} pc;
+			pc.m_colorScale.x() = F32(m_colorChannel[0]);
+			pc.m_colorScale.y() = F32(m_colorChannel[1]);
+			pc.m_colorScale.z() = F32(m_colorChannel[2]);
+			pc.m_colorScale.w() = F32(m_colorChannel[3]);
 
 
-		ImGui::Image(const_cast<TextureView*>(m_textureView.get()),
-					 ImVec2(F32(grTex.getWidth()) * m_zoom, F32(grTex.getHeight()) * m_zoom), ImVec2(0.0f, 0.0f),
-					 ImVec2(1.0f, 1.0f), Vec4(1.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f));
+			canvas->setShaderProgram(m_imageGrProgram, &pc, sizeof(pc));
 
 
+			ImGui::Image(UiImageId(m_textureView, m_pointSampling),
+						 ImVec2(F32(grTex.getWidth()) * m_zoom, F32(grTex.getHeight()) * m_zoom), ImVec2(0.0f, 0.0f),
+						 ImVec2(1.0f, 1.0f), Vec4(1.0f), Vec4(0.0f, 0.0f, 0.0f, 1.0f));
+
+			canvas->clearShaderProgram();
+		}
 		ImGui::EndChild();
 		ImGui::EndChild();
 
 
 		canvas->popFont();
 		canvas->popFont();