diff --git a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp
index 6455771da..a9fe7c7b0 100644
--- a/renderdoc/driver/d3d11/d3d11_context_wrap.cpp
+++ b/renderdoc/driver/d3d11/d3d11_context_wrap.cpp
@@ -6427,6 +6427,20 @@ bool WrappedID3D11DeviceContext::Serialise_ClearState(SerialiserType &ser)
{
if(IsReplayingAndReading())
{
+ // end stream-out queries for outgoing targets
+ for(UINT b = 0; b < D3D11_SO_STREAM_COUNT; b++)
+ {
+ ID3D11Buffer *buf = m_CurrentPipelineState->SO.Buffers[b];
+
+ if(buf)
+ {
+ ResourceId id = GetIDForResource(buf);
+
+ m_pRealContext->End(m_StreamOutCounters[id].query);
+ m_StreamOutCounters[id].running = false;
+ }
+ }
+
m_CurrentPipelineState->Clear();
m_pRealContext->ClearState();
VerifyState();
@@ -6454,6 +6468,20 @@ void WrappedID3D11DeviceContext::ClearState()
m_ContextRecord->AddChunk(scope.Get());
}
+ // end stream-out queries for outgoing targets
+ for(UINT b = 0; b < D3D11_SO_STREAM_COUNT; b++)
+ {
+ ID3D11Buffer *buf = m_CurrentPipelineState->SO.Buffers[b];
+
+ if(buf)
+ {
+ ResourceId id = GetIDForResource(buf);
+
+ m_pRealContext->End(m_StreamOutCounters[id].query);
+ m_StreamOutCounters[id].running = false;
+ }
+ }
+
m_CurrentPipelineState->Clear();
VerifyState();
}
diff --git a/util/test/demos/d3d11/d3d11_streamout.cpp b/util/test/demos/d3d11/d3d11_stream_out.cpp
similarity index 63%
rename from util/test/demos/d3d11/d3d11_streamout.cpp
rename to util/test/demos/d3d11/d3d11_stream_out.cpp
index a35dfd101..f3de3db91 100644
--- a/util/test/demos/d3d11/d3d11_streamout.cpp
+++ b/util/test/demos/d3d11/d3d11_stream_out.cpp
@@ -24,7 +24,7 @@
#include "d3d11_test.h"
-TEST(D3D11_StreamOut, D3D11GraphicsTest)
+TEST(D3D11_Stream_Out, D3D11GraphicsTest)
{
static constexpr const char *Description = "Test using D3D11's streamout feature";
@@ -74,9 +74,25 @@ TEST(D3D11_StreamOut, D3D11GraphicsTest)
ID3D11BufferPtr vb = MakeBuffer().Vertex().Data(DefaultTri);
ID3D11BufferPtr so[2] = {
- MakeBuffer().StreamOut().Size(2048), MakeBuffer().StreamOut().Size(2048),
+ MakeBuffer().StreamOut().Vertex().Size(2048), MakeBuffer().StreamOut().Vertex().Size(2048),
};
+ D3D11_INPUT_ELEMENT_DESC layoutdesc[] = {
+ {
+ "POSITION", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0,
+ },
+ {
+ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 1, 0, D3D11_INPUT_PER_VERTEX_DATA, 0,
+ },
+ {
+ "TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0,
+ },
+ };
+
+ ID3D11InputLayoutPtr streamoutLayout;
+ CHECK_HR(dev->CreateInputLayout(layoutdesc, ARRAY_COUNT(layoutdesc), vsblob->GetBufferPointer(),
+ vsblob->GetBufferSize(), &streamoutLayout));
+
while(Running())
{
ctx->ClearState();
@@ -124,6 +140,51 @@ TEST(D3D11_StreamOut, D3D11GraphicsTest)
ctx->Draw(3, 0);
+ ctx->UpdateSubresource(so[0], 0, NULL, empty, 2048, 2048);
+ ctx->UpdateSubresource(so[1], 0, NULL, empty, 2048, 2048);
+
+ // test DrawAuto()
+
+ RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});
+
+ // draw with streamout and explicitly unbind
+ ctx->SOSetTargets(2, bufs, offs);
+ ctx->Draw(3, 0);
+ ctx->SOSetTargets(0, NULL, NULL);
+
+ RSSetViewport({0.0f, 0.0f, (float)screenWidth / 4.0f, (float)screenHeight / 4.0f, 0.0f, 1.0f});
+
+ ctx->IASetVertexBuffers(0, 2, bufs, &strides[0], offs);
+ ctx->IASetInputLayout(streamoutLayout);
+ ctx->DrawAuto();
+
+ RSSetViewport({0.0f, 0.0f, (float)screenWidth, (float)screenHeight, 0.0f, 1.0f});
+
+ ID3D11Buffer *emptyBuf[2] = {};
+ ctx->IASetVertexBuffers(0, 2, emptyBuf, &strides[0], offs);
+ IASetVertexBuffer(vb, sizeof(DefaultA2V), 0);
+ ctx->IASetInputLayout(defaultLayout);
+
+ // draw with streamout and clear state
+ ctx->SOSetTargets(2, bufs, offs);
+ ctx->DrawInstanced(3, 2, 0, 0);
+ ctx->ClearState();
+
+ ctx->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
+
+ ctx->VSSetShader(vs, NULL, 0);
+ ctx->GSSetShader(gs, NULL, 0);
+ ctx->PSSetShader(ps, NULL, 0);
+
+ ctx->OMSetRenderTargets(1, &bbRTV.GetInterfacePtr(), NULL);
+
+ RSSetViewport({screenWidth / 4.0f, 0.0f, (float)screenWidth / 4.0f,
+ (float)screenHeight / 4.0f, 0.0f, 1.0f});
+
+ ctx->IASetVertexBuffers(0, 2, bufs, &strides[0], offs);
+ ctx->IASetInputLayout(streamoutLayout);
+ ctx->DrawAuto();
+
Present();
}
diff --git a/util/test/demos/demos.vcxproj b/util/test/demos/demos.vcxproj
index 0d927c0cd..0b489f948 100644
--- a/util/test/demos/demos.vcxproj
+++ b/util/test/demos/demos.vcxproj
@@ -145,7 +145,7 @@
-
+
diff --git a/util/test/demos/demos.vcxproj.filters b/util/test/demos/demos.vcxproj.filters
index 506bd0e35..8acd6803b 100644
--- a/util/test/demos/demos.vcxproj.filters
+++ b/util/test/demos/demos.vcxproj.filters
@@ -70,9 +70,6 @@
OpenGL\demos
-
- D3D11\demos
-
D3D11\demos
@@ -309,6 +306,9 @@
Vulkan\demos
+
+ D3D11\demos
+
diff --git a/util/test/tests/D3D11/D3D11_Stream_Out.py b/util/test/tests/D3D11/D3D11_Stream_Out.py
new file mode 100644
index 000000000..f67c60f9c
--- /dev/null
+++ b/util/test/tests/D3D11/D3D11_Stream_Out.py
@@ -0,0 +1,58 @@
+import rdtest
+import struct
+import renderdoc as rd
+
+
+class D3D11_Stream_Out(rdtest.TestCase):
+ demos_test_name = 'D3D11_Stream_Out'
+
+ def check_capture(self):
+ draw = self.find_draw("Draw")
+
+ self.check(draw is not None)
+
+ self.controller.SetFrameEvent(draw.eventId, False)
+
+ pipe: rd.PipeState = self.controller.GetPipelineState()
+
+ # Get the input data as our reference
+
+ # First draw should have data at offset 0. First buffer has positions, second has colors (with doubled stride)
+
+ vsin = self.get_vsin(draw)
+
+ pos = [(*v['POSITION'], 1.0) for v in vsin]
+ col = [v['COLOR'] for v in vsin]
+
+ d3d11pipe: rd.D3D11State = self.controller.GetD3D11PipelineState()
+
+ so: rd.D3D11StreamOut = d3d11pipe.streamOut
+ so_bytes = self.controller.GetBufferData(so.outputs[0].resourceId, so.outputs[0].byteOffset, 0)
+
+ for i,p in enumerate(pos):
+ so_p = struct.unpack_from("4f", so_bytes, 0 + 4*4*i)
+ if not rdtest.value_compare(p, so_p):
+ raise rdtest.TestFailureException("Streamed-out position {} doesn't match expected {}".format(so_p, p))
+
+ so_bytes = self.controller.GetBufferData(so.outputs[1].resourceId, so.outputs[1].byteOffset, 0)
+
+ for i,c in enumerate(col):
+ so_c = struct.unpack_from("4f", so_bytes, 0 + 8*4*i)
+ if not rdtest.value_compare(c, so_c):
+ raise rdtest.TestFailureException("Streamed-out color {} doesn't match expected {}".format(so_c, c))
+
+ draw_auto = self.find_draw("DrawAuto", draw.eventId)
+
+ # First draw should be 3 vertices
+ if not rdtest.value_compare(draw_auto.numIndices, 3):
+ raise rdtest.TestFailureException("First DrawAuto() draws {} vertices".format(draw_auto.numIndices))
+
+ draw_auto = self.find_draw("DrawAuto", draw_auto.eventId+1)
+
+ # Second draw should be 6 vertices (3 vertices, instanced twice
+ if not rdtest.value_compare(draw_auto.numIndices, 6):
+ raise rdtest.TestFailureException("Second DrawAuto() draws {} vertices".format(draw_auto.numIndices))
+ if not rdtest.value_compare(draw_auto.numInstances, 1):
+ raise rdtest.TestFailureException("Second DrawAuto() draws {} instances".format(draw_auto.numInstances))
+
+ rdtest.log.success("First draw stream-out data is correct")