summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorlucy96chen <47800040+lucy96chen@users.noreply.github.com>2021-12-16 14:48:04 -0800
committerGitHub <noreply@github.com>2021-12-16 14:48:04 -0800
commitac88374136ae0c9e83d3350ca3113f4bce893911 (patch)
tree0e8e839eff6cddae41a8e4af2a013ea7e0cfe5a7 /tools
parentf024d7299831c0312877e315e8d78b920aaafbaf (diff)
gfx: Add tests for instanced, indexed instanced, and indirect draw calls (#2060)
* Implemented instancing for the drawInstanced test and confirmed that test values taken at specific pixel location match expected values * Removed writeImage() helper function as this is for debugging only * Factored out shared test code and wrapped tests inside a base class with derived structs for each individual draw call variant; Added a test for indexed, instanced draws but test is currently not working correctly * Indexed instancing test finish and working; Further refactor to move code that fills the test result array and creates the vertex and color buffers to the base test class * Commented out image dump helper function at the top as it may still be needed for the remaining draw tests * Added a new struct derived from the base test class for testing indirect but non-indexed draws; Moved required command signatures for indirect draws into D3D12Device to ensure they continue to exist outside of their respective draw calls; Moved command signature creation into D3D12Device::initialize(); Small consistency cleanup changes * Added working indexed indirect draw call test * Moved expectedResult and compareComputeResult() call into getTestResults() and renamed to checkTestResults() * Rerun tests
Diffstat (limited to 'tools')
-rw-r--r--tools/gfx-unit-test/draw-instanced-test.cpp156
-rw-r--r--tools/gfx-unit-test/gfx-test-util.cpp13
-rw-r--r--tools/gfx-unit-test/gfx-test-util.h5
-rw-r--r--tools/gfx-unit-test/graphics-smoke.slang5
-rw-r--r--tools/gfx-unit-test/instanced-draw-tests.cpp581
-rw-r--r--tools/gfx/d3d12/render-d3d12.cpp71
6 files changed, 637 insertions, 194 deletions
diff --git a/tools/gfx-unit-test/draw-instanced-test.cpp b/tools/gfx-unit-test/draw-instanced-test.cpp
deleted file mode 100644
index e15e6beda..000000000
--- a/tools/gfx-unit-test/draw-instanced-test.cpp
+++ /dev/null
@@ -1,156 +0,0 @@
-#include "tools/unit-test/slang-unit-test.h"
-
-#include "slang-gfx.h"
-#include "gfx-test-util.h"
-#include "tools/gfx-util/shader-cursor.h"
-#include "source/core/slang-basic.h"
-
-using namespace gfx;
-
-namespace gfx_test
-{
- struct Vertex
- {
- float position[3];
- float color[3];
- };
-
- static const int kVertexCount = 3;
- static const Vertex kVertexData[kVertexCount] =
- {
- { { -1, -1, 0.5 }, { 1, 0, 0 } },
- { { -1, 3, 0.5 }, { 1, 0, 0 } },
- { { 3, -1, 0.5 }, { 1, 0, 0 } },
- };
-
- void drawInstancedTestImpl(IDevice* device, UnitTestContext* context)
- {
- Slang::ComPtr<ITransientResourceHeap> transientHeap;
- ITransientResourceHeap::Desc transientHeapDesc = {};
- transientHeapDesc.constantBufferSize = 4096;
- GFX_CHECK_CALL_ABORT(
- device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
-
- ComPtr<IShaderProgram> shaderProgram;
- slang::ProgramLayout* slangReflection;
- GFX_CHECK_CALL_ABORT(loadGraphicsProgram(device, shaderProgram, "graphics-smoke", "vertexMain", "fragmentMain", slangReflection));
-
- Format format = Format::R32G32B32A32_FLOAT;
-
- InputElementDesc inputElements[] = {
- { "POSITION", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position) },
- { "COLOR", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, color) },
- };
- ComPtr<gfx::IInputLayout> inputLayout = device->createInputLayout(inputElements, 2);
- SLANG_CHECK_ABORT(inputLayout != nullptr);
-
- IBufferResource::Desc vertexBufferDesc;
- vertexBufferDesc.type = IResource::Type::Buffer;
- vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex);
- vertexBufferDesc.defaultState = ResourceState::VertexBuffer;
- vertexBufferDesc.allowedStates = ResourceState::VertexBuffer;
- ComPtr<IBufferResource> vertexBuffer = device->createBufferResource(vertexBufferDesc, &kVertexData[0]);
- SLANG_CHECK_ABORT(vertexBuffer != nullptr);
-
- IFramebufferLayout::AttachmentLayout attachmentLayout;
- attachmentLayout.format = format;
- attachmentLayout.sampleCount = 1;
-
- IFramebufferLayout::Desc framebufferLayoutDesc;
- framebufferLayoutDesc.renderTargetCount = 1;
- framebufferLayoutDesc.renderTargets = &attachmentLayout;
- ComPtr<gfx::IFramebufferLayout> framebufferLayout = device->createFramebufferLayout(framebufferLayoutDesc);
-
- GraphicsPipelineStateDesc pipelineDesc = {};
- pipelineDesc.program = shaderProgram.get();
- pipelineDesc.inputLayout = inputLayout;
- pipelineDesc.framebufferLayout = framebufferLayout;
- pipelineDesc.depthStencil.depthTestEnable = false;
- pipelineDesc.depthStencil.depthWriteEnable = false;
- ComPtr<gfx::IPipelineState> pipelineState;
- GFX_CHECK_CALL_ABORT(
- device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef()));
-
- ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
- auto queue = device->createCommandQueue(queueDesc);
- auto commandBuffer = transientHeap->createCommandBuffer();
-
- IRenderPassLayout::Desc renderPassDesc = {};
- renderPassDesc.framebufferLayout = framebufferLayout;
- renderPassDesc.renderTargetCount = 1;
- IRenderPassLayout::AttachmentAccessDesc renderTargetAccess = {};
- renderTargetAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear;
- renderTargetAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store;
- renderTargetAccess.initialState = ResourceState::Undefined;
- renderTargetAccess.finalState = ResourceState::CopySource;
- renderPassDesc.renderTargetAccess = &renderTargetAccess;
- ComPtr<IRenderPassLayout> renderPass = device->createRenderPassLayout(renderPassDesc);
-
- const int width = 2;
- const int height = 2;
-
- gfx::ITextureResource::Desc colorBufferDesc;
- colorBufferDesc.type = IResource::Type::Texture2D;
- colorBufferDesc.size.width = width;
- colorBufferDesc.size.height = height;
- colorBufferDesc.size.depth = 1;
- colorBufferDesc.numMipLevels = 1;
- colorBufferDesc.format = format;
- colorBufferDesc.defaultState = ResourceState::RenderTarget;
- colorBufferDesc.allowedStates = { ResourceState::RenderTarget, ResourceState::CopySource };
- ComPtr<ITextureResource> colorBuffer = device->createTextureResource(colorBufferDesc, nullptr);
-
- gfx::IResourceView::Desc colorBufferViewDesc;
- memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc));
- colorBufferViewDesc.format = format;
- colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D;
- colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget;
- ComPtr<gfx::IResourceView> rtv =
- device->createTextureView(colorBuffer.get(), colorBufferViewDesc);
-
- gfx::IFramebuffer::Desc framebufferDesc;
- framebufferDesc.renderTargetCount = 1;
- framebufferDesc.depthStencilView = nullptr;
- framebufferDesc.renderTargetViews = rtv.readRef();
- framebufferDesc.layout = framebufferLayout;
- ComPtr<gfx::IFramebuffer> framebuffer = device->createFramebuffer(framebufferDesc);
-
- auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
- auto rootObject = encoder->bindPipeline(pipelineState);
-
- UInt vertexCount = 3;
- UInt instanceCount = 1;
- UInt startVertex = 0;
- UInt startInstanceLocation = 0;
-
- gfx::Viewport viewport = {};
- viewport.maxZ = 1.0f;
- viewport.extentX = width;
- viewport.extentY = height;
- encoder->setViewportAndScissor(viewport);
-
- encoder->setVertexBuffer(0, vertexBuffer, sizeof(Vertex));
- encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
- encoder->drawInstanced(vertexCount, instanceCount, startVertex, startInstanceLocation);
- encoder->endEncoding();
- commandBuffer->close();
- queue->executeCommandBuffer(commandBuffer);
- queue->waitOnHost();
-
- float expectedResult[] = { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
- 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f };
- compareComputeResult(device, colorBuffer, ResourceState::CopySource, expectedResult, 32, 2);
- }
-
- SLANG_UNIT_TEST(drawInstancedD3D12)
- {
- runTestImpl(drawInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12);
- }
-
-#if 0
- SLANG_UNIT_TEST(drawInstancedVulkan)
- {
- runTestImpl(drawInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan);
- }
-#endif
- }
diff --git a/tools/gfx-unit-test/gfx-test-util.cpp b/tools/gfx-unit-test/gfx-test-util.cpp
index 619d2feb0..7514f22cf 100644
--- a/tools/gfx-unit-test/gfx-test-util.cpp
+++ b/tools/gfx-unit-test/gfx-test-util.cpp
@@ -150,6 +150,14 @@ namespace gfx_test
SLANG_CHECK(memcmp(resultBlob->getBufferPointer(), expectedResult, expectedBufferSize) == 0);
}
+ void compareComputeResultFuzzy(const float* result, float* expectedResult, size_t expectedBufferSize)
+ {
+ for (int i = 0; i < expectedBufferSize / sizeof(float); ++i)
+ {
+ SLANG_CHECK(abs(result[i] - expectedResult[i]) <= 0.01);
+ }
+ }
+
void compareComputeResultFuzzy(gfx::IDevice* device, gfx::IBufferResource* buffer, float* expectedResult, size_t expectedBufferSize)
{
// Read back the results.
@@ -159,10 +167,7 @@ namespace gfx_test
SLANG_CHECK(resultBlob->getBufferSize() == expectedBufferSize);
// Compare results with a tolerance of 0.01.
auto result = (float*)resultBlob->getBufferPointer();
- for (int i = 0; i < expectedBufferSize / sizeof(float); ++i)
- {
- SLANG_CHECK(abs(result[i] - expectedResult[i]) <= 0.01);
- }
+ compareComputeResultFuzzy(result, expectedResult, expectedBufferSize);
}
Slang::ComPtr<gfx::IDevice> createTestingDevice(UnitTestContext* context, Slang::RenderApiFlag::Enum api)
diff --git a/tools/gfx-unit-test/gfx-test-util.h b/tools/gfx-unit-test/gfx-test-util.h
index addcb151b..04c602359 100644
--- a/tools/gfx-unit-test/gfx-test-util.h
+++ b/tools/gfx-unit-test/gfx-test-util.h
@@ -42,6 +42,11 @@ namespace gfx_test
size_t expectedResultRowPitch,
size_t rowCount);
+ void compareComputeResultFuzzy(
+ const float* result,
+ float* expectedResult,
+ size_t expectedBufferSize);
+
/// Reads back the content of `buffer` and compares it against `expectedResult` with a set tolerance.
void compareComputeResultFuzzy(
gfx::IDevice* device,
diff --git a/tools/gfx-unit-test/graphics-smoke.slang b/tools/gfx-unit-test/graphics-smoke.slang
index 145b83a62..464d6f723 100644
--- a/tools/gfx-unit-test/graphics-smoke.slang
+++ b/tools/gfx-unit-test/graphics-smoke.slang
@@ -3,7 +3,8 @@
// Per-vertex attributes to be assembled from bound vertex buffers.
struct AssembledVertex
{
- float3 position : POSITION;
+ float3 positionA : POSITIONA;
+ float3 positionB : POSITIONB;
float3 color : COLOR;
};
@@ -33,7 +34,7 @@ VertexStageOutput vertexMain(
{
VertexStageOutput output;
- float3 position = assembledVertex.position;
+ float3 position = assembledVertex.positionA + assembledVertex.positionB;
float3 color = assembledVertex.color;
output.coarseVertex.color = color;
diff --git a/tools/gfx-unit-test/instanced-draw-tests.cpp b/tools/gfx-unit-test/instanced-draw-tests.cpp
new file mode 100644
index 000000000..3e6f123ee
--- /dev/null
+++ b/tools/gfx-unit-test/instanced-draw-tests.cpp
@@ -0,0 +1,581 @@
+#include "tools/unit-test/slang-unit-test.h"
+
+#include "slang-gfx.h"
+#include "gfx-test-util.h"
+#include "tools/gfx-util/shader-cursor.h"
+#include "source/core/slang-basic.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define STB_IMAGE_WRITE_IMPLEMENTATION
+#include "external/stb/stb_image_write.h"
+
+using namespace gfx;
+
+namespace gfx_test
+{
+ // Testing only code used to dump images to visually confirm correctness.
+ // Will be removed once all draw tests are complete.
+// Slang::Result writeImage(
+// const char* filename,
+// ISlangBlob* pixels,
+// uint32_t width,
+// uint32_t height)
+// {
+// int stbResult =
+// stbi_write_hdr(filename, width, height, 4, (float*)pixels->getBufferPointer());
+//
+// return stbResult ? SLANG_OK : SLANG_FAIL;
+// }
+
+ struct Vertex
+ {
+ float position[3];
+ };
+
+ struct Instance
+ {
+ float position[3];
+ float color[3];
+ };
+
+ static const int kVertexCount = 6;
+ static const Vertex kVertexData[kVertexCount] =
+ {
+ // Triangle 1
+ { 0, 0, 0.5 },
+ { 1, 0, 0.5 },
+ { 0, 1, 0.5 },
+
+ // Triangle 2
+ { -1, 0, 0.5 },
+ { 0, 0, 0.5 },
+ { -1, 1, 0.5 },
+ };
+
+ static const int kInstanceCount = 2;
+ static const Instance kInstanceData[kInstanceCount] =
+ {
+ { { 0, 0, 0 }, { 1, 0, 0 } },
+ { { 0, -1, 0 }, { 0, 0, 1 } },
+ };
+
+ static const int kIndexCount = 6;
+ static const uint32_t kIndexData[kIndexCount] =
+ {
+ 0, 2, 5,
+ 0, 1, 2,
+ };
+
+ const int kWidth = 256;
+ const int kHeight = 256;
+ const Format format = Format::R32G32B32A32_FLOAT;
+
+ ComPtr<IBufferResource> createVertexBuffer(IDevice* device)
+ {
+ IBufferResource::Desc vertexBufferDesc;
+ vertexBufferDesc.type = IResource::Type::Buffer;
+ vertexBufferDesc.sizeInBytes = kVertexCount * sizeof(Vertex);
+ vertexBufferDesc.defaultState = ResourceState::VertexBuffer;
+ vertexBufferDesc.allowedStates = ResourceState::VertexBuffer;
+ ComPtr<IBufferResource> vertexBuffer = device->createBufferResource(vertexBufferDesc, &kVertexData[0]);
+ SLANG_CHECK_ABORT(vertexBuffer != nullptr);
+ return vertexBuffer;
+ }
+
+ ComPtr<IBufferResource> createInstanceBuffer(IDevice* device)
+ {
+ IBufferResource::Desc instanceBufferDesc;
+ instanceBufferDesc.type = IResource::Type::Buffer;
+ instanceBufferDesc.sizeInBytes = kInstanceCount * sizeof(Instance);
+ instanceBufferDesc.defaultState = ResourceState::VertexBuffer;
+ instanceBufferDesc.allowedStates = ResourceState::VertexBuffer;
+ ComPtr<IBufferResource> instanceBuffer = device->createBufferResource(instanceBufferDesc, &kInstanceData[0]);
+ SLANG_CHECK_ABORT(instanceBuffer != nullptr);
+ return instanceBuffer;
+ }
+
+ ComPtr<IBufferResource> createIndexBuffer(IDevice* device)
+ {
+ IBufferResource::Desc indexBufferDesc;
+ indexBufferDesc.type = IResource::Type::Buffer;
+ indexBufferDesc.sizeInBytes = kIndexCount * sizeof(uint32_t);
+ indexBufferDesc.defaultState = ResourceState::IndexBuffer;
+ indexBufferDesc.allowedStates = ResourceState::IndexBuffer;
+ ComPtr<IBufferResource> indexBuffer = device->createBufferResource(indexBufferDesc, &kIndexData[0]);
+ SLANG_CHECK_ABORT(indexBuffer != nullptr);
+ return indexBuffer;
+ }
+
+ ComPtr<ITextureResource> createColorBuffer(IDevice* device)
+ {
+ gfx::ITextureResource::Desc colorBufferDesc;
+ colorBufferDesc.type = IResource::Type::Texture2D;
+ colorBufferDesc.size.width = kWidth;
+ colorBufferDesc.size.height = kHeight;
+ colorBufferDesc.size.depth = 1;
+ colorBufferDesc.numMipLevels = 1;
+ colorBufferDesc.format = format;
+ colorBufferDesc.defaultState = ResourceState::RenderTarget;
+ colorBufferDesc.allowedStates = { ResourceState::RenderTarget, ResourceState::CopySource };
+ ComPtr<ITextureResource> colorBuffer = device->createTextureResource(colorBufferDesc, nullptr);
+ SLANG_CHECK_ABORT(colorBuffer != nullptr);
+ return colorBuffer;
+ }
+
+ class BaseDrawTest
+ {
+ public:
+ ComPtr<IDevice> device;
+ UnitTestContext* context;
+
+ ComPtr<ITransientResourceHeap> transientHeap;
+ ComPtr<IPipelineState> pipelineState;
+ ComPtr<IRenderPassLayout> renderPass;
+ ComPtr<IFramebuffer> framebuffer;
+
+ ComPtr<IBufferResource> vertexBuffer;
+ ComPtr<IBufferResource> instanceBuffer;
+ ComPtr<ITextureResource> colorBuffer;
+
+ void init(IDevice* device, UnitTestContext* context)
+ {
+ this->device = device;
+ this->context = context;
+ }
+
+ void createRequiredResources()
+ {
+ InputElementDesc inputElements[] = {
+ // Vertex buffer data
+ { "POSITIONA", 0, Format::R32G32B32_FLOAT, offsetof(Vertex, position), InputSlotClass::PerVertex, 0 },
+
+ // Instance buffer data
+ { "POSITIONB", 0, Format::R32G32B32_FLOAT, offsetof(Instance, position), InputSlotClass::PerInstance, 1, 1 },
+ { "COLOR", 0, Format::R32G32B32_FLOAT, offsetof(Instance, color), InputSlotClass::PerInstance, 1, 1 },
+ };
+ auto inputLayout = device->createInputLayout(inputElements, SLANG_COUNT_OF(inputElements));
+ SLANG_CHECK_ABORT(inputLayout != nullptr);
+
+ vertexBuffer = createVertexBuffer(device);
+ instanceBuffer = createInstanceBuffer(device);
+ colorBuffer = createColorBuffer(device);
+
+ ITransientResourceHeap::Desc transientHeapDesc = {};
+ transientHeapDesc.constantBufferSize = 4096;
+ GFX_CHECK_CALL_ABORT(
+ device->createTransientResourceHeap(transientHeapDesc, transientHeap.writeRef()));
+
+ ComPtr<IShaderProgram> shaderProgram;
+ slang::ProgramLayout* slangReflection;
+ GFX_CHECK_CALL_ABORT(loadGraphicsProgram(device, shaderProgram, "graphics-smoke", "vertexMain", "fragmentMain", slangReflection));
+
+ IFramebufferLayout::AttachmentLayout attachmentLayout;
+ attachmentLayout.format = format;
+ attachmentLayout.sampleCount = 1;
+
+ IFramebufferLayout::Desc framebufferLayoutDesc;
+ framebufferLayoutDesc.renderTargetCount = 1;
+ framebufferLayoutDesc.renderTargets = &attachmentLayout;
+ ComPtr<gfx::IFramebufferLayout> framebufferLayout = device->createFramebufferLayout(framebufferLayoutDesc);
+ SLANG_CHECK_ABORT(framebufferLayout != nullptr);
+
+ GraphicsPipelineStateDesc pipelineDesc = {};
+ pipelineDesc.program = shaderProgram.get();
+ pipelineDesc.inputLayout = inputLayout;
+ pipelineDesc.framebufferLayout = framebufferLayout;
+ pipelineDesc.depthStencil.depthTestEnable = false;
+ pipelineDesc.depthStencil.depthWriteEnable = false;
+ GFX_CHECK_CALL_ABORT(
+ device->createGraphicsPipelineState(pipelineDesc, pipelineState.writeRef()));
+
+ IRenderPassLayout::Desc renderPassDesc = {};
+ renderPassDesc.framebufferLayout = framebufferLayout;
+ renderPassDesc.renderTargetCount = 1;
+ IRenderPassLayout::AttachmentAccessDesc renderTargetAccess = {};
+ renderTargetAccess.loadOp = IRenderPassLayout::AttachmentLoadOp::Clear;
+ renderTargetAccess.storeOp = IRenderPassLayout::AttachmentStoreOp::Store;
+ renderTargetAccess.initialState = ResourceState::Undefined;
+ renderTargetAccess.finalState = ResourceState::CopySource;
+ renderPassDesc.renderTargetAccess = &renderTargetAccess;
+ GFX_CHECK_CALL_ABORT(device->createRenderPassLayout(renderPassDesc, renderPass.writeRef()));
+
+ gfx::IResourceView::Desc colorBufferViewDesc;
+ memset(&colorBufferViewDesc, 0, sizeof(colorBufferViewDesc));
+ colorBufferViewDesc.format = format;
+ colorBufferViewDesc.renderTarget.shape = gfx::IResource::Type::Texture2D;
+ colorBufferViewDesc.type = gfx::IResourceView::Type::RenderTarget;
+ auto rtv = device->createTextureView(colorBuffer, colorBufferViewDesc);
+
+ gfx::IFramebuffer::Desc framebufferDesc;
+ framebufferDesc.renderTargetCount = 1;
+ framebufferDesc.depthStencilView = nullptr;
+ framebufferDesc.renderTargetViews = rtv.readRef();
+ framebufferDesc.layout = framebufferLayout;
+ GFX_CHECK_CALL_ABORT(device->createFramebuffer(framebufferDesc, framebuffer.writeRef()));
+ }
+
+ void checkTestResults(int pixelCount, int channelCount, const int* testXCoords, const int* testYCoords, float* testResults)
+ {
+ // Read texture values back from four specific pixels located within the triangles
+ // and compare against expected values (because testing every single pixel will be too long and tedious
+ // and requires maintaining reference images).
+ ComPtr<ISlangBlob> resultBlob;
+ size_t rowPitch = 0;
+ size_t pixelSize = 0;
+ GFX_CHECK_CALL_ABORT(device->readTextureResource(
+ colorBuffer, ResourceState::CopySource, resultBlob.writeRef(), &rowPitch, &pixelSize));
+ auto result = (float*)resultBlob->getBufferPointer();
+
+ // Dump image to disk so we can visually confirm the output
+ //writeImage("C:/Users/lucchen/Documents/dump.hdr", resultBlob, kWidth, kHeight);
+
+ int cursor = 0;
+ for (int i = 0; i < pixelCount; ++i)
+ {
+ auto x = testXCoords[i];
+ auto y = testYCoords[i];
+ auto pixelPtr = result + x * channelCount + y * rowPitch / sizeof(float);
+ for (int j = 0; j < channelCount; ++j)
+ {
+ testResults[cursor] = pixelPtr[j];
+ cursor++;
+ }
+ }
+
+ float expectedResult[] = { 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f,
+ 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f };
+ compareComputeResultFuzzy(testResults, expectedResult, sizeof(expectedResult));
+ }
+ };
+
+ struct DrawInstancedTest : BaseDrawTest
+ {
+ void setUpAndDraw()
+ {
+ createRequiredResources();
+
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = device->createCommandQueue(queueDesc);
+ auto commandBuffer = transientHeap->createCommandBuffer();
+
+ auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = kWidth;
+ viewport.extentY = kHeight;
+ encoder->setViewportAndScissor(viewport);
+
+ uint32_t startVertex = 0;
+ uint32_t startInstanceLocation = 0;
+
+ encoder->setVertexBuffer(0, vertexBuffer, sizeof(Vertex));
+ encoder->setVertexBuffer(1, instanceBuffer, sizeof(Instance));
+ encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
+
+ encoder->drawInstanced(kVertexCount, kInstanceCount, startVertex, startInstanceLocation);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+
+ void run()
+ {
+ setUpAndDraw();
+
+ const int kPixelCount = 4;
+ const int kChannelCount = 4;
+ int testXCoords[kPixelCount] = { 64, 192, 64, 192 };
+ int testYCoords[kPixelCount] = { 100, 100, 250, 250 };
+ float testResults[kPixelCount * kChannelCount];
+
+ checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
+ }
+ };
+
+ struct DrawIndexedInstancedTest : BaseDrawTest
+ {
+ ComPtr<IBufferResource> indexBuffer;
+
+ void setUpAndDraw()
+ {
+ createRequiredResources();
+
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = device->createCommandQueue(queueDesc);
+ auto commandBuffer = transientHeap->createCommandBuffer();
+
+ auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = kWidth;
+ viewport.extentY = kHeight;
+ encoder->setViewportAndScissor(viewport);
+
+ uint32_t startIndex = 0;
+ int32_t startVertex = 0;
+ uint32_t startInstanceLocation = 0;
+
+ encoder->setVertexBuffer(0, vertexBuffer, sizeof(Vertex));
+ encoder->setVertexBuffer(1, instanceBuffer, sizeof(Instance));
+ encoder->setIndexBuffer(indexBuffer, Format::R32_UINT);
+ encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
+
+ encoder->drawIndexedInstanced(kIndexCount, kInstanceCount, startIndex, startVertex, startInstanceLocation);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+
+ void run()
+ {
+ indexBuffer = createIndexBuffer(device);
+
+ setUpAndDraw();
+
+ const int kPixelCount = 4;
+ const int kChannelCount = 4;
+ int testXCoords[kPixelCount] = { 64, 192, 64, 192 };
+ int testYCoords[kPixelCount] = { 32, 100, 150, 250 };
+ float testResults[kPixelCount * kChannelCount];
+
+ checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
+ }
+ };
+
+ struct DrawIndirectTest : BaseDrawTest
+ {
+ ComPtr<IBufferResource> indirectBuffer;
+
+ struct IndirectArgData
+ {
+ float padding; // Ensure args and count don't start at 0 offset for testing purposes
+ IndirectDrawArguments args;
+ uint32_t count;
+ };
+
+ ComPtr<IBufferResource> createIndirectBuffer(IDevice* device)
+ {
+ static const IndirectArgData kIndirectData =
+ {
+ 42.0f, // padding
+ {6, 2, 0, 0}, // args
+ 1, // count
+ };
+
+ IBufferResource::Desc indirectBufferDesc;
+ indirectBufferDesc.type = IResource::Type::Buffer;
+ indirectBufferDesc.sizeInBytes = sizeof(IndirectArgData);
+ indirectBufferDesc.defaultState = ResourceState::IndirectArgument;
+ indirectBufferDesc.allowedStates = ResourceState::IndirectArgument;
+ ComPtr<IBufferResource> indexBuffer = device->createBufferResource(indirectBufferDesc, &kIndirectData);
+ SLANG_CHECK_ABORT(indexBuffer != nullptr);
+ return indexBuffer;
+ }
+
+ void setUpAndDraw()
+ {
+ createRequiredResources();
+
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = device->createCommandQueue(queueDesc);
+ auto commandBuffer = transientHeap->createCommandBuffer();
+
+ auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = kWidth;
+ viewport.extentY = kHeight;
+ encoder->setViewportAndScissor(viewport);
+
+ encoder->setVertexBuffer(0, vertexBuffer, sizeof(Vertex));
+ encoder->setVertexBuffer(1, instanceBuffer, sizeof(Instance));
+ encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
+
+ uint32_t maxDrawCount = 1;
+ uint64_t argOffset = offsetof(IndirectArgData, args);
+ uint64_t countOffset = offsetof(IndirectArgData, count);
+
+ encoder->drawIndirect(maxDrawCount, indirectBuffer, argOffset, indirectBuffer, countOffset);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+
+ void run()
+ {
+ indirectBuffer = createIndirectBuffer(device);
+
+ setUpAndDraw();
+
+ const int kPixelCount = 4;
+ const int kChannelCount = 4;
+ int testXCoords[kPixelCount] = { 64, 192, 64, 192 };
+ int testYCoords[kPixelCount] = { 100, 100, 250, 250 };
+ float testResults[kPixelCount * kChannelCount];
+
+ checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
+ }
+ };
+
+ struct DrawIndexedIndirectTest : BaseDrawTest
+ {
+ ComPtr<IBufferResource> indexBuffer;
+ ComPtr<IBufferResource> indirectBuffer;
+
+ struct IndexedIndirectArgData
+ {
+ float padding; // Ensure args and count don't start at 0 offset for testing purposes
+ IndirectDrawIndexedArguments args;
+ uint32_t count;
+ };
+
+ ComPtr<IBufferResource> createIndirectBuffer(IDevice* device)
+ {
+ static const IndexedIndirectArgData kIndexedIndirectData =
+ {
+ 42.0f, // padding
+ {6, 2, 0, 0, 0}, // args
+ 1, // count
+ };
+
+ IBufferResource::Desc indirectBufferDesc;
+ indirectBufferDesc.type = IResource::Type::Buffer;
+ indirectBufferDesc.sizeInBytes = sizeof(IndexedIndirectArgData);
+ indirectBufferDesc.defaultState = ResourceState::IndirectArgument;
+ indirectBufferDesc.allowedStates = ResourceState::IndirectArgument;
+ ComPtr<IBufferResource> indexBuffer = device->createBufferResource(indirectBufferDesc, &kIndexedIndirectData);
+ SLANG_CHECK_ABORT(indexBuffer != nullptr);
+ return indexBuffer;
+ }
+
+ void setUpAndDraw()
+ {
+ createRequiredResources();
+
+ ICommandQueue::Desc queueDesc = { ICommandQueue::QueueType::Graphics };
+ auto queue = device->createCommandQueue(queueDesc);
+ auto commandBuffer = transientHeap->createCommandBuffer();
+
+ auto encoder = commandBuffer->encodeRenderCommands(renderPass, framebuffer);
+ auto rootObject = encoder->bindPipeline(pipelineState);
+
+ gfx::Viewport viewport = {};
+ viewport.maxZ = 1.0f;
+ viewport.extentX = kWidth;
+ viewport.extentY = kHeight;
+ encoder->setViewportAndScissor(viewport);
+
+ encoder->setVertexBuffer(0, vertexBuffer, sizeof(Vertex));
+ encoder->setVertexBuffer(1, instanceBuffer, sizeof(Instance));
+ encoder->setIndexBuffer(indexBuffer, Format::R32_UINT);
+ encoder->setPrimitiveTopology(PrimitiveTopology::TriangleList);
+
+ uint32_t maxDrawCount = 1;
+ uint64_t argOffset = offsetof(IndexedIndirectArgData, args);
+ uint64_t countOffset = offsetof(IndexedIndirectArgData, count);
+
+ encoder->drawIndexedIndirect(maxDrawCount, indirectBuffer, argOffset, indirectBuffer, countOffset);
+ encoder->endEncoding();
+ commandBuffer->close();
+ queue->executeCommandBuffer(commandBuffer);
+ queue->waitOnHost();
+ }
+
+ void run()
+ {
+ indexBuffer = createIndexBuffer(device);
+ indirectBuffer = createIndirectBuffer(device);
+
+ setUpAndDraw();
+
+ const int kPixelCount = 4;
+ const int kChannelCount = 4;
+ int testXCoords[kPixelCount] = { 64, 192, 64, 192 };
+ int testYCoords[kPixelCount] = { 32, 100, 150, 250 };
+ float testResults[kPixelCount * kChannelCount];
+
+ checkTestResults(kPixelCount, kChannelCount, testXCoords, testYCoords, testResults);
+ }
+ };
+
+ void drawInstancedTestImpl(IDevice* device, UnitTestContext* context)
+ {
+ DrawInstancedTest test;
+ test.init(device, context);
+ test.run();
+ }
+
+ void drawIndexedInstancedTestImpl(IDevice* device, UnitTestContext* context)
+ {
+ DrawIndexedInstancedTest test;
+ test.init(device, context);
+ test.run();
+ }
+
+ void drawIndirectTestImpl(IDevice* device, UnitTestContext* context)
+ {
+ DrawIndirectTest test;
+ test.init(device, context);
+ test.run();
+ }
+
+ void drawIndexedIndirectTestImpl(IDevice* device, UnitTestContext* context)
+ {
+ DrawIndexedIndirectTest test;
+ test.init(device, context);
+ test.run();
+ }
+
+ SLANG_UNIT_TEST(drawInstancedD3D12)
+ {
+ runTestImpl(drawInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12);
+ }
+
+ SLANG_UNIT_TEST(drawIndexedInstancedD3D12)
+ {
+ runTestImpl(drawIndexedInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12);
+ }
+
+ SLANG_UNIT_TEST(drawIndirectD3D12)
+ {
+ runTestImpl(drawIndirectTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12);
+ }
+
+ SLANG_UNIT_TEST(drawIndexedIndirectD3D12)
+ {
+ runTestImpl(drawIndexedIndirectTestImpl, unitTestContext, Slang::RenderApiFlag::D3D12);
+ }
+
+#if 0
+ SLANG_UNIT_TEST(drawInstancedVulkan)
+ {
+ runTestImpl(drawInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan);
+ }
+
+ SLANG_UNIT_TEST(drawIndexedInstancedVulkan)
+ {
+ runTestImpl(drawIndexedInstancedTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan);
+ }
+
+ SLANG_UNIT_TEST(drawIndirectVulkan)
+ {
+ runTestImpl(drawIndirectTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan);
+ }
+
+ SLANG_UNIT_TEST(drawIndexedIndirectVulkan)
+ {
+ runTestImpl(drawIndexedIndirectTestImpl, unitTestContext, Slang::RenderApiFlag::Vulkan);
+ }
+#endif
+ }
diff --git a/tools/gfx/d3d12/render-d3d12.cpp b/tools/gfx/d3d12/render-d3d12.cpp
index 3daddc73f..aab260cd1 100644
--- a/tools/gfx/d3d12/render-d3d12.cpp
+++ b/tools/gfx/d3d12/render-d3d12.cpp
@@ -3425,26 +3425,15 @@ public:
{
prepareDraw();
- D3D12_INDIRECT_ARGUMENT_DESC args[1];
- args[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
-
- D3D12_COMMAND_SIGNATURE_DESC desc;
- desc.ByteStride = 36;
- desc.NumArgumentDescs = 1;
- desc.pArgumentDescs = args;
-
- ComPtr<ID3D12CommandSignature> cmdSignature = nullptr;
- if (FAILED(m_device->CreateCommandSignature(&desc, nullptr, IID_PPV_ARGS(cmdSignature.writeRef()))))
- {
- return;
- }
+ auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
+ auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
m_d3dCmdList->ExecuteIndirect(
- cmdSignature,
+ m_renderer->drawIndirectCmdSignature,
maxDrawCount,
- (ID3D12Resource*)argBuffer,
+ argBufferImpl->m_resource,
argOffset,
- (ID3D12Resource*)countBuffer,
+ countBufferImpl->m_resource,
countOffset);
}
@@ -3457,26 +3446,15 @@ public:
{
prepareDraw();
- D3D12_INDIRECT_ARGUMENT_DESC args[1];
- args[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
-
- D3D12_COMMAND_SIGNATURE_DESC desc;
- desc.ByteStride = 36;
- desc.NumArgumentDescs = 1;
- desc.pArgumentDescs = args;
-
- ComPtr<ID3D12CommandSignature> cmdSignature = nullptr;
- if (FAILED(m_device->CreateCommandSignature(&desc, nullptr, IID_PPV_ARGS(cmdSignature.writeRef()))))
- {
- return;
- }
+ auto argBufferImpl = static_cast<BufferResourceImpl*>(argBuffer);
+ auto countBufferImpl = static_cast<BufferResourceImpl*>(countBuffer);
m_d3dCmdList->ExecuteIndirect(
- cmdSignature,
+ m_renderer->drawIndexedIndirectCmdSignature,
maxDrawCount,
- (ID3D12Resource*)argBuffer,
+ argBufferImpl->m_resource,
argOffset,
- (ID3D12Resource*)countBuffer,
+ countBufferImpl->m_resource,
countOffset);
}
@@ -4549,6 +4527,9 @@ public:
PFN_D3D12_SERIALIZE_ROOT_SIGNATURE m_D3D12SerializeRootSignature = nullptr;
bool m_nvapi = false;
+
+ ComPtr<ID3D12CommandSignature> drawIndirectCmdSignature;
+ ComPtr<ID3D12CommandSignature> drawIndexedIndirectCmdSignature;
};
SLANG_NO_THROW Result SLANG_MCALL D3D12Device::TransientResourceHeapImpl::synchronizeAndReset()
@@ -5328,6 +5309,32 @@ Result D3D12Device::initialize(const Desc& desc)
profileName,
makeArray(slang::PreprocessorMacroDesc{"__D3D12__", "1"}).getView()));
+ {
+ D3D12_INDIRECT_ARGUMENT_DESC args[1];
+ args[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW;
+
+ D3D12_COMMAND_SIGNATURE_DESC desc;
+ desc.ByteStride = 36;
+ desc.NumArgumentDescs = 1;
+ desc.pArgumentDescs = args;
+ desc.NodeMask = 0;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(&desc, nullptr, IID_PPV_ARGS(drawIndirectCmdSignature.writeRef())));
+ }
+
+ {
+ D3D12_INDIRECT_ARGUMENT_DESC args[1];
+ args[0].Type = D3D12_INDIRECT_ARGUMENT_TYPE_DRAW_INDEXED;
+
+ D3D12_COMMAND_SIGNATURE_DESC desc;
+ desc.ByteStride = 36;
+ desc.NumArgumentDescs = 1;
+ desc.pArgumentDescs = args;
+ desc.NodeMask = 0;
+
+ SLANG_RETURN_ON_FAIL(m_device->CreateCommandSignature(&desc, nullptr, IID_PPV_ARGS(drawIndexedIndirectCmdSignature.writeRef())));
+ }
+
m_isInitialized = true;
return SLANG_OK;
}