Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ projects/
workdir/cache/
workdir/Assets/
workdir/pso
workdir/assert.txt

*.dat

AgilitySDK/
Expand Down
29 changes: 23 additions & 6 deletions sources/HAL/API/D3D12/HAL.D3D12.CommandList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -431,12 +431,29 @@ namespace HAL
barrier.LayoutAfter = to_native(e.after.get_layout());
barrier.pResource = e.resource->get_dx();

barrier.Subresources.IndexOrFirstMipLevel = e.resource->get_desc().as_texture().get_mip(e.subres);
barrier.Subresources.NumMipLevels = 1;
barrier.Subresources.FirstArraySlice = e.resource->get_desc().as_texture().get_array(e.subres);
barrier.Subresources.NumArraySlices = 1;
barrier.Subresources.FirstPlane = e.resource->get_desc().as_texture().get_plane(e.subres);
barrier.Subresources.NumPlanes = 1;
if (e.subres == 0xffffffffu) // HAL::ALL_SUBRESOURCES
{
// Single barrier covering every subresource. D3D12 uses
// IndexOrFirstMipLevel == 0xffffffff (== ALL_SUBRESOURCES)
// as the "all subresources" sentinel; the other range
// fields are then ignored. The uniform-state fast path
// emits this instead of one barrier per mip/array/plane.
barrier.Subresources.IndexOrFirstMipLevel = 0xffffffff;
barrier.Subresources.NumMipLevels = 0;
barrier.Subresources.FirstArraySlice = 0;
barrier.Subresources.NumArraySlices = 0;
barrier.Subresources.FirstPlane = 0;
barrier.Subresources.NumPlanes = 0;
}
else
{
barrier.Subresources.IndexOrFirstMipLevel = e.resource->get_desc().as_texture().get_mip(e.subres);
barrier.Subresources.NumMipLevels = 1;
barrier.Subresources.FirstArraySlice = e.resource->get_desc().as_texture().get_array(e.subres);
barrier.Subresources.NumArraySlices = 1;
barrier.Subresources.FirstPlane = e.resource->get_desc().as_texture().get_plane(e.subres);
barrier.Subresources.NumPlanes = 1;
}

barrier.Flags = D3D12_TEXTURE_BARRIER_FLAG_NONE;
if (check(e.flags & BarrierFlags::DISCARD))
Expand Down
31 changes: 31 additions & 0 deletions sources/HAL/API/D3D12/HAL.D3D12.Device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,37 @@ namespace HAL
filter.DenyList.pIDList = hide;
d3dInfoQueue->AddStorageFilterEntries(&filter);
}

// Mirror debug-layer output into workdir/log.txt. Without this the
// messages only reach the attached debugger's output window, which
// makes barrier work (where the validation layer IS the test) hard
// to iterate on from a plain console run.
ComPtr<ID3D12InfoQueue1> d3dInfoQueue1;
if (SUCCEEDED(native_device.As(&d3dInfoQueue1)))
{
DWORD cookie = 0;
d3dInfoQueue1->RegisterMessageCallback(
[](D3D12_MESSAGE_CATEGORY, D3D12_MESSAGE_SEVERITY severity,
D3D12_MESSAGE_ID id, LPCSTR description, void*)
{
const char* tag =
severity == D3D12_MESSAGE_SEVERITY_CORRUPTION ? "D3D12 CORRUPTION" :
severity == D3D12_MESSAGE_SEVERITY_ERROR ? "D3D12 ERROR" :
severity == D3D12_MESSAGE_SEVERITY_WARNING ? "D3D12 WARNING" :
nullptr;

if (!tag) return; // INFO/MESSAGE would drown the log

Log::get() << tag << " #" << int(id) << ": " << description << Log::endl;
},
D3D12_MESSAGE_CALLBACK_FLAG_NONE, nullptr, &cookie);

Log::get() << "D3D12 SETUP: message callback registered, cookie " << int(cookie) << Log::endl;
}
else
{
Log::get() << "D3D12 SETUP: ID3D12InfoQueue1 unavailable — no callback" << Log::endl;
}
}

DSTORAGE_CONFIGURATION ds_config{};
Expand Down
10 changes: 7 additions & 3 deletions sources/HAL/API/D3D12/HAL.D3D12.Queue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,10 +264,14 @@ namespace HAL
void Queue::execute(const API::CommandList* list)
{
PROFILE(L"Queue::execute");
queued.emplace_back(list->get_native().Get());

// My framegraph system pushes Sync&Access barrier as NONE at the end of the pass so its not possible to use it later, need to redesign it
flush();
// Accumulate lists into one ExecuteCommandLists — no per-list flush.
// Legal because TaskBuilder::link_list_groups chains resource state
// across list boundaries within a group: D3D12 forbids, within one
// scope, accessing a resource after a SyncAfter == SYNC_NONE barrier or
// emitting a SyncBefore == SYNC_NONE barrier once it has been accessed
// (#1417). gpu_wait/signal still flush at real sync points.
queued.emplace_back(list->get_native().Get());
}

