<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Sun, Nov 27, 2016 at 7:23 PM, Ilia Mirkin <span dir="ltr"><<a href="mailto:imirkin@alum.mit.edu" target="_blank">imirkin@alum.mit.edu</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">The strategy is to just keep n anv_query_pool_slot entries per query<br>
instead of one. The available bit is only valid in the last one.<br>
<br>
Signed-off-by: Ilia Mirkin <<a href="mailto:imirkin@alum.mit.edu">imirkin@alum.mit.edu</a>><br>
---<br>
<br>
I think this is in a pretty good state now. I've tested both the direct and<br>
buffer paths with a hacked up cube application, and I'm seeing non-ridiculous<br>
values for the various counters, although I haven't 100% verified them for<br>
accuracy.<br>
<br>
This also implements the hsw/bdw workaround for dividing frag invocations by 4,<br>
copied from hsw_queryobj. I tested this on SKL and it seem to divide the values<br>
as expected.<br>
<br>
The cube patch I've been testing with is at <a href="http://paste.debian.net/899374/" rel="noreferrer" target="_blank">http://paste.debian.net/<wbr>899374/</a><br>
You can flip between copying to a buffer and explicit retrieval by commenting<br>
out the relevant function calls.<br>
<br>
src/intel/vulkan/anv_device.c | 2 +-<br>
src/intel/vulkan/anv_private.h | 4 +<br>
src/intel/vulkan/anv_query.c | 99 ++++++++++----<br>
src/intel/vulkan/genX_cmd_<wbr>buffer.c | 260 ++++++++++++++++++++++++++++++<wbr>++-----<br>
4 files changed, 308 insertions(+), 57 deletions(-)<br></blockquote><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<br>
diff --git a/src/intel/vulkan/anv_device.<wbr>c b/src/intel/vulkan/anv_device.<wbr>c<br>
index 99eb73c..7ad1970 100644<br>
--- a/src/intel/vulkan/anv_device.<wbr>c<br>
+++ b/src/intel/vulkan/anv_device.<wbr>c<br>
@@ -427,7 +427,7 @@ void anv_GetPhysicalDeviceFeatures(<br>
.textureCompressionASTC_LDR = pdevice->info.gen >= 9, /* FINISHME CHV */<br>
.textureCompressionBC = true,<br>
.occlusionQueryPrecise = true,<br>
- .pipelineStatisticsQuery = false,<br>
+ .pipelineStatisticsQuery = true,<br>
.fragmentStoresAndAtomics = true,<br>
.<wbr>shaderTessellationAndGeometryP<wbr>ointSize = true,<br>
.shaderImageGatherExtended = false,<br>
diff --git a/src/intel/vulkan/anv_<wbr>private.h b/src/intel/vulkan/anv_<wbr>private.h<br>
index 2fc543d..7271609 100644<br>
--- a/src/intel/vulkan/anv_<wbr>private.h<br>
+++ b/src/intel/vulkan/anv_<wbr>private.h<br>
@@ -1763,6 +1763,8 @@ struct anv_render_pass {<br>
struct anv_subpass subpasses[0];<br>
};<br>
<br>
+#define ANV_PIPELINE_STATISTICS_COUNT 11<br>
+<br>
struct anv_query_pool_slot {<br>
uint64_t begin;<br>
uint64_t end;<br>
@@ -1772,6 +1774,8 @@ struct anv_query_pool_slot {<br>
struct anv_query_pool {<br>
VkQueryType type;<br>
uint32_t slots;<br>
+ uint32_t pipeline_statistics;<br>
+ uint32_t slot_stride;<br>
struct anv_bo bo;<br>
};<br>
<br>
diff --git a/src/intel/vulkan/anv_query.c b/src/intel/vulkan/anv_query.c<br>
index 293257b..dc00859 100644<br>
--- a/src/intel/vulkan/anv_query.c<br>
+++ b/src/intel/vulkan/anv_query.c<br>
@@ -38,8 +38,10 @@ VkResult anv_CreateQueryPool(<br>
ANV_FROM_HANDLE(anv_device, device, _device);<br>
struct anv_query_pool *pool;<br>
VkResult result;<br>
- uint32_t slot_size;<br>
- uint64_t size;<br>
+ uint32_t slot_size = sizeof(struct anv_query_pool_slot);<br>
+ uint32_t slot_stride = 1;<br>
+ uint64_t size = pCreateInfo->queryCount * slot_size;<br>
+ uint32_t pipeline_statistics = 0;<br>
<br>
assert(pCreateInfo->sType == VK_STRUCTURE_TYPE_QUERY_POOL_<wbr>CREATE_INFO);<br>
<br>
@@ -48,12 +50,16 @@ VkResult anv_CreateQueryPool(<br>
case VK_QUERY_TYPE_TIMESTAMP:<br>
break;<br>
case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS:<br>
- return VK_ERROR_INCOMPATIBLE_DRIVER;<br>
+ pipeline_statistics = pCreateInfo-><wbr>pipelineStatistics &<br>
+ ((1 << ANV_PIPELINE_STATISTICS_COUNT) - 1);<br>
+ slot_stride = _mesa_bitcount(pipeline_<wbr>statistics);<br>
+ size *= slot_stride;<br>
+ break;<br>
default:<br>
assert(!"Invalid query type");<br>
+ return VK_ERROR_INCOMPATIBLE_DRIVER;<br>
}<br>
<br>
- slot_size = sizeof(struct anv_query_pool_slot);<br>
pool = vk_alloc2(&device->alloc, pAllocator, sizeof(*pool), 8,<br>
VK_SYSTEM_ALLOCATION_SCOPE_<wbr>OBJECT);<br>
if (pool == NULL)<br>
@@ -61,8 +67,9 @@ VkResult anv_CreateQueryPool(<br>
<br>
pool->type = pCreateInfo->queryType;<br>
pool->slots = pCreateInfo->queryCount;<br>
+ pool->pipeline_statistics = pipeline_statistics;<br>
+ pool->slot_stride = slot_stride;<br>
<br>
- size = pCreateInfo->queryCount * slot_size;<br>
result = anv_bo_init_new(&pool->bo, device, size);<br>
if (result != VK_SUCCESS)<br>
goto fail;<br>
@@ -95,6 +102,27 @@ void anv_DestroyQueryPool(<br>
vk_free2(&device->alloc, pAllocator, pool);<br>
}<br>
<br>
+static void *<br>
+store_query_result(void *pData, VkQueryResultFlags flags,<br>
+ uint64_t result, uint64_t available)<br>
+{<br>
+ if (flags & VK_QUERY_RESULT_64_BIT) {<br>
+ uint64_t *dst = pData;<br>
+ *dst++ = result;<br>
+ if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT)<br>
+ *dst++ = available;<br>
+ return dst;<br>
+ } else {<br>
+ uint32_t *dst = pData;<br>
+ if (result > UINT32_MAX)<br>
+ result = UINT32_MAX;<br>
+ *dst++ = result;<br>
+ if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT)<br>
+ *dst++ = available;<br>
+ return dst;<br>
+ }<br>
+}<br>
+<br>
VkResult anv_GetQueryPoolResults(<br>
VkDevice _device,<br>
VkQueryPool queryPool,<br>
@@ -112,6 +140,7 @@ VkResult anv_GetQueryPoolResults(<br>
int ret;<br>
<br>
assert(pool->type == VK_QUERY_TYPE_OCCLUSION ||<br>
+ pool->type == VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS ||<br>
pool->type == VK_QUERY_TYPE_TIMESTAMP);<br>
<br>
if (pData == NULL)<br>
@@ -129,14 +158,42 @@ VkResult anv_GetQueryPoolResults(<br>
void *data_end = pData + dataSize;<br>
struct anv_query_pool_slot *slot = pool->bo.map;<br>
<br>
- for (uint32_t i = 0; i < queryCount; i++) {<br>
+ for (uint32_t i = 0; i < queryCount && pData < data_end;<br>
+ i++, pData += stride) {<br>
+ if (pool->type == VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS) {<br>
+ VkQueryResultFlags f = flags & ~VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT;<br>
+ void *pos = pData;<br>
+ uint32_t pipeline_statistics = pool->pipeline_statistics;<br>
+ struct anv_query_pool_slot *base =<br>
+ &slot[(firstQuery + i) * pool->slot_stride];<br>
+<br>
+ while (pipeline_statistics) {<br>
+ uint32_t stat = u_bit_scan(&pipeline_<wbr>statistics);<br>
+ uint64_t result = base->end - base->begin;<br>
+<br>
+ /* WaDividePSInvocationCountBy4:<wbr>HSW,BDW */<br>
+ if ((device->info.gen == 8 || device->info.is_haswell) &&<br>
+ (1 << stat) == VK_QUERY_PIPELINE_STATISTIC_<wbr>FRAGMENT_SHADER_INVOCATIONS_<wbr>BIT)<br>
+ result >>= 2;<br>
+<br>
+ pos = store_query_result(pos, f, result, 0);<br>
+ base++;<br>
+ }<br>
+ if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT) {<br>
+ base--;<br>
+ if (flags & VK_QUERY_RESULT_64_BIT)<br>
+ *(uint64_t *)pos = base->available;<br>
+ else<br>
+ *(uint32_t *)pos = base->available;<br>
+ }<br>
+ continue;<br>
+ }<br>
+<br>
switch (pool->type) {<br>
case VK_QUERY_TYPE_OCCLUSION: {<br>
result = slot[firstQuery + i].end - slot[firstQuery + i].begin;<br>
break;<br>
}<br>
- case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS:<br>
- unreachable("pipeline stats not supported");<br>
case VK_QUERY_TYPE_TIMESTAMP: {<br>
result = slot[firstQuery + i].begin;<br>
break;<br>
@@ -145,23 +202,7 @@ VkResult anv_GetQueryPoolResults(<br>
unreachable("invalid pool type");<br>
}<br>
<br>
- if (flags & VK_QUERY_RESULT_64_BIT) {<br>
- uint64_t *dst = pData;<br>
- dst[0] = result;<br>
- if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT)<br>
- dst[1] = slot[firstQuery + i].available;<br>
- } else {<br>
- uint32_t *dst = pData;<br>
- if (result > UINT32_MAX)<br>
- result = UINT32_MAX;<br>
- dst[0] = result;<br>
- if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT)<br>
- dst[1] = slot[firstQuery + i].available;<br>
- }<br>
-<br>
- pData += stride;<br>
- if (pData >= data_end)<br>
- break;<br>
+ store_query_result(pData, flags, result, slot[firstQuery + i].available);<br>
}<br>
<br>
return VK_SUCCESS;<br>
@@ -183,6 +224,14 @@ void anv_CmdResetQueryPool(<br>
slot[firstQuery + i].available = 0;<br>
break;<br>
}<br>
+ case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS: {<br>
+ struct anv_query_pool_slot *slot = pool->bo.map;<br>
+<br>
+ slot = &slot[(firstQuery + i) * pool->slot_stride];<br>
+ for (uint32_t j = 0; j < pool->slot_stride; j++)<br>
+ slot[j].available = 0;<br>
+ break;<br>
+ }<br>
default:<br>
assert(!"Invalid query type");<br>
}<br>
diff --git a/src/intel/vulkan/genX_cmd_<wbr>buffer.c b/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
index a965cd6..1369ac2 100644<br>
--- a/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
+++ b/src/intel/vulkan/genX_cmd_<wbr>buffer.c<br>
@@ -2272,6 +2272,50 @@ emit_query_availability(struct anv_cmd_buffer *cmd_buffer,<br>
}<br>
}<br>
<br>
+#define IA_VERTICES_COUNT 0x2310<br>
+#define IA_PRIMITIVES_COUNT 0x2318<br>
+#define VS_INVOCATION_COUNT 0x2320<br>
+#define HS_INVOCATION_COUNT 0x2300<br>
+#define DS_INVOCATION_COUNT 0x2308<br>
+#define GS_INVOCATION_COUNT 0x2328<br>
+#define GS_PRIMITIVES_COUNT 0x2330<br>
+#define CL_INVOCATION_COUNT 0x2338<br>
+#define CL_PRIMITIVES_COUNT 0x2340<br>
+#define PS_INVOCATION_COUNT 0x2348<br>
+#define CS_INVOCATION_COUNT 0x2290<br>
+<br>
+static const uint32_t PIPELINE_STAT_TO_REG[] = {<br>
+ IA_VERTICES_COUNT,<br>
+ IA_PRIMITIVES_COUNT,<br>
+ VS_INVOCATION_COUNT,<br>
+ GS_INVOCATION_COUNT,<br>
+ GS_PRIMITIVES_COUNT,<br>
+ CL_INVOCATION_COUNT,<br>
+ CL_PRIMITIVES_COUNT,<br>
+ PS_INVOCATION_COUNT,<br>
+ HS_INVOCATION_COUNT,<br>
+ DS_INVOCATION_COUNT,<br>
+ CS_INVOCATION_COUNT<br>
+};<br>
+<br>
+static void<br>
+emit_pipeline_stat(struct anv_cmd_buffer *cmd_buffer, uint32_t stat,<br>
+ struct anv_bo *bo, uint32_t offset) {<br>
+ STATIC_ASSERT(ARRAY_SIZE(<wbr>PIPELINE_STAT_TO_REG) ==<br>
+ ANV_PIPELINE_STATISTICS_COUNT)<wbr>;<br>
+<br>
+ uint32_t reg = PIPELINE_STAT_TO_REG[stat];<br>
+<br>
+ anv_batch_emit(&cmd_buffer-><wbr>batch, GENX(MI_STORE_REGISTER_MEM), lrm) {<br>
+ lrm.RegisterAddress = reg,<br>
+ lrm.MemoryAddress = (struct anv_address) { bo, offset };<br>
+ }<br>
+ anv_batch_emit(&cmd_buffer-><wbr>batch, GENX(MI_STORE_REGISTER_MEM), lrm) {<br>
+ lrm.RegisterAddress = reg + 4,<br>
+ lrm.MemoryAddress = (struct anv_address) { bo, offset + 4 };<br>
+ }<br>
+}<br>
+<br>
void genX(CmdBeginQuery)(<br>
VkCommandBuffer commandBuffer,<br>
VkQueryPool queryPool,<br>
@@ -2301,7 +2345,25 @@ void genX(CmdBeginQuery)(<br>
query * sizeof(struct anv_query_pool_slot));<br>
break;<br>
<br>
- case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS:<br>
+ case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS: {<br>
+ uint32_t pipeline_statistics = pool->pipeline_statistics;<br>
+ uint32_t slot_offset = query * pool->slot_stride *<br>
+ sizeof(struct anv_query_pool_slot);<br>
+<br>
+ /* TODO: This might only be necessary for certain stats */<br>
+ anv_batch_emit(&cmd_buffer-><wbr>batch, GENX(PIPE_CONTROL), pc) {<br>
+ pc.CommandStreamerStallEnable = true;<br>
+ pc.StallAtPixelScoreboard = true;<br>
+ }<br></blockquote><div><br></div><div>I couldn't find a reference for needing this for workarounds, though I saw the workaround Ben implemented for Gen9 + GT4 using a CS stall when querying timestamps, which is handled separately.<br><br></div><div>Depending on how strictly we consider that the queries should only measure the commands they bracket then I think some stalling will be necessary to serialize the work associated with a query and defer reading the end state until after the relevant stages have completed their work.<br><br></div><div>We aren't very precise about this in GL currently, but in Begin maybe we should stall until everything >= the statistic-stage is idle and in End stall until everything <= the statistic-stage is idle before reading (where 'statistic-stage' here is the pipeline stage associated with the pipeline statistic being queried (or respectively the min/max stage for a set)).<br></div><div><br></div><div>For reference in my implementation of INTEL_performance_query facing this same question, I'm currently just stalling before and after queries:<br><br><a href="https://github.com/rib/mesa/blob/wip/rib/oa-next/src/mesa/drivers/dri/i965/brw_performance_query.c#L994">https://github.com/rib/mesa/blob/wip/rib/oa-next/src/mesa/drivers/dri/i965/brw_performance_query.c#L994</a><br><a href="https://github.com/rib/mesa/blob/wip/rib/oa-next/src/mesa/drivers/dri/i965/brw_performance_query.c#L1136">https://github.com/rib/mesa/blob/wip/rib/oa-next/src/mesa/drivers/dri/i965/brw_performance_query.c#L1136</a> <br><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
+<br>
+ while (pipeline_statistics) {<br>
+ uint32_t stat = u_bit_scan(&pipeline_<wbr>statistics);<br>
+<br>
+ emit_pipeline_stat(cmd_buffer, stat, &pool->bo, slot_offset);<br>
+ slot_offset += sizeof(struct anv_query_pool_slot);<br>
+ }<br>
+ break;<br>
+ }<br>
default:<br>
unreachable("");<br>
}<br>
@@ -2314,17 +2376,35 @@ void genX(CmdEndQuery)(<br>
{<br>
ANV_FROM_HANDLE(anv_cmd_<wbr>buffer, cmd_buffer, commandBuffer);<br>
ANV_FROM_HANDLE(anv_query_<wbr>pool, pool, queryPool);<br>
+ uint32_t slot_offset = query * pool->slot_stride *<br>
+ sizeof(struct anv_query_pool_slot);<br>
<br>
switch (pool->type) {<br>
case VK_QUERY_TYPE_OCCLUSION:<br>
- emit_ps_depth_count(cmd_<wbr>buffer, &pool->bo,<br>
- query * sizeof(struct anv_query_pool_slot) + 8);<br>
+ emit_ps_depth_count(cmd_<wbr>buffer, &pool->bo, slot_offset + 8);<br>
+ emit_query_availability(cmd_<wbr>buffer, &pool->bo, slot_offset + 16);<br>
+ break;<br>
+<br>
+ case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS: {<br>
+ uint32_t pipeline_statistics = pool->pipeline_statistics;<br>
+ /* TODO: This might only be necessary for certain stats */<br>
+ anv_batch_emit(&cmd_buffer-><wbr>batch, GENX(PIPE_CONTROL), pc) {<br>
+ pc.CommandStreamerStallEnable = true;<br>
+ pc.StallAtPixelScoreboard = true;<br>
+ }<br>
+<br>
+ while (pipeline_statistics) {<br>
+ uint32_t stat = u_bit_scan(&pipeline_<wbr>statistics);<br>
<br>
- emit_query_availability(cmd_<wbr>buffer, &pool->bo,<br>
- query * sizeof(struct anv_query_pool_slot) + 16);<br>
+ emit_pipeline_stat(cmd_buffer, stat, &pool->bo, slot_offset + 8);<br>
+ slot_offset += sizeof(struct anv_query_pool_slot);<br>
+ }<br>
+<br>
+ slot_offset -= sizeof(struct anv_query_pool_slot);<br>
+ emit_query_availability(cmd_<wbr>buffer, &pool->bo, slot_offset + 16);<br>
break;<br>
+ }<br>
<br>
- case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS:<br>
default:<br>
unreachable("");<br>
}<br>
@@ -2421,6 +2501,31 @@ emit_load_alu_reg_u64(struct anv_batch *batch, uint32_t reg,<br>
}<br>
<br>
static void<br>
+emit_load_alu_reg_imm32(<wbr>struct anv_batch *batch, uint32_t reg, uint32_t imm)<br>
+{<br>
+ anv_batch_emit(batch, GENX(MI_LOAD_REGISTER_IMM), lri) {<br>
+ lri.RegisterOffset = reg;<br>
+ lri.DataDWord = imm;<br>
+ }<br>
+}<br>
+<br>
+static void<br>
+emit_load_alu_reg_imm64(<wbr>struct anv_batch *batch, uint32_t reg, uint64_t imm)<br>
+{<br>
+ emit_load_alu_reg_imm32(batch, reg, (uint32_t)imm);<br>
+ emit_load_alu_reg_imm32(batch, reg + 4, (uint32_t)(imm >> 32));<br>
+}<br>
+<br>
+static void<br>
+emit_load_alu_reg_reg32(<wbr>struct anv_batch *batch, uint32_t src, uint32_t dst)<br>
+{<br>
+ anv_batch_emit(batch, GENX(MI_LOAD_REGISTER_REG), lrr) {<br>
+ lrr.SourceRegisterAddress = src;<br>
+ lrr.DestinationRegisterAddress = dst;<br>
+ }<br>
+}<br>
+<br>
+static uint32_t<br>
store_query_result(struct anv_batch *batch, uint32_t reg,<br>
struct anv_bo *bo, uint32_t offset, VkQueryResultFlags flags)<br>
{<br>
@@ -2434,9 +2539,88 @@ store_query_result(struct anv_batch *batch, uint32_t reg,<br>
srm.RegisterAddress = reg + 4;<br>
srm.MemoryAddress = (struct anv_address) { bo, offset + 4 };<br>
}<br>
+<br>
+ return offset + 8;<br>
+ }<br>
+<br>
+ return offset + 4;<br>
+}<br>
+<br>
+/*<br>
+ * GPR0 = GPR0 & ((1ull << n) - 1);<br>
+ */<br>
+static void<br>
+keep_gpr0_lower_n_bits(struct anv_batch *batch, uint32_t n)<br>
+{<br>
+ assert(n < 64);<br>
+ emit_load_alu_reg_imm64(batch, CS_GPR(1), (1ull << n) - 1);<br>
+<br>
+ uint32_t *dw = anv_batch_emitn(batch, 5, GENX(MI_MATH));<br>
+ dw[1] = alu(OPCODE_LOAD, OPERAND_SRCA, OPERAND_R0);<br>
+ dw[2] = alu(OPCODE_LOAD, OPERAND_SRCB, OPERAND_R1);<br>
+ dw[3] = alu(OPCODE_AND, 0, 0);<br>
+ dw[4] = alu(OPCODE_STORE, OPERAND_R0, OPERAND_ACCU);<br>
+}<br>
+<br>
+/*<br>
+ * GPR0 = GPR0 << 30;<br>
+ */<br>
+static void<br>
+shl_gpr0_by_30_bits(struct anv_batch *batch)<br>
+{<br>
+ /* First we mask 34 bits of GPR0 to prevent overflow */<br>
+ keep_gpr0_lower_n_bits(batch, 34);<br>
+<br>
+ const uint32_t outer_count = 5;<br>
+ const uint32_t inner_count = 6;<br>
+ STATIC_ASSERT(outer_count * inner_count == 30);<br>
+ const uint32_t cmd_len = 1 + inner_count * 4;<br>
+<br>
+ /* We'll emit 5 commands, each shifting GPR0 left by 6 bits, for a total of<br>
+ * 30 left shifts.<br>
+ */<br>
+ for (int o = 0; o < outer_count; o++) {<br>
+ /* Submit one MI_MATH to shift left by 6 bits */<br>
+ uint32_t *dw = anv_batch_emitn(batch, cmd_len, GENX(MI_MATH));<br>
+ dw++;<br>
+ for (int i = 0; i < inner_count; i++, dw += 4) {<br>
+ dw[0] = alu(OPCODE_LOAD, OPERAND_SRCA, OPERAND_R0);<br>
+ dw[1] = alu(OPCODE_LOAD, OPERAND_SRCB, OPERAND_R0);<br>
+ dw[2] = alu(OPCODE_ADD, 0, 0);<br>
+ dw[3] = alu(OPCODE_STORE, OPERAND_R0, OPERAND_ACCU);<br>
+ }<br>
}<br>
}<br>
<br>
+/*<br>
+ * GPR0 = GPR0 >> 2;<br>
+ *<br>
+ * Note that the upper 30 bits of GPR are lost!<br>
+ */<br>
+static void<br>
+shr_gpr0_by_2_bits(struct anv_batch *batch)<br>
+{<br>
+ shl_gpr0_by_30_bits(batch);<br>
+ emit_load_alu_reg_reg32(batch, CS_GPR(0) + 4, CS_GPR(0));<br>
+ emit_load_alu_reg_imm32(batch, CS_GPR(0) + 4, 0);<br>
+}<br>
+<br>
+static void<br>
+compute_query_result(struct anv_batch *batch, struct anv_bo *bo,<br>
+ uint32_t dst_reg, uint32_t offset)<br>
+{<br>
+ emit_load_alu_reg_u64(batch, CS_GPR(0), bo, offset);<br>
+ emit_load_alu_reg_u64(batch, CS_GPR(1), bo, offset + 8);<br>
+<br>
+ /* FIXME: We need to clamp the result for 32 bit. */<br>
+<br>
+ uint32_t *dw = anv_batch_emitn(batch, 5, GENX(MI_MATH));<br>
+ dw[1] = alu(OPCODE_LOAD, OPERAND_SRCA, OPERAND_R1);<br>
+ dw[2] = alu(OPCODE_LOAD, OPERAND_SRCB, OPERAND_R0);<br>
+ dw[3] = alu(OPCODE_SUB, 0, 0);<br>
+ dw[4] = alu(OPCODE_STORE, dst_reg, OPERAND_ACCU);<br>
+}<br>
+<br>
void genX(CmdCopyQueryPoolResults)(<br>
VkCommandBuffer commandBuffer,<br>
VkQueryPool queryPool,<br>
@@ -2459,50 +2643,64 @@ void genX(CmdCopyQueryPoolResults)(<br>
}<br>
}<br>
<br>
- dst_offset = buffer->offset + destOffset;<br>
for (uint32_t i = 0; i < queryCount; i++) {<br>
-<br>
+ dst_offset = buffer->offset + destOffset + destStride * i;<br>
slot_offset = (firstQuery + i) * sizeof(struct anv_query_pool_slot);<br>
switch (pool->type) {<br>
case VK_QUERY_TYPE_OCCLUSION:<br>
- emit_load_alu_reg_u64(&cmd_<wbr>buffer->batch,<br>
- CS_GPR(0), &pool->bo, slot_offset);<br>
- emit_load_alu_reg_u64(&cmd_<wbr>buffer->batch,<br>
- CS_GPR(1), &pool->bo, slot_offset + 8);<br>
-<br>
- /* FIXME: We need to clamp the result for 32 bit. */<br>
-<br>
- uint32_t *dw = anv_batch_emitn(&cmd_buffer-><wbr>batch, 5, GENX(MI_MATH));<br>
- dw[1] = alu(OPCODE_LOAD, OPERAND_SRCA, OPERAND_R1);<br>
- dw[2] = alu(OPCODE_LOAD, OPERAND_SRCB, OPERAND_R0);<br>
- dw[3] = alu(OPCODE_SUB, 0, 0);<br>
- dw[4] = alu(OPCODE_STORE, OPERAND_R2, OPERAND_ACCU);<br>
+ compute_query_result(&cmd_<wbr>buffer->batch, &pool->bo, OPERAND_R2,<br>
+ slot_offset);<br>
+ dst_offset = store_query_result(<br>
+ &cmd_buffer->batch,<br>
+ CS_GPR(2), buffer->bo, dst_offset, flags);<br>
break;<br>
<br>
case VK_QUERY_TYPE_TIMESTAMP:<br>
emit_load_alu_reg_u64(&cmd_<wbr>buffer->batch,<br>
CS_GPR(2), &pool->bo, slot_offset);<br>
+ dst_offset = store_query_result(<br>
+ &cmd_buffer->batch,<br>
+ CS_GPR(2), buffer->bo, dst_offset, flags);<br>
break;<br>
<br>
+ case VK_QUERY_TYPE_PIPELINE_<wbr>STATISTICS: {<br>
+ uint32_t pipeline_statistics = pool->pipeline_statistics;<br>
+<br>
+ slot_offset *= pool->slot_stride;<br>
+ while (pipeline_statistics) {<br>
+ uint32_t stat = u_bit_scan(&pipeline_<wbr>statistics);<br>
+<br>
+ compute_query_result(&cmd_<wbr>buffer->batch, &pool->bo, OPERAND_R0,<br>
+ slot_offset);<br>
+<br>
+ /* WaDividePSInvocationCountBy4:<wbr>HSW,BDW */<br>
+ if ((cmd_buffer->device->info.gen == 8 ||<br>
+ cmd_buffer->device->info.is_<wbr>haswell) &&<br>
+ (1 << stat) == VK_QUERY_PIPELINE_STATISTIC_<wbr>FRAGMENT_SHADER_INVOCATIONS_<wbr>BIT) {<br>
+ shr_gpr0_by_2_bits(&cmd_<wbr>buffer->batch);<br>
+ }<br>
+ dst_offset = store_query_result(<br>
+ &cmd_buffer->batch,<br>
+ CS_GPR(0), buffer->bo, dst_offset, flags);<br>
+ slot_offset += sizeof(struct anv_query_pool_slot);<br>
+ }<br>
+<br>
+ /* Get the slot offset to where it's supposed to be for the<br>
+ * availability bit.<br>
+ */<br>
+ slot_offset -= sizeof(struct anv_query_pool_slot);<br>
+ break;<br>
+ }<br>
default:<br>
unreachable("unhandled query type");<br>
}<br>
<br>
- store_query_result(&cmd_<wbr>buffer->batch,<br>
- CS_GPR(2), buffer->bo, dst_offset, flags);<br>
-<br>
if (flags & VK_QUERY_RESULT_WITH_<wbr>AVAILABILITY_BIT) {<br>
emit_load_alu_reg_u64(&cmd_<wbr>buffer->batch, CS_GPR(0),<br>
&pool->bo, slot_offset + 16);<br>
- if (flags & VK_QUERY_RESULT_64_BIT)<br>
- store_query_result(&cmd_<wbr>buffer->batch,<br>
- CS_GPR(0), buffer->bo, dst_offset + 8, flags);<br>
- else<br>
- store_query_result(&cmd_<wbr>buffer->batch,<br>
- CS_GPR(0), buffer->bo, dst_offset + 4, flags);<br>
+ store_query_result(&cmd_<wbr>buffer->batch,<br>
+ CS_GPR(0), buffer->bo, dst_offset, flags);<br>
}<br>
-<br>
- dst_offset += destStride;<br>
}<br>
}<br></blockquote><div><br>I was a bit surprised to see us needing to go the same route as GL
query objects with using the command streamer's very limited ALU for
fixing up metrics on the gpu instead of doing that on the CPU if
possible.<br><br><div>My understanding had been that multiple HW
vendors pushed back on the design of these query apis to ensure they
could deal with counter scaling and normalization on the CPU without
needing to support direct feedback into the gpu. Reading the spec though
it does unfortunately look like vkCmdCopyQueryPoolResults implies
scaling on the gpu :-/<br><br></div><div>One thorny problem we have
today at least with GL is that we don't scale Skylake timestamps
properly when using the ALU considering the extra annoyance of scaling
by a floating point factor of 83.3333. In the future this scale factor
might ideally not even be a constant if we can take advantage of
training done in the kernel to determine a more accurate frequency (ref:
<a href="https://lists.freedesktop.org/archives/intel-gfx/2016-June/097272.html">https://lists.freedesktop.org/archives/intel-gfx/2016-June/097272.html</a>)
Also for reference here, there's this patch that looks at fixing the
timestamp scaling done on the cpu for SKL:
<a href="https://patchwork.freedesktop.org/patch/118855/">https://patchwork.freedesktop.org/patch/118855/</a> I understand you aren't
dealing with timestamp scaling here, but it seems like an a example of
where it's getting a bit out-of-hand using the ALU for this type of
purpose. <br><br></div><div>Can we maybe at least consider the complexity
trade-off from using a shader instead of ALU - or consider if there's a
legitimate way we're missing of using the cpu here (though I suppose not).<br><br></div><div>Br,<br></div><div>- Robert<br></div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
<span class="gmail-HOEnZb"><font color="#888888"><br>
--<br>
2.7.3<br>
<br>
______________________________<wbr>_________________<br>
mesa-dev mailing list<br>
<a href="mailto:mesa-dev@lists.freedesktop.org">mesa-dev@lists.freedesktop.org</a><br>
<a href="https://lists.freedesktop.org/mailman/listinfo/mesa-dev" rel="noreferrer" target="_blank">https://lists.freedesktop.org/<wbr>mailman/listinfo/mesa-dev</a><br>
</font></span></blockquote></div><br></div></div>