vkd3d: Add support for a shader debug ring.
Will allow replaced shaders to emit debug messages to a buffer. Signed-off-by: Hans-Kristian Arntzen <post@arntzen-software.no>
This commit is contained in:
parent
29fe4da015
commit
9d36ab59d6
|
@ -118,6 +118,7 @@ libvkd3d_la_SOURCES = \
|
|||
libs/vkd3d/resource.c \
|
||||
libs/vkd3d/state.c \
|
||||
libs/vkd3d/utils.c \
|
||||
libs/vkd3d/debug_ring.c \
|
||||
libs/vkd3d/vkd3d.map \
|
||||
libs/vkd3d/vkd3d_main.c \
|
||||
libs/vkd3d/vkd3d_private.h \
|
||||
|
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* Copyright 2020 Hans-Kristian Arntzen for Valve Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#ifndef DEBUG_CHANNEL_H_
|
||||
#define DEBUG_CHANNEL_H_
|
||||
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
#extension GL_ARB_gpu_shader_int64 : require
|
||||
#extension GL_KHR_shader_subgroup_basic : require
|
||||
#extension GL_KHR_shader_subgroup_ballot : require
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 4) buffer ControlBlock
|
||||
{
|
||||
uint message_counter;
|
||||
uint instance_counter;
|
||||
};
|
||||
|
||||
layout(buffer_reference, std430, buffer_reference_align = 4) buffer RingBuffer
|
||||
{
|
||||
uint data[];
|
||||
};
|
||||
|
||||
layout(constant_id = 0) const uint64_t DEBUG_SHADER_HASH = 0;
|
||||
layout(constant_id = 1) const uint64_t DEBUG_SHADER_ATOMIC_BDA = 0;
|
||||
layout(constant_id = 2) const uint64_t DEBUG_SHADER_RING_BDA = 0;
|
||||
layout(constant_id = 3) const uint DEBUG_SHADER_RING_SIZE = 0;
|
||||
const uint DEBUG_SHADER_RING_MASK = DEBUG_SHADER_RING_SIZE - 1;
|
||||
const bool DEBUG_SHADER_RING_ACTIVE = DEBUG_SHADER_ATOMIC_BDA != 0;
|
||||
|
||||
const uint DEBUG_CHANNEL_FMT_HEX = 0;
|
||||
const uint DEBUG_CHANNEL_FMT_I32 = 1;
|
||||
const uint DEBUG_CHANNEL_FMT_F32 = 2;
|
||||
const uint DEBUG_CHANNEL_FMT_HEX_ALL = DEBUG_CHANNEL_FMT_HEX * 0x55555555u;
|
||||
const uint DEBUG_CHANNEL_FMT_I32_ALL = DEBUG_CHANNEL_FMT_I32 * 0x55555555u;
|
||||
const uint DEBUG_CHANNEL_FMT_F32_ALL = DEBUG_CHANNEL_FMT_F32 * 0x55555555u;
|
||||
|
||||
uint DEBUG_CHANNEL_INSTANCE_COUNTER;
|
||||
uvec3 DEBUG_CHANNEL_ID;
|
||||
|
||||
void DEBUG_CHANNEL_INIT(uvec3 id)
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
DEBUG_CHANNEL_ID = id;
|
||||
uint inst;
|
||||
if (subgroupElect())
|
||||
inst = atomicAdd(ControlBlock(DEBUG_SHADER_ATOMIC_BDA).instance_counter, 1u);
|
||||
DEBUG_CHANNEL_INSTANCE_COUNTER = subgroupBroadcastFirst(inst);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_WRITE_HEADER(RingBuffer buf, uint offset, uint num_words, uint fmt)
|
||||
{
|
||||
buf.data[(offset + 0) & DEBUG_SHADER_RING_MASK] = num_words;
|
||||
buf.data[(offset + 1) & DEBUG_SHADER_RING_MASK] = uint(DEBUG_SHADER_HASH);
|
||||
buf.data[(offset + 2) & DEBUG_SHADER_RING_MASK] = uint(DEBUG_SHADER_HASH >> 32);
|
||||
buf.data[(offset + 3) & DEBUG_SHADER_RING_MASK] = DEBUG_CHANNEL_INSTANCE_COUNTER;
|
||||
buf.data[(offset + 4) & DEBUG_SHADER_RING_MASK] = DEBUG_CHANNEL_ID.x;
|
||||
buf.data[(offset + 5) & DEBUG_SHADER_RING_MASK] = DEBUG_CHANNEL_ID.y;
|
||||
buf.data[(offset + 6) & DEBUG_SHADER_RING_MASK] = DEBUG_CHANNEL_ID.z;
|
||||
buf.data[(offset + 7) & DEBUG_SHADER_RING_MASK] = fmt;
|
||||
}
|
||||
|
||||
uint DEBUG_CHANNEL_ALLOCATE(uint words)
|
||||
{
|
||||
uint offset = atomicAdd(ControlBlock(DEBUG_SHADER_ATOMIC_BDA).message_counter, words);
|
||||
return offset;
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG_()
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
uint words = 8;
|
||||
uint offset = DEBUG_CHANNEL_ALLOCATE(words);
|
||||
DEBUG_CHANNEL_WRITE_HEADER(RingBuffer(DEBUG_SHADER_RING_BDA), offset, words, 0);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG_(uint fmt, uint v0)
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
RingBuffer buf = RingBuffer(DEBUG_SHADER_RING_BDA);
|
||||
uint words = 9;
|
||||
uint offset = DEBUG_CHANNEL_ALLOCATE(words);
|
||||
DEBUG_CHANNEL_WRITE_HEADER(buf, offset, words, fmt);
|
||||
buf.data[(offset + 8) & DEBUG_SHADER_RING_MASK] = v0;
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG_(uint fmt, uint v0, uint v1)
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
RingBuffer buf = RingBuffer(DEBUG_SHADER_RING_BDA);
|
||||
uint words = 10;
|
||||
uint offset = DEBUG_CHANNEL_ALLOCATE(words);
|
||||
DEBUG_CHANNEL_WRITE_HEADER(buf, offset, words, fmt);
|
||||
buf.data[(offset + 8) & DEBUG_SHADER_RING_MASK] = v0;
|
||||
buf.data[(offset + 9) & DEBUG_SHADER_RING_MASK] = v1;
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG_(uint fmt, uint v0, uint v1, uint v2)
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
RingBuffer buf = RingBuffer(DEBUG_SHADER_RING_BDA);
|
||||
uint words = 11;
|
||||
uint offset = DEBUG_CHANNEL_ALLOCATE(words);
|
||||
DEBUG_CHANNEL_WRITE_HEADER(buf, offset, words, fmt);
|
||||
buf.data[(offset + 8) & DEBUG_SHADER_RING_MASK] = v0;
|
||||
buf.data[(offset + 9) & DEBUG_SHADER_RING_MASK] = v1;
|
||||
buf.data[(offset + 10) & DEBUG_SHADER_RING_MASK] = v2;
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG_(uint fmt, uint v0, uint v1, uint v2, uint v3)
|
||||
{
|
||||
if (!DEBUG_SHADER_RING_ACTIVE)
|
||||
return;
|
||||
RingBuffer buf = RingBuffer(DEBUG_SHADER_RING_BDA);
|
||||
uint words = 12;
|
||||
uint offset = DEBUG_CHANNEL_ALLOCATE(words);
|
||||
DEBUG_CHANNEL_WRITE_HEADER(buf, offset, words, fmt);
|
||||
buf.data[(offset + 8) & DEBUG_SHADER_RING_MASK] = v0;
|
||||
buf.data[(offset + 9) & DEBUG_SHADER_RING_MASK] = v1;
|
||||
buf.data[(offset + 10) & DEBUG_SHADER_RING_MASK] = v2;
|
||||
buf.data[(offset + 11) & DEBUG_SHADER_RING_MASK] = v3;
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG()
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_();
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(uint v0)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_HEX_ALL, v0);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(uint v0, uint v1)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_HEX_ALL, v0, v1);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(uint v0, uint v1, uint v2)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_HEX_ALL, v0, v1, v2);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(uint v0, uint v1, uint v2, uint v3)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_HEX_ALL, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(int v0)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_I32_ALL, v0);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(int v0, int v1)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_I32_ALL, v0, v1);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(int v0, int v1, int v2)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_I32_ALL, v0, v1, v2);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(int v0, int v1, int v2, int v3)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_I32_ALL, v0, v1, v2, v3);
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(float v0)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_F32_ALL, floatBitsToUint(v0));
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(float v0, float v1)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_F32_ALL, floatBitsToUint(v0), floatBitsToUint(v1));
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(float v0, float v1, float v2)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_F32_ALL, floatBitsToUint(v0), floatBitsToUint(v1), floatBitsToUint(v2));
|
||||
}
|
||||
|
||||
void DEBUG_CHANNEL_MSG(float v0, float v1, float v2, float v3)
|
||||
{
|
||||
DEBUG_CHANNEL_MSG_(DEBUG_CHANNEL_FMT_F32_ALL, floatBitsToUint(v0), floatBitsToUint(v1), floatBitsToUint(v2), floatBitsToUint(v3));
|
||||
}
|
||||
|
||||
#endif
|
|
@ -278,6 +278,10 @@ static void vkd3d_wait_for_gpu_timeline_semaphores(struct vkd3d_fence_worker *wo
|
|||
return;
|
||||
}
|
||||
|
||||
/* This is a good time to kick the debug ring thread into action. */
|
||||
if (device->debug_ring.active)
|
||||
pthread_cond_signal(&device->debug_ring.ring_cond);
|
||||
|
||||
for (i = 0, j = 0; i < worker->fence_count; ++i)
|
||||
{
|
||||
struct vkd3d_waiting_fence *current = &worker->fences[i];
|
||||
|
@ -2759,6 +2763,8 @@ static HRESULT STDMETHODCALLTYPE d3d12_command_list_Close(d3d12_command_list_ifa
|
|||
1, &barrier, 0, NULL, 0, NULL));
|
||||
}
|
||||
|
||||
vkd3d_shader_debug_ring_end_command_buffer(list);
|
||||
|
||||
if ((vr = VK_CALL(vkEndCommandBuffer(list->vk_command_buffer))) < 0)
|
||||
{
|
||||
WARN("Failed to end command buffer, vr %d.\n", vr);
|
||||
|
@ -2807,6 +2813,7 @@ static void d3d12_command_list_reset_state(struct d3d12_command_list *list,
|
|||
#else
|
||||
list->debug_capture = false;
|
||||
#endif
|
||||
list->has_replaced_shaders = false;
|
||||
|
||||
list->current_framebuffer = VK_NULL_HANDLE;
|
||||
list->current_pipeline = VK_NULL_HANDLE;
|
||||
|
@ -4753,10 +4760,13 @@ static void STDMETHODCALLTYPE d3d12_command_list_SetPipelineState(d3d12_command_
|
|||
|
||||
TRACE("iface %p, pipeline_state %p.\n", iface, pipeline_state);
|
||||
|
||||
if (TRACE_ON() && state)
|
||||
if ((TRACE_ON() || list->device->debug_ring.active) && state)
|
||||
{
|
||||
if (d3d12_pipeline_state_has_replaced_shaders(state))
|
||||
{
|
||||
TRACE("Binding pipeline state %p which has replaced shader(s)!\n", pipeline_state);
|
||||
list->has_replaced_shaders = true;
|
||||
}
|
||||
|
||||
if (state->vk_bind_point == VK_PIPELINE_BIND_POINT_COMPUTE)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright 2020 Hans-Kristian Arntzen for Valve Corporation
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2.1 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
||||
*/
|
||||
|
||||
#define VKD3D_DBG_CHANNEL VKD3D_DBG_CHANNEL_API
|
||||
|
||||
#include "vkd3d_private.h"
|
||||
#include "vkd3d_debug.h"
|
||||
#include "vkd3d_common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
void vkd3d_shader_debug_ring_init_spec_constant(struct d3d12_device *device,
|
||||
struct vkd3d_shader_debug_ring_spec_info *info, vkd3d_shader_hash_t hash)
|
||||
{
|
||||
info->spec_info.pData = &info->constants;
|
||||
info->spec_info.dataSize = sizeof(info->constants);
|
||||
info->spec_info.pMapEntries = info->map_entries;
|
||||
info->spec_info.mapEntryCount = 4;
|
||||
|
||||
info->constants.hash = hash;
|
||||
info->constants.host_bda = device->debug_ring.ring_device_address;
|
||||
info->constants.atomic_bda = device->debug_ring.atomic_device_address;
|
||||
info->constants.ring_words = device->debug_ring.ring_size / sizeof(uint32_t);
|
||||
|
||||
info->map_entries[0].constantID = 0;
|
||||
info->map_entries[0].offset = offsetof(struct vkd3d_shader_debug_ring_spec_constants, hash);
|
||||
info->map_entries[0].size = sizeof(uint64_t);
|
||||
|
||||
info->map_entries[1].constantID = 1;
|
||||
info->map_entries[1].offset = offsetof(struct vkd3d_shader_debug_ring_spec_constants, atomic_bda);
|
||||
info->map_entries[1].size = sizeof(uint64_t);
|
||||
|
||||
info->map_entries[2].constantID = 2;
|
||||
info->map_entries[2].offset = offsetof(struct vkd3d_shader_debug_ring_spec_constants, host_bda);
|
||||
info->map_entries[2].size = sizeof(uint64_t);
|
||||
|
||||
info->map_entries[3].constantID = 3;
|
||||
info->map_entries[3].offset = offsetof(struct vkd3d_shader_debug_ring_spec_constants, ring_words);
|
||||
info->map_entries[3].size = sizeof(uint32_t);
|
||||
}
|
||||
|
||||
void *vkd3d_shader_debug_ring_thread_main(void *arg)
|
||||
{
|
||||
uint32_t last_counter, new_counter, count, i, j, message_word_count, debug_instance, debug_thread_id[3], fmt;
|
||||
struct vkd3d_shader_debug_ring *ring;
|
||||
struct d3d12_device *device = arg;
|
||||
const uint32_t *ring_counter;
|
||||
const uint32_t *ring_base;
|
||||
char message_buffer[4096];
|
||||
bool is_active = true;
|
||||
uint64_t shader_hash;
|
||||
size_t ring_mask;
|
||||
|
||||
ring = &device->debug_ring;
|
||||
ring_mask = ring->ring_size - 1;
|
||||
ring_counter = ring->mapped;
|
||||
ring_base = ring_counter + (ring->ring_offset / sizeof(uint32_t));
|
||||
last_counter = 0;
|
||||
|
||||
vkd3d_set_thread_name("debug-ring");
|
||||
|
||||
while (is_active)
|
||||
{
|
||||
pthread_mutex_lock(&ring->ring_lock);
|
||||
pthread_cond_wait(&ring->ring_cond, &ring->ring_lock);
|
||||
is_active = ring->active;
|
||||
pthread_mutex_unlock(&ring->ring_lock);
|
||||
|
||||
new_counter = *ring_counter;
|
||||
if (last_counter != new_counter)
|
||||
{
|
||||
count = (new_counter - last_counter) & ring_mask;
|
||||
|
||||
/* Assume that each iteration can safely use 1/4th of the buffer to avoid WAR hazards. */
|
||||
if ((new_counter - last_counter) > (ring->ring_size / 16))
|
||||
{
|
||||
ERR("Debug ring is probably too small (%u new words this iteration), increase size to avoid risk of dropping messages.\n",
|
||||
new_counter - last_counter);
|
||||
}
|
||||
|
||||
for (i = 0; i < count; )
|
||||
{
|
||||
#define READ_RING_WORD(off) ring_base[((off) + i + last_counter) & ring_mask]
|
||||
message_word_count = READ_RING_WORD(0);
|
||||
if (i + message_word_count > count)
|
||||
break;
|
||||
if (message_word_count < 8 || message_word_count > 16 + 8)
|
||||
break;
|
||||
|
||||
shader_hash = (uint64_t)READ_RING_WORD(1) | ((uint64_t)READ_RING_WORD(2) << 32);
|
||||
debug_instance = READ_RING_WORD(3);
|
||||
for (j = 0; j < 3; j++)
|
||||
debug_thread_id[j] = READ_RING_WORD(4 + j);
|
||||
fmt = READ_RING_WORD(7);
|
||||
|
||||
snprintf(message_buffer, sizeof(message_buffer), "Shader: %"PRIx64": Instance %u, ID (%u, %u, %u):",
|
||||
shader_hash, debug_instance,
|
||||
debug_thread_id[0], debug_thread_id[1], debug_thread_id[2]);
|
||||
|
||||
i += 8;
|
||||
message_word_count -= 8;
|
||||
|
||||
for (j = 0; j < message_word_count; j++)
|
||||
{
|
||||
union
|
||||
{
|
||||
float f32;
|
||||
uint32_t u32;
|
||||
int32_t i32;
|
||||
} u;
|
||||
const char *delim;
|
||||
size_t len, avail;
|
||||
u.u32 = READ_RING_WORD(j);
|
||||
|
||||
len = strlen(message_buffer);
|
||||
if (len + 1 >= sizeof(message_buffer))
|
||||
break;
|
||||
avail = sizeof(message_buffer) - len;
|
||||
|
||||
delim = j == 0 ? " " : ", ";
|
||||
|
||||
#define VKD3D_DEBUG_CHANNEL_FMT_HEX 0u
|
||||
#define VKD3D_DEBUG_CHANNEL_FMT_I32 1u
|
||||
#define VKD3D_DEBUG_CHANNEL_FMT_F32 2u
|
||||
switch ((fmt >> (2u * j)) & 3u)
|
||||
{
|
||||
case VKD3D_DEBUG_CHANNEL_FMT_HEX:
|
||||
snprintf(message_buffer + len, avail, "%s#%x", delim, u.u32);
|
||||
break;
|
||||
|
||||
case VKD3D_DEBUG_CHANNEL_FMT_I32:
|
||||
snprintf(message_buffer + len, avail, "%s%d", delim, u.i32);
|
||||
break;
|
||||
|
||||
case VKD3D_DEBUG_CHANNEL_FMT_F32:
|
||||
snprintf(message_buffer + len, avail, "%s%f", delim, u.f32);
|
||||
break;
|
||||
|
||||
default:
|
||||
snprintf(message_buffer + len, avail, "%s????", delim);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ERR("%s\n", message_buffer);
|
||||
|
||||
#undef READ_RING_WORD
|
||||
i += message_word_count;
|
||||
}
|
||||
}
|
||||
last_counter = new_counter;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
HRESULT vkd3d_shader_debug_ring_init(struct vkd3d_shader_debug_ring *ring,
|
||||
struct d3d12_device *device)
|
||||
{
|
||||
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
||||
D3D12_HEAP_PROPERTIES heap_properties;
|
||||
D3D12_RESOURCE_DESC resource_desc;
|
||||
const char *env;
|
||||
|
||||
memset(ring, 0, sizeof(*ring));
|
||||
if (!(env = getenv("VKD3D_SHADER_DEBUG_RING_SIZE_LOG2")))
|
||||
return S_OK;
|
||||
|
||||
ring->active = true;
|
||||
|
||||
ring->ring_size = (size_t)1 << strtoul(env, NULL, 0);
|
||||
// Reserve 4k to be used as a control block of some sort.
|
||||
ring->ring_offset = 4096;
|
||||
|
||||
WARN("Enabling shader debug ring of size: %zu.\n", ring->ring_size);
|
||||
|
||||
if (!device->device_info.buffer_device_address_features.bufferDeviceAddress)
|
||||
{
|
||||
ERR("Buffer device address must be supported to use VKD3D_SHADER_DEBUG_RING feature.\n");
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
memset(&heap_properties, 0, sizeof(heap_properties));
|
||||
heap_properties.CPUPageProperty = D3D12_CPU_PAGE_PROPERTY_WRITE_BACK;
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_CUSTOM;
|
||||
heap_properties.MemoryPoolPreference = D3D12_MEMORY_POOL_L0;
|
||||
|
||||
memset(&resource_desc, 0, sizeof(resource_desc));
|
||||
resource_desc.Width = ring->ring_offset + ring->ring_size;
|
||||
resource_desc.Height = 1;
|
||||
resource_desc.DepthOrArraySize = 1;
|
||||
resource_desc.MipLevels = 1;
|
||||
resource_desc.Format = DXGI_FORMAT_UNKNOWN;
|
||||
resource_desc.SampleDesc.Count = 1;
|
||||
resource_desc.SampleDesc.Quality = 0;
|
||||
resource_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
resource_desc.Flags = D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS;
|
||||
|
||||
if (FAILED(vkd3d_create_buffer(device, &heap_properties, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
|
||||
&resource_desc, &ring->host_buffer)))
|
||||
goto err_free_buffers;
|
||||
|
||||
if (FAILED(vkd3d_allocate_buffer_memory(device, ring->host_buffer, NULL,
|
||||
&heap_properties, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, &ring->host_buffer_memory, NULL, NULL)))
|
||||
goto err_free_buffers;
|
||||
|
||||
ring->ring_device_address = vkd3d_get_buffer_device_address(device, ring->host_buffer) + ring->ring_offset;
|
||||
|
||||
resource_desc.Width = ring->ring_offset;
|
||||
memset(&heap_properties, 0, sizeof(heap_properties));
|
||||
heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
|
||||
if (FAILED(vkd3d_create_buffer(device, &heap_properties, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS,
|
||||
&resource_desc, &ring->device_atomic_buffer)))
|
||||
goto err_free_buffers;
|
||||
|
||||
if (FAILED(vkd3d_allocate_buffer_memory(device, ring->device_atomic_buffer, NULL,
|
||||
&heap_properties, D3D12_HEAP_FLAG_ALLOW_ONLY_BUFFERS, &ring->device_atomic_buffer_memory, NULL, NULL)))
|
||||
goto err_free_buffers;
|
||||
|
||||
if (VK_CALL(vkMapMemory(device->vk_device, ring->host_buffer_memory, 0, VK_WHOLE_SIZE, 0, &ring->mapped)) != VK_SUCCESS)
|
||||
goto err_free_buffers;
|
||||
|
||||
ring->atomic_device_address = vkd3d_get_buffer_device_address(device, ring->device_atomic_buffer);
|
||||
|
||||
if (pthread_mutex_init(&ring->ring_lock, NULL) != 0)
|
||||
goto err_free_buffers;
|
||||
if (pthread_cond_init(&ring->ring_cond, NULL) != 0)
|
||||
goto err_destroy_mutex;
|
||||
|
||||
if (pthread_create(&ring->ring_thread, NULL, vkd3d_shader_debug_ring_thread_main, device) != 0)
|
||||
{
|
||||
ERR("Failed to create ring thread.\n");
|
||||
goto err_destroy_cond;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
err_destroy_mutex:
|
||||
pthread_mutex_destroy(&ring->ring_lock);
|
||||
err_destroy_cond:
|
||||
pthread_cond_destroy(&ring->ring_cond);
|
||||
err_free_buffers:
|
||||
VK_CALL(vkDestroyBuffer(device->vk_device, ring->host_buffer, NULL));
|
||||
VK_CALL(vkDestroyBuffer(device->vk_device, ring->device_atomic_buffer, NULL));
|
||||
VK_CALL(vkFreeMemory(device->vk_device, ring->host_buffer_memory, NULL));
|
||||
VK_CALL(vkFreeMemory(device->vk_device, ring->device_atomic_buffer_memory, NULL));
|
||||
memset(ring, 0, sizeof(*ring));
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
void vkd3d_shader_debug_ring_cleanup(struct vkd3d_shader_debug_ring *ring,
|
||||
struct d3d12_device *device)
|
||||
{
|
||||
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
||||
if (!ring->active)
|
||||
return;
|
||||
|
||||
pthread_mutex_lock(&ring->ring_lock);
|
||||
ring->active = false;
|
||||
pthread_cond_signal(&ring->ring_cond);
|
||||
pthread_mutex_unlock(&ring->ring_lock);
|
||||
pthread_join(ring->ring_thread, NULL);
|
||||
pthread_mutex_destroy(&ring->ring_lock);
|
||||
pthread_cond_destroy(&ring->ring_cond);
|
||||
|
||||
VK_CALL(vkDestroyBuffer(device->vk_device, ring->host_buffer, NULL));
|
||||
VK_CALL(vkDestroyBuffer(device->vk_device, ring->device_atomic_buffer, NULL));
|
||||
VK_CALL(vkFreeMemory(device->vk_device, ring->host_buffer_memory, NULL));
|
||||
VK_CALL(vkFreeMemory(device->vk_device, ring->device_atomic_buffer_memory, NULL));
|
||||
}
|
||||
|
||||
void vkd3d_shader_debug_ring_end_command_buffer(struct d3d12_command_list *list)
|
||||
{
|
||||
const struct vkd3d_vk_device_procs *vk_procs = &list->device->vk_procs;
|
||||
VkBufferCopy buffer_copy;
|
||||
VkMemoryBarrier barrier;
|
||||
|
||||
if (list->device->debug_ring.active &&
|
||||
list->has_replaced_shaders &&
|
||||
(list->type == D3D12_COMMAND_LIST_TYPE_DIRECT || list->type == D3D12_COMMAND_LIST_TYPE_COMPUTE))
|
||||
{
|
||||
barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||
barrier.pNext = NULL;
|
||||
barrier.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
|
||||
|
||||
VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
|
||||
VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
1, &barrier, 0, NULL, 0, NULL));
|
||||
|
||||
buffer_copy.size = list->device->debug_ring.ring_offset;
|
||||
buffer_copy.dstOffset = 0;
|
||||
buffer_copy.srcOffset = 0;
|
||||
|
||||
VK_CALL(vkCmdCopyBuffer(list->vk_command_buffer,
|
||||
list->device->debug_ring.device_atomic_buffer,
|
||||
list->device->debug_ring.host_buffer,
|
||||
1, &buffer_copy));
|
||||
|
||||
barrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
barrier.dstAccessMask = VK_ACCESS_HOST_READ_BIT;
|
||||
VK_CALL(vkCmdPipelineBarrier(list->vk_command_buffer,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, 0,
|
||||
1, &barrier, 0, NULL, 0, NULL));
|
||||
}
|
||||
}
|
|
@ -2204,6 +2204,7 @@ static void d3d12_device_destroy(struct d3d12_device *device)
|
|||
vkd3d_private_store_destroy(&device->private_store);
|
||||
|
||||
vkd3d_cleanup_format_info(device);
|
||||
vkd3d_shader_debug_ring_cleanup(&device->debug_ring, device);
|
||||
vkd3d_sampler_state_cleanup(&device->sampler_state, device);
|
||||
vkd3d_view_map_destroy(&device->sampler_map, device);
|
||||
vkd3d_meta_ops_cleanup(&device->meta_ops, device);
|
||||
|
@ -4582,6 +4583,9 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
|
|||
if (FAILED(hr = vkd3d_sampler_state_init(&device->sampler_state, device)))
|
||||
goto out_cleanup_view_map;
|
||||
|
||||
if (FAILED(hr = vkd3d_shader_debug_ring_init(&device->debug_ring, device)))
|
||||
goto out_cleanup_sampler_state;
|
||||
|
||||
vkd3d_render_pass_cache_init(&device->render_pass_cache);
|
||||
vkd3d_gpu_va_allocator_init(&device->gpu_va_allocator);
|
||||
|
||||
|
@ -4591,6 +4595,8 @@ static HRESULT d3d12_device_init(struct d3d12_device *device,
|
|||
d3d12_device_caps_init(device);
|
||||
return S_OK;
|
||||
|
||||
out_cleanup_sampler_state:
|
||||
vkd3d_sampler_state_cleanup(&device->sampler_state, device);
|
||||
out_cleanup_view_map:
|
||||
vkd3d_view_map_destroy(&device->sampler_map, device);
|
||||
out_cleanup_meta_ops:
|
||||
|
|
|
@ -28,6 +28,7 @@ vkd3d_src = [
|
|||
'resource.c',
|
||||
'state.c',
|
||||
'utils.c',
|
||||
'debug_ring.c',
|
||||
'vkd3d_main.c',
|
||||
]
|
||||
|
||||
|
|
|
@ -4204,7 +4204,7 @@ static unsigned int vkd3d_view_flags_from_d3d12_buffer_uav_flags(D3D12_BUFFER_UA
|
|||
return 0;
|
||||
}
|
||||
|
||||
static VkDeviceAddress vkd3d_get_buffer_device_address(struct d3d12_device *device, VkBuffer vk_buffer)
|
||||
VkDeviceAddress vkd3d_get_buffer_device_address(struct d3d12_device *device, VkBuffer vk_buffer)
|
||||
{
|
||||
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
||||
|
||||
|
|
|
@ -1601,6 +1601,7 @@ static HRESULT vkd3d_create_compute_pipeline(struct d3d12_device *device,
|
|||
struct vkd3d_shader_meta *meta)
|
||||
{
|
||||
const struct vkd3d_vk_device_procs *vk_procs = &device->vk_procs;
|
||||
struct vkd3d_shader_debug_ring_spec_info spec_info;
|
||||
VkComputePipelineCreateInfo pipeline_info;
|
||||
VkResult vr;
|
||||
HRESULT hr;
|
||||
|
@ -1615,6 +1616,12 @@ static HRESULT vkd3d_create_compute_pipeline(struct d3d12_device *device,
|
|||
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
pipeline_info.basePipelineIndex = -1;
|
||||
|
||||
if (meta->replaced && device->debug_ring.active)
|
||||
{
|
||||
vkd3d_shader_debug_ring_init_spec_constant(device, &spec_info, meta->hash);
|
||||
pipeline_info.stage.pSpecializationInfo = &spec_info.spec_info;
|
||||
}
|
||||
|
||||
vr = VK_CALL(vkCreateComputePipelines(device->vk_device,
|
||||
vk_cache, 1, &pipeline_info, NULL, vk_pipeline));
|
||||
VK_CALL(vkDestroyShaderModule(device->vk_device, pipeline_info.stage.module, NULL));
|
||||
|
@ -2540,6 +2547,14 @@ static HRESULT d3d12_pipeline_state_init_graphics(struct d3d12_pipeline_state *s
|
|||
shader_stages[i].stage, b, &shader_interface, compile_args, &graphics->stage_meta[graphics->stage_count])))
|
||||
goto fail;
|
||||
|
||||
if (graphics->stage_meta[graphics->stage_count].replaced && device->debug_ring.active)
|
||||
{
|
||||
vkd3d_shader_debug_ring_init_spec_constant(device,
|
||||
&graphics->spec_info[graphics->stage_count],
|
||||
graphics->stage_meta[graphics->stage_count].hash);
|
||||
graphics->stages[graphics->stage_count].pSpecializationInfo = &graphics->spec_info[graphics->stage_count].spec_info;
|
||||
}
|
||||
|
||||
++graphics->stage_count;
|
||||
}
|
||||
|
||||
|
|
|
@ -924,8 +924,24 @@ enum vkd3d_dynamic_state_flag
|
|||
VKD3D_DYNAMIC_STATE_VERTEX_BUFFER_STRIDE = (1 << 9),
|
||||
};
|
||||
|
||||
struct vkd3d_shader_debug_ring_spec_constants
|
||||
{
|
||||
uint64_t hash;
|
||||
uint64_t atomic_bda;
|
||||
uint64_t host_bda;
|
||||
uint32_t ring_words;
|
||||
};
|
||||
|
||||
struct vkd3d_shader_debug_ring_spec_info
|
||||
{
|
||||
struct vkd3d_shader_debug_ring_spec_constants constants;
|
||||
VkSpecializationMapEntry map_entries[4];
|
||||
VkSpecializationInfo spec_info;
|
||||
};
|
||||
|
||||
struct d3d12_graphics_pipeline_state
|
||||
{
|
||||
struct vkd3d_shader_debug_ring_spec_info spec_info[VKD3D_MAX_SHADER_STAGES];
|
||||
VkPipelineShaderStageCreateInfo stages[VKD3D_MAX_SHADER_STAGES];
|
||||
struct vkd3d_shader_meta stage_meta[VKD3D_MAX_SHADER_STAGES];
|
||||
size_t stage_count;
|
||||
|
@ -1261,6 +1277,7 @@ struct d3d12_command_list
|
|||
bool is_valid;
|
||||
bool need_host_barrier;
|
||||
bool debug_capture;
|
||||
bool has_replaced_shaders;
|
||||
VkCommandBuffer vk_command_buffer;
|
||||
|
||||
DXGI_FORMAT index_buffer_format;
|
||||
|
@ -1479,6 +1496,26 @@ struct vkd3d_sampler_state
|
|||
size_t vk_descriptor_pool_count;
|
||||
};
|
||||
|
||||
struct vkd3d_shader_debug_ring
|
||||
{
|
||||
VkBuffer host_buffer;
|
||||
VkBuffer device_atomic_buffer;
|
||||
|
||||
VkDeviceMemory host_buffer_memory;
|
||||
VkDeviceMemory device_atomic_buffer_memory;
|
||||
|
||||
void *mapped;
|
||||
VkDeviceAddress ring_device_address;
|
||||
VkDeviceAddress atomic_device_address;
|
||||
size_t ring_size;
|
||||
size_t ring_offset;
|
||||
|
||||
pthread_t ring_thread;
|
||||
pthread_mutex_t ring_lock;
|
||||
pthread_cond_t ring_cond;
|
||||
bool active;
|
||||
};
|
||||
|
||||
HRESULT vkd3d_sampler_state_init(struct vkd3d_sampler_state *state,
|
||||
struct d3d12_device *device) DECLSPEC_HIDDEN;
|
||||
void vkd3d_sampler_state_cleanup(struct vkd3d_sampler_state *state,
|
||||
|
@ -1491,6 +1528,15 @@ HRESULT vkd3d_sampler_state_allocate_descriptor_set(struct vkd3d_sampler_state *
|
|||
void vkd3d_sampler_state_free_descriptor_set(struct vkd3d_sampler_state *state,
|
||||
struct d3d12_device *device, VkDescriptorSet vk_set, VkDescriptorPool vk_pool) DECLSPEC_HIDDEN;
|
||||
|
||||
HRESULT vkd3d_shader_debug_ring_init(struct vkd3d_shader_debug_ring *state,
|
||||
struct d3d12_device *device) DECLSPEC_HIDDEN;
|
||||
void vkd3d_shader_debug_ring_cleanup(struct vkd3d_shader_debug_ring *state,
|
||||
struct d3d12_device *device) DECLSPEC_HIDDEN;
|
||||
void *vkd3d_shader_debug_ring_thread_main(void *arg) DECLSPEC_HIDDEN;
|
||||
void vkd3d_shader_debug_ring_init_spec_constant(struct d3d12_device *device,
|
||||
struct vkd3d_shader_debug_ring_spec_info *info, vkd3d_shader_hash_t hash) DECLSPEC_HIDDEN;
|
||||
void vkd3d_shader_debug_ring_end_command_buffer(struct d3d12_command_list *list) DECLSPEC_HIDDEN;
|
||||
|
||||
/* NULL resources */
|
||||
struct vkd3d_null_resources
|
||||
{
|
||||
|
@ -1827,6 +1873,7 @@ struct d3d12_device
|
|||
struct vkd3d_meta_ops meta_ops;
|
||||
struct vkd3d_view_map sampler_map;
|
||||
struct vkd3d_sampler_state sampler_state;
|
||||
struct vkd3d_shader_debug_ring debug_ring;
|
||||
};
|
||||
|
||||
HRESULT d3d12_device_create(struct vkd3d_instance *instance,
|
||||
|
@ -2055,6 +2102,8 @@ static inline void vk_prepend_struct(void *header, void *structure)
|
|||
vk_header->pNext = vk_structure;
|
||||
}
|
||||
|
||||
VkDeviceAddress vkd3d_get_buffer_device_address(struct d3d12_device *device, VkBuffer vk_buffer) DECLSPEC_HIDDEN;
|
||||
|
||||
#define VKD3D_NULL_BUFFER_SIZE 16
|
||||
|
||||
#endif /* __VKD3D_PRIVATE_H */
|
||||
|
|
Loading…
Reference in New Issue