Fix non-zero validation in Draw and DrawIndexed
Metal validation layer does not allow indexCount or instanceCount to be 0 in
drawIndexedPrimitives and drawPrimitives, otherwise we should draw nothing
with these operations.
BUG=dawn:76
TEST=dawn_end2end_tests
Change-Id: Ic22be73ac992289d4bc8d7b3d4d30d20c4488776
Reviewed-on: https://dawn-review.googlesource.com/c/3900
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 9475c87..4604f8a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -980,6 +980,7 @@
"src/tests/end2end/CopyTests.cpp",
"src/tests/end2end/DepthStencilStateTests.cpp",
"src/tests/end2end/DrawIndexedTests.cpp",
+ "src/tests/end2end/DrawTests.cpp",
"src/tests/end2end/FenceTests.cpp",
"src/tests/end2end/IndexFormatTests.cpp",
"src/tests/end2end/InputStateTests.cpp",
diff --git a/src/dawn_native/metal/CommandBufferMTL.mm b/src/dawn_native/metal/CommandBufferMTL.mm
index 22f8241..7fd8ad4 100644
--- a/src/dawn_native/metal/CommandBufferMTL.mm
+++ b/src/dawn_native/metal/CommandBufferMTL.mm
@@ -408,26 +408,32 @@
case Command::Draw: {
DrawCmd* draw = mCommands.NextCommand<DrawCmd>();
- [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology()
- vertexStart:draw->firstVertex
- vertexCount:draw->vertexCount
- instanceCount:draw->instanceCount
- baseInstance:draw->firstInstance];
+ // The instance count must be non-zero, otherwise no-op
+ if (draw->instanceCount != 0) {
+ [encoder drawPrimitives:lastPipeline->GetMTLPrimitiveTopology()
+ vertexStart:draw->firstVertex
+ vertexCount:draw->vertexCount
+ instanceCount:draw->instanceCount
+ baseInstance:draw->firstInstance];
+ }
} break;
case Command::DrawIndexed: {
DrawIndexedCmd* draw = mCommands.NextCommand<DrawIndexedCmd>();
size_t formatSize = IndexFormatSize(lastPipeline->GetIndexFormat());
- [encoder
- drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology()
- indexCount:draw->indexCount
- indexType:lastPipeline->GetMTLIndexType()
- indexBuffer:indexBuffer
- indexBufferOffset:indexBufferBaseOffset + draw->firstIndex * formatSize
- instanceCount:draw->instanceCount
- baseVertex:draw->baseVertex
- baseInstance:draw->firstInstance];
+ // The index and instance count must be non-zero, otherwise no-op
+ if (draw->indexCount != 0 && draw->instanceCount != 0) {
+ [encoder drawIndexedPrimitives:lastPipeline->GetMTLPrimitiveTopology()
+ indexCount:draw->indexCount
+ indexType:lastPipeline->GetMTLIndexType()
+ indexBuffer:indexBuffer
+ indexBufferOffset:indexBufferBaseOffset +
+ draw->firstIndex * formatSize
+ instanceCount:draw->instanceCount
+ baseVertex:draw->baseVertex
+ baseInstance:draw->firstInstance];
+ }
} break;
case Command::SetRenderPipeline: {
diff --git a/src/tests/end2end/DrawTests.cpp b/src/tests/end2end/DrawTests.cpp
new file mode 100644
index 0000000..0df0d90
--- /dev/null
+++ b/src/tests/end2end/DrawTests.cpp
@@ -0,0 +1,113 @@
+// Copyright 2019 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "tests/DawnTest.h"
+
+#include "utils/ComboRenderPipelineDescriptor.h"
+#include "utils/DawnHelpers.h"
+
+constexpr uint32_t kRTSize = 4;
+
+class DrawTest : public DawnTest {
+ protected:
+ void SetUp() override {
+ DawnTest::SetUp();
+
+ renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
+
+ dawn::InputState inputState =
+ device.CreateInputStateBuilder()
+ .SetInput(0, 4 * sizeof(float), dawn::InputStepMode::Vertex)
+ .SetAttribute(0, 0, dawn::VertexFormat::FloatR32G32B32A32, 0)
+ .GetResult();
+
+ dawn::ShaderModule vsModule =
+ utils::CreateShaderModule(device, dawn::ShaderStage::Vertex, R"(
+ #version 450
+ layout(location = 0) in vec4 pos;
+ void main() {
+ gl_Position = pos;
+ })");
+
+ dawn::ShaderModule fsModule =
+ utils::CreateShaderModule(device, dawn::ShaderStage::Fragment, R"(
+ #version 450
+ layout(location = 0) out vec4 fragColor;
+ void main() {
+ fragColor = vec4(0.0, 1.0, 0.0, 1.0);
+ })");
+
+ utils::ComboRenderPipelineDescriptor descriptor(device);
+ descriptor.cVertexStage.module = vsModule;
+ descriptor.cFragmentStage.module = fsModule;
+ descriptor.primitiveTopology = dawn::PrimitiveTopology::TriangleStrip;
+ descriptor.indexFormat = dawn::IndexFormat::Uint32;
+ descriptor.inputState = inputState;
+ descriptor.cColorAttachments[0]->format = renderPass.colorFormat;
+
+ pipeline = device.CreateRenderPipeline(&descriptor);
+
+ vertexBuffer = utils::CreateBufferFromData<float>(
+ device, dawn::BufferUsageBit::Vertex,
+ {// The bottom left triangle
+ -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f,
+
+ // The top right triangle
+ -1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f});
+ }
+
+ utils::BasicRenderPass renderPass;
+ dawn::RenderPipeline pipeline;
+ dawn::Buffer vertexBuffer;
+
+ void Test(uint32_t vertexCount,
+ uint32_t instanceCount,
+ uint32_t firstIndex,
+ uint32_t firstInstance,
+ RGBA8 bottomLeftExpected,
+ RGBA8 topRightExpected) {
+ uint32_t zeroOffset = 0;
+ dawn::CommandBufferBuilder builder = device.CreateCommandBufferBuilder();
+ {
+ dawn::RenderPassEncoder pass = builder.BeginRenderPass(renderPass.renderPassInfo);
+ pass.SetPipeline(pipeline);
+ pass.SetVertexBuffers(0, 1, &vertexBuffer, &zeroOffset);
+ pass.Draw(vertexCount, instanceCount, firstIndex, firstInstance);
+ pass.EndPass();
+ }
+
+ dawn::CommandBuffer commands = builder.GetResult();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3);
+ EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1);
+ }
+};
+
+// The basic triangle draw.
+TEST_P(DrawTest, Uint32) {
+ RGBA8 filled(0, 255, 0, 255);
+ RGBA8 notFilled(0, 0, 0, 0);
+
+ // Test a draw with no indices.
+ Test(0, 0, 0, 0, notFilled, notFilled);
+ // Test a draw with only the first 3 indices (bottom left triangle)
+ Test(3, 1, 0, 0, filled, notFilled);
+ // Test a draw with only the last 3 indices (top right triangle)
+ Test(3, 1, 3, 0, notFilled, filled);
+ // Test a draw with all 6 indices (both triangles).
+ Test(6, 1, 0, 0, filled, filled);
+}
+
+DAWN_INSTANTIATE_TEST(DrawTest, D3D12Backend, MetalBackend, OpenGLBackend, VulkanBackend)