Browse Source

Add render area support in both GL and vulkan

Panagiotis Christopoulos Charitos 8 years ago
parent
commit
d8eb4ad04b

+ 3 - 1
src/anki/gr/CommandBuffer.h

@@ -257,7 +257,9 @@ public:
 	void bindShaderProgram(ShaderProgramPtr prog);
 
 	/// Begin renderpass.
-	void beginRenderPass(FramebufferPtr fb);
+	/// The minx, miny, maxx, maxy control the area that the load and store operations will happen. If the scissor is
+	/// bigger than the render area the results are undefined.
+	void beginRenderPass(FramebufferPtr fb, U16 minx = 0, U16 miny = 0, U16 maxx = MAX_U16, U16 maxy = MAX_U16);
 
 	/// End renderpass.
 	void endRenderPass();

+ 8 - 4
src/anki/gr/gl/CommandBuffer.cpp

@@ -870,28 +870,32 @@ void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 	}
 }
 
-void CommandBuffer::beginRenderPass(FramebufferPtr fb)
+void CommandBuffer::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
+	ANKI_ASSERT(minx < maxx && miny < maxy);
+
 	class BindFramebufferCommand final : public GlCommand
 	{
 	public:
 		FramebufferPtr m_fb;
+		Array<U16, 4> m_renderArea;
 
-		BindFramebufferCommand(FramebufferPtr fb)
+		BindFramebufferCommand(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
 			: m_fb(fb)
+			, m_renderArea{{minx, miny, maxx, maxy}}
 		{
 		}
 
 		Error operator()(GlState& state)
 		{
-			m_fb->m_impl->bind(state);
+			m_fb->m_impl->bind(state, m_renderArea[0], m_renderArea[1], m_renderArea[2], m_renderArea[3]);
 			return ErrorCode::NONE;
 		}
 	};
 
 	if(m_impl->m_state.beginRenderPass(fb))
 	{
-		m_impl->pushBackNewCommand<BindFramebufferCommand>(fb);
+		m_impl->pushBackNewCommand<BindFramebufferCommand>(fb, minx, miny, maxx, maxy);
 	}
 }
 

+ 4 - 12
src/anki/gr/gl/FramebufferImpl.cpp

@@ -151,20 +151,15 @@ void FramebufferImpl::attachTextureInternal(
 	}
 }
 
-void FramebufferImpl::bind(const GlState& state)
+void FramebufferImpl::bind(const GlState& state, U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
 	if(m_in.getName() && getManager().getImplementation().debugMarkersEnabled())
 	{
 		glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, m_glName, 0, &m_in.getName()[0]);
 	}
 
-	// Disable the scissor to make sure clears will clear everything
-	Bool disableScissor = !(state.m_scissor[0] == 0 && state.m_scissor[1] == 0 && state.m_scissor[2] == MAX_U16
-		&& state.m_scissor[3] == MAX_U16);
-	if(disableScissor)
-	{
-		glDisable(GL_SCISSOR_TEST);
-	}
+	// Clear in the render area
+	glScissor(minx, miny, maxx - minx, maxy - miny);
 
 	if(m_bindDefault)
 	{
@@ -271,10 +266,7 @@ void FramebufferImpl::bind(const GlState& state)
 		}
 	}
 
-	if(disableScissor)
-	{
-		glEnable(GL_SCISSOR_TEST);
-	}
+	glScissor(state.m_scissor[0], state.m_scissor[1], state.m_scissor[2], state.m_scissor[3]);
 }
 
 void FramebufferImpl::endRenderPass() const

+ 1 - 1
src/anki/gr/gl/FramebufferImpl.h

@@ -33,7 +33,7 @@ public:
 	ANKI_USE_RESULT Error init(const FramebufferInitInfo& init);
 
 	/// Bind it to the state. Call it in rendering thread
-	void bind(const GlState& state);
+	void bind(const GlState& state, U16 minx, U16 miny, U16 maxx, U16 maxy);
 
 	void endRenderPass() const;
 

+ 3 - 2
src/anki/gr/vulkan/CommandBuffer.cpp

