mesa/src/util/indices/u_primconvert.c

376 lines
15 KiB
C

/*
* Copyright (C) 2013 Rob Clark <robclark@freedesktop.org>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*
* Authors:
* Rob Clark <robclark@freedesktop.org>
*/
/**
* This module converts provides a more convenient front-end to u_indices,
* etc, utils to convert primitive types supported not supported by the
* hardware. It handles binding new index buffer state, and restoring
* previous state after. To use, put something like this at the front of
* drivers pipe->draw_vbo():
*
* // emulate unsupported primitives:
* if (info->mode needs emulating) {
* util_primconvert_save_rasterizer_state(ctx->primconvert, ctx->rasterizer);
* util_primconvert_draw_vbo(ctx->primconvert, info);
* return;
* }
*
*/
#include "pipe/p_state.h"
#include "util/u_draw.h"
#include "util/u_inlines.h"
#include "util/u_memory.h"
#include "util/u_prim.h"
#include "util/u_prim_restart.h"
#include "util/u_upload_mgr.h"
#include "indices/u_indices.h"
#include "indices/u_primconvert.h"
struct primconvert_context
{
struct pipe_context *pipe;
struct primconvert_config cfg;
unsigned api_pv;
};
struct primconvert_context *
util_primconvert_create_config(struct pipe_context *pipe,
struct primconvert_config *cfg)
{
struct primconvert_context *pc = CALLOC_STRUCT(primconvert_context);
if (!pc)
return NULL;
pc->pipe = pipe;
pc->cfg = *cfg;
return pc;
}
struct primconvert_context *
util_primconvert_create(struct pipe_context *pipe, uint32_t primtypes_mask)
{
struct primconvert_config cfg = { .primtypes_mask = primtypes_mask, .restart_primtypes_mask = primtypes_mask };
return util_primconvert_create_config(pipe, &cfg);
}
void
util_primconvert_destroy(struct primconvert_context *pc)
{
FREE(pc);
}
void
util_primconvert_save_rasterizer_state(struct primconvert_context *pc,
const struct pipe_rasterizer_state
*rast)
{
util_primconvert_save_flatshade_first(pc, rast->flatshade_first);
}
void
util_primconvert_save_flatshade_first(struct primconvert_context *pc, bool flatshade_first)
{
/* if we actually translated the provoking vertex for the buffer,
* we would actually need to save/restore rasterizer state. As
* it is, we just need to make note of the pv.
*/
pc->api_pv = flatshade_first ? PV_FIRST : PV_LAST;
}
static bool
primconvert_init_draw(struct primconvert_context *pc,
const struct pipe_draw_info *info,
const struct pipe_draw_start_count_bias *draws,
struct pipe_draw_info *new_info,
struct pipe_draw_start_count_bias *new_draw)
{
struct pipe_draw_start_count_bias *direct_draws = NULL;
unsigned num_direct_draws = 0;
struct pipe_transfer *src_transfer = NULL;
u_translate_func trans_func, direct_draw_func;
u_generate_func gen_func;
const void *src = NULL;
void *dst;
unsigned ib_offset;
unsigned total_index_count = draws->count;
void *rewrite_buffer = NULL;
struct pipe_draw_start_count_bias draw = draws[0];
/* Filter out degenerate primitives, u_upload_alloc() will assert
* on size==0 so just bail:
*/
if (!info->primitive_restart &&
!u_trim_pipe_prim(info->mode, (unsigned*)&draw.count))
return false;
util_draw_init_info(new_info);
new_info->index_bounds_valid = info->index_bounds_valid;
new_info->min_index = info->min_index;
new_info->max_index = info->max_index;
new_info->start_instance = info->start_instance;
new_info->instance_count = info->instance_count;
new_info->primitive_restart = info->primitive_restart;
new_info->restart_index = info->restart_index;
if (info->index_size) {
enum pipe_prim_type mode = new_info->mode = u_index_prim_type_convert(pc->cfg.primtypes_mask, info->mode, true);
unsigned index_size = info->index_size;
unsigned offset = draw.start * info->index_size;
new_info->index_size = u_index_size_convert(info->index_size);
src = info->has_user_indices ? info->index.user : NULL;
if (!src) {
/* Map the index range we're interested in (not the whole buffer) */
src = pipe_buffer_map_range(pc->pipe, info->index.resource,
offset,
draw.count * info->index_size,
PIPE_MAP_READ, &src_transfer);
offset = 0;
draw.start = 0;
}
const void *restart_src = (const uint8_t *)src + offset;
/* if the resulting primitive type is not supported by the driver for primitive restart,
* or if the original primitive type was not supported by the driver,
* the draw needs to be rewritten to not use primitive restart
*/
if (info->primitive_restart &&
(!(pc->cfg.restart_primtypes_mask & BITFIELD_BIT(mode)) ||
!(pc->cfg.primtypes_mask & BITFIELD_BIT(info->mode)))) {
/* step 1: rewrite draw to not use primitive primitive restart;
* this pre-filters degenerate primitives
*/
direct_draws = util_prim_restart_convert_to_direct(restart_src, info, &draw, &num_direct_draws,
&new_info->min_index, &new_info->max_index, &total_index_count);
new_info->primitive_restart = false;
/* step 2: get a translator function which does nothing but handle any index size conversions
* which may or may not occur (8bit -> 16bit)
*/
u_index_translator(0xffff,
info->mode, index_size, total_index_count,
pc->api_pv, pc->api_pv,
PR_DISABLE,
&mode, &index_size, &new_draw->count,
&direct_draw_func);
/* this should always be a direct translation */
assert(new_draw->count == total_index_count);
/* step 3: allocate a temp buffer for an intermediate rewrite step
* if no indices were found, this was a single incomplete restart and can be discarded
*/
if (total_index_count)
rewrite_buffer = malloc(index_size * total_index_count);
if (!rewrite_buffer) {
if (src_transfer)
pipe_buffer_unmap(pc->pipe, src_transfer);
return false;
}
}
/* (step 4: get the actual primitive conversion translator function) */
u_index_translator(pc->cfg.primtypes_mask,
info->mode, index_size, total_index_count,
pc->api_pv, pc->api_pv,
new_info->primitive_restart ? PR_ENABLE : PR_DISABLE,
&mode, &index_size, &new_draw->count,
&trans_func);
assert(new_info->mode == mode);
assert(new_info->index_size == index_size);
}
else {
enum pipe_prim_type mode = 0;
unsigned index_size;
u_index_generator(pc->cfg.primtypes_mask,
info->mode, draw.start, draw.count,
pc->api_pv, pc->api_pv,
&mode, &index_size, &new_draw->count,
&gen_func);
new_info->mode = mode;
new_info->index_size = index_size;
}
/* (step 5: allocate gpu memory sized for the FINAL index count) */
u_upload_alloc(pc->pipe->stream_uploader, 0, new_info->index_size * new_draw->count, 4,
&ib_offset, &new_info->index.resource, &dst);
new_draw->start = ib_offset / new_info->index_size;
new_draw->index_bias = info->index_size ? draw.index_bias : 0;
if (info->index_size) {
if (num_direct_draws) {
uint8_t *ptr = rewrite_buffer;
uint8_t *dst_ptr = dst;
/* step 6: if rewriting a prim-restart draw to direct draws,
* loop over all the direct draws in order to rewrite them into a single index buffer
* and draw in order to match the original call
*/
for (unsigned i = 0; i < num_direct_draws; i++) {
/* step 6a: get the index count for this draw, once converted */
unsigned tmp_count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, direct_draws[i].count);
/* step 6b: handle index size conversion using the temp buffer; no change in index count
* TODO: this step can be optimized out if the index size is known to not change
*/
direct_draw_func(src, direct_draws[i].start, direct_draws[i].count, direct_draws[i].count, info->restart_index, ptr);
/* step 6c: handle the primitive type conversion rewriting to the converted index count */
trans_func(ptr, 0, direct_draws[i].count, tmp_count, info->restart_index, dst_ptr);
/* step 6d: increment the temp buffer and mapped final index buffer pointers */
ptr += new_info->index_size * direct_draws[i].count;
dst_ptr += new_info->index_size * tmp_count;
}
/* step 7: set the final index count, which is the converted total index count from the original draw rewrite */
new_draw->count = u_index_count_converted_indices(pc->cfg.primtypes_mask, true, info->mode, total_index_count);
} else
trans_func(src, draw.start, draw.count, new_draw->count, info->restart_index, dst);
if (pc->cfg.fixed_prim_restart && new_info->primitive_restart) {
new_info->restart_index = (1ull << (new_info->index_size * 8)) - 1;
if (info->restart_index != new_info->restart_index)
util_translate_prim_restart_data(new_info->index_size, dst, dst,
new_draw->count,
info->restart_index);
}
}
else {
gen_func(draw.start, new_draw->count, dst);
}
new_info->was_line_loop = info->mode == PIPE_PRIM_LINE_LOOP;
if (src_transfer)
pipe_buffer_unmap(pc->pipe, src_transfer);
u_upload_unmap(pc->pipe->stream_uploader);
free(direct_draws);
free(rewrite_buffer);
return true;
}
static void
util_primconvert_draw_single_vbo(struct primconvert_context *pc,
const struct pipe_draw_info *info,
unsigned drawid_offset,
const struct pipe_draw_start_count_bias *draw)
{
struct pipe_draw_info new_info;
struct pipe_draw_start_count_bias new_draw;
if (!primconvert_init_draw(pc, info, draw, &new_info, &new_draw))
return;
/* to the translated draw: */
pc->pipe->draw_vbo(pc->pipe, &new_info, drawid_offset, NULL, &new_draw, 1);
pipe_resource_reference(&new_info.index.resource, NULL);
}
void
util_primconvert_draw_vbo(struct primconvert_context *pc,
const struct pipe_draw_info *info,
unsigned drawid_offset,
const struct pipe_draw_indirect_info *indirect,
const struct pipe_draw_start_count_bias *draws,
unsigned num_draws)
{
if (indirect && indirect->buffer) {
/* this is stupid, but we're already doing a readback,
* so this thing may as well get the rest of the job done
*/
unsigned draw_count = 0;
struct u_indirect_params *new_draws = util_draw_indirect_read(pc->pipe, info, indirect, &draw_count);
if (!new_draws)
goto cleanup;
for (unsigned i = 0; i < draw_count; i++)
util_primconvert_draw_single_vbo(pc, &new_draws[i].info, drawid_offset + i, &new_draws[i].draw);
free(new_draws);
} else {
unsigned drawid = drawid_offset;
for (unsigned i = 0; i < num_draws; i++) {
if (draws[i].count && info->instance_count)
util_primconvert_draw_single_vbo(pc, info, drawid, &draws[i]);
if (info->increment_draw_id)
drawid++;
}
}
cleanup:
if (info->take_index_buffer_ownership) {
struct pipe_resource *buffer = info->index.resource;
pipe_resource_reference(&buffer, NULL);
}
}
void
util_primconvert_draw_vertex_state(struct primconvert_context *pc,
struct pipe_vertex_state *vstate,
uint32_t partial_velem_mask,
struct pipe_draw_vertex_state_info info,
const struct pipe_draw_start_count_bias *draws,
unsigned num_draws)
{
struct pipe_draw_info new_info;
struct pipe_draw_start_count_bias new_draw;
if (pc->cfg.primtypes_mask & BITFIELD_BIT(info.mode)) {
pc->pipe->draw_vertex_state(pc->pipe, vstate, partial_velem_mask, info, draws, num_draws);
return;
}
if (num_draws > 1) {
for (unsigned i = 0; i < num_draws; i++) {
if (draws[i].count)
util_primconvert_draw_vertex_state(pc, vstate, partial_velem_mask, info, &draws[i], 1);
}
return;
}
struct pipe_draw_info dinfo = {0};
dinfo.mode = info.mode;
dinfo.index_size = 4;
dinfo.instance_count = 1;
dinfo.index.resource = vstate->input.indexbuf;
if (!primconvert_init_draw(pc, &dinfo, draws, &new_info, &new_draw))
return;
struct pipe_vertex_state *new_state = pc->pipe->screen->create_vertex_state(pc->pipe->screen,
&vstate->input.vbuffer,
vstate->input.elements,
vstate->input.num_elements,
new_info.index.resource,
vstate->input.full_velem_mask);
if (new_state) {
struct pipe_draw_vertex_state_info new_vinfo;
new_vinfo.mode = new_info.mode;
new_vinfo.take_vertex_state_ownership = true;
/* to the translated draw: */
pc->pipe->draw_vertex_state(pc->pipe, new_state, partial_velem_mask, new_vinfo, &new_draw, 1);
}
if (info.take_vertex_state_ownership)
pipe_vertex_state_reference(&vstate, NULL);
pipe_resource_reference(&new_info.index.resource, NULL);
}