Skip to content

Commit 4e8881a

Browse files
committed
Direct3D: allocations during play counted, and none left but the shaders made ahead
tools/rend/d3d_allocs.sh runs the test discs under d3d_hdr with Direct3D 11 and 12, each renderer, threaded rendering on and off, counting from frame 20 on the core's heap - what it imports for it pointed at tools/rend/alloc_count.c's Windows half, the C++ runtime linked into it included - and the Direct3D objects it makes through the device's methods, named by the build's symbol table, against tools/rend/d3d_allocs.allowed. In CI. A render read back takes its room from one readback buffer (12) or one staging picture (11), a ring made with the renderer: nothing is made in the fault handler. A texture's name for the log is written into the caller's room. The thread that makes shaders and pipelines ahead is started with the renderer and parks on an eventcount, not started for each batch. Direct3D 11's shader sources are put together once, and what it compiles is kept as the compiler's blob in a fixed map. The texture pool has render targets four times as long as they are wide, to 256. Left on the list: a polygon's own Direct3D 11 pixel shader, made on that thread when a frame first wants it. d3d_warm_test, dx11_shaders_ahead_test and dx12_pipelines_ahead_test run in CI, the last two brought up to the renderers as they are; Direct3D 12's on wine's own vkd3d, with its virtual heaps.
1 parent 35ac6e3 commit 4e8881a

37 files changed

Lines changed: 1061 additions & 317 deletions

‎.github/workflows/vulkan-negotiation.yml‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ name: Vulkan negotiation
1414
# 24-bit, HDR10 and scRGB pictures read back and judged:
1515
# tools/hdr/d3d_hdr.sh. SSAA's exact average on Direct3D 12 is the
1616
# encoders' (hdr_d3d 12ssaa, 12sums): wine's vkd3d cannot draw SSAA.
17+
# And with Direct3D 11 and 12, no allocation during play
18+
# (tools/rend/d3d_allocs.sh), the thread that makes shaders and pipelines
19+
# ahead (tools/rend/d3d_warm_test.sh) and what it makes, on a device
20+
# (tools/rend/dx11_shaders_ahead_test.sh, dx12_pipelines_ahead_test.sh).
1721
# The HDR post effects (bloom, halation, chromatic aberration) are judged
1822
# in each against what the CPU makes of the picture (vk_hdr --post).
1923
# And renders to textures: what is fetched off the card and when
@@ -71,6 +75,12 @@ on:
7175
- 'core/rend/gl4/**'
7276
- 'tools/gl/**'
7377
- 'tools/rend/alloc_count.*'
78+
- 'tools/vulkan/vk_alloc_sites.py'
79+
- 'tools/rend/d3d_allocs.*'
80+
- 'core/rend/d3d_warm.*'
81+
- 'tools/rend/d3d_warm_test.*'
82+
- 'tools/rend/dx11_shaders_ahead_test.*'
83+
- 'tools/rend/dx12_pipelines_ahead_test.*'
7484
- 'core/rend/TexCache.*'
7585
- 'core/hw/sh4/sh4_mem.cpp'
7686
- 'tools/rend/rtt_watch_test.*'
@@ -122,6 +132,12 @@ on:
122132
- 'core/rend/gl4/**'
123133
- 'tools/gl/**'
124134
- 'tools/rend/alloc_count.*'
135+
- 'tools/vulkan/vk_alloc_sites.py'
136+
- 'tools/rend/d3d_allocs.*'
137+
- 'core/rend/d3d_warm.*'
138+
- 'tools/rend/d3d_warm_test.*'
139+
- 'tools/rend/dx11_shaders_ahead_test.*'
140+
- 'tools/rend/dx12_pipelines_ahead_test.*'
125141
- 'core/rend/TexCache.*'
126142
- 'core/hw/sh4/sh4_mem.cpp'
127143
- 'tools/rend/rtt_watch_test.*'
@@ -251,3 +267,13 @@ jobs:
251267
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/d3d_dxil.sh parallel_dc_libretro.dll
252268
- name: Direct3D 11 - nothing compiled or made on the draw path, cold, every renderer (AGENTS.md)
253269
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/d3d_draw_path.sh parallel_dc_libretro.dll
270+
- name: Direct3D 11 and 12 - no allocation during play, every renderer (AGENTS.md)
271+
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/d3d_allocs.sh parallel_dc_libretro.dll
272+
- name: The Direct3D renderers' thread ahead and their shader file (TSan, ASan/UBSan, no threads, Windows)
273+
run: |
274+
sh tools/rend/d3d_warm_test.sh
275+
WINDOWS=1 sh tools/rend/d3d_warm_test.sh
276+
- name: Direct3D 11's shaders made ahead and kept on disk, on a device
277+
run: sh tools/rend/dx11_shaders_ahead_test.sh
278+
- name: Direct3D 12's pipelines made ahead, on a device
279+
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/dx12_pipelines_ahead_test.sh