void Queue::flush()
Expand Down
19 changes: 13 additions & 6 deletions sources/HAL/API/D3D12/HAL.D3D12.Resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ namespace HAL
return ptr;
}

void Resource::init(Device& device, const ResourceDesc& _desc, const PlacementAddress& address, TextureLayout initialLayout)
void Resource::init(Device& device, const ResourceDesc& _desc, const PlacementAddress& address, TextureLayout initialLayout, vec4 clear_value)
{
auto THIS = static_cast<HAL::Resource*>(this);
auto& desc = THIS->desc;
Expand Down Expand Up @@ -92,11 +92,15 @@ namespace HAL

if (check(desc.Flags & HAL::ResFlags::RenderTarget))
{
// Honour the requested optimized clear value (was hardcoded to
// black, so any ClearRenderTargetView with another colour got
// D3D12 #820 MISMATCHINGCLEARVALUE and took the slow path).
// Defaults to (0,0,0,0), so existing callers are unaffected.
value.Format = to_native(texture_desc.Format.to_srv());
value.Color[0] = 0;
value.Color[1] = 0;
value.Color[2] = 0;
value.Color[3] = 0;
value.Color[0] = clear_value.x;
value.Color[1] = clear_value.y;
value.Color[2] = clear_value.z;
value.Color[3] = clear_value.w;
pass_value = &value;
}

Expand Down Expand Up @@ -226,7 +230,10 @@ namespace HAL
address = { alloc_handle.get_heap().get(),alloc_handle.get_offset() };
}

init(device, desc, address, initialLayout);
// Forward the optimized clear value — it was being dropped here, so the
// parameter plumbed through TextureResource/Resource never reached
// resource creation.
init(device, desc, address, initialLayout, clear_value);
}
Resource::Resource(Device& device, const ResourceDesc& desc, HeapType heap_type, TextureLayout initialLayout, vec4 clear_value) :state_manager(this), tiled_manager(this)
{
Expand Down
5 changes: 4 additions & 1 deletion sources/HAL/API/D3D12/HAL.D3D12.Resource.ixx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,10 @@ export namespace HAL
GPUAddressPtr address;
public:
using ptr = std::shared_ptr<Resource>;
void init(Device& device, const ResourceDesc& desc, const PlacementAddress& address, TextureLayout initialLayout = TextureLayout::UNDEFINED);
// clear_value: optimized clear value baked into a RenderTarget
// resource. Must match the colour passed to ClearRenderTargetView or
// D3D12 warns (#820) and takes a slower clear path.
void init(Device& device, const ResourceDesc& desc, const PlacementAddress& address, TextureLayout initialLayout = TextureLayout::UNDEFINED, vec4 clear_value = vec4(0, 0, 0, 0));

// Replaces the old D3D::Resource constructor; backend-neutral.
void init(const NativeImportHandle& handle, TextureLayout layout, Device& device);
Expand Down
135 changes: 95 additions & 40 deletions sources/HAL/HAL.CommandList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,10 +885,67 @@ namespace HAL
compiler.func_barrier(point);
}

// Open a new batch of `op`, continue the current one if same, or close
// the previous (different class) and open a new one.
void Transitions::begin_op(BarrierSync op)
{
if (HAL::Debug::EnableOpBatching && open_op == op)
return; // same class → keep open
if (open_op != BarrierSync::NONE)
create_usage_point(open_op, false); // close previous batch
create_usage_point(op); // open new batch
open_op = op;
current_batch_id++; // fresh batch id
}

// Close the currently open batch (list end / flush).
void Transitions::close_op()
{
if (open_op == BarrierSync::NONE) return;
create_usage_point(open_op, false);
open_op = BarrierSync::NONE;
}

// Split the open batch when this transition would need a real barrier
// against an earlier op in the same batch (different, non-mergeable
// state on the same resource — write-after-write in another state,
// read-after-write, etc.). Same/mergeable state = no barrier = no
// split, exactly matching the un-batched barrier set. Reads the
// resource's per-list state directly (no parallel map).
void Transitions::batch_hazard_check(const HAL::Resource* resource, ResourceState to)
{
if (open_op == BarrierSync::NONE) return;

auto& cpu = const_cast<HAL::Resource*>(resource)->get_state_manager().get_cpu_state(this);

if (cpu.batch_touch_id == current_batch_id
&& !(cpu.batch_touch_state == to) && !merge_state(cpu.batch_touch_state, to))
{
// Split so this transition (and the ops after it) land in a new
// point positioned after the earlier op — the barrier must run
// between the two conflicting uses. current_batch_id is NOT
// bumped: it is the OP-CLASS batch epoch (advanced only by
// begin_op). A resource touched anywhere in the op-class batch
// must stay detectable across intra-batch splits — otherwise a
// later conflicting use (e.g. SMAA_blend written RT by one draw,
// read SR by the next) would miss its split after an unrelated
// split already occurred, mis-positioning the barrier.
create_usage_point(open_op, false); // close before the hazard
create_usage_point(open_op); // reopen same class after
}

cpu.batch_touch_id = current_batch_id;
cpu.batch_touch_state = to;
}

