Make SetViewport validation match upstream WebGPU.

As a side-effect this allows empty viewports which need special handling
in Vulkan because it is not allowed to set width to 0 (but ok to set
height to 0).

Validation tests are updated to cover the new validation checks.

Most of the viewport end2end tests are rewritten because they didn't
pass the new validation.

A new end2end test is added to test various kinds of empty viewports to
cover the extra logic in the Vulkan backend.

Bug: dawn:542
Change-Id: I8bb25612eeed04162a6b942983167eacab3a1906
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/29681
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
This commit is contained in:
Corentin Wallez
2020-10-11 18:39:32 +00:00
committed by Commit Bot service account
parent 1c25198384
commit 6c3da3dc5b
6 changed files with 275 additions and 535 deletions

View File

@@ -14,213 +14,123 @@
#include "tests/unittests/validation/ValidationTest.h"
#include "utils/WGPUHelpers.h"
#include <cmath>
class SetViewportTest : public ValidationTest {};
class SetViewportTest : public ValidationTest {
protected:
void TestViewportCall(bool success,
float x,
float y,
float width,
float height,
float minDepth,
float maxDepth) {
utils::BasicRenderPass rp = utils::CreateBasicRenderPass(device, kWidth, kHeight);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&rp.renderPassInfo);
pass.SetViewport(x, y, width, height, minDepth, maxDepth);
pass.EndPass();
if (success) {
encoder.Finish();
} else {
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
static constexpr uint32_t kWidth = 5;
static constexpr uint32_t kHeight = 3;
};
// Test to check basic use of SetViewport
TEST_F(SetViewportTest, Success) {
DummyRenderPass renderPass(device);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, 1.0);
pass.EndPass();
}
encoder.Finish();
TestViewportCall(true, 0.0, 0.0, 1.0, 1.0, 0.0, 1.0);
}
// Test to check that NaN in viewport parameters is not allowed
TEST_F(SetViewportTest, ViewportParameterNaN) {
DummyRenderPass renderPass(device);
// x or y is NaN.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(NAN, 0.0, 1.0, 1.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// width or height is NaN.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, NAN, 1.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// minDepth or maxDepth is NaN.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, NAN, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
TestViewportCall(false, NAN, 0.0, 1.0, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, NAN, 1.0, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, NAN, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, 1.0, NAN, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, NAN, 1.0);
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 0.0, NAN);
}
// Test to check that an empty viewport is not allowed
// Test to check that an empty viewport is allowed.
TEST_F(SetViewportTest, EmptyViewport) {
DummyRenderPass renderPass(device);
// Width of viewport is zero.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 0.0, 1.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
TestViewportCall(true, 0.0, 0.0, 0.0, 1.0, 0.0, 1.0);
// Height of viewport is zero.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
TestViewportCall(true, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0);
// Both width and height of viewport are zero.
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
TestViewportCall(true, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0);
}
// Test to check that viewport larger than the framebuffer is allowed
// Test to check that viewport larger than the framebuffer is disallowed
TEST_F(SetViewportTest, ViewportLargerThanFramebuffer) {
DummyRenderPass renderPass(device);
// Control case: width and height are set to the render target size.
TestViewportCall(true, 0.0, 0.0, kWidth, kHeight, 0.0, 1.0);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, renderPass.width + 1, renderPass.height + 1, 0.0, 1.0);
pass.EndPass();
}
encoder.Finish();
// Width is larger than the rendertarget's width
TestViewportCall(false, 0.0, 0.0, kWidth + 1.0, kHeight, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, nextafter(float(kWidth), 1000.0f), kHeight, 0.0, 1.0);
// Height is larger than the rendertarget's height
TestViewportCall(false, 0.0, 0.0, kWidth, kHeight + 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, kWidth, nextafter(float(kHeight), 1000.0f), 0.0, 1.0);
// x + width is larger than the rendertarget's width
TestViewportCall(false, 2.0, 0.0, kWidth - 1.0, kHeight, 0.0, 1.0);
TestViewportCall(false, 1.0, 0.0, nextafter(float(kWidth - 1.0), 1000.0f), kHeight, 0.0, 1.0);
// Height is larger than the rendertarget's height
TestViewportCall(false, 0.0, 2.0, kWidth, kHeight - 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 1.0, kWidth, nextafter(float(kHeight - 1.0), 1000.0f), 0.0, 1.0);
}
// Test to check that negative x in viewport is allowed
TEST_F(SetViewportTest, NegativeX) {
DummyRenderPass renderPass(device);
// Test to check that negative x in viewport is disallowed
TEST_F(SetViewportTest, NegativeXYWidthHeight) {
// Control case: everything set to 0 is allowed.
TestViewportCall(true, +0.0, +0.0, +0.0, +0.0, 0.0, 1.0);
TestViewportCall(true, -0.0, -0.0, -0.0, -0.0, 0.0, 1.0);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(-1.0, 0.0, 1.0, 1.0, 0.0, 1.0);
pass.EndPass();
}
encoder.Finish();
// Nonzero negative values are disallowed
TestViewportCall(false, -1.0, 0.0, 1.0, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, -1.0, 1.0, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, -1.0, 1.0, 0.0, 1.0);
TestViewportCall(false, 0.0, 0.0, 1.0, -1.0, 0.0, 1.0);
}
// Test to check that negative y in viewport is allowed
TEST_F(SetViewportTest, NegativeY) {
DummyRenderPass renderPass(device);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, -1.0, 1.0, 1.0, 0.0, 1.0);
pass.EndPass();
}
encoder.Finish();
}
// Test to check that negative width in viewport is not allowed
TEST_F(SetViewportTest, NegativeWidth) {
DummyRenderPass renderPass(device);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, -1.0, 1.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
// Test to check that negative height in viewport is not allowed
TEST_F(SetViewportTest, NegativeHeight) {
DummyRenderPass renderPass(device);
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
{
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 0.0, -1.0, 0.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
}
// Test to check that minDepth out of range [0, 1] is not allowed
// Test to check that minDepth out of range [0, 1] is disallowed
TEST_F(SetViewportTest, MinDepthOutOfRange) {
DummyRenderPass renderPass(device);
// MinDepth is -1
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, -1.0, 1.0);
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, -1.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 2.0, 1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// MinDepth is 2 or 1 + epsilon
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 2.0, 1.0);
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, nextafter(1.0f, 1000.0f), 1.0);
}
// Test to check that maxDepth out of range [0, 1] is not allowed
// Test to check that minDepth out of range [0, 1] is disallowed
TEST_F(SetViewportTest, MaxDepthOutOfRange) {
DummyRenderPass renderPass(device);
// MaxDepth is -1
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 1.0, -1.0);
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, -1.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.0, 2.0);
pass.EndPass();
ASSERT_DEVICE_ERROR(encoder.Finish());
}
// MaxDepth is 2 or 1 + epsilon
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 1.0, 2.0);
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 1.0, nextafter(1.0f, 1000.0f));
}
// Test to check that minDepth equal or greater than maxDepth is allowed
// Test to check that minDepth equal or greater than maxDepth is disallowed
TEST_F(SetViewportTest, MinDepthEqualOrGreaterThanMaxDepth) {
DummyRenderPass renderPass(device);
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.5, 0.5);
pass.EndPass();
encoder.Finish();
}
{
wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass);
pass.SetViewport(0.0, 0.0, 1.0, 1.0, 0.8, 0.5);
pass.EndPass();
encoder.Finish();
}
TestViewportCall(true, 0.0, 0.0, 1.0, 1.0, 0.5, 0.5);
TestViewportCall(false, 0.0, 0.0, 1.0, 1.0, 0.8, 0.5);
}
class SetScissorRectTest : public ValidationTest {};