‎core/rend/TexCache.cpp‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -646,8 +646,11 @@ void BaseTextureCacheData::PrintTextureName()
646646
strcat(str, " Bilinear");
647647

648648
sprintf(str + strlen(str), " %dx%d @ 0x%X", 8 << tsp.TexU, 8 << tsp.TexV, tcw.TexAddr << 3);
649-
std::string id = GetId();
650-
sprintf(str + strlen(str), " id=%s", id.c_str());
649+
{
650+
char id[32];
651+
GetId(id, sizeof(id));
652+
sprintf(str + strlen(str), " id=%s", id);
653+
}
651654
DEBUG_LOG(RENDERER, "%s", str);
652655
}
653656

‎core/rend/TexCache.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1082,7 +1082,8 @@ class BaseTextureCacheData
10821082
bool changed_pieces;
10831083

10841084
void PrintTextureName();
1085-
virtual std::string GetId() = 0;
1085+
/* what the renderer calls it, into @s (@n bytes) */
1086+
virtual void GetId(char *s, size_t n) = 0;
10861087

10871088
bool IsPaletted()
10881089
{

‎core/rend/d3d_warm.c‎

Lines changed: 73 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#ifdef D3D_WARM_THREADS
3131
#include <retro_spsc.h>
3232
#include <rthreads/rthreads.h>
33+
#include <rthreads/retro_eventcount.h>
3334

3435
struct d3d_warm
3536
{
@@ -39,12 +40,12 @@ struct d3d_warm
3940
d3d_warm_release_t release;
4041
void *ctx;
4142
sthread_t *thread; /* renderer's thread only */
43+
retro_eventcount_t work; /* the worker parks here */
44+
retro_eventcount_t idle; /* and d3d_warm_drain_wait(), here */
4245
/* Jobs handed over and not done with: added to by the renderer after
4346
* it queues them, taken from by the worker once one is built and its
44-
* result handed back. Changed by read-modify-writes only, one total
45-
* order: the renderer that finds it 0 starts a worker; the worker that
46-
* takes it to 0 has nothing left and leaves; while it is not 0 there is
47-
* a worker, which has every job counted in it to read. */
47+
* result handed back. Once 0, the worker touches nothing of the
48+
* renderer's until more are added. */
4849
retro_atomic_int_t pending;
4950
retro_atomic_int_t stop;
5051
};
@@ -57,25 +58,37 @@ static void d3d_warm_thread(void *data)
5758

5859
for (;;)
5960
{
61+
int key;
62+
6063
/* (stopped: whoever stopped it joins it and drops the rest) */
6164
if (retro_atomic_load_acquire_int(&w->stop))
6265
break;
63-
/* (always there: counted after it was queued) */
64-
if (retro_spsc_read(&w->jobs, &job, sizeof(job)) != sizeof(job))
65-
break;
66-
memset(&res, 0, sizeof(res));
67-
res.key = job.key;
68-
res.kind = job.kind;
69-
res.aux = job.aux;
70-
if (w->build(w->ctx, &job, &res))
66+
if (retro_spsc_read(&w->jobs, &job, sizeof(job)) == sizeof(job))
7167
{
72-
if ( retro_spsc_write_avail(&w->results) < sizeof(res)
73-
|| retro_spsc_write(&w->results, &res, sizeof(res)) != sizeof(res))
74-
w->release(w->ctx, &res);
68+
memset(&res, 0, sizeof(res));
69+
res.key = job.key;
70+
res.kind = job.kind;
71+
res.aux = job.aux;
72+
if (w->build(w->ctx, &job, &res))
73+
{
74+
if ( retro_spsc_write_avail(&w->results) < sizeof(res)
75+
|| retro_spsc_write(&w->results, &res, sizeof(res)) != sizeof(res))
76+
w->release(w->ctx, &res);
77+
}
78+
/* the last one: nothing of the renderer's is touched after this */
79+
if (retro_atomic_fetch_sub_int(&w->pending, 1) == 1)
80+
retro_eventcount_notify(&w->idle);
81+
continue;
7582
}
76-
/* the last one: nothing of the renderer's is touched after this */
77-
if (retro_atomic_fetch_sub_int(&w->pending, 1) == 1)
78-
break;
83+
/* none: parked until there are, or it is stopped */
84+
key = retro_eventcount_prepare_wait(&w->work);
85+
if ( retro_atomic_load_acquire_int(&w->stop)
86+
|| retro_spsc_read_avail(&w->jobs) >= sizeof(job))
87+
{
88+
retro_eventcount_cancel_wait(&w->work);
89+
continue;
90+
}
91+
retro_eventcount_commit_wait(&w->work, key);
7992
}
8093
}
8194

