aboutsummaryrefslogtreecommitdiff
path: root/dxg/src
diff options
context:
space:
mode:
Diffstat (limited to 'dxg/src')
-rw-r--r--dxg/src/dxcommon.c173
-rw-r--r--dxg/src/dxg.c1
-rw-r--r--dxg/src/imm.c368
3 files changed, 541 insertions, 1 deletions
diff --git a/dxg/src/dxcommon.c b/dxg/src/dxcommon.c
new file mode 100644
index 0000000..ecc9a88
--- /dev/null
+++ b/dxg/src/dxcommon.c
@@ -0,0 +1,173 @@
1#include <dxg/dxcommon.h>
2
3// Required so that D3D12.dll can find and load D3D12Core.dll and other DLLs
4// from the Agility SDK. The macro comes from CMakeLists.txt.
5__declspec(dllexport) extern const UINT D3D12SDKVersion = AGILITY_SDK_VERSION;
6__declspec(dllexport) extern const char* D3D12SDKPath = AGILITY_SDK_INSTALL;
7D3D12_RESOURCE_BARRIER CD3DX12_RESOURCE_BARRIER_Transition(
8 ID3D12Resource* pResource,
9 D3D12_RESOURCE_STATES stateBefore,
10 D3D12_RESOURCE_STATES stateAfter) {
11 return (D3D12_RESOURCE_BARRIER){
12 .Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION,
13 .Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE,
14 .Transition.pResource = pResource,
15 .Transition.StateBefore = stateBefore,
16 .Transition.StateAfter = stateAfter,
17 .Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES};
18}
19
20D3D12_RASTERIZER_DESC CD3DX12_RASTERIZER_DESC_DEFAULT() {
21 return (D3D12_RASTERIZER_DESC){
22 .FillMode = D3D12_FILL_MODE_SOLID,
23 .CullMode = D3D12_CULL_MODE_BACK,
24 .FrontCounterClockwise = FALSE,
25 .DepthBias = D3D12_DEFAULT_DEPTH_BIAS,
26 .DepthBiasClamp = D3D12_DEFAULT_DEPTH_BIAS_CLAMP,
27 .SlopeScaledDepthBias = D3D12_DEFAULT_SLOPE_SCALED_DEPTH_BIAS,
28 .DepthClipEnable = TRUE,
29 .MultisampleEnable = FALSE,
30 .AntialiasedLineEnable = FALSE,
31 .ForcedSampleCount = 0,
32 .ConservativeRaster = D3D12_CONSERVATIVE_RASTERIZATION_MODE_OFF};
33}
34
35D3D12_BLEND_DESC CD3DX12_BLEND_DESC_DEFAULT() {
36 const D3D12_RENDER_TARGET_BLEND_DESC defaultRenderTargetBlendDesc = {
37 FALSE, FALSE,
38 D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
39 D3D12_BLEND_ONE, D3D12_BLEND_ZERO, D3D12_BLEND_OP_ADD,
40 D3D12_LOGIC_OP_NOOP,
41 D3D12_COLOR_WRITE_ENABLE_ALL,
42 };
43 D3D12_BLEND_DESC desc = {
44 .AlphaToCoverageEnable = FALSE,
45 .IndependentBlendEnable = FALSE,
46 };
47 for (UINT i = 0; i < D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) {
48 desc.RenderTarget[i] = defaultRenderTargetBlendDesc;
49 }
50 return desc;
51}
52
53void dxg_wait(ID3D12Fence* pFence, HANDLE fenceEvent, UINT64 fenceValue) {
54 assert(pFence);
55 // Wait for commands to finish execution.
56 // It is possible that execution has already finished by the time we
57 // get here, so first check the fence's completed value.
58 if (pFence->lpVtbl->GetCompletedValue(pFence) < fenceValue) {
59 // GPU Signal still pending. Configure a Windows event and wait for it.
60 // The event fires when the GPU signals.
61 //
62 // Indicate that the fence event is to be fired when the fence reaches
63 // the given fence value.
64 TrapIfFailed(pFence->lpVtbl->SetEventOnCompletion(pFence, fenceValue, fenceEvent));
65 // Will wake up when the fence takes on the given fence value.
66 WaitForSingleObject(fenceEvent, INFINITE);
67 }
68}
69
70// -----------------------------------------------------------------------------
71// Command Recorder
72// -----------------------------------------------------------------------------
73
74HRESULT dxg_cmdrec_init(CommandRecorder* pRec, ID3D12Device* pDevice) {
75 assert(pRec);
76 assert(pDevice);
77
78 HRESULT result = S_OK;
79
80 const D3D12_COMMAND_LIST_TYPE type = D3D12_COMMAND_LIST_TYPE_DIRECT;
81
82 if ((result = pDevice->lpVtbl->CreateCommandAllocator(
83 pDevice, type, &IID_ID3D12CommandAllocator, &pRec->pCmdAllocator)) != S_OK) {
84 return result;
85 }
86
87 if ((result = pDevice->lpVtbl->CreateCommandList(
88 pDevice, 0, type, pRec->pCmdAllocator, NULL, &IID_ID3D12CommandList, &pRec->pCmdList)) != S_OK) {
89 return result;
90 }
91
92 // Command lists start open. Close it for API convenience.
93 if ((result = pRec->pCmdList->lpVtbl->Close(pRec->pCmdList)) != S_OK) {
94 return result;
95 }
96
97 return result;
98}
99
100void dxg_cmdrec_destroy(CommandRecorder* pRec) {
101 assert(pRec);
102 SafeRelease(pRec->pCmdList);
103 SafeRelease(pRec->pCmdAllocator);
104}
105
106HRESULT dxg_cmdrec_reset(CommandRecorder* pRec) {
107 assert(pRec);
108 assert(pRec->pCmdAllocator);
109 assert(pRec->pCmdList);
110 HRESULT result = S_OK;
111 if ((result = pRec->pCmdAllocator->lpVtbl->Reset(pRec->pCmdAllocator)) != S_OK) {
112 return result;
113 }
114 if ((result = pRec->pCmdList->lpVtbl->Reset(pRec->pCmdList, pRec->pCmdAllocator, NULL)) != S_OK) {
115 return result;
116 }
117 return result;
118}
119
120// -----------------------------------------------------------------------------
121// Upload Buffer
122// -----------------------------------------------------------------------------
123
124void dxg_upload_buffer_init(UploadBuffer* pBuf, ID3D12Device* pDevice, size_t size) {
125 assert(pBuf);
126 assert(pDevice);
127
128 pBuf->size = size;
129
130 const D3D12_HEAP_PROPERTIES props = {
131 .Type = D3D12_HEAP_TYPE_UPLOAD,
132 .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_COMBINE,
133 .MemoryPoolPreference = D3D12_MEMORY_POOL_L0,
134 .CreationNodeMask = 0,
135 .VisibleNodeMask = 0
136 };
137 // Constant buffers need to be aligned to 256 bytes. Other types of buffers
138 // do not have this requirement. To make the upload buffer general, use the
139 // worst-case alignment.
140 const D3D12_RESOURCE_DESC desc = {
141 .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
142 .Alignment = 256,
143 .Width = size,
144 .Height = 0,
145 .DepthOrArraySize = 0,
146 .MipLevels = 0,
147 .Format = DXGI_FORMAT_UNKNOWN,
148 .SampleDesc = (DXGI_SAMPLE_DESC){0},
149 .Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN,
150 .Flags = D3D12_RESOURCE_FLAG_NONE
151 };
152 TrapIfFailed(pDevice->lpVtbl->CreateCommittedResource(
153 pDevice,
154 &props,
155 D3D12_HEAP_FLAG_NONE,
156 &desc,
157 D3D12_RESOURCE_STATE_COPY_SOURCE,
158 NULL,
159 &IID_ID3D12Resource,
160 &pBuf->pUploadBuffer));
161}
162
163void dxg_upload_buffer_destroy(UploadBuffer* pBuf, ID3D12Device* pDevice) {
164 assert(pDevice);
165 assert(pBuf);
166 SafeRelease(pBuf->pUploadBuffer);
167}
168
169void dxg_upload_buffer_load(UploadBuffer* pBuf, const void* pData, size_t bytes, ID3D12Resource* pDstBuffer) {
170 assert(pBuf);
171 assert(pData);
172 assert(pDstBuffer);
173}
diff --git a/dxg/src/dxg.c b/dxg/src/dxg.c
deleted file mode 100644
index e985d3d..0000000
--- a/dxg/src/dxg.c
+++ /dev/null
@@ -1 +0,0 @@
1int x = 2;
diff --git a/dxg/src/imm.c b/dxg/src/imm.c
new file mode 100644
index 0000000..28baa99
--- /dev/null
+++ b/dxg/src/imm.c
@@ -0,0 +1,368 @@
1/* Immediate-mode renderer.
2
3Geometry is given by client code and buffered in an upload-heap buffer stored
4in host memory.
5When the buffer fills up or the client is done, a draw call is issued. The draw
6call reads directly from the buffer in host memory; there is no intermediate
7buffer copy.
8The renderer double-buffers two host-side buffers so that the client can
9continue specifying more data into a second buffer while the contents of the
10first buffer are rendered.
11If the first buffer is still being rendered while the client loops around, then
12the client must wait before issuing further geometry.
13Once the render of the first buffer completes, the process starts again,
14ping-ponging between the two buffers.*/
15#include <dxg/imm.h>
16#include <dxg/dxcommon.h>
17
18#include <imm_vs.h> // generated
19#include <imm_ps.h> // generated
20
21#define WIN32_LEAN_AND_MEAN
22#include <Windows.h> // OutputDebugStringA
23
24#include <stdint.h>
25#include <stdlib.h>
26
27static ID3D12Resource* create_buffer(ID3D12Device* pDevice, size_t size) {
28 assert(pDevice);
29 const D3D12_HEAP_PROPERTIES props = {
30 .Type = D3D12_HEAP_TYPE_UPLOAD,
31 .CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_UNKNOWN,
32 .MemoryPoolPreference = D3D12_MEMORY_POOL_UNKNOWN,
33 .CreationNodeMask = 0,
34 .VisibleNodeMask = 0
35 };
36 const D3D12_RESOURCE_DESC desc = {
37 .Dimension = D3D12_RESOURCE_DIMENSION_BUFFER,
38 .Alignment = D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
39 .Width = size,
40 .Height = 1,
41 .DepthOrArraySize = 1,
42 .MipLevels = 1,
43 .Format = DXGI_FORMAT_UNKNOWN,
44 .SampleDesc = {.Count = 1, .Quality = 0},
45 .Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR,
46 .Flags = D3D12_RESOURCE_FLAG_NONE
47 };
48 ID3D12Resource* pBuffer = NULL;
49 TrapIfFailed(pDevice->lpVtbl->CreateCommittedResource(
50 pDevice,
51 &props,
52 D3D12_HEAP_FLAG_NONE,
53 &desc,
54 D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER,
55 NULL,
56 &IID_ID3D12Resource,
57 &pBuffer));
58 return pBuffer;
59}
60
61typedef struct GraphicsState {
62 D3D12_VIEWPORT viewport;
63 D3D12_CPU_DESCRIPTOR_HANDLE hBackBufferView;
64 D3D12_CPU_DESCRIPTOR_HANDLE hDepthStencilView;
65} GraphicsState;
66
67// Set of per-draw resources. The renderer cycles between sets per draw.
68typedef struct ResourceSet {
69 ID3D12Resource* pVertexBuffer;
70 CommandRecorder cmdRec;
71} ResourceSet;
72
73typedef struct DxgImm {
74 ID3D12Device* pDevice;
75 ID3D12CommandQueue* pCmdQueue;
76 ID3D12PipelineState* pPipelineState;
77 ID3D12RootSignature* pRootSignature;
78 GraphicsState graphicsState;
79 ResourceSet resources[2];
80 int cur; // Index to current resource set. New geometry written here.
81 float* pCurrentBufferData; // Mapped region of current buffer.
82 size_t bufferSizeVerts; // Num verts per buffer.
83 ID3D12Fence* pFence;
84 HANDLE fenceEvent;
85 uint64_t fenceValue;
86 size_t vertsWritten; // Verts written to current buffer.
87 bool wait; // Whether the next draw should wait.
88} DxgImm;
89
90static inline size_t vertex_size_bytes() {
91 return 3 * sizeof(float);
92}
93
94static inline size_t verts_byte_count(size_t numVerts) {
95 return numVerts * vertex_size_bytes();
96}
97
98static inline size_t dxg_imm_verts_left(const DxgImm* imm) {
99 assert(imm);
100 assert(imm->bufferSizeVerts >= imm->vertsWritten);
101 return imm->bufferSizeVerts - imm->vertsWritten;
102}
103
104static void dxg_imm_copy_verts(DxgImm* imm, const float* pVerts, size_t count) {
105 assert(imm);
106 assert(pVerts);
107 assert(count <= dxg_imm_verts_left(imm));
108 memcpy(&imm->pCurrentBufferData[imm->vertsWritten], pVerts, verts_byte_count(count));
109 imm->vertsWritten += count;
110}
111
112// Set up the current resource set for drawing.
113static void dxg_imm_set_up_resource_set(DxgImm* imm) {
114 assert(imm);
115 ResourceSet* const pResources = &imm->resources[imm->cur];
116 TrapIfFailed(pResources->pVertexBuffer->lpVtbl->Map(
117 pResources->pVertexBuffer, 0, NULL, &imm->pCurrentBufferData));
118 dxg_cmdrec_reset(&pResources->cmdRec);
119}
120
121// Move on to the next resource set.
122static ID3D12Resource* dxg_imm_next_resource_set(DxgImm* imm) {
123 assert(imm);
124 ResourceSet* const pResources = &imm->resources[imm->cur];
125 // Unmap the current buffer.
126 // TODO: Do we actually need to do this or can we leave it mapped? If the
127 // latter, then we could just map both buffers and let them be.
128 pResources->pVertexBuffer->lpVtbl->Unmap(pResources->pVertexBuffer, 0, NULL);
129 // Move on to the next resource set.
130 imm->cur = (imm->cur + 1) & 1;
131 imm->vertsWritten = 0;
132 // Set up the new resource set.
133 dxg_imm_set_up_resource_set(imm);
134}
135
136// Wait for the current buffer to be available for writing.
137static void dxg_imm_wait(DxgImm* imm) {
138 assert(imm);
139 assert(imm->wait);
140 // We only need to wait upon the first round around both buffers.
141 // First Signal is on fence value 1, 0 is not actually Signaled.
142 if (imm->fenceValue > 2) { // TODO: Do we need this check?
143 // The last buffer (not current) was Signaled with fenceValue - 1.
144 // The current buffer was therefore Signaled two fence values ago, or
145 // fenceValue - 2.
146 dxg_wait(imm->pFence, imm->fenceEvent, imm->fenceValue - 2);
147 }
148 imm->wait = false;
149}
150
151// Draw the current buffer.
152static void dxg_imm_draw(DxgImm* imm) {
153 assert(imm);
154 ResourceSet* const pResourceSet = &imm->resources[imm->cur];
155 ID3D12Resource* const pCurrentBuffer = pResourceSet->pVertexBuffer;
156 ID3D12GraphicsCommandList* const pCmdList = pResourceSet->cmdRec.pCmdList;
157 const D3D12_VIEWPORT* const pViewport = &imm->graphicsState.viewport;
158 const D3D12_RECT scissor = {
159 .bottom = pViewport->Height,
160 .left = 0,
161 .right = pViewport->Width,
162 .top = 0,
163 };
164 const D3D12_VERTEX_BUFFER_VIEW vertexBufferView = {
165 .BufferLocation = pCurrentBuffer->lpVtbl->GetGPUVirtualAddress(pCurrentBuffer),
166 .SizeInBytes = verts_byte_count(imm->vertsWritten),
167 .StrideInBytes = vertex_size_bytes(),
168 };
169 pCmdList->lpVtbl->RSSetViewports(pCmdList, 1, pViewport);
170 pCmdList->lpVtbl->RSSetScissorRects(pCmdList, 1, &scissor);
171 pCmdList->lpVtbl->OMSetRenderTargets(
172 pCmdList, 1, &imm->graphicsState.hBackBufferView, false, &imm->graphicsState.hDepthStencilView);
173 pCmdList->lpVtbl->SetPipelineState(pCmdList, imm->pPipelineState);
174 pCmdList->lpVtbl->SetGraphicsRootSignature(pCmdList, imm->pRootSignature);
175 pCmdList->lpVtbl->IASetPrimitiveTopology(pCmdList, D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
176 pCmdList->lpVtbl->IASetVertexBuffers(pCmdList, 0, 1, &vertexBufferView);
177 pCmdList->lpVtbl->DrawInstanced(pCmdList, imm->vertsWritten, 1, 0, 0);
178 pCmdList->lpVtbl->Close(pCmdList);
179 ID3D12CommandList* const cmdLists[] = {(ID3D12CommandList*)pCmdList};
180 ID3D12CommandQueue* const pCmdQueue = imm->pCmdQueue;
181 pCmdQueue->lpVtbl->ExecuteCommandLists(pCmdQueue, 1, cmdLists);
182}
183
184DxgImm* dxg_imm_init(ID3D12Device* pDevice, ID3D12CommandQueue* pCmdQueue, DXGI_FORMAT swapChainRtvFormat, DXGI_SAMPLE_DESC swapChainSampleDesc, size_t bufferSizeVerts) {
185 assert(pDevice);
186 assert(pCmdQueue);
187
188 DxgImm* imm = calloc(1, sizeof(DxgImm));
189 if (!imm) {
190 return 0;
191 }
192
193 imm->pDevice = pDevice;
194 imm->pCmdQueue = pCmdQueue;
195 imm->bufferSizeVerts = bufferSizeVerts;
196 imm->fenceValue = 0;
197
198 // TODO: Move this to the application side.
199 const D3D_SHADER_MODEL model = D3D_SHADER_MODEL_6_5;
200 D3D12_FEATURE_DATA_SHADER_MODEL shaderModel = { model };
201 HRESULT result = pDevice->lpVtbl->CheckFeatureSupport(
202 pDevice, D3D12_FEATURE_SHADER_MODEL, &shaderModel, sizeof(shaderModel));
203 if (FAILED(result) || (shaderModel.HighestShaderModel < model)) {
204 DEBUG_PRINT("ERROR: Shader Model 6.5 is not supported!\n");
205 TrapIfFailed(result);
206 }
207
208 const D3D12_SHADER_BYTECODE vs_bytecode = {
209 .pShaderBytecode = imm_vs,
210 .BytecodeLength = sizeof(imm_vs)
211 };
212
213 const D3D12_SHADER_BYTECODE ps_bytecode = {
214 .pShaderBytecode = imm_ps,
215 .BytecodeLength = sizeof(imm_ps)
216 };
217
218 // TODO: Find out how many root parameters to use.
219 // Let's do bindless rendering to keep things flexible.
220 const D3D12_ROOT_SIGNATURE_DESC rootsig_desc = {
221 .NumParameters = 0,
222 .pParameters = NULL,
223 .NumStaticSamplers = 0,
224 .pStaticSamplers = NULL,
225 .Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT
226 };
227
228 ID3DBlob* pRootSignature = NULL;
229 ID3DBlob* pErrors = NULL;
230 result = D3D12SerializeRootSignature(
231 &rootsig_desc,
232 D3D_ROOT_SIGNATURE_VERSION_1,
233 &pRootSignature,
234 &pErrors);
235 if (FAILED(result)) {
236 if (pErrors) {
237 DEBUG_PRINT(pErrors->lpVtbl->GetBufferPointer(pErrors));
238 }
239 TrapIfFailed(result);
240 }
241
242 TrapIfFailed(imm->pDevice->lpVtbl->CreateRootSignature(
243 imm->pDevice,
244 0,
245 pRootSignature->lpVtbl->GetBufferPointer(pRootSignature),
246 pRootSignature->lpVtbl->GetBufferSize(pRootSignature),
247 &IID_ID3D12RootSignature,
248 &imm->pRootSignature));
249
250 const D3D12_INPUT_ELEMENT_DESC input_layout[] = {
251 { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA, 0 }
252 };
253 const D3D12_INPUT_LAYOUT_DESC input_layout_desc = {
254 .pInputElementDescs = input_layout,
255 .NumElements = COUNTOF(input_layout)
256 };
257
258 const D3D12_GRAPHICS_PIPELINE_STATE_DESC gpso = {
259 .pRootSignature = imm->pRootSignature,
260 .VS = vs_bytecode,
261 .PS = ps_bytecode,
262 .BlendState = CD3DX12_BLEND_DESC_DEFAULT(),
263 .SampleMask = PointSampling,
264 .RasterizerState = CD3DX12_RASTERIZER_DESC_DEFAULT(),
265 .InputLayout = input_layout_desc,
266 .PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE,
267 .NumRenderTargets = 1,
268 .RTVFormats = {swapChainRtvFormat},
269 .SampleDesc = swapChainSampleDesc
270 };
271 TrapIfFailed(imm->pDevice->lpVtbl->CreateGraphicsPipelineState(
272 imm->pDevice, &gpso, &IID_ID3D12PipelineState, &imm->pPipelineState));
273
274 const size_t bufferSize = verts_byte_count(bufferSizeVerts);
275 for (int i = 0; i < 2; ++i) {
276 imm->resources[i].pVertexBuffer = create_buffer(pDevice, bufferSize);
277 if (!imm->resources[i].pVertexBuffer) {
278 dxg_imm_destroy(&imm);
279 }
280 TrapIfFailed(dxg_cmdrec_init(&imm->resources[i].cmdRec, pDevice));
281 }
282 imm->cur = 0;
283 dxg_imm_set_up_resource_set(imm);
284
285 TrapIfFailed(pDevice->lpVtbl->CreateFence(
286 pDevice, imm->fenceValue, D3D12_FENCE_FLAG_NONE, &IID_ID3D12Fence, &imm->pFence));
287
288 if ((imm->fenceEvent = CreateEvent(NULL, FALSE, FALSE, NULL)) == NULL) {
289 TrapIfFailed(HRESULT_FROM_WIN32(GetLastError()));
290 }
291
292 return imm;
293}
294
295void dxg_imm_destroy(DxgImm** ppImm) {
296 assert(ppImm);
297 DxgImm* imm = *ppImm;
298 if (imm) {
299 for (int i = 0; i < 2; ++i) {
300 SafeRelease(imm->resources[i].pVertexBuffer);
301 dxg_cmdrec_destroy(&imm->resources[i].cmdRec);
302 }
303 SafeRelease(imm->pRootSignature);
304 SafeRelease(imm->pPipelineState);
305 SafeRelease(imm->pFence);
306 if (imm->fenceEvent != NULL) {
307 CloseHandle(imm->fenceEvent);
308 }
309 free(imm);
310 *ppImm = 0;
311 }
312}
313
314void dxg_imm_set_graphics_state(
315 DxgImm* imm,
316 const D3D12_VIEWPORT* pViewport,
317 D3D12_CPU_DESCRIPTOR_HANDLE hBackBufferView,
318 D3D12_CPU_DESCRIPTOR_HANDLE hDepthStencilView) {
319 assert(imm);
320 assert(pViewport);
321 assert(hBackBufferView.ptr);
322 assert(hDepthStencilView.ptr);
323 imm->graphicsState = (GraphicsState) {
324 .viewport = *pViewport,
325 .hBackBufferView = hBackBufferView,
326 .hDepthStencilView = hDepthStencilView,
327 };
328}
329
330void dxg_imm_flush(DxgImm* imm) {
331 assert(imm);
332 if (imm->vertsWritten > 0) {
333 dxg_imm_draw(imm);
334 // Signal the fence so that the current buffer can be reused once the
335 // draw has finished.
336 ID3D12CommandQueue* pCmdQueue = imm->pCmdQueue;
337 imm->fenceValue++;
338 pCmdQueue->lpVtbl->Signal(pCmdQueue, imm->pFence, imm->fenceValue);
339 // Next draw should Wait for the next buffer. Wait lazily on the next
340 // draw to avoid a stall here.
341 imm->wait = true;
342 dxg_imm_next_resource_set(imm);
343 }
344}
345
346void dxg_imm_draw_triangles(DxgImm* imm, const float* pVerts, size_t numTris) {
347 assert(imm);
348 assert(pVerts);
349 // TODO: This could be a loop to handle the case where the max buffer
350 // capacity cannot hold numTris. Or maybe we should rely on the caller
351 // to specify a big enough capacity, but that makes the API less
352 // friendly.
353 size_t triCapacity = dxg_imm_verts_left(imm) / 3;
354 if (triCapacity == 0) {
355 dxg_imm_flush(imm);
356 }
357 // If we just flushed the previous buffer, then we have to wait on the next
358 // one. The wait is done here, and not inside the branch above, because the
359 // client code can also flush the buffer.
360 if (imm->wait) {
361 dxg_imm_wait(imm);
362 }
363 // Re-evaluate capacity. It must be >0 now.
364 triCapacity = dxg_imm_verts_left(imm) / 3;
365 assert(triCapacity > 0);
366 const size_t numVerts = MIN(triCapacity, numTris) * 3;
367 dxg_imm_copy_verts(imm, pVerts, numVerts);
368}