Direct3D 12: draw the frame from where it is parsed

Where the adapter's memory is the processor's, kept coherent with its
cache (CacheCoherentUMA), the frame's vertices, indices and modifier
volumes' triangles are parsed straight into a buffer of a custom heap
with write-back pages, mapped for good, in four slices
(dx12/parse_arena_d3d12.h, C89): no copy into the upload ring. The
sorted passes' indices go after the frame's there. A slice is taken when
the context's fence has passed the value of the submission that last
drew from it, polled; with none free, or another adapter, the frame is
copied as ever. Render() is the base's, final, around RenderFrame().

d3d_hdr.c's D3DH_NOT_UMA has the adapter say otherwise and D3DH_EVERY
prints every frame's sum; d3d_parse_arena.sh checks every render drawn
from where it was parsed and every frame the one of the lists copied,
under wine.
This commit is contained in:
libretroadmin committed 2026-11-02 17:04:49 +01:00
1 parent bc87a9a59d
commit b60d97627a
9 files changed
+397 -7

No files matched your search

+7 -2
View File
@@ -30,8 +30,9 @@ name: Vulkan negotiation
# And a frame's vertices and indices parsed straight into memory the card
# reads, none copied, the pictures those of the lists copied, with Vulkan
# and with OpenGL (tools/vulkan/vk_parse_arena.sh, tools/gl/gl_parse_arena.sh:
# no OpenGL error either, and OpenGL ES drawing as ever), and the arena's
# slices (tools/rend/parse_arena_test.sh).
# no OpenGL error either, and OpenGL ES drawing as ever), and with Direct3D
# 12 where the adapter's memory is the processor's (tools/rend/d3d_parse_arena.sh,
# under wine), and the arena's slices (tools/rend/parse_arena_test.sh).
on:
push:
@@ -66,6 +67,7 @@ on:
- 'core/hw/pvr/ta_list.*'
- 'core/hw/pvr/helper_classes.h'
- 'tools/rend/parse_arena_test.*'
- 'tools/rend/d3d_parse_arena.sh'
- 'tools/rend/clip_check.py'
- 'tools/threads/live_prog.c'
- 'Makefile*'
@@ -102,6 +104,7 @@ on:
- 'core/hw/pvr/ta_list.*'
- 'core/hw/pvr/helper_classes.h'
- 'tools/rend/parse_arena_test.*'
- 'tools/rend/d3d_parse_arena.sh'
- 'tools/rend/clip_check.py'
- 'tools/threads/live_prog.c'
- 'Makefile*'
@@ -180,3 +183,5 @@ jobs:
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/hdr/d3d_hdr.sh parallel_dc_libretro.dll
- name: Direct3D 11 - video memory written by DMA over renders to textures (Direct3D 12 with D3D12_DLLS)
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/d3d_dma.sh parallel_dc_libretro.dll
- name: Direct3D 12 drawing the frame from where it is parsed, every frame the one of the lists copied
run: VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json sh tools/rend/d3d_parse_arena.sh parallel_dc_libretro.dll
+2 -1
View File
@@ -325,7 +325,8 @@ ifeq ($(HAVE_D3D12), 1)
$(CORE_DIR)/core/rend/dx12/dx12_texdecoder.cpp \
$(CORE_DIR)/core/rend/dx12/dx12_texture.cpp \
$(CORE_DIR)/core/rend/dx12/oit/dx12_oitrenderer.cpp
SOURCES_C += $(CORE_DIR)/core/rend/hdr/hdr_encode_d3d12.c
SOURCES_C += $(CORE_DIR)/core/rend/hdr/hdr_encode_d3d12.c \
$(CORE_DIR)/core/rend/dx12/parse_arena_d3d12.c
endif
# The HDR output's Display Transform (rend/hdr/, Color Format), for the
+59
View File
@@ -238,6 +238,17 @@ bool DX12Renderer::Init()
writeOut.Init(context);
writeOutPictures = DX12WriteOut::Wanted();
SetupSsaa(screen_width, screen_height);
// where frames are parsed when the card can draw them from there: made once
if (!parseArena.ready)
{
size_t room[PARSE_ARENA_PARTS];
TA_context::ArenaRooms(room);
arenaMissed = false;
if (parse_arena_d3d12_init(&parseArena, device, context->GetFence(), room, 4))
NOTICE_LOG(RENDERER, "DX12: renders parsed where the card reads them, not copied there");
else
INFO_LOG(RENDERER, "DX12: renders copied to the card (its memory is not the processor's, cache-coherent)");
}
SetupUpscaler();
/* GPU-side TA processing, if it is on: here, not in a frame; and the
* buffer its frames' vertex constants go to (UploadConstants()) */
@@ -297,6 +308,8 @@ void DX12Renderer::Term()
gpuVtxConstants.reset();
}
gpuVtxConstantsState = D3D12_RESOURCE_STATE_COPY_DEST;
// (let go once the frames drawn from it are done)
parse_arena_d3d12_term(&parseArena, [](void *c, IUnknown *object) { ((DX12Context *)c)->Release(object); }, context);
overlay.Term();
DropImage(fbTexture);
DropRestored();
@@ -548,7 +561,22 @@ bool DX12Renderer::ProcessLocked(TA_context* ctx)
* recorded now; or it is parsed as ever */
bool result;
if (!taDecoder.Ready() || !taDecoder.Prepare(ctx, TaFlags(), &result))
{
/* into a slice of the memory the card reads, where there is one
* free: the draws read it there, uncopied (with the background
* polygon's four vertices FillBGP() wrote, at the head of the list
* the parse empties and takes them back in) */
ta_list_t *lists[PARSE_ARENA_PARTS];
ctx->ArenaLists(lists);
if (!parse_arena_d3d12_attach(&parseArena, lists, 4 * sizeof(Vertex)) && parseArena.ready && !arenaMissed)
{
arenaMissed = true;
NOTICE_LOG(RENDERER, "DX12: a render copied to the card, no slice of the parse's free");
}
result = ta_parse_vdrc(ctx);
if (!result)
parse_arena_d3d12_cancel(&parseArena);
}
else if (result)
taDecoder.Record(ctx);
// the textures the frame's polygons asked for that are made on the GPU, before the draws
@@ -694,6 +722,28 @@ bool DX12Renderer::UploadGeometry()
const u32 indexBytes = pvrrc.idx.bytes() + SortPassesRoom() * sizeof(u32);
const List<ModTriangle>& modVolList = pvrrc.mv_clip.used() != 0 ? pvrrc.mv_clip : pvrrc.modtrig;
const u32 modVolBytes = modVolList.bytes();
/* Parsed where the card reads it (parseArena): drawn from there, the
* sorted passes' indices after the frame's, in the room they have
* there - or, with more of them, copied as below */
const D3D12_GPU_VIRTUAL_ADDRESS here = parse_arena_d3d12_gpu(&parseArena, PARSE_ARENA_VERTS);
if (here != 0 && indexBytes <= parseArena.arena.space[PARSE_ARENA_IDX])
{
// sorted from the vertices as the game sent them, then those made for the card, in place
SortTriangles(pvrrc.idx.head() + pvrrc.idx.used());
SetProvokingVertices();
vertexView.BufferLocation = here;
vertexView.SizeInBytes = vertexBytes;
vertexView.StrideInBytes = sizeof(Vertex);
indexView.BufferLocation = parse_arena_d3d12_gpu(&parseArena, PARSE_ARENA_IDX);
indexView.SizeInBytes = pvrrc.idx.bytes() + sortedIndexCount * sizeof(u32);
indexView.Format = DXGI_FORMAT_R32_UINT;
modVolView.BufferLocation = parse_arena_d3d12_gpu(&parseArena,
pvrrc.mv_clip.used() != 0 ? PARSE_ARENA_MV_CLIP : PARSE_ARENA_MODTRIG);
modVolView.SizeInBytes = modVolBytes;
modVolView.StrideInBytes = sizeof(float) * 3;
haveModVols = modVolBytes != 0;
return UploadConstants();
}
/* the vertices, the indexes, the volumes' triangles, each at a multiple
* of its size, in one piece of the ring */
const u32 indexes = (vertexBytes + 3) & ~3u;
@@ -1627,6 +1677,15 @@ void DX12Renderer::DrawFramebuffer()
/* ---- The frame ---- */
bool DX12Renderer::Render()
{
const bool rv = RenderFrame();
/* (the commands that draw it: submitted already, or in the list the next
* submission executes) */
parse_arena_d3d12_drawn(&parseArena, context->IsRecording() ? context->LastSubmitted() + 1 : context->LastSubmitted());
return rv;
}
bool DX12Renderer::RenderFrame()
{
const bool held = heldFromProcess;
heldFromProcess = false;
+10 -1
View File
@@ -28,6 +28,7 @@
#include "rend/transform_matrix.h"
#include "rend/shadows.h"
#include "dx12context_lr.h"
#include "parse_arena_d3d12.h"
#include "dx12_overlay.h"
#include "dx12_writeout.h"
#include "dx12_texdecoder.h"
@@ -98,13 +99,21 @@ public:
void Resize(int w, int h) override;
void Term() override;
bool Process(TA_context* ctx) override;
bool Render() override;
/* The frame drawn (RenderFrame()), and its lists its own again: what the
* parse wrote where the card reads it (parseArena) the card's till the
* commands that draw it are done */
bool Render() final;
void KeepPicture() override;
u64 GetTexture(TSP tsp, TCW tcw, int area = 0) override;
// (TA processing switched on or off)
void OptionsChanged() override;
protected:
virtual bool RenderFrame();
/* Where a frame is parsed when the card can draw it from there: a
* machine whose memory is the processor's (parse_arena_d3d12.h) */
parse_arena_d3d12_t parseArena {};
bool arenaMissed = false; // (said once: a render copied, no slice free)
struct VertexConstants
{
float scale[4]; // x, y, z, and the depth's scale (shaders/dx12_raster.hlsl)
+3 -3
View File
@@ -123,7 +123,7 @@ public:
InitCompute();
}
bool Render() override;
bool RenderFrame() override;
void Term() override
{
@@ -1351,7 +1351,7 @@ void DX12OITRenderer::DrawFrame()
/* ---- Rendering Method: Compute ---- */
bool DX12OITRenderer::Render()
bool DX12OITRenderer::RenderFrame()
{
/* (a render to a texture with an empty pixel clip writes nothing: Render()'s) */
if (compute.Ready() && device != nullptr && !pvrrc.isRenderFramebuffer
@@ -1364,7 +1364,7 @@ bool DX12OITRenderer::Render()
else if (pvrrc.isRTT ? RenderComputeRtt() : RenderCompute())
return !pvrrc.isRTT;
}
return DX12Renderer::Render();
return DX12Renderer::RenderFrame();
}
bool DX12OITRenderer::RenderComputeRtt()
+119
View File
@@ -0,0 +1,119 @@
/* See parse_arena_d3d12.h. C89. */
#ifndef COBJMACROS
#define COBJMACROS
#endif
#include <string.h>
#include "parse_arena_d3d12.h"
/* (ID3D12Resource's, here: nothing is linked for it) */
static const GUID resource_iid = { 0x696442be, 0xa72e, 0x4059, { 0xbc, 0x79, 0x5b, 0x5c, 0x98, 0x04, 0x0f, 0xad } };
int parse_arena_d3d12_init(parse_arena_d3d12_t *a, ID3D12Device *device, ID3D12Fence *fence,
const size_t *room, unsigned slots)
{
D3D12_FEATURE_DATA_ARCHITECTURE arch;
D3D12_HEAP_PROPERTIES heap;
D3D12_RESOURCE_DESC desc;
D3D12_RANGE none;
void *map = NULL;
size_t bytes;
memset(a, 0, sizeof(*a));
memset(&arch, 0, sizeof(arch));
if (FAILED(ID3D12Device_CheckFeatureSupport(device, D3D12_FEATURE_ARCHITECTURE, &arch, sizeof(arch)))
|| !arch.CacheCoherentUMA)
return 0;
/* (a slice's parts 256 bytes aligned: more than a vertex or index
* buffer's view needs) */
bytes = parse_arena_layout(&a->arena, room, slots, 256);
if (bytes == 0)
return 0;
memset(&heap, 0, sizeof(heap));
heap.Type = D3D12_HEAP_TYPE_CUSTOM;
heap.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
heap.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
memset(&desc, 0, sizeof(desc));
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER;
desc.Width = bytes;
desc.Height = 1;
desc.DepthOrArraySize = 1;
desc.MipLevels = 1;
desc.SampleDesc.Count = 1;
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
if (FAILED(ID3D12Device_CreateCommittedResource(device, &heap, D3D12_HEAP_FLAG_NONE, &desc,
D3D12_RESOURCE_STATE_GENERIC_READ, NULL, &resource_iid, (void **)&a->buffer)))
{
a->buffer = NULL;
return 0;
}
/* (nothing read through the mapping yet) */
none.Begin = 0;
none.End = 0;
if (FAILED(ID3D12Resource_Map(a->buffer, 0, &none, &map)) || map == NULL)
{
ID3D12Resource_Release(a->buffer);
a->buffer = NULL;
return 0;
}
parse_arena_bind(&a->arena, map);
a->gpu = ID3D12Resource_GetGPUVirtualAddress(a->buffer);
a->fence = fence;
a->ready = 1;
return 1;
}
void parse_arena_d3d12_term(parse_arena_d3d12_t *a, void (*release)(void *arg, IUnknown *object), void *arg)
{
if (a->ready)
parse_arena_detach(&a->arena);
if (a->buffer != NULL)
{
if (release != NULL)
release(arg, (IUnknown *)a->buffer);
else
ID3D12Resource_Release(a->buffer);
}
memset(a, 0, sizeof(*a));
}
int parse_arena_d3d12_attach(parse_arena_d3d12_t *a, ta_list_t *const *lists, size_t vert_head)
{
UINT64 done;
unsigned i;
if (!a->ready || a->arena.slot >= 0)
return 0;
done = ID3D12Fence_GetCompletedValue(a->fence);
for (i = 0; i < a->arena.slices; i++)
{
const unsigned s = (a->next + i) % a->arena.slices;
if (a->drawn[s] > done)
continue;
if (!parse_arena_attach(&a->arena, s, lists, vert_head))
return 0;
a->next = (s + 1) % a->arena.slices;
return 1;
}
return 0;
}
void parse_arena_d3d12_drawn(parse_arena_d3d12_t *a, UINT64 value)
{
if (!a->ready || a->arena.slot < 0)
return;
a->drawn[a->arena.slot] = value;
parse_arena_detach(&a->arena);
}
void parse_arena_d3d12_cancel(parse_arena_d3d12_t *a)
{
if (a->ready)
parse_arena_detach(&a->arena);
}
D3D12_GPU_VIRTUAL_ADDRESS parse_arena_d3d12_gpu(const parse_arena_d3d12_t *a, unsigned part)
{
if (!a->ready || a->arena.slot < 0 || part >= PARSE_ARENA_PARTS)
return 0;
return a->gpu + parse_arena_offset(&a->arena, (unsigned)a->arena.slot, part);
}
+61
View File
@@ -0,0 +1,61 @@
#ifndef PARSE_ARENA_D3D12_H
#define PARSE_ARENA_D3D12_H
/* The parse's arena (rend/parse_arena.h) with Direct3D 12: one buffer of
* a custom heap with the processor's own, cached pages (write-back) in the
* memory the card reads, mapped for good, in slices for a few frames in
* flight, where the frame is parsed and then drawn from: no copy into the
* upload ring. Only where the adapter's memory is the processor's, kept
* coherent with its cache (CacheCoherentUMA): elsewhere such pages are
* read over the bus, and the frame is copied, as ever.
*
* A slice is taken when the context's fence has passed the value it was
* last drawn with (polled: nothing waits); with none free, the frame is
* copied. C89. */
#include <d3d12.h>
#include "../parse_arena.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct parse_arena_d3d12
{
parse_arena_t arena;
ID3D12Resource *buffer;
D3D12_GPU_VIRTUAL_ADDRESS gpu;
ID3D12Fence *fence; /* the context's (not this one's to release) */
UINT64 drawn[PARSE_ARENA_SLOTS]; /* the fence's value once a slice is done with */
unsigned next; /* the slice looked at first */
int ready; /* (all zero, as a static one is before it is made: none) */
} parse_arena_d3d12_t;
/* Made on @device for @slots frames of lists of the rooms @room (by
* PARSE_ARENA_*), its slices free when @fence says so; false, and nothing
* kept, where the adapter is not CacheCoherentUMA or the buffer cannot be
* made and mapped */
int parse_arena_d3d12_init(parse_arena_d3d12_t *a, ID3D12Device *device, ID3D12Fence *fence,
const size_t *room, unsigned slots);
/* (the frames drawn from it done on the card; @buffer is given to
* @release where the context lets it go later, or released now) */
void parse_arena_d3d12_term(parse_arena_d3d12_t *a, void (*release)(void *arg, IUnknown *object), void *arg);
/* The frame whose lists these are (@lists, by PARSE_ARENA_*; @vert_head as
* parse_arena_attach()'s) parsed into a free slice, till
* parse_arena_d3d12_drawn() or parse_arena_d3d12_cancel(); false where
* none is free or the lists are not of the arena's rooms */
int parse_arena_d3d12_attach(parse_arena_d3d12_t *a, ta_list_t *const *lists, size_t vert_head);
/* ...drawn by the commands the fence's @value is signalled after: its
* slice the card's till then, and its lists its own again */
void parse_arena_d3d12_drawn(parse_arena_d3d12_t *a, UINT64 value);
/* ...not drawn: its lists its own again, and its slice free */
void parse_arena_d3d12_cancel(parse_arena_d3d12_t *a);
/* Where part @part of the frame's slice is, for the card; 0 when no frame
* is attached */
D3D12_GPU_VIRTUAL_ADDRESS parse_arena_d3d12_gpu(const parse_arena_d3d12_t *a, unsigned part);
#ifdef __cplusplus
}
#endif
#endif
+50
View File
@@ -15,6 +15,14 @@
* frame's motion vectors and depth, which are read back and looked at as
* vk_hdr.c looks at the Vulkan renderers'.
*
* With D3DH_NOT_UMA=1 (Direct3D 12) the adapter says its memory is not the
* processor's (CacheCoherentUMA 0, as a card of its own has it): the core
* copies the frame's lists to the card, not parse them where it reads
* them (tools/rend/d3d_parse_arena.sh). With D3DH_EVERY=1 (Direct3D 12)
* the picture handed over is read back after every frame and its sum
* printed, "frame N SUM", for runs to be compared frame for frame (as
* gl_frames.c's GLF_EVERY).
*
* D3DH_API=11|12 D3DH_FRONTEND=sdr|hdr10|scrgb D3DH_DIR=dir \
* d3d_hdr core.dll content out.raw frames [key=value ...]
*
@@ -79,6 +87,29 @@ static struct retro_hw_render_interface_d3d11 iface11;
/* ---- Direct3D 12, version 1: the frame is handed by set_texture ---- */
static ID3D12Device *d12;
/* D3DH_NOT_UMA: the device's CheckFeatureSupport(), its answer about the
* architecture changed, through a copy of its table of methods - a table
* the newer interfaces of the object (ID3D12Device1 and on, which the
* core asks for) share and go on past ID3D12Device's in: so a good many
* more of its methods than the headers here know copied too */
static void *d12_not_uma[192];
static HRESULT (STDMETHODCALLTYPE *d12_check)(ID3D12Device *, D3D12_FEATURE, void *, UINT);
static HRESULT STDMETHODCALLTYPE not_uma_check(ID3D12Device *device, D3D12_FEATURE feature, void *data, UINT size)
{
const HRESULT hr = d12_check(device, feature, data, size);
if (SUCCEEDED(hr) && feature == D3D12_FEATURE_ARCHITECTURE && size >= sizeof(D3D12_FEATURE_DATA_ARCHITECTURE))
{
((D3D12_FEATURE_DATA_ARCHITECTURE *)data)->UMA = FALSE;
((D3D12_FEATURE_DATA_ARCHITECTURE *)data)->CacheCoherentUMA = FALSE;
}
if (SUCCEEDED(hr) && feature == D3D12_FEATURE_ARCHITECTURE1 && size >= sizeof(D3D12_FEATURE_DATA_ARCHITECTURE1))
{
((D3D12_FEATURE_DATA_ARCHITECTURE1 *)data)->UMA = FALSE;
((D3D12_FEATURE_DATA_ARCHITECTURE1 *)data)->CacheCoherentUMA = FALSE;
}
return hr;
}
static ID3D12CommandQueue *q12;
static ID3D12Resource *frame12;
static DXGI_FORMAT frame12_format;
@@ -601,6 +632,13 @@ int main(int argc, char **argv)
fprintf(stderr, "d3d_hdr: no Direct3D 12 device\n");
return 2;
}
if (getenv("D3DH_NOT_UMA"))
{
memcpy(d12_not_uma, d12->lpVtbl, sizeof(d12_not_uma));
d12_check = d12->lpVtbl->CheckFeatureSupport;
((ID3D12DeviceVtbl *)d12_not_uma)->CheckFeatureSupport = not_uma_check;
d12->lpVtbl = (ID3D12DeviceVtbl *)d12_not_uma;
}
memset(&qd, 0, sizeof(qd));
qd.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
if (FAILED(ID3D12Device_CreateCommandQueue(d12, &qd, &IID_ID3D12CommandQueue, (void**)&q12)))
@@ -656,7 +694,19 @@ int main(int argc, char **argv)
}
hw.context_reset();
for (n = 0; n < frames; n++)
{
retro_run();
if (getenv("D3DH_EVERY") && api == 12 && frame12 != NULL)
{
UINT pitch, y, x;
unsigned long sum = 2166136261ul;
const unsigned char *rows = copy_out12(frame12, iface12.required_state, frame_w, frame_h, &pitch);
for (y = 0; rows != NULL && y < frame_h; y++)
for (x = 0; x < frame_w * 4; x++)
sum = ((sum ^ rows[(size_t)y * pitch + x]) * 16777619ul) & 0xfffffffful;
printf("frame %u %ux%u %08lx\n", n, frame_w, frame_h, sum);
}
}
if (getenv("D3DH_REFUSED"))
{
+86
View File
@@ -0,0 +1,86 @@
#!/bin/sh
# A frame's vertices, indices and modifier volumes' triangles parsed
# straight into memory the card reads (core/rend/dx12/parse_arena_d3d12.h),
# not copied into the upload ring after, with Direct3D 12: the core's
# Windows build under wine (vkd3d on lavapipe, whose adapter's memory is
# the processor's: CacheCoherentUMA), on a frontend that runs the core's
# frames as RetroArch does (tools/hdr/d3d_hdr.c). Every render the CPU
# parses is drawn from where it was parsed, and every frame is the one of
# the lists copied (D3DH_EVERY), as an adapter of its own has it (D3DH_NOT_UMA) - the
# test discs' plain one, heavy one (a volume far off the screen among it),
# DMA one and the one with modifier volumes, with each renderer, threaded
# rendering on and off.
#
# sh tools/rend/d3d_parse_arena.sh [core.dll]
#
# KEEP=1: the pictures and logs are kept, in $TMPDIR/d3d_parse_arena.<pid>.
#
# Needs wine, Xvfb, mingw-w64 and a Vulkan driver (lavapipe:
# VK_DRIVER_FILES=/usr/share/vulkan/icd.d/lvp_icd.json).
set -e
cd "$(dirname "$0")/../.."
CORE=$(readlink -f "${1:-parallel_dc_libretro.dll}")
WORK=$(readlink -f "${TMPDIR:-/tmp}")/d3d_parse_arena.$$
mkdir -p "$WORK"
DISPLAY_NO=${D3D_PARSE_ARENA_DISPLAY:-:94}
Xvfb $DISPLAY_NO -screen 0 640x480x24 >/dev/null 2>&1 &
XVFB=$!
trap 'kill $XVFB 2>/dev/null; [ -n "${KEEP:-}" ] || rm -rf "$WORK"' EXIT
${CC_WIN:-x86_64-w64-mingw32-gcc} -O1 -Wall -I./core/libretro-common/include -I./core/deps/khronos \
-o "$WORK/d3d_hdr.exe" tools/hdr/d3d_hdr.c -ld3d11 -ld3d12 -ldxguid
# (a directory a disc: their tracks' names are the same)
for disc in plain heavy dma cull; do
mkdir -p "$WORK/$disc"
case $disc in
plain) python3 tools/threads/live_disc.py "$WORK/$disc/test.gdi" ;;
*) python3 tools/threads/live_disc.py "$WORK/$disc/test.gdi" --$disc ;;
esac
done
fails=0
# run <disc> <name> <options...>: 100 frames, from nothing kept by an
# earlier run (the discs write to the memory card)
run() {
run_disc=$1; run_name=$2; shift 2
rm -rf "$WORK/dir"; mkdir -p "$WORK/dir"
env -u TMPDIR DISPLAY=$DISPLAY_NO WINEDEBUG=-all WINE_D3D_CONFIG=renderer=vulkan VKD3D_CONFIG=virtual_heaps \
D3DH_EVERY=1 D3DH_VERBOSE=1 D3DH_API=12 D3DH_FRONTEND=sdr D3DH_DIR="Z:$WORK/dir" WINEDLLOVERRIDES="winedbg.exe=d" \
timeout 600 wine "$WORK/d3d_hdr.exe" "Z:$CORE" "Z:$WORK/$run_disc/test.gdi" "Z:$WORK/$run_name.raw" 100 "$@" \
> "$WORK/$run_name.log" 2>&1 || { echo " FAIL: the $run_name run failed:"; tail -5 "$WORK/$run_name.log"; return 1; }
}
for disc in plain heavy dma cull; do
for r in per-triangle per-pixel; do
case $r in
per-triangle) set -- "reicast_alpha_sorting=per-triangle (normal)" ;;
per-pixel) set -- "reicast_alpha_sorting=per-pixel (accurate)" ;;
esac
for threaded in enabled disabled; do
name="$disc-$r-$threaded"
echo "$name"
run $disc "$name" "$@" reicast_threaded_rendering=$threaded || { fails=$((fails + 1)); continue; }
D3DH_NOT_UMA=1 run $disc "$name-copied" "$@" reicast_threaded_rendering=$threaded \
|| { fails=$((fails + 1)); continue; }
if ! grep -q "renders parsed where the card reads them" "$WORK/$name.log" \
|| grep -q "a render copied to the card" "$WORK/$name.log"; then
echo " FAIL: not every render parsed where the card reads it"
fails=$((fails + 1))
fi
if grep -q "renders parsed where the card reads them" "$WORK/$name-copied.log"; then
echo " FAIL: with an adapter of its own, the parse is still put there"
fails=$((fails + 1))
fi
if ! cmp -s "$WORK/$name.raw" "$WORK/$name-copied.raw" \
|| [ "$(grep '^frame ' "$WORK/$name.log")" != "$(grep '^frame ' "$WORK/$name-copied.log")" ]; then
echo " FAIL: the frames are not those of the lists copied"
fails=$((fails + 1))
fi
done
done
done
if [ $fails != 0 ]; then
echo "d3d_parse_arena: $fails failed"
exit 1
fi
echo "d3d_parse_arena: all passed"