@@ -202,9 +202,10 @@ void CommandBuffer::bindShaderProgram(ShaderProgramPtr prog)
 	m_impl->bindShaderProgram(prog);
 }
 
-void CommandBuffer::beginRenderPass(FramebufferPtr fb)
+void CommandBuffer::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
-	m_impl->beginRenderPass(fb);
+	ANKI_ASSERT(minx < maxx && miny < maxy);
+	m_impl->beginRenderPass(fb, minx, miny, maxx, maxy);
 }
 
 void CommandBuffer::endRenderPass()

+ 22 - 5
src/anki/gr/vulkan/CommandBufferImpl.cpp

@@ -109,7 +109,7 @@ void CommandBufferImpl::beginRecording()
 	vkBeginCommandBuffer(m_handle, &begin);
 }
 
-void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
+void CommandBufferImpl::beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy)
 {
 	commandCommon();
 	ANKI_ASSERT(!insideRenderPass());
@@ -117,6 +117,17 @@ void CommandBufferImpl::beginRenderPass(FramebufferPtr fb)
 	m_rpCommandCount = 0;
 	m_activeFb = fb;
 
+	U32 fbWidth, fbHeight;
+	fb->m_impl->getAttachmentsSize(fbWidth, fbHeight);
+	m_fbSize[0] = fbWidth;
+	m_fbSize[1] = fbHeight;
+
+	m_renderArea[0] = max<U16>(0, minx);
+	m_renderArea[1] = max<U16>(0, miny);
+	m_renderArea[2] = min<U16>(m_fbSize[0], maxx);
+	m_renderArea[3] = min<U16>(m_fbSize[1], maxy);
+	ANKI_ASSERT(m_renderArea[0] < m_renderArea[2] && m_renderArea[1] < m_renderArea[3]);
+
 	m_microCmdb->pushObjectRef(fb);
 
 	m_subpassContents = VK_SUBPASS_CONTENTS_MAX_ENUM;
@@ -138,7 +149,6 @@ void CommandBufferImpl::beginRenderPassInternal()
 		// Bind a non-default FB
 
 		bi.framebuffer = impl.getFramebufferHandle(0);
-		impl.getAttachmentsSize(bi.renderArea.extent.width, bi.renderArea.extent.height);
 
 		// Calc the layouts
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> colAttLayouts;
@@ -166,9 +176,6 @@ void CommandBufferImpl::beginRenderPassInternal()
 		Array<VkImageLayout, MAX_COLOR_ATTACHMENTS> dummy;
 		bi.renderPass = impl.getRenderPassHandle(dummy, VK_IMAGE_LAYOUT_MAX_ENUM);
 
-		bi.renderArea.extent.width = getGrManagerImpl().getDefaultSurfaceWidth();
-		bi.renderArea.extent.height = getGrManagerImpl().getDefaultSurfaceHeight();
-
 		// Perform the transition
 		setImageBarrier(VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
 			0,
@@ -180,6 +187,16 @@ void CommandBufferImpl::beginRenderPassInternal()
 			VkImageSubresourceRange{VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1});
 	}
 
+	const Bool flipvp = flipViewport();
+	bi.renderArea.offset.x = m_renderArea[0];
+	if(flipvp)
+	{
+		ANKI_ASSERT(m_renderArea[3] <= m_fbSize[1]);
+	}
+	bi.renderArea.offset.y = (flipvp) ? m_fbSize[1] - m_renderArea[3] : m_renderArea[1];
+	bi.renderArea.extent.width = m_renderArea[2] - m_renderArea[0];
+	bi.renderArea.extent.height = m_renderArea[3] - m_renderArea[1];
+
 	ANKI_CMD(vkCmdBeginRenderPass(m_handle, &bi, m_subpassContents), ANY_OTHER_COMMAND);
 }
 

+ 3 - 1
src/anki/gr/vulkan/CommandBufferImpl.h

@@ -244,7 +244,7 @@ public:
 		m_microCmdb->pushObjectRef(img);
 	}
 
-	void beginRenderPass(FramebufferPtr fb);
+	void beginRenderPass(FramebufferPtr fb, U16 minx, U16 miny, U16 maxx, U16 maxy);
 
 	void endRenderPass();
 
