• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2023 The Khronos Group Inc.
6  * Copyright (c) 2023 Valve Corporation.
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  *
20  *//*!
21  * \file
22  * \brief Test for VK_EXT_depth_bias_control.
23  *//*--------------------------------------------------------------------*/
24 
25 #include "vktRasterizationDepthBiasControlTests.hpp"
26 #include "vktTestCase.hpp"
27 
28 #include "vkImageUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkCmdUtil.hpp"
31 #include "vkBarrierUtil.hpp"
32 
33 #include "tcuTextureUtil.hpp"
34 #include "tcuImageCompare.hpp"
35 
36 #include "deUniquePtr.hpp"
37 
38 #include <sstream>
39 #include <vector>
40 #include <string>
41 #include <cstring>
42 
43 namespace vkt
44 {
45 namespace rasterization
46 {
47 
48 namespace
49 {
50 
51 using namespace vk;
52 
53 using MaybeRepr = tcu::Maybe<VkDepthBiasRepresentationInfoEXT>;
54 
makeDepthBiasRepresentationInfo(const VkDepthBiasRepresentationEXT repr,const bool exact)55 VkDepthBiasRepresentationInfoEXT makeDepthBiasRepresentationInfo (const VkDepthBiasRepresentationEXT repr, const bool exact)
56 {
57 	VkDepthBiasRepresentationInfoEXT info	= initVulkanStructure();
58 	info.depthBiasRepresentation			= repr;
59 	info.depthBiasExact						= (exact ? VK_TRUE : VK_FALSE);
60 	return info;
61 }
62 
getFormatNameShort(const VkFormat format)63 std::string getFormatNameShort (const VkFormat format)
64 {
65 	static const size_t	prefixLen	= std::strlen("VK_FORMAT_");
66 	const auto			fullName	= getFormatName(format);
67 	const auto			shortName	= de::toLower(std::string(fullName).substr(prefixLen));
68 	return shortName;
69 }
70 
getExtent(void)71 inline tcu::IVec3			getExtent		(void) { return tcu::IVec3(1, 1, 1); }
getColorUsage(void)72 inline VkImageUsageFlags	getColorUsage	(void) { return (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); }
getDepthUsage(void)73 inline VkImageUsageFlags	getDepthUsage	(void) { return (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT); }
74 
getImageCreateInfo(const VkFormat format,const VkExtent3D & extent,const VkImageUsageFlags usage)75 VkImageCreateInfo getImageCreateInfo (const VkFormat format, const VkExtent3D& extent, const VkImageUsageFlags usage)
76 {
77 	const VkImageCreateInfo imageCreateInfo
78 	{
79 		VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,	//	VkStructureType			sType;
80 		nullptr,								//	const void*				pNext;
81 		0u,										//	VkImageCreateFlags		flags;
82 		VK_IMAGE_TYPE_2D,						//	VkImageType				imageType;
83 		format,									//	VkFormat				format;
84 		extent,									//	VkExtent3D				extent;
85 		1u,										//	uint32_t				mipLevels;
86 		1u,										//	uint32_t				arrayLayers;
87 		VK_SAMPLE_COUNT_1_BIT,					//	VkSampleCountFlagBits	samples;
88 		VK_IMAGE_TILING_OPTIMAL,				//	VkImageTiling			tiling;
89 		usage,									//	VkImageUsageFlags		usage;
90 		VK_SHARING_MODE_EXCLUSIVE,				//	VkSharingMode			sharingMode;
91 		0u,										//	uint32_t				queueFamilyIndexCount;
92 		nullptr,								//	const uint32_t*			pQueueFamilyIndices;
93 		VK_IMAGE_LAYOUT_UNDEFINED,				//	VkImageLayout			initialLayout;
94 	};
95 
96 	return imageCreateInfo;
97 }
98 
99 using MinResolvDiff = std::pair<double, double>; // Min and Max values.
100 
calcPowerOf2(int exponent)101 double calcPowerOf2 (int exponent)
102 {
103 	if (exponent >= 0)
104 		return static_cast<double>(1u << exponent);
105 	return (1.0 / static_cast<double>(1u << (-exponent)));
106 }
107 
getChannelClass(const tcu::TextureFormat & format)108 tcu::TextureChannelClass getChannelClass (const tcu::TextureFormat& format)
109 {
110 	const auto generalClass = getTextureChannelClass(format.type);
111 	// Fix for VK_FORMAT_X8_D24_UNORM_PACK32
112 	return ((generalClass == tcu::TEXTURECHANNELCLASS_LAST) ? tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT : generalClass);
113 }
114 
115 // Returns a couple of numbers with the minimum and maximum values R (minimum resolvable difference) can have according to the spec.
116 // As explained there, this depends on the depth attachment format, the depth bias representation parameters and sometimes the
117 // geometry itself.
calcMinResolvableDiff(const tcu::TextureFormat & format,const VkDepthBiasRepresentationEXT repr,bool exact,float sampleDepth)118 MinResolvDiff calcMinResolvableDiff (const tcu::TextureFormat& format, const VkDepthBiasRepresentationEXT repr, bool exact, float sampleDepth)
119 {
120 	MinResolvDiff	r				(0.0, 0.0);
121 	const auto		channelClass	= getChannelClass(format);
122 
123 	switch (repr)
124 	{
125 	case VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT:
126 		if (channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
127 		{
128 			// Up to r = 2x2^(-n) where n is bit width.
129 			const auto		bitDepth	= getTextureFormatBitDepth(format);
130 			const double	minR		= calcPowerOf2(-bitDepth[0]);
131 
132 			r.first		= minR;
133 			r.second	= (exact ? 1.0 : 2.0) * minR;
134 		}
135 		else if (channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT)
136 		{
137 			// r = 2^(e-n): e is max exponent in z values, n is mantissa bits.
138 			const tcu::Float32	value		(sampleDepth);
139 			const int			exponent	= value.exponent() - tcu::Float32::MANTISSA_BITS; // (e-n)
140 			const double		minR		= calcPowerOf2(exponent);
141 
142 			r.first		= minR;
143 			r.second	= minR;
144 		}
145 		else
146 			DE_ASSERT(false);
147 		break;
148 	case VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT:
149 		{
150 			// Up to r = 2x2^(-n), where n is the bit width for fixed-point formats or the number of mantissa bits plus one for
151 			// floating-point formats.
152 			int n = 0;
153 
154 			if (channelClass == tcu::TEXTURECHANNELCLASS_UNSIGNED_FIXED_POINT)
155 				n = getTextureFormatBitDepth(format)[0];
156 			else if (channelClass == tcu::TEXTURECHANNELCLASS_FLOATING_POINT)
157 				n = tcu::Float32::MANTISSA_BITS + 1;
158 			else
159 				DE_ASSERT(false);
160 
161 			DE_ASSERT(n > 0); // Make sure the bitwidth is positive.
162 			const double minR = calcPowerOf2(-n);
163 
164 			r.first		= minR;
165 			r.second	= (exact ? 1.0 : 2.0) * minR;
166 		}
167 		break;
168 	case VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT:
169 		// r is always 1.
170 		r.first		= 1.0;
171 		r.second	= 1.0;
172 		break;
173 	default:
174 		DE_ASSERT(false);
175 		break;
176 	}
177 
178 	return r;
179 }
180 
181 // Calculates error threshold when representing values in the given format. This is equivalent to calculating the minimum resolvable
182 // difference R according to the format, with exact precision.
getDepthErrorThreshold(const tcu::TextureFormat & format,const float sampleDepth)183 double getDepthErrorThreshold (const tcu::TextureFormat& format, const float sampleDepth)
184 {
185 	const auto r = calcMinResolvableDiff(format, VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, true, sampleDepth);
186 	return r.first;
187 }
188 
189 // Which depth bias factor will be used in the tests: focus on depthBiasSlopeFactor or depthBiasConstantFactor.
190 enum class UsedFactor	{ SLOPE = 0, CONSTANT = 1 };
191 
192 // How are we going to set the depth bias parameters: statically or dynamically.
193 enum class SetMechanism	{ STATIC = 0, DYNAMIC_1 = 1 /*vkCmdSetDepthBias*/, DYNAMIC_2 = 2 /*vkCmdSetDepthBias2*/};
194 
getMechanismName(const SetMechanism m)195 std::string getMechanismName (const SetMechanism m)
196 {
197 	switch (m)
198 	{
199 	case SetMechanism::STATIC:		return "Static";
200 	case SetMechanism::DYNAMIC_1:	return "vkCmdSetDepthBias";
201 	case SetMechanism::DYNAMIC_2:	return "vkCmdSetDepthBias2";
202 	default:						break;
203 	}
204 
205 	DE_ASSERT(false);
206 	return "";
207 }
208 
209 struct TestParams
210 {
211 	const VkFormat		attachmentFormat;	// Depth attachment format.
212 	const MaybeRepr		reprInfo;			// Representation info. We omit it for some cases.
213 	const SetMechanism	setMechanism;
214 	const float			targetBias;			// The bias we aim to get, before clamping. Based on this and R, we can calculate factors.
215 	const UsedFactor	usedFactor;
216 	const float			constantDepth;		// When using UsedFactor::CONSTANT.
217 	const float			depthBiasClamp;
218 	const bool			secondaryCmdBuffer;	// Use secondary command buffers or not.
219 
logvkt::rasterization::__anone2f729880111::TestParams220 	void log (tcu::TestLog& testLog) const
221 	{
222 		testLog << tcu::TestLog::Message << "Depth format: " << attachmentFormat << tcu::TestLog::EndMessage;
223 
224 		if (!reprInfo)
225 			testLog << tcu::TestLog::Message << "No VkDepthBiasRepresentationInfoEXT extension structure" << tcu::TestLog::EndMessage;
226 		else
227 			testLog << tcu::TestLog::Message << *reprInfo << tcu::TestLog::EndMessage;
228 
229 		testLog
230 			<< tcu::TestLog::Message << "Set mechanism: " << getMechanismName(setMechanism) << tcu::TestLog::EndMessage
231 			<< tcu::TestLog::Message << "Target bias: " << targetBias << tcu::TestLog::EndMessage
232 			<< tcu::TestLog::Message << "Used factor: " << ((usedFactor == UsedFactor::SLOPE) ? "depthBiasSlopeFactor" : "depthBiasConstantFactor") << tcu::TestLog::EndMessage
233 			;
234 
235 		if (usedFactor == UsedFactor::SLOPE)
236 			testLog << tcu::TestLog::Message << "Maximum depth slope: " << 1.0f << tcu::TestLog::EndMessage;
237 		else
238 			testLog << tcu::TestLog::Message << "Constant depth: " << constantDepth << tcu::TestLog::EndMessage;
239 
240 		testLog << tcu::TestLog::Message << "Depth bias clamp: " << depthBiasClamp << tcu::TestLog::EndMessage;
241 	}
242 };
243 
244 class DepthBiasControlInstance : public vkt::TestInstance
245 {
246 public:
DepthBiasControlInstance(Context & context,const TestParams & params)247 						DepthBiasControlInstance	(Context& context, const TestParams& params)
248 							: vkt::TestInstance	(context)
249 							, m_params			(params)
250 							{}
~DepthBiasControlInstance(void)251 	virtual				~DepthBiasControlInstance	(void) {}
252 
253 	tcu::TestStatus		iterate						(void) override;
254 
255 protected:
256 	const TestParams	m_params;
257 };
258 
259 class DepthBiasControlCase : public vkt::TestCase
260 {
261 public:
262 	static tcu::Vec4 kOutColor;
263 
DepthBiasControlCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)264 					DepthBiasControlCase		(tcu::TestContext& testCtx, const std::string& name, const TestParams& params)
265 						: vkt::TestCase	(testCtx, name)
266 						, m_params		(params)
267 						{}
~DepthBiasControlCase(void)268 	virtual			~DepthBiasControlCase		(void) {}
269 
270 	void			initPrograms				(vk::SourceCollections& programCollection) const override;
271 	TestInstance*	createInstance				(Context& context) const override;
272 	void			checkSupport				(Context& context) const override;
273 
274 protected:
275 	const TestParams m_params;
276 };
277 
278 tcu::Vec4 DepthBiasControlCase::kOutColor (0.0f, 0.0f, 1.0f, 1.0f);
279 
checkSupport(Context & context) const280 void DepthBiasControlCase::checkSupport (Context &context) const
281 {
282 	context.requireDeviceFunctionality("VK_EXT_depth_bias_control");
283 
284 	if (m_params.reprInfo)
285 	{
286 		const auto& reprInfo	= m_params.reprInfo.get();
287 		const auto& dbcFeatures	= context.getDepthBiasControlFeaturesEXT();
288 
289 		if (reprInfo.depthBiasExact && !dbcFeatures.depthBiasExact)
290 			TCU_THROW(NotSupportedError, "depthBiasExact not supported");
291 
292 		if (reprInfo.depthBiasRepresentation == VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT
293 			&& !dbcFeatures.leastRepresentableValueForceUnormRepresentation)
294 		{
295 			TCU_THROW(NotSupportedError, "leastRepresentableValueForceUnormRepresentation not supported");
296 		}
297 
298 		if (reprInfo.depthBiasRepresentation == VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT && !dbcFeatures.floatRepresentation)
299 			TCU_THROW(NotSupportedError, "floatRepresentation not supported");
300 	}
301 
302 	// Check format support.
303 	const auto&	vki				= context.getInstanceInterface();
304 	const auto	physDev			= context.getPhysicalDevice();
305 
306 	const auto	imageExtent		= makeExtent3D(getExtent());
307 	const auto	imageUsage		= getDepthUsage();
308 	const auto	imageCreateInfo	= getImageCreateInfo(m_params.attachmentFormat, imageExtent, imageUsage);
309 
310 	VkImageFormatProperties formatProperties;
311 	const auto formatSupport = vki.getPhysicalDeviceImageFormatProperties(physDev, m_params.attachmentFormat, imageCreateInfo.imageType, imageCreateInfo.tiling, imageUsage, imageCreateInfo.flags, &formatProperties);
312 	if (formatSupport == VK_ERROR_FORMAT_NOT_SUPPORTED)
313 		TCU_THROW(NotSupportedError, getFormatNameShort(m_params.attachmentFormat) + " not supported");
314 }
315 
createInstance(Context & context) const316 TestInstance* DepthBiasControlCase::createInstance (Context& context) const
317 {
318 	return new DepthBiasControlInstance(context, m_params);
319 }
320 
initPrograms(vk::SourceCollections & programCollection) const321 void DepthBiasControlCase::initPrograms (vk::SourceCollections &programCollection) const
322 {
323 	std::ostringstream vert;
324 	vert
325 		<< "#version 460\n"
326 		<< "layout (location=0) in vec4 inPos;\n"
327 		<< "void main (void) {\n"
328 		<< "    gl_Position = inPos;\n"
329 		<< "}\n"
330 		;
331 	programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
332 
333 	std::ostringstream frag;
334 	frag
335 		<< "#version 460\n"
336 		<< "layout (location=0) out vec4 outColor;\n"
337 		<< "void main (void) {\n"
338 		<< "    outColor = vec4" << kOutColor << ";\n"
339 		<< "}\n"
340 		;
341 	programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
342 }
343 
iterate(void)344 tcu::TestStatus DepthBiasControlInstance::iterate (void)
345 {
346 	const auto			ctx					= m_context.getContextCommonData();
347 	const tcu::IVec3	fbExtent			(1, 1, 1);
348 	const auto			vkExtent			= makeExtent3D(fbExtent);
349 	const auto			colorFormat			= VK_FORMAT_R8G8B8A8_UNORM;
350 	const auto			colorUsage			= getColorUsage();
351 	const auto			depthFormat			= m_params.attachmentFormat;
352 	const auto			depthUsage			= getDepthUsage();
353 	const auto			bindPoint			= VK_PIPELINE_BIND_POINT_GRAPHICS;
354 	const auto			colorSRR			= makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u);
355 	const auto			depthSRR			= makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u);
356 	const auto			colorSRL			= makeImageSubresourceLayers(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 0u, 1u);
357 	const auto			depthSRL			= makeImageSubresourceLayers(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 0u, 1u);
358 	const auto			tcuDepthFormat		= getDepthCopyFormat(depthFormat);
359 	const auto			tcuColorFormat		= mapVkFormat(colorFormat);
360 	const bool			setStatically		= (m_params.setMechanism == SetMechanism::STATIC);
361 	auto&				log					= m_context.getTestContext().getLog();
362 
363 	const auto			colorCreateInfo		= getImageCreateInfo(colorFormat, vkExtent, colorUsage);
364 	const auto			depthCreateInfo		= getImageCreateInfo(depthFormat, vkExtent, depthUsage);
365 
366 	// Color buffer.
367 	ImageWithBuffer colorBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, colorCreateInfo.imageType, colorSRR,
368 		colorCreateInfo.arrayLayers, colorCreateInfo.samples, colorCreateInfo.tiling, colorCreateInfo.mipLevels, colorCreateInfo.sharingMode);
369 
370 	// Depth buffer.
371 	ImageWithBuffer depthBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, depthFormat, depthUsage, depthCreateInfo.imageType, depthSRR,
372 		depthCreateInfo.arrayLayers, depthCreateInfo.samples, depthCreateInfo.tiling, depthCreateInfo.mipLevels, depthCreateInfo.sharingMode);
373 
374 	// Vertices and vertex buffer.
375 	//
376 	// Generate two triangles as a triangle strip covering the whole framebuffer (4 vertices).
377 	// +--+
378 	// | /|
379 	// |/ |
380 	// +--+
381 	//
382 	// WHEN USING THE DEPTH SLOPE FACTOR:
383 	// If the framebuffer is 1x1, the delta-X and delta-Y accross the whole framebuffer is 1.
384 	// If we make the left-side vertices have a depth of 1.0 and the other 2 have 0.0, delta-Z is 1.
385 	// Using both alternative formulas for calculating M, M is 1. This means depthSlopeFactor applies directly.
386 	// The depth at the sampling point would be 0.5.
387 	//
388 	// WHEN USING THE CONSTANT FACTOR:
389 	// Generate geometry with a chosen constant depth, so M is zero and depthSlopeFactor never applies.
390 	// We will make depthSlopeFactor 0 in any case.
391 	// The constant depth value allows us to control the depth value exponent, which affects some calculations.
392 	const std::vector<tcu::Vec4> vertices
393 	{
394 		tcu::Vec4(-1.0f, -1.0f, ((m_params.usedFactor == UsedFactor::CONSTANT) ? m_params.constantDepth : 0.0f), 1.0f),
395 		tcu::Vec4(-1.0f,  1.0f, ((m_params.usedFactor == UsedFactor::CONSTANT) ? m_params.constantDepth : 0.0f), 1.0f),
396 		tcu::Vec4( 1.0f, -1.0f, ((m_params.usedFactor == UsedFactor::CONSTANT) ? m_params.constantDepth : 1.0f), 1.0f),
397 		tcu::Vec4( 1.0f,  1.0f, ((m_params.usedFactor == UsedFactor::CONSTANT) ? m_params.constantDepth : 1.0f), 1.0f),
398 	};
399 	const float sampleDepth = ((m_params.usedFactor == UsedFactor::CONSTANT) ? m_params.constantDepth : 0.5f);
400 
401 	const auto			vertexBufferSize	= static_cast<VkDeviceSize>(de::dataSize(vertices));
402 	const auto			vertexBufferInfo	= makeBufferCreateInfo(vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
403 	BufferWithMemory	vertexBuffer		(ctx.vkd, ctx.device, ctx.allocator, vertexBufferInfo, MemoryRequirement::HostVisible);
404 	auto&				vertexAlloc			= vertexBuffer.getAllocation();
405 	const auto			vertexBufferOffset	= static_cast<VkDeviceSize>(0);
406 
407 	deMemcpy(vertexAlloc.getHostPtr(), de::dataOrNull(vertices), de::dataSize(vertices));
408 	flushAlloc(ctx.vkd, ctx.device, vertexAlloc);
409 
410 	// Render pass with depth attachment.
411 	const auto renderPass = makeRenderPass(ctx.vkd, ctx.device, colorFormat, depthFormat);
412 
413 	// Framebuffer.
414 	const std::vector<VkImageView> imageViews
415 	{
416 		colorBuffer.getImageView(),
417 		depthBuffer.getImageView(),
418 	};
419 
420 	const auto framebuffer = makeFramebuffer(ctx.vkd, ctx.device, renderPass.get(),
421 											 de::sizeU32(imageViews), de::dataOrNull(imageViews),
422 											 vkExtent.width, vkExtent.height);
423 
424 	// Pipeline.
425 	const auto&	binaries		= m_context.getBinaryCollection();
426 	const auto	vertModule		= createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
427 	const auto	fragModule		= createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
428 	const auto	pipelineLayout	= makePipelineLayout(ctx.vkd, ctx.device);
429 
430 	// Viewports and scissors.
431 	const std::vector<VkViewport>	viewports	(1u, makeViewport(fbExtent));
432 	const std::vector<VkRect2D>		scissors	(1u, makeRect2D(fbExtent));
433 
434 	// Calculate depth bias parameters.
435 	const auto representation	= (m_params.reprInfo ? m_params.reprInfo->depthBiasRepresentation : VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT);
436 	const bool exactRepr		= (m_params.reprInfo ? m_params.reprInfo->depthBiasExact : false);
437 	const auto rValue			= calcMinResolvableDiff(tcuDepthFormat, representation, exactRepr, sampleDepth);
438 
439 	// Calculate factors based on the target bias and the minimum resolvable difference.
440 	const float depthBiasConstantFactor	= ((m_params.usedFactor == UsedFactor::CONSTANT) ? static_cast<float>(static_cast<double>(m_params.targetBias) / rValue.first) : 0.0f);
441 	const float depthBiasSlopeFactor	= ((m_params.usedFactor == UsedFactor::SLOPE) ? m_params.targetBias : 0.0f); // Note M is 1.
442 	const float depthBiasClamp			= m_params.depthBiasClamp;
443 	{
444 		// Log some interesting test details, including computed factors.
445 		m_params.log(log);
446 		log
447 			<< tcu::TestLog::Message << "Rmin:                    " << rValue.first << tcu::TestLog::EndMessage
448 			<< tcu::TestLog::Message << "Rmax:                    " << rValue.second << tcu::TestLog::EndMessage
449 			<< tcu::TestLog::Message << "depthBiasConstantFactor: " << depthBiasConstantFactor << tcu::TestLog::EndMessage
450 			<< tcu::TestLog::Message << "depthBiasSlopeFactor:    " << depthBiasSlopeFactor << tcu::TestLog::EndMessage
451 			<< tcu::TestLog::Message << "depthBiasClamp:          " << depthBiasClamp << tcu::TestLog::EndMessage
452 			;
453 	}
454 
455 	const void* rasterizationPnext = ((setStatically && m_params.reprInfo) ? &m_params.reprInfo.get() : nullptr);
456 
457 	const VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo =
458 	{
459 		VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,	//	VkStructureType							sType;
460 		rasterizationPnext,											//	const void*								pNext;
461 		0u,															//	VkPipelineRasterizationStateCreateFlags	flags;
462 		VK_FALSE,													//	VkBool32								depthClampEnable;
463 		VK_FALSE,													//	VkBool32								rasterizerDiscardEnable;
464 		VK_POLYGON_MODE_FILL,										//	VkPolygonMode							polygonMode;
465 		VK_CULL_MODE_BACK_BIT,										//	VkCullModeFlags							cullMode;
466 		VK_FRONT_FACE_COUNTER_CLOCKWISE,							//	VkFrontFace								frontFace;
467 		VK_TRUE,													//	VkBool32								depthBiasEnable;
468 		(setStatically ? depthBiasConstantFactor : 0.0f),			//	float									depthBiasConstantFactor;
469 		(setStatically ? depthBiasClamp : 0.0f),					//	float									depthBiasClamp;
470 		(setStatically ? depthBiasSlopeFactor : 0.0f),				//	float									depthBiasSlopeFactor;
471 		1.0f,														//	float									lineWidth;
472 	};
473 
474 	const auto stencilOp = makeStencilOpState(VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_STENCIL_OP_KEEP, VK_COMPARE_OP_ALWAYS, 0xFFu, 0xFFu, 0xFFu);
475 
476 	const VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo =
477 	{
478 		VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,	//	VkStructureType							sType;
479 		nullptr,													//	const void*								pNext;
480 		0u,															//	VkPipelineDepthStencilStateCreateFlags	flags;
481 		VK_TRUE,													//	VkBool32								depthTestEnable;
482 		VK_TRUE,													//	VkBool32								depthWriteEnable;
483 		VK_COMPARE_OP_LESS_OR_EQUAL,								//	VkCompareOp								depthCompareOp;
484 		VK_FALSE,													//	VkBool32								depthBoundsTestEnable;
485 		VK_FALSE,													//	VkBool32								stencilTestEnable;
486 		stencilOp,													//	VkStencilOpState						front;
487 		stencilOp,													//	VkStencilOpState						back;
488 		0.0f,														//	float									minDepthBounds;
489 		1.0f,														//	float									maxDepthBounds;
490 	};
491 
492 	std::vector<VkDynamicState> dynamicStates;
493 	if (!setStatically)
494 		dynamicStates.push_back(VK_DYNAMIC_STATE_DEPTH_BIAS);
495 
496 	const VkPipelineDynamicStateCreateInfo dynamicStateCreateInfo =
497 	{
498 		VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,	//	VkStructureType						sType;
499 		nullptr,												//	const void*							pNext;
500 		0u,														//	VkPipelineDynamicStateCreateFlags	flags;
501 		de::sizeU32(dynamicStates),								//	uint32_t							dynamicStateCount;
502 		de::dataOrNull(dynamicStates),							//	const VkDynamicState*				pDynamicStates;
503 	};
504 
505 	const auto pipeline = makeGraphicsPipeline(
506 		ctx.vkd, ctx.device, pipelineLayout.get(),
507 		vertModule.get(), VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, fragModule.get(),
508 		renderPass.get(), viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP, 0u, 0u,
509 		nullptr, &rasterizationStateCreateInfo, nullptr, &depthStencilStateCreateInfo, nullptr, &dynamicStateCreateInfo);
510 
511 	// Command buffers.
512 	CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
513 	const auto primaryCmdBuffer = cmd.cmdBuffer.get();
514 
515 	// Optional secondary command buffer
516 	const auto secondaryCmdBufferPtr	= (m_params.secondaryCmdBuffer
517 										? allocateCommandBuffer(ctx.vkd, ctx.device, cmd.cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY)
518 										: Move<VkCommandBuffer>());
519 	const auto secondaryCmdBuffer		= (m_params.secondaryCmdBuffer ? secondaryCmdBufferPtr.get() : VK_NULL_HANDLE);
520 	const auto subpassContents			= (m_params.secondaryCmdBuffer ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
521 
522 	// For render pass contents.
523 	const auto rpCmdBuffer = (m_params.secondaryCmdBuffer ? secondaryCmdBuffer : primaryCmdBuffer);
524 
525 	const std::vector<VkClearValue> clearValues
526 	{
527 		makeClearValueColor(tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f)),
528 		makeClearValueDepthStencil(1.0f, 0u),
529 	};
530 
531 	beginCommandBuffer(ctx.vkd, primaryCmdBuffer);
532 	if (m_params.secondaryCmdBuffer)
533 		beginSecondaryCommandBuffer(ctx.vkd, secondaryCmdBuffer, renderPass.get(), framebuffer.get());
534 	beginRenderPass(ctx.vkd, primaryCmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0u), de::sizeU32(clearValues), de::dataOrNull(clearValues), subpassContents);
535 
536 	// Render pass contents.
537 	ctx.vkd.cmdBindVertexBuffers(rpCmdBuffer, 0u, 1u, &vertexBuffer.get(), &vertexBufferOffset);
538 	ctx.vkd.cmdBindPipeline(rpCmdBuffer, bindPoint, pipeline.get());
539 	if (!setStatically)
540 	{
541 		if (m_params.setMechanism == SetMechanism::DYNAMIC_1)
542 		{
543 			DE_ASSERT(!m_params.reprInfo);
544 			ctx.vkd.cmdSetDepthBias(rpCmdBuffer, depthBiasConstantFactor, depthBiasClamp, depthBiasSlopeFactor);
545 		}
546 		else if (m_params.setMechanism == SetMechanism::DYNAMIC_2)
547 		{
548 			const void* biasInfoPnext = (m_params.reprInfo ? &m_params.reprInfo.get() : nullptr);
549 
550 			const VkDepthBiasInfoEXT depthBiasInfo =
551 			{
552 				VK_STRUCTURE_TYPE_DEPTH_BIAS_INFO_EXT,	//	VkStructureType	sType;
553 				biasInfoPnext,							//	const void*		pNext;
554 				depthBiasConstantFactor,				//	float			depthBiasConstantFactor;
555 				depthBiasClamp,							//	float			depthBiasClamp;
556 				depthBiasSlopeFactor,					//	float			depthBiasSlopeFactor;
557 			};
558 			ctx.vkd.cmdSetDepthBias2EXT(rpCmdBuffer, &depthBiasInfo);
559 		}
560 		else
561 			DE_ASSERT(false);
562 	}
563 	ctx.vkd.cmdDraw(rpCmdBuffer, de::sizeU32(vertices), 1u, 0u, 0u);
564 
565 	if (m_params.secondaryCmdBuffer)
566 	{
567 		endCommandBuffer(ctx.vkd, secondaryCmdBuffer);
568 		ctx.vkd.cmdExecuteCommands(primaryCmdBuffer, 1u, &secondaryCmdBuffer);
569 	}
570 	endRenderPass(ctx.vkd, primaryCmdBuffer);
571 
572 	// Copy color and depth buffers to their verification buffers.
573 	{
574 		const std::vector<VkImageMemoryBarrier> preTransferBarriers
575 		{
576 			makeImageMemoryBarrier(
577 				VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
578 				VK_ACCESS_TRANSFER_READ_BIT,
579 				VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
580 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
581 				depthBuffer.getImage(), depthSRR),
582 
583 			makeImageMemoryBarrier(
584 				VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
585 				VK_ACCESS_TRANSFER_READ_BIT,
586 				VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
587 				VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
588 				colorBuffer.getImage(), colorSRR),
589 		};
590 
591 		const auto preTransferStages = (VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT);
592 		cmdPipelineImageMemoryBarrier(ctx.vkd, primaryCmdBuffer, preTransferStages, VK_PIPELINE_STAGE_TRANSFER_BIT, de::dataOrNull(preTransferBarriers), de::sizeU32(preTransferBarriers));
593 
594 		const auto depthRegion = makeBufferImageCopy(vkExtent, depthSRL);
595 		const auto colorRegion = makeBufferImageCopy(vkExtent, colorSRL);
596 
597 		ctx.vkd.cmdCopyImageToBuffer(primaryCmdBuffer, depthBuffer.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, depthBuffer.getBuffer(), 1u, &depthRegion);
598 		ctx.vkd.cmdCopyImageToBuffer(primaryCmdBuffer, colorBuffer.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer.getBuffer(), 1u, &colorRegion);
599 
600 		const auto transfer2Host = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
601 		cmdPipelineMemoryBarrier(ctx.vkd, primaryCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &transfer2Host);
602 	}
603 
604 	endCommandBuffer(ctx.vkd, primaryCmdBuffer);
605 	submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, primaryCmdBuffer);
606 
607 	// Invalidate allocations and verify contents.
608 	invalidateAlloc(ctx.vkd, ctx.device, depthBuffer.getBufferAllocation());
609 	invalidateAlloc(ctx.vkd, ctx.device, colorBuffer.getBufferAllocation());
610 
611 	// Depth reference.
612 	tcu::TextureLevel		depthReferenceLevel		(tcuDepthFormat, fbExtent.x(), fbExtent.y());
613 	tcu::PixelBufferAccess	depthReferenceAccess	(depthReferenceLevel.getAccess());
614 	const bool				noClamp					= (m_params.depthBiasClamp == 0.0f);
615 	const float				clampedBias				= std::min(m_params.targetBias, (noClamp ? m_params.targetBias : m_params.depthBiasClamp));
616 	const float				expectedDepth			= sampleDepth + clampedBias; // Must match vertex depth + actual bias.
617 	tcu::clearDepth(depthReferenceAccess, expectedDepth);
618 
619 	// We calculated depth bias constant factors based on the most precise minimum resolvable diff, but the actual resolvable diff
620 	// may be bigger in some cases. We take that into account when calculating the error threshold for depth values, and we add the
621 	// format precision on top.
622 	const double			constantFactorD			= static_cast<double>(depthBiasConstantFactor);
623 	const double			constantBiasMin			= constantFactorD * rValue.first;
624 	const double			constantBiasMax			= constantFactorD * rValue.second;
625 	const double			constantBiasErrorThres	= constantBiasMax - constantBiasMin;
626 	const float				depthThreshold			= static_cast<float>(constantBiasErrorThres + getDepthErrorThreshold(tcuDepthFormat, expectedDepth));
627 	{
628 		log
629 			<< tcu::TestLog::Message << "Constant Bias Min:             " << constantBiasMin << tcu::TestLog::EndMessage
630 			<< tcu::TestLog::Message << "Constant Bias Max:             " << constantBiasMax << tcu::TestLog::EndMessage
631 			<< tcu::TestLog::Message << "Constant Bias Error Threshold: " << constantBiasErrorThres << tcu::TestLog::EndMessage
632 			;
633 	}
634 
635 	// Color reference.
636 	const tcu::Vec4&	expectedColor	= DepthBiasControlCase::kOutColor;
637 	const tcu::Vec4		colorThreshold	(0.0f, 0.0f, 0.0f, 0.0f); // Expect exact result in color.
638 
639 	// Result pixel buffer accesses.
640 	const tcu::ConstPixelBufferAccess depthResultAccess (tcuDepthFormat, fbExtent, depthBuffer.getBufferAllocation().getHostPtr());
641 	const tcu::ConstPixelBufferAccess colorResultAccess (tcuColorFormat, fbExtent, colorBuffer.getBufferAllocation().getHostPtr());
642 
643 	bool fail = false;
644 
645 	if (!tcu::dsThresholdCompare(log, "DepthResult", "", depthReferenceAccess, depthResultAccess, depthThreshold, tcu::COMPARE_LOG_ON_ERROR))
646 	{
647 		log << tcu::TestLog::Message << "Depth buffer failed: expected " << expectedDepth << " (threshold " << depthThreshold
648 			<< ") and found " << depthResultAccess.getPixDepth(0, 0) << tcu::TestLog::EndMessage;
649 		fail = true;
650 	}
651 
652 	if (!tcu::floatThresholdCompare(log, "ColorResult", "", expectedColor, colorResultAccess, colorThreshold, tcu::COMPARE_LOG_ON_ERROR))
653 	{
654 		log << tcu::TestLog::Message << "Color buffer failed: expected " << expectedColor << " (threshold " << colorThreshold
655 			<< ") and found " << depthResultAccess.getPixel(0, 0) << tcu::TestLog::EndMessage;
656 		fail = true;
657 	}
658 
659 	if (fail)
660 		return tcu::TestStatus::fail("Failed -- check log for details");
661 	return tcu::TestStatus::pass("Pass");
662 }
663 
664 } // anonymous namespace
665 
createDepthBiasControlTests(tcu::TestContext & testCtx)666 tcu::TestCaseGroup* createDepthBiasControlTests (tcu::TestContext& testCtx)
667 {
668 	using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
669 
670 	const std::vector<VkFormat> attachmentFormats
671 	{
672 		VK_FORMAT_D16_UNORM,
673 		VK_FORMAT_X8_D24_UNORM_PACK32,
674 		VK_FORMAT_D32_SFLOAT,
675 		VK_FORMAT_D16_UNORM_S8_UINT,
676 		VK_FORMAT_D24_UNORM_S8_UINT,
677 		VK_FORMAT_D32_SFLOAT_S8_UINT,
678 	};
679 
680 	const struct
681 	{
682 		const UsedFactor	usedFactor;
683 		const char*			name;
684 	} usedFactorCases[] =
685 	{
686 		{ UsedFactor::SLOPE,	"slope"		},
687 		{ UsedFactor::CONSTANT,	"constant"	},
688 	};
689 
690 	const struct
691 	{
692 		const MaybeRepr	reprInfo;
693 		const char*		name;
694 	} reprInfoCases[] =
695 	{
696 		{ tcu::Nothing,																										"no_repr_info"			},
697 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, false),		"format_inexact"			},
698 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORMAT_EXT, true),			"format_exact"		},
699 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT, false),	"force_unorm_inexact"		},
700 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_LEAST_REPRESENTABLE_VALUE_FORCE_UNORM_EXT, true),	"force_unorm_exact"	},
701 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT, false),									"float_inexact"			},
702 		{ makeDepthBiasRepresentationInfo(VK_DEPTH_BIAS_REPRESENTATION_FLOAT_EXT, true),									"float_exact"			},
703 	};
704 
705 	struct ConstantDepthCase
706 	{
707 		const float constantDepth;
708 		const char* name;
709 	};
710 	using ConstantDepthCaseVec = std::vector<ConstantDepthCase>;
711 
712 	const ConstantDepthCaseVec constantDepthSlopeCases // For these subcases, the constant depth is not used.
713 	{
714 		{ 0.0f,				"slope_depth_1_0"				},
715 	};
716 	const ConstantDepthCaseVec constantDepthConstantCases
717 	{
718 		{ 0.25f,			"constant_depth_0_25"			},
719 		{ 0.3125f,			"constant_depth_0_3125"			},
720 		{ 0.489742279053f,	"constant_depth_close_to_0_5"	},
721 		{ 0.625f,			"constant_depth_0_625"			},
722 		{ 0.125f,			"constant_depth_0_125"			},
723 	};
724 
725 	const struct
726 	{
727 		const float targetBias;
728 		const char* name;
729 	} targetBiasCases[] =
730 	{
731 		{ 0.0625f,	"target_bias_0_0625"	},
732 		{ 0.125f,	"target_bias_0_125"		},
733 		{ 0.25f,	"target_bias_0_25"		},
734 	};
735 
736 	const struct
737 	{
738 		const SetMechanism		setMechanism;
739 		const char*				name;
740 	} setMechanismCases[] =
741 	{
742 		{ SetMechanism::STATIC,		"static"			},
743 		{ SetMechanism::DYNAMIC_1,	"dynamic_set_1"		},
744 		{ SetMechanism::DYNAMIC_2,	"dynamic_set_2"		},
745 	};
746 
747 	enum class ClampCase { ZERO = 0, LARGE = 1, SMALL = 2 };
748 	const struct
749 	{
750 		const ClampCase		clampCase;
751 		const char*			suffix;
752 	} clampValueCases[] =
753 	{
754 		{	ClampCase::ZERO,		"_no_clamp"				},
755 		{	ClampCase::LARGE,		"_no_effective_clamp"	},
756 		{	ClampCase::SMALL,		"_clamp_to_half"		},
757 	};
758 
759 	const struct
760 	{
761 		const bool	secondaryCmdBuffer;
762 		const char*	suffix;
763 	} secondaryCmdBufferCases[] =
764 	{
765 		{ false,	""						},
766 		{ true,		"_secondary_cmd_buffer"	},
767 	};
768 
769 	GroupPtr dbcGroup (new tcu::TestCaseGroup(testCtx, "depth_bias_control"));
770 
771 	for (const auto& format : attachmentFormats)
772 	{
773 		const auto formatName = getFormatNameShort(format);
774 
775 		GroupPtr formatGroup (new tcu::TestCaseGroup(testCtx, formatName.c_str()));
776 
777 		for (const auto& reprInfoCase : reprInfoCases)
778 		{
779 			GroupPtr reprInfoGroup (new tcu::TestCaseGroup(testCtx, reprInfoCase.name));
780 
781 			for (const auto& usedFactorCase : usedFactorCases)
782 			{
783 				GroupPtr usedFactorGroup (new tcu::TestCaseGroup(testCtx, usedFactorCase.name));
784 
785 				const bool constantFactor = (usedFactorCase.usedFactor == UsedFactor::CONSTANT);
786 				const ConstantDepthCaseVec& constantDepthCases = (constantFactor ? constantDepthConstantCases : constantDepthSlopeCases);
787 
788 				for (const auto& constantDepthCase : constantDepthCases)
789 				{
790 					GroupPtr constantDepthGroup (new tcu::TestCaseGroup(testCtx, constantDepthCase.name));
791 
792 					for (const auto& targetBiasCase : targetBiasCases)
793 					{
794 						GroupPtr targetBiasGroup (new tcu::TestCaseGroup(testCtx, targetBiasCase.name));
795 
796 						for (const auto& setMechanismCase : setMechanismCases)
797 						{
798 							// We cannot use the representation info with vkCmdSetDepthBias.
799 							if (setMechanismCase.setMechanism == SetMechanism::DYNAMIC_1 && static_cast<bool>(reprInfoCase.reprInfo))
800 								continue;
801 
802 							for (const auto& clampValueCase : clampValueCases)
803 							{
804 								float depthBiasClamp = 0.0f;
805 								switch (clampValueCase.clampCase)
806 								{
807 								case ClampCase::ZERO:			depthBiasClamp = 0.0f;									break;
808 								case ClampCase::LARGE:			depthBiasClamp = targetBiasCase.targetBias * 2.0f;		break;
809 								case ClampCase::SMALL:			depthBiasClamp = targetBiasCase.targetBias * 0.5f;		break;
810 								default:						DE_ASSERT(false);										break;
811 								}
812 
813 								for (const auto& secondaryCmdBufferCase : secondaryCmdBufferCases)
814 								{
815 									// Some selected combinations will use secondary command buffers. Avoid applying this to all
816 									// combinations to keep the number of cases low.
817 									if (secondaryCmdBufferCase.secondaryCmdBuffer)
818 									{
819 										if (usedFactorCase.usedFactor != UsedFactor::CONSTANT)
820 											continue;
821 
822 										if (setMechanismCase.setMechanism == SetMechanism::DYNAMIC_1)
823 											continue;
824 
825 										if (clampValueCase.clampCase != ClampCase::ZERO)
826 											continue;
827 
828 										if (!static_cast<bool>(reprInfoCase.reprInfo))
829 											continue;
830 									}
831 
832 									const TestParams params
833 									{
834 										format,
835 										reprInfoCase.reprInfo,
836 										setMechanismCase.setMechanism,
837 										targetBiasCase.targetBias,
838 										usedFactorCase.usedFactor,
839 										constantDepthCase.constantDepth,
840 										depthBiasClamp,
841 										secondaryCmdBufferCase.secondaryCmdBuffer,
842 									};
843 									const std::string testName = std::string(setMechanismCase.name) + clampValueCase.suffix + secondaryCmdBufferCase.suffix;
844 									targetBiasGroup->addChild(new DepthBiasControlCase(testCtx, testName, params));
845 								}
846 							}
847 						}
848 
849 						constantDepthGroup->addChild(targetBiasGroup.release());
850 					}
851 
852 					usedFactorGroup->addChild(constantDepthGroup.release());
853 				}
854 
855 				reprInfoGroup->addChild(usedFactorGroup.release());
856 			}
857 
858 			formatGroup->addChild(reprInfoGroup.release());
859 		}
860 
861 		dbcGroup->addChild(formatGroup.release());
862 	}
863 
864 	return dbcGroup.release();
865 }
866 
867 } // rasterization
868 } // vkt
869