@@ -88,38 +101,49 @@ d3d_warm_t *d3d_warm_new(d3d_warm_build_t build, d3d_warm_release_t release, voi
88101
if (!(w = (d3d_warm_t*)calloc(1, sizeof(*w))))
89102
return NULL;
90103
if (!retro_spsc_init(&w->jobs, max_jobs * sizeof(d3d_warm_job_t)))
91-
{
92-
free(w);
93-
return NULL;
94-
}
104+
goto no_jobs;
95105
if (!retro_spsc_init(&w->results, max_jobs * sizeof(d3d_warm_result_t)))
96-
{
97-
retro_spsc_free(&w->jobs);
98-
free(w);
99-
return NULL;
100-
}
106+
goto no_results;
107+
if (!retro_eventcount_init(&w->work))
108+
goto no_work;
109+
if (!retro_eventcount_init(&w->idle))
110+
goto no_idle;
101111
w->build = build;
102112
w->release = release;
103113
w->ctx = ctx;
104114
retro_atomic_int_init(&w->pending, 0);
105115
retro_atomic_int_init(&w->stop, 0);
116+
if (!(w->thread = sthread_create(d3d_warm_thread, w)))
117+
goto no_thread;
106118
return w;
107-
}
108119

109-
/* The thread joined, so the renderer is the only one at either queue. */
110-
static void d3d_warm_join(d3d_warm_t *w)
111-
{
112-
if (w->thread)
113-
{
114-
sthread_join(w->thread);
115-
w->thread = NULL;
116-
}
120+
no_thread:
121+
retro_eventcount_free(&w->idle);
122+
no_idle:
123+
retro_eventcount_free(&w->work);
124+
no_work:
125+
retro_spsc_free(&w->results);
126+
no_results:
127+
retro_spsc_free(&w->jobs);
128+
no_jobs:
129+
free(w);
130+
return NULL;
117131
}
118132

119133
void d3d_warm_drain_wait(d3d_warm_t *w)
120134
{
121-
if (w)
122-
d3d_warm_join(w);
135+
if (!w)
136+
return;
137+
while (retro_atomic_load_acquire_int(&w->pending) != 0)
138+
{
139+
int key = retro_eventcount_prepare_wait(&w->idle);
140+
if (retro_atomic_load_acquire_int(&w->pending) == 0)
141+
{
142+
retro_eventcount_cancel_wait(&w->idle);
143+
break;
144+
}
145+
retro_eventcount_commit_wait(&w->idle, key);
146+
}
123147
}
124148

125149
size_t d3d_warm_submit(d3d_warm_t *w, const d3d_warm_job_t *jobs, size_t n)
@@ -133,20 +157,8 @@ size_t d3d_warm_submit(d3d_warm_t *w, const d3d_warm_job_t *jobs, size_t n)
133157
if (!put)
134158
return 0;
135159
retro_spsc_write(&w->jobs, jobs, put * sizeof(*jobs));
136-
/* None pending: no worker (the last one has left, or is leaving, and
137-
* reads nothing more). Start one. */
138-
if (retro_atomic_fetch_add_int(&w->pending, (int)put) == 0)
139-
{
140-
d3d_warm_join(w);
141-
if (!(w->thread = sthread_create(d3d_warm_thread, w)))
142-
{
143-
/* Nothing will take them: they go back to the caller (none
144-
* other is queued: none was pending). */
145-
retro_spsc_clear(&w->jobs);
146-
retro_atomic_store_release_int(&w->pending, 0);
147-
return 0;
148-
}
149-
}
160+
retro_atomic_fetch_add_int(&w->pending, (int)put);
161+
retro_eventcount_notify(&w->work);
150162
return put;
151163
}
152164