@@ -358,6 +358,8 @@ private:
 
 	U m_rpCommandCount = 0; ///< Number of drawcalls or pushed cmdbs in rp.
 	FramebufferPtr m_activeFb;
+	Array<U16, 4> m_renderArea = {{0, 0, MAX_U16, MAX_U16}};
+	Array<U16, 2> m_fbSize = {{0, 0}};
 
 	ShaderProgramImpl* m_graphicsProg ANKI_DBG_NULLIFY; ///< Last bound graphics program
 

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

@@ -94,6 +94,7 @@ public:
 
 	void getAttachmentsSize(U32& width, U32& height) const
 	{
+		ANKI_ASSERT(m_width != 0 && m_height != 0);
 		width = m_width;
 		height = m_height;
 	}

+ 214 - 6
tests/gr/Gr.cpp

@@ -30,6 +30,19 @@ void main()
 	ANKI_WRITE_POSITION(vec4(POSITIONS[gl_VertexID % 3], 0.0, 1.0));
 })";
 
+static const char* VERT_QUAD_STRIP_SRC = R"(
+out gl_PerVertex
+{
+	vec4 gl_Position;
+};
+
+void main()
+{
+	const vec2 POSITIONS[4] = vec2[](vec2(-1.0, -1.0), vec2(1.0, -1.0), vec2(-1.0, 1.0), vec2(1.0, 1.0));
+
+	ANKI_WRITE_POSITION(vec4(POSITIONS[gl_VertexID % 4], 0.0, 1.0));
+})";
+
 static const char* VERT_UBO_SRC = R"(
 out gl_PerVertex
 {
@@ -148,6 +161,17 @@ static const char* FRAG_TEX_SRC = R"(layout (location = 0) out vec4 out_color;
 
 layout(location = 0) in vec2 in_uv;
 
+layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_tex0;
+
+void main()
+{
+	out_color = texture(u_tex0, in_uv);
+})";
+
+static const char* FRAG_2TEX_SRC = R"(layout (location = 0) out vec4 out_color;
+
+layout(location = 0) in vec2 in_uv;
+
 layout(ANKI_TEX_BINDING(0, 0)) uniform sampler2D u_tex0;
 layout(ANKI_TEX_BINDING(0, 1)) uniform sampler2D u_tex1;
 
@@ -418,17 +442,69 @@ ANKI_TEST(Gr, SimpleDrawcall)
 {
 	COMMON_BEGIN()
 
-	ANKI_TEST_LOGI("Expect to see a grey triangle appearing in the 4 corners");
+	ANKI_TEST_LOGI("Expect to see a grey triangle");
 	ShaderProgramPtr prog = createProgram(VERT_SRC, FRAG_SRC, *gr);
 	FramebufferPtr fb = createDefaultFb(*gr);
 
+	const U ITERATIONS = 200;
+	for(U i = 0; i < ITERATIONS; ++i)
+	{
+		HighRezTimer timer;
+		timer.start();
+
+		gr->beginFrame();
+
+		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
+		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
+
+		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
+		cmdb->bindShaderProgram(prog);
+		cmdb->beginRenderPass(fb);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
+		cmdb->endRenderPass();
+		cmdb->flush();
+
+		gr->swapBuffers();
+
+		timer.stop();
+		const F32 TICK = 1.0 / 30.0;
+		if(timer.getElapsedTime() < TICK)
+		{
+			HighRezTimer::sleep(TICK - timer.getElapsedTime());
+		}
+	}
+
+	COMMON_END()
+}
+
+ANKI_TEST(Gr, ViewportAndScissor)
+{
+	COMMON_BEGIN()
+
+	ANKI_TEST_LOGI("Expect to see a grey quad appearing in the 4 corners. The clear color will change and affect only"
+				   "the area around the quad");
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_STRIP_SRC, FRAG_SRC, *gr);
+
+	srand(time(NULL));
+	Array<FramebufferPtr, 4> fb;
+	for(FramebufferPtr& f : fb)
+	{
+		FramebufferInitInfo fbinit;
+		fbinit.m_colorAttachmentCount = 1;
+		fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{randFloat(1.0), randFloat(1.0), randFloat(1.0), 1.0}};
+
+		f = gr->newInstance<Framebuffer>(fbinit);
+	}
+
 	static const Array2d<U, 4, 4> VIEWPORTS = {{{{0, 0, WIDTH / 2, HEIGHT / 2}},
 		{{WIDTH / 2, 0, WIDTH, HEIGHT / 2}},
 		{{WIDTH / 2, HEIGHT / 2, WIDTH, HEIGHT}},
 		{{0, HEIGHT / 2, WIDTH / 2, HEIGHT}}}};
 