void Transitions::compile_transitions()
{
if(transitions_compiled)
if(transitions_compiled)
return;

// Close any batch still open at list end so its resources are
// fully recorded before the usage points are walked below.
close_op();
std::set<Resource*> non_tracked_resources;


Expand All @@ -899,8 +956,16 @@ namespace HAL
auto& usage = *u;
auto prev_usage = usage.prev_usage;

// A list-boundary marker bypassed by chain_lists: the
// neighbouring list is in this ExecuteCommandLists group,
// so state is handed over directly and this node must not
// publish a SYNC_NONE barrier (D3D12 #1417). It is also not
// an unsynchronized first touch, so keep it out of
// non_tracked_resources below.
if (usage.suppressed) continue;

// ASSERT(!usage.debug);
if(!prev_usage)
if(!prev_usage)
{
bool is_automatic = check(usage.resource->get_desc().Flags &HAL::ResFlags::DisableStateTracking);
// ASSERT(!);
Expand Down Expand Up @@ -1003,13 +1068,35 @@ namespace HAL
if (!subres_cpu.used) return;
ASSERT(subres_cpu.used);

// A later list in the same ExecuteCommandLists group still
// uses this subresource, and its state was chained across
// the boundary (chain_lists) — releasing it here to
// NO_ACCESS/SYNC_NONE would make it untouchable for the
// rest of the scope (D3D12 #1417). The group's last user
// releases it. Checked per subresource so that mips which
// were NOT carried forward still decay to initial_layout.
if (subres_cpu.skip_end_decay) return;



auto target = ResourceStates::NO_ACCESS;
target.layout = resource->get_state_manager().initial_layout;

auto end_point = subres_cpu.last_usage->point->next_point;

// A bare transition-only list (built with the public
// transition() API, e.g. the upload/promote list) never opened
// an op batch, so close_op()'s open_op==NONE guard skipped the
// trailing point — the last usage sits at the final point with
// no next_point. Append one now (usage_points is a deque, so
// existing UsagePoint* stay valid) so the release lands AFTER
// the last usage. Op-batched lists already have this point.
if (!end_point)
{
create_usage_point(BarrierSync::NONE, false);
end_point = subres_cpu.last_usage->point->next_point;
}


if (resource->debug_transitions)
Log::get() << "TRANSITION" << subres_cpu.last_usage->wanted_state << " " << target << "subres: " << subres << Log::endl;
Expand Down Expand Up @@ -1053,6 +1140,7 @@ namespace HAL
usage.point = point;
usage.last_point = nullptr;
usage.debug = false;
usage.suppressed = false;

HAL::Debug::BarrierBreakpoints::check_usage(resource->name, subres, state);

Expand Down Expand Up @@ -1143,6 +1231,7 @@ namespace HAL
if (resource->get_heap_type() == HeapType::DEFAULT || resource->get_heap_type() == HeapType::RESERVED)
{
use_resource(resource);
batch_hazard_check(resource, target_state);
visit_subres(info, [&](const HAL::Resource::ptr&, UINT subres) {
use_resource(resource);
resource->get_state_manager().transition(this, target_state, subres);
Expand All @@ -1161,44 +1250,6 @@ namespace HAL
}


#ifdef PRETRANSITIONS_FIX
std::shared_ptr<TransitionCommandList> Transitions::fix_pretransitions()
{
PROFILE(L"fix_pretransitions");

HAL::Barriers result(CommandListType::DIRECT);

CommandListType transition_type = type;

{
PROFILE(L"processing resources");


for (auto& r : need_check_transitions)
{
auto res_type = r->get_state_manager().process_transitions(result, this);

transition_type = Merge(transition_type, res_type);
}

}
if (result)
{
PROFILE(L"creating list");
auto cmd = static_cast<CommandList*>(this);


auto transition_list = (static_cast<CommandList*>(this)->get_device().get_queue(transition_type)->get_transition_list());
transition_list->compiler.set_name(L":Transitions");
transition_list->create_transition_list(*cmd->frame_resources, result);
return transition_list;
}
return nullptr;
}
#endif



void Transitions::transition(const Resource::ptr& resource, ResourceState to, UINT subres)
{
transition(resource.get(), to, subres);
Expand Down Expand Up @@ -1228,6 +1279,7 @@ namespace HAL
if (resource->get_heap_type() == HeapType::DEFAULT || resource->get_heap_type() == HeapType::RESERVED)
{
use_resource(resource);
batch_hazard_check(resource, to);
const_cast<Resource*>(resource)->get_state_manager().transition(this, to, subres);
}
}
Expand Down Expand Up @@ -1608,6 +1660,8 @@ namespace HAL
{
transition_count = 0;
pool_used = 0;
open_op = BarrierSync::NONE;
current_batch_id = 0;
tracked_resources.reserve(512);
used_resources.reserve(256);
create_usage_point(BarrierSync::NONE, false);
Expand All @@ -1620,6 +1674,7 @@ namespace HAL
used_resources.clear();
need_check_transitions.clear();
transitions_compiled = false;
open_op = BarrierSync::NONE;
}

void SignatureDataSetter::commit_tables(BarrierSync operation, UsedSlots* slots)
Expand Down
Loading
Loading