@@ -155,10 +167,16 @@ void d3d_warm_stop(d3d_warm_t *w)
155167
if (!w)
156168
return;
157169
retro_atomic_store_release_int(&w->stop, 1);
158-
d3d_warm_join(w);
170+
retro_eventcount_notify(&w->work);
171+
if (w->thread)
172+
{
173+
sthread_join(w->thread);
174+
w->thread = NULL;
175+
}
159176
/* (what was not started is not going to be) */
160177
retro_spsc_clear(&w->jobs);
161178
retro_atomic_store_release_int(&w->pending, 0);
179+
retro_eventcount_notify(&w->idle);
162180
}
163181

164182
bool d3d_warm_take(d3d_warm_t *w, d3d_warm_result_t *res)
@@ -180,6 +198,8 @@ void d3d_warm_free(d3d_warm_t *w)
180198
d3d_warm_stop(w);
181199
while (retro_spsc_read(&w->results, &res, sizeof(res)) == sizeof(res))
182200
w->release(w->ctx, &res);
201+
retro_eventcount_free(&w->work);
202+
retro_eventcount_free(&w->idle);
183203
retro_spsc_free(&w->jobs);
184204
retro_spsc_free(&w->results);
185205
free(w);

‎core/rend/d3d_warm.h‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@
1919
*
2020
* The renderer hands over jobs (a kind and a key of its own); a thread
2121
* builds each with the renderer's build function and hands the result
22-
* back. The thread exits once there is no job left, and the next jobs
23-
* handed over start another: it never waits for work. Both hand-overs are
24-
* lock-free single-producer single-consumer queues: the renderer's thread
25-
* gives jobs and takes results, the worker the other way round.
22+
* back. The thread is started with it (d3d_warm_new()), not as jobs come:
23+
* with none left it parks on an eventcount, which the next jobs handed
24+
* over wake it from - nothing made, and no lock taken, to start it again.
25+
* Both hand-overs are lock-free single-producer single-consumer queues:
26+
* the renderer's thread gives jobs and takes results, the worker the
27+
* other way round.
2628
*
2729
* The renderer takes results when it misses something it would otherwise
2830
* build: a result is never required, and one that could not be handed back
@@ -69,16 +71,16 @@ d3d_warm_t *d3d_warm_new(d3d_warm_build_t build, d3d_warm_release_t release, voi
6971
/* Stops the thread (the job it is building is finished), releases what
7072
* was not taken, frees. */
7173
void d3d_warm_free(d3d_warm_t *w);
72-
/* Hands over @n jobs, starting a thread if none is running. Returns how
73-
* many were taken: the rest are the caller's to build. */
74+
/* Hands over @n jobs, waking the thread. Returns how many were taken: the
75+
* rest are the caller's to build. */
7476
size_t d3d_warm_submit(d3d_warm_t *w, const d3d_warm_job_t *jobs, size_t n);
7577
/* One result, if there is one. */
7678
bool d3d_warm_take(d3d_warm_t *w, d3d_warm_result_t *res);
7779
/* Whether any job handed over is not done yet (renderer's thread). Once
7880
* false, the worker touches nothing of the renderer's until the next
7981
* submit. */
8082
bool d3d_warm_busy(d3d_warm_t *w);
81-
/* Waits for the thread to finish what it was given (tests). */
83+
/* Waits for the thread to finish what it was given. */
8284
void d3d_warm_drain_wait(d3d_warm_t *w);
8385
/* Stops the thread after the job it is building, and joins it: what it
8486
* finished is still there to take, and nothing more is built. */

‎core/rend/dx11/dx11_renderer.cpp‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ bool DX11Renderer::Init()
108108
success = depthStencilStates.MakeAll() && success;
109109
success = blendStates->MakeAll() && success;
110110
success = samplers->MakeAll() && success;
111+
// what a render read back takes, made now (dx11_rtt_init())
112+
dx11_rtt_init();
111113
ID3DBlob *blob = shaders->GetVertexShaderBlob();
112114
success = success && blob != nullptr
113115
&& SUCCEEDED(device->CreateInputLayout(MainLayout, ARRAY_SIZE(MainLayout), blob->GetBufferPointer(), blob->GetBufferSize(), mainInputLayout.put()));

0 commit comments

Comments
 (0)