blob: 10cb9b0750f2f055dda9900711e453edd1d50df9 [file] [log] [blame]
#include "zink_query.h"
#include "zink_context.h"
#include "zink_resource.h"
#include "zink_screen.h"
#include "util/u_dump.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
struct zink_query {
enum pipe_query_type type;
VkQueryPool query_pool;
unsigned curr_query, num_queries;
VkQueryType vkqtype;
unsigned index;
bool use_64bit;
bool precise;
struct list_head active_list;
};
static VkQueryType
convert_query_type(unsigned query_type, bool *use_64bit, bool *precise)
{
*use_64bit = false;
*precise = false;
switch (query_type) {
case PIPE_QUERY_OCCLUSION_COUNTER:
*precise = true;
*use_64bit = true;
case PIPE_QUERY_OCCLUSION_PREDICATE:
case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
return VK_QUERY_TYPE_OCCLUSION;
case PIPE_QUERY_TIMESTAMP:
*use_64bit = true;
return VK_QUERY_TYPE_TIMESTAMP;
case PIPE_QUERY_PIPELINE_STATISTICS:
case PIPE_QUERY_PRIMITIVES_GENERATED:
return VK_QUERY_TYPE_PIPELINE_STATISTICS;
case PIPE_QUERY_PRIMITIVES_EMITTED:
*use_64bit = true;
return VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT;
default:
debug_printf("unknown query: %s\n",
util_str_query_type(query_type, true));
unreachable("zink: unknown query type");
}
}
static struct pipe_query *
zink_create_query(struct pipe_context *pctx,
unsigned query_type, unsigned index)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_query *query = CALLOC_STRUCT(zink_query);
VkQueryPoolCreateInfo pool_create = {};
if (!query)
return NULL;
query->index = index;
query->type = query_type;
query->vkqtype = convert_query_type(query_type, &query->use_64bit, &query->precise);
if (query->vkqtype == -1)
return NULL;
query->num_queries = query_type == PIPE_QUERY_TIMESTAMP ? 1 : 100;
query->curr_query = 0;
pool_create.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO;
pool_create.queryType = query->vkqtype;
pool_create.queryCount = query->num_queries;
if (query_type == PIPE_QUERY_PRIMITIVES_GENERATED)
pool_create.pipelineStatistics = VK_QUERY_PIPELINE_STATISTIC_INPUT_ASSEMBLY_PRIMITIVES_BIT;
VkResult status = vkCreateQueryPool(screen->dev, &pool_create, NULL, &query->query_pool);
if (status != VK_SUCCESS) {
FREE(query);
return NULL;
}
return (struct pipe_query *)query;
}
/* TODO: rework this to be less hammer-ish using deferred destroy */
static void
wait_query(struct pipe_context *pctx, struct zink_query *query)
{
struct pipe_fence_handle *fence = NULL;
pctx->flush(pctx, &fence, PIPE_FLUSH_HINT_FINISH);
if (fence) {
pctx->screen->fence_finish(pctx->screen, NULL, fence,
PIPE_TIMEOUT_INFINITE);
pctx->screen->fence_reference(pctx->screen, &fence, NULL);
}
}
static void
zink_destroy_query(struct pipe_context *pctx,
struct pipe_query *q)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_query *query = (struct zink_query *)q;
if (!list_is_empty(&query->active_list)) {
wait_query(pctx, query);
}
vkDestroyQueryPool(screen->dev, query->query_pool, NULL);
FREE(query);
}
static void
begin_query(struct zink_context *ctx, struct zink_query *q)
{
VkQueryControlFlags flags = 0;
struct zink_batch *batch = zink_curr_batch(ctx);
if (q->precise)
flags |= VK_QUERY_CONTROL_PRECISE_BIT;
if (q->vkqtype == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
zink_screen(ctx->base.screen)->vk_CmdBeginQueryIndexedEXT(batch->cmdbuf,
q->query_pool,
q->curr_query,
flags,
q->index);
else
vkCmdBeginQuery(batch->cmdbuf, q->query_pool, q->curr_query, flags);
}
static bool
zink_begin_query(struct pipe_context *pctx,
struct pipe_query *q)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_query *query = (struct zink_query *)q;
/* ignore begin_query for timestamps */
if (query->type == PIPE_QUERY_TIMESTAMP)
return true;
/* TODO: resetting on begin isn't ideal, as it forces render-pass exit...
* should instead reset on creation (if possible?)... Or perhaps maintain
* the pool in the batch instead?
*/
struct zink_batch *batch = zink_batch_no_rp(zink_context(pctx));
vkCmdResetQueryPool(batch->cmdbuf, query->query_pool, 0, MIN2(query->curr_query + 1, query->num_queries));
query->curr_query = 0;
begin_query(ctx, query);
list_addtail(&query->active_list, &ctx->active_queries);
return true;
}
static void
end_query(struct zink_context *ctx, struct zink_query *q)
{
struct zink_screen *screen = zink_screen(ctx->base.screen);
struct zink_batch *batch = zink_curr_batch(ctx);
assert(q->type != PIPE_QUERY_TIMESTAMP);
if (q->vkqtype == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT)
screen->vk_CmdEndQueryIndexedEXT(batch->cmdbuf, q->query_pool, q->curr_query, q->index);
else
vkCmdEndQuery(batch->cmdbuf, q->query_pool, q->curr_query);
if (++q->curr_query == q->num_queries) {
assert(0);
/* need to reset pool! */
}
}
static bool
zink_end_query(struct pipe_context *pctx,
struct pipe_query *q)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_query *query = (struct zink_query *)q;
if (query->type == PIPE_QUERY_TIMESTAMP) {
assert(query->curr_query == 0);
struct zink_batch *batch = zink_curr_batch(ctx);
vkCmdWriteTimestamp(batch->cmdbuf, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
query->query_pool, 0);
} else {
end_query(ctx, query);
list_delinit(&query->active_list);
}
return true;
}
static bool
zink_get_query_result(struct pipe_context *pctx,
struct pipe_query *q,
bool wait,
union pipe_query_result *result)
{
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_query *query = (struct zink_query *)q;
VkQueryResultFlagBits flags = 0;
if (wait) {
wait_query(pctx, query);
flags |= VK_QUERY_RESULT_WAIT_BIT;
} else
pctx->flush(pctx, NULL, 0);
if (query->use_64bit)
flags |= VK_QUERY_RESULT_64_BIT;
// TODO: handle curr_query > 100
// union pipe_query_result results[100];
uint64_t results[100];
memset(results, 0, sizeof(results));
int num_results;
if (query->vkqtype == VK_QUERY_TYPE_TRANSFORM_FEEDBACK_STREAM_EXT) {
char tf_result[16] = {};
/* this query emits 2 values */
assert(query->curr_query <= ARRAY_SIZE(results) / 2);
num_results = query->curr_query * 2;
VkResult status = vkGetQueryPoolResults(screen->dev, query->query_pool,
0, query->curr_query,
sizeof(results),
results,
sizeof(uint64_t),
flags);
if (status != VK_SUCCESS)
return false;
memcpy(result, tf_result + (query->type == PIPE_QUERY_PRIMITIVES_GENERATED ? 8 : 0), 8);
} else {
assert(query->curr_query <= ARRAY_SIZE(results));
num_results = query->curr_query;
VkResult status = vkGetQueryPoolResults(screen->dev, query->query_pool,
0, query->curr_query,
sizeof(results),
results,
sizeof(uint64_t),
flags);
if (status != VK_SUCCESS)
return false;
}
util_query_clear_result(result, query->type);
for (int i = 0; i < num_results; ++i) {
switch (query->type) {
case PIPE_QUERY_OCCLUSION_PREDICATE:
case PIPE_QUERY_OCCLUSION_PREDICATE_CONSERVATIVE:
case PIPE_QUERY_SO_OVERFLOW_PREDICATE:
case PIPE_QUERY_SO_OVERFLOW_ANY_PREDICATE:
case PIPE_QUERY_GPU_FINISHED:
result->b |= results[i] != 0;
break;
case PIPE_QUERY_OCCLUSION_COUNTER:
result->u64 += results[i];
break;
case PIPE_QUERY_PRIMITIVES_GENERATED:
result->u32 += results[i];
break;
case PIPE_QUERY_PRIMITIVES_EMITTED:
/* A query pool created with this type will capture 2 integers -
* numPrimitivesWritten and numPrimitivesNeeded -
* for the specified vertex stream output from the last vertex processing stage.
* - from VK_EXT_transform_feedback spec
*/
result->u64 += results[i];
i++;
break;
default:
debug_printf("unhangled query type: %s\n",
util_str_query_type(query->type, true));
unreachable("unexpected query type");
}
}
return TRUE;
}
void
zink_suspend_queries(struct zink_context *ctx, struct zink_batch *batch)
{
struct zink_query *query;
LIST_FOR_EACH_ENTRY(query, &ctx->active_queries, active_list) {
end_query(ctx, query);
}
}
void
zink_resume_queries(struct zink_context *ctx, struct zink_batch *batch)
{
struct zink_query *query;
LIST_FOR_EACH_ENTRY(query, &ctx->active_queries, active_list) {
vkCmdResetQueryPool(batch->cmdbuf, query->query_pool, query->curr_query, 1);
begin_query(ctx, query);
}
}
static void
zink_set_active_query_state(struct pipe_context *pctx, bool enable)
{
struct zink_context *ctx = zink_context(pctx);
ctx->queries_disabled = !enable;
struct zink_batch *batch = zink_curr_batch(ctx);
if (ctx->queries_disabled)
zink_suspend_queries(ctx, batch);
else
zink_resume_queries(ctx, batch);
}
static void
zink_render_condition(struct pipe_context *pctx,
struct pipe_query *pquery,
bool condition,
enum pipe_render_cond_flag mode)
{
struct zink_context *ctx = zink_context(pctx);
struct zink_screen *screen = zink_screen(pctx->screen);
struct zink_query *query = (struct zink_query *)pquery;
struct zink_batch *batch = zink_curr_batch(ctx);
VkQueryResultFlagBits flags = 0;
if (query == NULL) {
screen->vk_CmdEndConditionalRenderingEXT(batch->cmdbuf);
return;
}
struct pipe_resource *pres;
struct zink_resource *res;
struct pipe_resource templ = {};
templ.width0 = 8;
templ.height0 = 1;
templ.depth0 = 1;
templ.format = PIPE_FORMAT_R8_UINT;
templ.target = PIPE_BUFFER;
/* need to create a vulkan buffer to copy the data into */
pres = pctx->screen->resource_create(pctx->screen, &templ);
if (!pres)
return;
res = (struct zink_resource *)pres;
if (mode == PIPE_RENDER_COND_WAIT || mode == PIPE_RENDER_COND_BY_REGION_WAIT)
flags |= VK_QUERY_RESULT_WAIT_BIT;
if (query->use_64bit)
flags |= VK_QUERY_RESULT_64_BIT;
vkCmdCopyQueryPoolResults(batch->cmdbuf, query->query_pool, 0, 1,
res->buffer, 0, 0, flags);
VkConditionalRenderingFlagsEXT begin_flags = 0;
if (condition)
begin_flags = VK_CONDITIONAL_RENDERING_INVERTED_BIT_EXT;
VkConditionalRenderingBeginInfoEXT begin_info = {};
begin_info.sType = VK_STRUCTURE_TYPE_CONDITIONAL_RENDERING_BEGIN_INFO_EXT;
begin_info.buffer = res->buffer;
begin_info.flags = begin_flags;
screen->vk_CmdBeginConditionalRenderingEXT(batch->cmdbuf, &begin_info);
zink_batch_reference_resoure(batch, res);
pipe_resource_reference(&pres, NULL);
}
void
zink_context_query_init(struct pipe_context *pctx)
{
struct zink_context *ctx = zink_context(pctx);
list_inithead(&ctx->active_queries);
pctx->create_query = zink_create_query;
pctx->destroy_query = zink_destroy_query;
pctx->begin_query = zink_begin_query;
pctx->end_query = zink_end_query;
pctx->get_query_result = zink_get_query_result;
pctx->set_active_query_state = zink_set_active_query_state;
pctx->render_condition = zink_render_condition;
}