-	const U ITERATIONS = 200;
+	const U ITERATIONS = 400;
 	const U SCISSOR_MARGIN = 20;
+	const U RENDER_AREA_MARGIN = 10;
 	for(U i = 0; i < ITERATIONS; ++i)
 	{
 		HighRezTimer timer;
@@ -437,17 +513,149 @@ ANKI_TEST(Gr, SimpleDrawcall)
 		gr->beginFrame();
 
 		CommandBufferInitInfo cinit;
-		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
 		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
 
+		U idx = (i / 30) % 4;
+		auto vp = VIEWPORTS[idx];
+		cmdb->setViewport(vp[0], vp[1], vp[2], vp[3]);
+		cmdb->setScissor(
+			vp[0] + SCISSOR_MARGIN, vp[1] + SCISSOR_MARGIN, vp[2] - SCISSOR_MARGIN, vp[3] - SCISSOR_MARGIN);
+		cmdb->bindShaderProgram(prog);
+		cmdb->beginRenderPass(fb[i % 4],
+			vp[0] + RENDER_AREA_MARGIN,
+			vp[1] + RENDER_AREA_MARGIN,
+			vp[2] - RENDER_AREA_MARGIN,
+			vp[3] - RENDER_AREA_MARGIN);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4);
+		cmdb->endRenderPass();
+		cmdb->flush();
+
+		gr->swapBuffers();
+
+		timer.stop();
+		const F32 TICK = 1.0 / 30.0;
+		if(timer.getElapsedTime() < TICK)
+		{
+			HighRezTimer::sleep(TICK - timer.getElapsedTime());
+		}
+	}
+
+	COMMON_END()
+}
+
+ANKI_TEST(Gr, ViewportAndScissorOffscreen)
+{
+	srand(time(NULL));
+	COMMON_BEGIN()
+
+	ANKI_TEST_LOGI("Expect to see a grey quad appearing in the 4 corners. "
+				   "Around that quad is a border that changes color. "
+				   "The quads appear counter-clockwise");
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_STRIP_SRC, FRAG_SRC, *gr);
+	ShaderProgramPtr blitProg = createProgram(VERT_QUAD_SRC, FRAG_TEX_SRC, *gr);
+
+	const PixelFormat COL_FORMAT = PixelFormat(ComponentFormat::R8G8B8A8, TransformFormat::UNORM);
+	const U RT_WIDTH = 32;
+	const U RT_HEIGHT = 16;
+	TextureInitInfo init;
+	init.m_depth = 1;
+	init.m_format = COL_FORMAT;
+	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_depth = 1;
+	init.m_layerCount = 1;
+	init.m_samples = 1;
+	init.m_sampling.m_minMagFilter = SamplingFilter::NEAREST;
+	init.m_sampling.m_mipmapFilter = SamplingFilter::NEAREST;
+	init.m_type = TextureType::_2D;
+	TexturePtr rt = gr->newInstance<Texture>(init);
+
+	Array<FramebufferPtr, 4> fb;
+	for(FramebufferPtr& f : fb)
+	{
+		FramebufferInitInfo fbinit;
+		fbinit.m_colorAttachmentCount = 1;
+		fbinit.m_colorAttachments[0].m_clearValue.m_colorf = {{randFloat(1.0), randFloat(1.0), randFloat(1.0), 1.0}};
+		fbinit.m_colorAttachments[0].m_texture = rt;
+
+		f = gr->newInstance<Framebuffer>(fbinit);
+	}
+
+	FramebufferPtr defaultFb = createDefaultFb(*gr);
+
+	static const Array2d<U, 4, 4> VIEWPORTS = {{{{0, 0, RT_WIDTH / 2, RT_HEIGHT / 2}},
+		{{RT_WIDTH / 2, 0, RT_WIDTH, RT_HEIGHT / 2}},
+		{{RT_WIDTH / 2, RT_HEIGHT / 2, RT_WIDTH, RT_HEIGHT}},
+		{{0, RT_HEIGHT / 2, RT_WIDTH / 2, RT_HEIGHT}}}};
+
+	const U ITERATIONS = 400;
+	const U SCISSOR_MARGIN = 2;
+	const U RENDER_AREA_MARGIN = 1;
+	for(U i = 0; i < ITERATIONS; ++i)
+	{
+		HighRezTimer timer;
+		timer.start();
+
+		gr->beginFrame();
+
+		if(i == 0)
+		{
+			CommandBufferInitInfo cinit;
+			cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
+			CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
+
+			cmdb->setViewport(0, 0, RT_WIDTH, RT_HEIGHT);
+			cmdb->setTextureSurfaceBarrier(rt,
+				TextureUsageBit::NONE,
+				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+				TextureSurfaceInfo(0, 0, 0, 0));
+			cmdb->beginRenderPass(fb[0]);
+			cmdb->endRenderPass();
+			cmdb->setTextureSurfaceBarrier(rt,
+				TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+				TextureUsageBit::SAMPLED_FRAGMENT,
+				TextureSurfaceInfo(0, 0, 0, 0));
+			cmdb->flush();
+		}
+
+		CommandBufferInitInfo cinit;
+		cinit.m_flags = CommandBufferFlag::GRAPHICS_WORK | CommandBufferFlag::SMALL_BATCH;
+		CommandBufferPtr cmdb = gr->newInstance<CommandBuffer>(cinit);
+
+		// Draw offscreen
+		cmdb->setTextureSurfaceBarrier(rt,
+			TextureUsageBit::SAMPLED_FRAGMENT,
+			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			TextureSurfaceInfo(0, 0, 0, 0));
 		auto vp = VIEWPORTS[(i / 30) % 4];
 		cmdb->setViewport(vp[0], vp[1], vp[2], vp[3]);
 		cmdb->setScissor(
 			vp[0] + SCISSOR_MARGIN, vp[1] + SCISSOR_MARGIN, vp[2] - SCISSOR_MARGIN, vp[3] - SCISSOR_MARGIN);
 		cmdb->bindShaderProgram(prog);
-		cmdb->beginRenderPass(fb);
-		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 3);
+		cmdb->beginRenderPass(fb[i % 4],
+			vp[0] + RENDER_AREA_MARGIN,
+			vp[1] + RENDER_AREA_MARGIN,
+			vp[2] - RENDER_AREA_MARGIN,
+			vp[3] - RENDER_AREA_MARGIN);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLE_STRIP, 4);
 		cmdb->endRenderPass();
+
+		// Draw onscreen
+		cmdb->setViewport(0, 0, WIDTH, HEIGHT);
+		cmdb->setScissor(0, 0, WIDTH, HEIGHT);
+		cmdb->bindShaderProgram(blitProg);
+		cmdb->setTextureSurfaceBarrier(rt,
+			TextureUsageBit::FRAMEBUFFER_ATTACHMENT_WRITE,
+			TextureUsageBit::SAMPLED_FRAGMENT,
+			TextureSurfaceInfo(0, 0, 0, 0));
+		cmdb->bindTexture(0, 0, rt);
+		cmdb->beginRenderPass(defaultFb);
+		cmdb->drawArrays(PrimitiveTopology::TRIANGLES, 6);
+		cmdb->endRenderPass();
+
 		cmdb->flush();
 
 		gr->swapBuffers();
@@ -806,7 +1014,7 @@ ANKI_TEST(Gr, DrawWithTexture)
 	//
 	// Create prog
 	//
-	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_TEX_SRC, *gr);
+	ShaderProgramPtr prog = createProgram(VERT_QUAD_SRC, FRAG_2TEX_SRC, *gr);
 
 	//
 	// Create FB