Fix indirect draw output indirect param offset on D3D12
When the flag kDuplicateBaseVertexInstance is on, we need to add
2 * sizeof(uint32_t) for the outputParamsOffset for each draw
Bug: chromium:1478906
Change-Id: I77b7d93c7e1bbb186911caebc3410360caef23c2
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/157941
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
Commit-Queue: Shrek Shao <shrekshao@google.com>
diff --git a/src/dawn/native/IndirectDrawValidationEncoder.cpp b/src/dawn/native/IndirectDrawValidationEncoder.cpp
index 7391461..7057a40 100644
--- a/src/dawn/native/IndirectDrawValidationEncoder.cpp
+++ b/src/dawn/native/IndirectDrawValidationEncoder.cpp
@@ -409,6 +409,10 @@
} else {
outputParamsOffset += kDrawIndirectSize;
}
+ if (pass.flags & kDuplicateBaseVertexInstance) {
+ // Add the extra offset for the duplicated base vertex and instance.
+ outputParamsOffset += 2 * sizeof(uint32_t);
+ }
}
}
}
diff --git a/src/dawn/tests/end2end/DrawIndirectTests.cpp b/src/dawn/tests/end2end/DrawIndirectTests.cpp
index 92a4696..4ec2bca 100644
--- a/src/dawn/tests/end2end/DrawIndirectTests.cpp
+++ b/src/dawn/tests/end2end/DrawIndirectTests.cpp
@@ -25,6 +25,8 @@
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#include <vector>
+
#include "dawn/tests/DawnTest.h"
#include "dawn/utils/ComboRenderPipelineDescriptor.h"
@@ -34,6 +36,8 @@
namespace {
constexpr uint32_t kRTSize = 4;
+constexpr utils::RGBA8 filled(0, 255, 0, 255);
+constexpr utils::RGBA8 notFilled(0, 0, 0, 0);
class DrawIndirectTest : public DawnTest {
protected:
@@ -109,9 +113,6 @@
// the offsets that Tint/GLSL produces.
DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux());
- utils::RGBA8 filled(0, 255, 0, 255);
- utils::RGBA8 notFilled(0, 0, 0, 0);
-
// Test a draw with no indices.
Test({0, 0, 0, 0}, 0, notFilled, notFilled);
@@ -130,9 +131,6 @@
// the offsets that Tint/GLSL produces.
DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux());
- utils::RGBA8 filled(0, 255, 0, 255);
- utils::RGBA8 notFilled(0, 0, 0, 0);
-
// Test an offset draw call, with indirect buffer containing 2 calls:
// 1) only the first 3 indices (bottom left triangle)
// 2) only the last 3 indices (top right triangle)
@@ -152,5 +150,203 @@
OpenGLESBackend(),
VulkanBackend());
+class DrawIndirectUsingFirstVertexTest : public DawnTest {
+ protected:
+ virtual void SetupShaderModule() {
+ vsModule = utils::CreateShaderModule(device, R"(
+ struct VertexInput {
+ @builtin(vertex_index) id : u32,
+ @location(0) pos: vec4f,
+ };
+
+ @group(0) @binding(0) var<uniform> offset: array<vec4f, 2>;
+
+ @vertex
+ fn main(input: VertexInput) -> @builtin(position) vec4f {
+ return input.pos + offset[input.id / 3u];
+ })");
+
+ fsModule = utils::CreateShaderModule(device, R"(
+ @fragment fn main() -> @location(0) vec4f {
+ return vec4f(0.0, 1.0, 0.0, 1.0);
+ })");
+ }
+
+ void GeneralSetup() {
+ renderPass = utils::CreateBasicRenderPass(device, kRTSize, kRTSize);
+
+ SetupShaderModule();
+
+ utils::ComboRenderPipelineDescriptor descriptor;
+ descriptor.vertex.module = vsModule;
+ descriptor.cFragment.module = fsModule;
+ descriptor.primitive.topology = wgpu::PrimitiveTopology::TriangleStrip;
+ descriptor.primitive.stripIndexFormat = wgpu::IndexFormat::Uint32;
+ descriptor.vertex.bufferCount = 1;
+ descriptor.cBuffers[0].arrayStride = 4 * sizeof(float);
+ descriptor.cBuffers[0].attributeCount = 1;
+ descriptor.cAttributes[0].format = wgpu::VertexFormat::Float32x4;
+ descriptor.cTargets[0].format = renderPass.colorFormat;
+
+ pipeline = device.CreateRenderPipeline(&descriptor);
+
+ // Offset to the vertices, that needs correcting by the calibration offset from uniform
+ // buffer referenced by instance index to get filled triangle on screen.
+ constexpr float calibration = 99.0f;
+
+ vertexBuffer = utils::CreateBufferFromData<float>(
+ device, wgpu::BufferUsage::Vertex,
+
+ {// The bottom left triangle
+ -1.0f - calibration, 1.0f, 0.0f, 1.0f, 1.0f - calibration, -1.0f, 0.0f, 1.0f,
+ -1.0f - calibration, -1.0f, 0.0f, 1.0f,
+
+ // The top right triangle
+ -1.0f - calibration, 1.0f, 0.0f, 1.0f, 1.0f - calibration, -1.0f, 0.0f, 1.0f,
+ 1.0f - calibration, 1.0f, 0.0f, 1.0f});
+
+ // Providing calibration vec4f offset values
+ wgpu::Buffer uniformBuffer =
+ utils::CreateBufferFromData<float>(device, wgpu::BufferUsage::Uniform,
+ {
+ // Bad calibration at [0]
+ 0.0,
+ 0.0,
+ 0.0,
+ 0.0,
+ // Good calibration at [1]
+ calibration,
+ 0.0,
+ 0.0,
+ 0.0,
+ });
+
+ bindGroup =
+ utils::MakeBindGroup(device, pipeline.GetBindGroupLayout(0), {{0, uniformBuffer}});
+ }
+
+ void SetUp() override {
+ DawnTest::SetUp();
+ GeneralSetup();
+ }
+
+ utils::BasicRenderPass renderPass;
+ wgpu::RenderPipeline pipeline;
+ wgpu::Buffer vertexBuffer;
+ wgpu::BindGroup bindGroup;
+ wgpu::ShaderModule vsModule;
+ wgpu::ShaderModule fsModule;
+
+ // Test two DrawIndirect calls with different indirect offsets within one pass.
+ void Test(std::initializer_list<uint32_t> bufferList,
+ utils::RGBA8 bottomLeftExpected,
+ utils::RGBA8 topRightExpected) {
+ // Indirect buffer contains 2 draw params
+ DAWN_ASSERT(bufferList.size() == 8);
+ wgpu::Buffer indirectBuffer =
+ utils::CreateBufferFromData<uint32_t>(device, wgpu::BufferUsage::Indirect, bufferList);
+
+ wgpu::CommandEncoder encoder = device.CreateCommandEncoder();
+ {
+ wgpu::RenderPassEncoder pass = encoder.BeginRenderPass(&renderPass.renderPassInfo);
+ pass.SetPipeline(pipeline);
+ pass.SetVertexBuffer(0, vertexBuffer);
+ pass.SetBindGroup(0, bindGroup);
+ pass.DrawIndirect(indirectBuffer, 0);
+ pass.DrawIndirect(indirectBuffer, 4 * sizeof(uint32_t));
+ pass.End();
+ }
+
+ wgpu::CommandBuffer commands = encoder.Finish();
+ queue.Submit(1, &commands);
+
+ EXPECT_PIXEL_RGBA8_EQ(bottomLeftExpected, renderPass.color, 1, 3);
+ EXPECT_PIXEL_RGBA8_EQ(topRightExpected, renderPass.color, 3, 1);
+ }
+};
+
+TEST_P(DrawIndirectUsingFirstVertexTest, IndirectOffset) {
+ // TODO(crbug.com/dawn/1292): Some Intel OpenGL drivers don't seem to like
+ // the offsets that Tint/GLSL produces.
+ DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux());
+ // Won't fix for OpenGLES + ANGLE D3D11
+ DAWN_SUPPRESS_TEST_IF(IsANGLED3D11());
+
+ // Test an offset draw call, with indirect buffer containing 2 calls:
+ // 1) only the first 3 indices (bottom left triangle)
+ // 2) only the last 3 indices (top right triangle)
+
+ // #2 draw has the correct offset applied by vertex index
+ Test({3, 1, 0, 0, 3, 1, 3, 0}, notFilled, filled);
+}
+
+DAWN_INSTANTIATE_TEST(DrawIndirectUsingFirstVertexTest,
+ D3D11Backend(),
+ D3D12Backend(),
+ MetalBackend(),
+ OpenGLBackend(),
+ OpenGLESBackend(),
+ VulkanBackend());
+
+class DrawIndirectUsingInstanceIndexTest : public DrawIndirectUsingFirstVertexTest {
+ protected:
+ std::vector<wgpu::FeatureName> GetRequiredFeatures() override {
+ if (!SupportsFeatures({wgpu::FeatureName::IndirectFirstInstance})) {
+ return {};
+ }
+ return {wgpu::FeatureName::IndirectFirstInstance};
+ }
+
+ void SetupShaderModule() override {
+ vsModule = utils::CreateShaderModule(device, R"(
+ struct VertexInput {
+ @builtin(instance_index) id : u32,
+ @location(0) pos: vec4f,
+ };
+
+ @group(0) @binding(0) var<uniform> offset: array<vec4f, 2>;
+
+ @vertex
+ fn main(input: VertexInput) -> @builtin(position) vec4f {
+ return input.pos + offset[input.id];
+ })");
+
+ fsModule = utils::CreateShaderModule(device, R"(
+ @fragment fn main() -> @location(0) vec4f {
+ return vec4f(0.0, 1.0, 0.0, 1.0);
+ })");
+ }
+
+ void SetUp() override {
+ DawnTest::SetUp();
+ DAWN_TEST_UNSUPPORTED_IF(!device.HasFeature(wgpu::FeatureName::IndirectFirstInstance));
+ GeneralSetup();
+ }
+};
+
+TEST_P(DrawIndirectUsingInstanceIndexTest, IndirectOffset) {
+ // TODO(crbug.com/dawn/1292): Some Intel OpenGL drivers don't seem to like
+ // the offsets that Tint/GLSL produces.
+ DAWN_SUPPRESS_TEST_IF(IsIntel() && IsOpenGL() && IsLinux());
+
+ // Test an offset draw call, with indirect buffer containing 2 calls:
+ // 1) only the first 3 indices (bottom left triangle)
+ // 2) only the last 3 indices (top right triangle)
+
+ // Test 1: #1 draw has the correct calibration referenced by instance index
+ Test({3, 1, 0, 1, 3, 1, 3, 0}, filled, notFilled);
+
+ // Test 2: #2 draw has the correct offset applied by instance index
+ Test({3, 1, 0, 0, 3, 1, 3, 1}, notFilled, filled);
+}
+
+DAWN_INSTANTIATE_TEST(DrawIndirectUsingInstanceIndexTest,
+ D3D11Backend(),
+ D3D12Backend(),
+ MetalBackend(),
+ OpenGLBackend(),
+ OpenGLESBackend(),
+ VulkanBackend());
+
} // anonymous namespace
} // namespace dawn